如何使用 Node.js 代码下载 Github issue 到本地

国内的开发者们,有使用 github 上开源的代码仓库上通过提交 issue 的方式进行一些技术或者非技术层面的讨论。

由于众所周知的原因,有些代码仓库并不总是可用,有时候会因为一些不可抗力的原因被删除。

此时就有了将 Github Repository 上某些有价值的 issue 的内容,保存到本地永久存储起来,比如以 text 文本文件的格式存储。

采用手动方式一个 issue 一个 issue 备份的方式比较愚蠢,因此我们可以动手写一个备份工具。

本文笔者选用了 Node.js 作为开发工具。

我们首先找一个例子,比如这个 Github issue:

github.com/SAP/openui5...

浏览器打开之后,issue 内容如下:

要想使用 Node.js 将这个 issue 的内容下载到本地,我们需要使用 Github API 这个工具。

GitHub API 是一种用于与 GitHub 平台交互的编程接口,其实也就是一组 Restful API 的集合啦,它允许开发者通过编写代码来访问 GitHub 上的资源和数据。

https://api.github.com/repos/ 是其中一个重要的端点,主要用于操作 GitHub 上的代码仓库资源。

API 的核心功能是为用户提供一种以编程方式访问和操作 GitHub 平台数据的方法,而不是通过用户界面手动操作。例如,通过 API,开发者可以获取仓库的详细信息,创建新仓库,修改现有仓库的设置,甚至删除仓库。

GitHub API 的 repos 端点主要围绕代码仓库展开,支持多种操作。这个端点的基本结构如下:

sh 复制代码
https://api.github.com/repos/{owner}/{repo}

其中,{owner} 是仓库所有者的用户名或组织名,{repo} 是具体的仓库名称。例如:

ruby 复制代码
https://api.github.com/repos/octocat/Hello-World

这个 URL 用于访问 octocat 用户的 Hello-World 仓库。

本文要使用的 API 是下面这个 API endpoint:

https://api.github.com/repos/{owner}/{repo}/issues:允许开发者获取仓库中的公开问题,包括问题标题、状态、作者和标签等信息。

GitHub API 实施速率限制,默认每小时允许 5000 次认证请求和 60 次未认证请求。如果用户的请求频率过高,可以使用 X-RateLimit-Remaining 标头查看剩余请求额度。

我们使用 Node.js 里的 https 工具库,来调用 Github API.

https 模块是 Node.js 内置的工具库,专门用于处理 HTTPS(Hyper Text Transfer Protocol Secure)协议。HTTPS 是 HTTP 的安全版本,利用 SSL/TLS 协议对传输内容进行加密,确保数据的保密性、完整性和真实性。在现代网络中,HTTPS 被广泛用于保护用户隐私,特别是在处理敏感数据(如密码、信用卡号)时。

我们想下载的 Github issue 的原始 url:

https://github.com/SAP/openui5/issues/4176

转换成对应的 Github API url 为:https://api.github.com/repos/SAP/openui5/issues/4176

浏览器里访问这个 url,得到 API response 如下:

所以这个工具的开发步骤如下。

1. 获取用户输入待下载的 Github url

这里我们使用 Node.js 工具库 readline 来解析用户输入。这个工具库专门用于处理命令行交互。它的功能主要包括从可读流中逐行读取数据,以及通过接口实现用户与程序之间的交互。对于构建命令行工具、接受用户输入或处理标准输入输出的数据流,readline 是一个非常强大的工具。

2. 通过正则表达式,将用户输入的 github url,替换成 github API 接受的 url:

3. 调用 https 向 Github API endpoint 发送请求,并解析结果

我把逻辑封装在函数 fetchGithubIssues 里,并添加了相应的错误处理。

本例完整的代码在文末。

我们将所有的逻辑实现在 download.js 文件里,然后用命令行 node download.js 启动这个 Node.js 应用。

执行之后,输入要下载的 Github issue url,工具就会打印出实际的 Github API url,并将该 issue 的内容通过 https 读取到本地,另存为 text 文件。

打开本地文件,发现下载成功:

本文完整的源代码:

javascript 复制代码
// 引入必要的库
const https = require('https');
const fs = require('fs');
const path = require('path');
const readline = require('readline');

// 创建一个函数用来下载 Github Issue 的内容
function fetchGithubIssue(issueUrl, outputFilePath) {
    // 验证 URL 格式
    const urlPattern = /^https:\/\/github\.com\/[^\/]+\/[^\/]+\/issues\/\d+$/;
    if (!urlPattern.test(issueUrl)) {
        console.error('请输入有效的 Github Issue URL,例如:https://github.com/owner/repo/issues/1');
        process.exit(1);
    }

    // 提取 API URL
    const apiUrl = issueUrl.replace('https://github.com/', 'https://api.github.com/repos/').replace('/issues/', '/issues/');

    // 配置请求头
    const options = {
        headers: {
            'User-Agent': 'Node.js Application',
            'Accept': 'application/vnd.github.v3+json',
        },
    };

    // 发送 HTTPS 请求获取 Issue 数据
    https.get(apiUrl, options, (res) => {
        let data = '';

        if (res.statusCode !== 200) {
            console.error(`请求失败,状态码: ${res.statusCode}`);
            res.resume();
            return;
        }

        res.on('data', (chunk) => {
            data += chunk;
        });

        res.on('end', () => {
            try {
                const issue = JSON.parse(data);

                const content = `Issue Title: ${issue.title}\n\n` +
                                `Issue Body:\n${issue.body || '无内容'}\n\n` +
                                `URL: ${issueUrl}\n`;

                // 将内容保存到指定文件
                fs.writeFileSync(outputFilePath, content, 'utf-8');
                console.log(`Issue 已成功保存到 ${outputFilePath}`);
            } catch (err) {
                console.error('解析响应数据时出错:', err.message);
            }
        });
    }).on('error', (err) => {
        console.error('请求过程中出错:', err.message);
    });
}

// 创建命令行接口
const rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout,
});

rl.question('请输入 Github Issue 的 URL: ', (url) => {
    const outputFilePath = path.resolve(__dirname, 'issue.txt');
    fetchGithubIssue(url, outputFilePath);
    rl.close();
});
相关推荐
SimonKing16 分钟前
Mysql分页:高效处理海量数据的核心技术
java·后端·程序员
洛卡卡了1 小时前
面试官问限流降级,我项目根本没做过,咋办?
后端·面试·架构
ezl1fe1 小时前
RAG 每日一技(十四):化繁为简,统揽全局——用LangChain构建高级RAG流程
人工智能·后端·算法
amazingCompass1 小时前
Java 开发必备技能:深入理解与实战 IntelliJ IDEA 中的 VM Options
后端
欧的曼2 小时前
cygwin+php教程(swoole扩展+redis扩展)
开发语言·redis·后端·mysql·nginx·php·swoole
巴拉巴巴巴拉2 小时前
Spring Boot 整合 Thymeleaf
java·spring boot·后端
一叶怎知秋2 小时前
【openlayers框架学习】九:openlayers中的交互类(select和draw)
前端·javascript·笔记·学习·交互
用户1512905452202 小时前
Docker部署 Alist
后端
白应穷奇2 小时前
Diesel的高性能特性: 深入理解Rust ORM的性能优化
后端·rust
用户1512905452202 小时前
HDOJ-ACM1017(JAVA)
后端