「这是我参与2022首次更文挑战的第27天,活动详情查看:2022首次更文挑战」。
我喜欢使用GitHub Actions。它们易于使用但功能强大。当我看到人们在使用他们来自动化不同的任务时有多么有创造力时,我感到兴奋。 如需阅读全文 ,请从上一篇开始 :如何构建你的第一个 GitHub 工作流(上)
5. 编写动作
在此阶段创建.gitignore文件很重要,以避免将不必要的文件推送到存储库。
我经常使用的一个很棒的工具是:https ://www.toptal.com/developers/gitignore
我的.gitignore文件是:
https://www.toptal.com/developers/gitignore/api/visualstudiocode,macos,node
创建一个特定于您的环境和项目的项目。
我们终于准备好创建我们的index.js文件了。这就是我们行动的所有逻辑所在。我们当然可以有一个更复杂的结构,但现在一个文件就可以了。
我已经注释了下面的所有代码,以便您逐步了解正在发生的事情。
const core = require('@actions/core');
const github = require('@actions/github');
const main = async () => {
try {
/**
* We need to fetch all the inputs that were provided to our action
* and store them in variables for us to use.
**/
const owner = core.getInput('owner', { required: true });
const repo = core.getInput('repo', { required: true });
const pr_number = core.getInput('pr_number', { required: true });
const token = core.getInput('token', { required: true });
/**
* Now we need to create an instance of Octokit which will use to call
* GitHub's REST API endpoints.
* We will pass the token as an argument to the constructor. This token
* will be used to authenticate our requests.
* You can find all the information about how to use Octokit here:
* https://octokit.github.io/rest.js/v18
**/
const octokit = new github.getOctokit(token);
/**
* We need to fetch the list of files that were changes in the Pull Request
* and store them in a variable.
* We use octokit.paginate() to automatically loop over all the pages of the
* results.
* Reference: https://octokit.github.io/rest.js/v18#pulls-list-files
*/
const { data: changedFiles } = await octokit.rest.pulls.listFiles({
owner,
repo,
pull_number: pr_number,
});
/**
* Contains the sum of all the additions, deletions, and changes
* in all the files in the Pull Request.
**/
let diffData = {
additions: 0,
deletions: 0,
changes: 0
};
// Reference for how to use Array.reduce():
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce
diffData = changedFiles.reduce((acc, file) => {
acc.additions += file.additions;
acc.deletions += file.deletions;
acc.changes += file.changes;
return acc;
}, diffData);
/**
* Loop over all the files changed in the PR and add labels according
* to files types.
**/
for (const file of changedFiles) {
/**
* Add labels according to file types.
*/
const fileExtension = file.filename.split('.').pop();
switch(fileExtension) {
case 'md':
await octokit.rest.issues.addLabels({
owner,
repo,
issue_number: pr_number,
labels: ['markdown'],
});
case 'js':
await octokit.rest.issues.addLabels({
owner,
repo,
issue_number: pr_number,
labels: ['javascript'],
});
case 'yml':
await octokit.rest.issues.addLabels({
owner,
repo,
issue_number: pr_number,
labels: ['yaml'],
});
case 'yaml':
await octokit.rest.issues.addLabels({
owner,
repo,
issue_number: pr_number,
labels: ['yaml'],
});
}
}
/**
* Create a comment on the PR with the information we compiled from the
* list of changed files.
*/
await octokit.rest.issues.createComment({
owner,
repo,
issue_number: pr_number,
body: `
Pull Request #${pr_number} has been updated with: \n
- ${diffData.changes} changes \n
- ${diffData.additions} additions \n
- ${diffData.deletions} deletions \n
`
});
} catch (error) {
core.setFailed(error.message);
}
}
// Call the main function to run the action
main();
6. 将我们的 Action 文件推送到 GitHub
让我们暂存、提交并将我们的文件推送到上游的主分支:
$ git status
On branch main
Your branch is up to date with 'origin/main'.
Untracked files:
(use "git add <file>..." to include in what will be committed)
.gitignore
action.yml
index.js
package-lock.json
package.json
nothing added to commit but untracked files present (use "git add" to track)
让我们添加所有要暂存的文件:
$ git add .
现在我们可以提交我们的更改:
$ git commit -m "Add main action structure"
[main 1fc5d18] Add main action structure
5 files changed, 686 insertions(+)
create mode 100644 .gitignore
create mode 100644 action.yml
create mode 100644 index.js
create mode 100644 package-lock.json
create mode 100644 package.json
并推动我们的改变:
$ git push origin main
Enumerating objects: 8, done.
Counting objects: 100% (8/8), done.
Delta compression using up to 16 threads
Compressing objects: 100% (7/7), done.
Writing objects: 100% (7/7), 5.82 KiB | 5.82 MiB/s, done.
Total 7 (delta 0), reused 0 (delta 0), pack-reused 0
To github.com:Link-/PR-metadata-action.git
457fee2..1fc5d18 main -> main
7.如何测试我们的Action
为了让我们能够测试我们的操作,我们需要创建一个包。如果您在上一步中注意到,我们没有推送node_modules包含我们在构建文件时使用的包的index.js文件夹。
没有这些软件包,我们的行动将无法运行!为了解决这个问题,我们可以使用一个名为ncc的好工具。它将帮助我们创建一个文件,其中包含我们的代码和运行操作所需的所有包。
让我们从安装开始ncc:
$ npm install @vercel/ncc
added 1 package, and audited 26 packages in 5s
found 0 vulnerabilities
编译我们的 JavaScript 就像运行一样简单:
$ ncc build index.js -o dist
ncc: Version 0.22.1
ncc: Compiling file index.js
530kB dist/index.js
530kB [845ms] - ncc 0.22.1
这将创建一个名为的新目录dist并创建一个名为的文件index.js,其中包含我们的代码和运行操作所需的所有包。
现在我们需要确保我们的action.yml文件包含正确的runs部分。您需要更换:
runs:
using: 'node16'
main: 'index.js'
和:
runs:
using: 'node16'
main: 'dist/index.js'
让我们再次将我们的更改推送到上游(到我们的 GitHub 存储库)。确保我们的dist/文件夹不在.gitignore文件中:
$ git status
$ git add .
$ git commit -m "Add compiled action"
[main adfc4f0] Add compiled action
4 files changed, 8505 insertions(+), 3 deletions(-)
create mode 100644 dist/index.js
$ git push origin main
我们终于准备好创建我们的工作流程了!在相同或任何其他存储库(公共或私有无关紧要)中创建新工作流,如下所示:
mkdir -p .github/workflows
touch .github/workflows/pr-metadata.yaml
将以下工作流程复制到我们的pr-metadata.yaml文件中:
name: PR metadata annotation
on:
pull_request:
types: [opened, reopened, synchronize]
jobs:
annotate-pr:
runs-on: ubuntu-latest
name: Annotates pull request with metadata
steps:
- name: Annotate PR
uses: link-/PR-metadata-action@main
with:
owner: ${{ github.repository_owner }}
repo: ${{ github.event.repository.name }}
pr_number: ${{ github.event.number }}
token: ${{ secrets.GITHUB_TOKEN }}
完成所有这些步骤后,我们的存储库应如下所示:
为了让我们测试这个工作流程,我们需要在我们的存储库中进行更改并创建一个拉取请求 (PR)。我们可以通过README.md直接在 GitHub 上编辑文件来做到这一点:
GitHub 操作最佳实践
最后,我想与你分享一些创建自定义操作时的最佳实践:
- 采用单一职责原则。确保你的行动只做一件事。它将使你的代码更易于维护和测试。
- 好好考虑一下你的操作界面(输入和输出)。通过减少可选输入的数量来保持界面简单明了。
- 我们在本教程中没有这样做,但你需要验证您的操作输入! 大多数安全项目可以通过验证输入来消除。
- 确保你的操作是幂等的,这意味着,如果你按顺序多次运行该操作,结果应该始终相同。在我们的例子中,动作应该执行并发表评论并添加标签,或者它应该优雅地退出。
- 阅读并遵循这些 GitHub 文档中记录的安全强化最佳实践。
- 如果你无法维护它,请不要创建新操作。在市场上搜索类似的操作并使用它们。
结论
感谢读完这篇文章 ,希望你有所收获!