实践指南:通过 GitHub Actions 脚本自动化批量关闭 issues,大幅提升开源仓库维护效率。
那为什么我想这样做呢?🤔
好吧,我们有一个私有仓库,在这个仓库中维护者可以提交请求加入私人维护者社区。
有人想到了一个"妙招",向仓库提交了 1000+ 个 issue,并用 @undefined 作为处理人,标题也是 undefined。这是个问题,因为这样很难找到实际由需要被批准的维护者提交的 issue。
在我能够向 issue 表单添加验证之前,还需要对仓库进行一些额外的工作,但与此同时,我们需要关闭这些 issue。所以,我想到了一个临时方案——创建一个 GitHub Action 来关闭所有标题包含"Pending invitation request for: @undefined"的 issue。而且成功了!!
我们用了几分钟的时间,就从拥有 1,600+ 个 issue 降到了 64 个有效 issue。
我做的第一件事是找我的好朋友 chatty(GitHub Copilot Chat)帮忙,问它我是否可以用 GitHub Action 关闭 1000+ 个 issue。它给了我几个选项,但我决定选择这个方案:
然后它生成了 closeIssue.js 文件,看起来像这样:
我对代码做了一些微调,但总的来说,这段代码主要是与 GitHub Copilot 合作编写的。
我创建了一个文件名为 close_issues.yml 的文件,放在 .github/workflows 目录下,并添加了以下代码:
name: Close Undefined Issues
on:
workflow_dispatch:
push:
branches: [ "main" ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ "main" ]
schedule:
- cron: '43 0 * * 1'
jobs:
close_issues:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Setup Node.js
uses: actions/setup-node@v2
with:
node-version: 14
- name: Install dependencies
run: npm install
- name: Run script
run: node closeIssues.js
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
接下来,我创建了 closeIssues.js 文件。我添加了以下代码:
import { Octokit } from "@octokit/rest";
const octokit = new Octokit({
auth: process.env.GITHUB_TOKEN,
});
async function closeIssues() {
const { data: issues } = await octokit.issues.listForRepo({
owner: 'OWNER',
repo: 'internal-repo',
});
const issuesToClose = issues.filter(issue => issue.title === 'Pending invitation request for: @undefined');
for (const issue of issuesToClose) {
await octokit.issues.update({
owner: 'OWNER',
repo: 'internal-repo',
issue_number: issue.number,
state: 'closed',
});
}
}
closeIssues().catch(console.error);
我向仓库添加了一个 package.json 文件,安装了所需的依赖项,然后将代码推送到了仓库。
大概尝试了 3 次(好吧,是 4 次!)之后,我终于让 Action 成功运行了。
最初,这个 Action 一次关闭 30 个 issue,因为这是 GitHub API 的限制。所以我不得不读文档 🤯 来理解如何获取所有匹配条件的 issue 并关闭它们。
读完文档后,我意识到我需要使用 paginate() 方法来获取指定仓库的所有 issue。我还了解到,使用 paginate 时,issue 会以数组的形式返回,所以我不再需要 const { data: issues }。我还需要在发起请求时使用 octokit.rest.issues。
最后,成功运行并一次性关闭了 1000+ 个 issue 的代码看起来像这样:
import { Octokit } from "@octokit/rest";
const octokit = new Octokit({
auth: process.env.GITHUB_TOKEN,
});
async function closeIssues() {
try {
console.log('Fetching issues...');
const issues = await octokit.paginate(octokit.rest.issues.listForRepo, {
owner: 'OWNER',
repo: 'internal-repo',
state: 'open',
});
console.log(`Fetched ${issues.length} issues.`);
const issuesToClose = issues.filter(issue => issue.title === 'Pending invitation request for: @undefined');
console.log(`Found ${issuesToClose.length} issues to close.`);
for (const issue of issuesToClose) {
console.log(`Closing issue #${issue.number}...`);
await octokit.rest.issues.update({
owner: 'OWNER',
repo: 'internal-repo',
issue_number: issue.number,
state: 'closed',
});
console.log(`Closed issue #${issue.number}.`);
}
} catch (error) {
console.error(error);
}
}
closeIssues();
我在代码中添加了一些日志,这样我就可以看到工作流运行时发生了什么。我还添加了一个 try/catch 块来捕获可能出现的任何错误。
我为此感到非常高兴,因为让我的队友手动关闭所有这些 issue 需要花费几个小时。而我只用了几分钟就完成了!我很兴奋 GitHub Copilot 能够教我一些新东西——如何使用 Octokit 库与 GitHub API 交互。🚀
你曾经使用过 GitHub Copilot 来帮你编写 GitHub Actions 吗?在下方评论区告诉我!我希望你今天学到了一些新东西。
下次再见,编码愉快!😁
某些评论可能仅对已登录的访客可见。登录查看全部评论。
如需进一步的操作,你可以考虑屏蔽此人和/或举报滥用。