创建自定义 GitHub 操作
GitHub Actions 是一项十分强大的功能,可帮助你凭借便捷易用的独有存储库,驾驭从代码到云的一切操作。 你将通过本单元了解不同类型的 GitHub 操作,以及用于创建自定义 GitHub 操作的元数据、语法和工作流命令。
GitHub 操作的类型
操作是可用于自定义开发工作流的单个任务。 你可以通过编写可与存储库交互的自定义代码来执行自定义任务,或者使用 GitHub 社区分享的操作,来创建自己的操作。 通过浏览各种操作,你会注意到有三种不同的操作类型:Docker 容器操作、JavaScript 操作和组合运行步骤操作。 接下来详细了解一下每种操作类型。
Docker 容器操作
Docker 容器使用 GitHub Actions 代码封装环境。 这意味着操作可在一致而可靠的环境中运行,原因在于操作的所有依赖项均在容器当中。 如果操作需要在特定环境配置中运行,则 Docker 容器是一个理想的选择,因为你可以自定义操作系统和工具。 但由于作业必须构建并检索容器,这导致 Docker 容器操作通常慢于 JavaScript 操作。
在构建 Docker 容器操作之前,你应对如何使用环境变量和 Docker 容器文件系统有一些基本了解。 构建 Docker 容器操作所需执行的步骤简单明了:
- 创建一个
Dockerfile,以定义用于组装 Docker 映像的命令。 - 创建一个
action.yml元数据文件,以定义操作的输入和输出。 在文件中,将runs: using:值设置为docker,并将runs: image:值设置为Dockerfile。 - 创建一个
entrypoint.sh文件,以描述 Docker 映像。 - 提交操作并将其推送到 GitHub,其中包含以下文件:
action.yml、entrypoint.sh、Dockerfile和README.md。
JavaScript 操作
JavaScript 操作可以直接在运行器计算机上运行,并将操作代码与用于运行操作的环境分开。 如此一来,操作代码得以简化,并且执行速度快于 Docker 容器中的操作。
若要创建和使用封装的 JavaScript 操作,需要下载 Node.js,其中包括 npm。 虽是一个可选步骤,但我们仍建议使用 GitHub Actions Toolkit Node.js,这是一个 Node.js 程序包集合,可用于持续快速构建 JavaScript 操作。
构建 JavaScript 操作的步骤简单明了:
- 创建一个
action.yml元数据文件,以定义操作的输入和输出,同时指出操作运行器如何开始运行此 JavaScript 操作。 - 创建一个
index.js文件,该文件包含有关工具包、路由和操作其他功能的上下文信息。 - 提交你的操作并将其推送到 GitHub,其中包含以下文件:
action.yml、index.js、node_modules、package.json、package-lock.json和README.md。
组合运行步骤操作
组合运行步骤操作让你可以借助 shell 脚本重复使用操作。 你甚至可以在同一操作内混合多种 shell 语言。 如有大量 shell 脚本可将多项任务自动化,则现在,你可以轻松地将其转化为操作并用于不同的工作流。 有时,与使用 JavaScript 或将代码封包装在 Docker 容器中相比,编写一个 shell 脚本更加容易。
打包组合操作
打包组合操作将多个步骤捆绑到可重用单元中。 这些操作在存储库中定义,可以在不同存储库的工作流中引用。 打包组合操作可简化工作流,减少重复,并提高可维护性。
创建打包组合操作时,步骤在单个 action.yml 文件中定义。 此文件指定要执行的命令或操作的输入、输出和序列。 打包组合操作特别适用于自动执行重复任务或将多个 shell 命令合并成单个可重用操作。
创建组合操作
1. 为组合操作设置目录
必须将组合操作放置在存储库中的自己的目录中。
示例目录结构:
.github/actions/my-composite-action/
├── action.yml
└── scripts/
└── my-script.sh
2. 定义 action.yml 文件
在 my-composite-action 目录中,创建一个 文件。action.yml
name: "My Composite Action"
description: "A reusable composite action that checks out code and sets up Node.js"
inputs:
node-version:
description: "The Node.js version to use"
required: true
runs:
using: "composite"
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ inputs.node-version }}
注意:using: "composite" 字段指示该操作是组合操作。
3. 在工作流中使用组合操作
创建组合操作后,可以在 GitHub Actions 工作流中引用它。
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Use my composite action
uses: ./.github/actions/my-composite-action
with:
node-version: '18'
如果组合操作从另一个存储库共享,请按照下方所示将其引用:
uses: owner/repository/.github/actions/my-composite-action@v1
向组合操作添加输出
组合操作可以定义工作流可用于在步骤或作业之间传递数据的输出。 输出特别适用于在不同操作之间共享结果或计算值。
以下示例演示如何在组合操作中定义和使用输出:
在 action.yml 中定义输出
action.yml 会指定一个名为 script-result 的输出。 此输出从 result 步骤的 run-script 输出中检索其值。
run-script 步骤会运行 Bash 命令来设置输出值。
outputs:
script-result:
description: "Result from the script"
value: ${{ steps.run-script.outputs.result }}
runs:
using: "composite"
steps:
- id: run-script
run: echo "result=Success" >> $GITHUB_OUTPUT
shell: bash
在工作流中使用输出
创建组合操作后,可以在工作流中访问其输出。 下面是一个示例:
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Run composite action
id: my-action
uses: ./.github/actions/my-composite-action
- name: Display result
run: echo "Script Result: ${{ steps.my-action.outputs.script-result }}"
在本示例中:
- 使用
uses关键字调用组合操作。 - 使用
script-result语法访问输出steps.<step-id>.outputs.<output-name>。 - 在工作流日志中显示结果。
在组合操作中定义输出,以创建可重用且模块化的工作流。 该方法简化了数据共享,还提高了可维护性。
组合操作的最佳做法
| 最佳做法 | 说明 |
|---|---|
| 使用版本控制 | 使用 v1 标记引用稳定版本 1。 |
| 保持操作模块化 | 对组合操作中的相关步骤进行分组。 |
| 文档输入和输出 | 在 action.yml 中添加输入/输出的说明。 |
| 发布前测试 | 验证测试存储库中的组合操作。 |
工作流中的组合操作
组合操作是将多个步骤捆绑到可重用单元中来简化工作流的强大方法。 这些操作让你能够在单个 action.yml 文件中定义一系列命令或操作,从而更轻松地跨工作流维护和重用逻辑。
组合操作的优势:
- 可重用性 - 定义一次操作后,即可在多个工作流中使用它们。
- 可维护性 - 通过将逻辑集中到单个操作中来减少重复。
- 模块化 - 将多个 shell 命令或其他操作合并到单个单元中。
开发用于在 GitHub Actions 运行程序上设置 CLI 的操作
许多 CI/CD 工作流需要特定版本的 CLI 工具才能与云服务交互、管理基础结构或执行脚本。 虽然 GitHub 托管的运行程序预安装了许多工具,但它们可能不包含工作流所需的确切版本,尤其是当它的版本较旧或不受支持时。 可以创建可重用的 GitHub 操作,而不是在每个工作流中安装所需的 CLI 版本,该操作可:
- 确保跨作业安装所需的 CLI 版本一致。
- 通过集中安装逻辑简化工作流。
- 优化缓存来提高工作流执行速度。
如何开发 CLI 设置操作
CLI 设置操作是基于 JavaScript 的操作,用于在 GitHub 运行程序上安装和配置 CLI。
创建操作的步骤:
步骤 1:设置操作目录
要为 CLI 设置操作手动创建目录,请执行以下步骤:
- 导航到存储库
为操作创建新目录
在my-cli-action文件夹内创建一个名为.github/actions的新目录。 这可确保操作井然有序且遵循 GitHub 为自定义操作推荐的结构。导航到新目录
更改为新创建的目录,开始为操作添加文件:验证目录结构
创建目录后,存储库结构应如下所示:
your-repository/
├── .github/
│ ├── actions/
│ │ ├── my-cli-action/
现在,可以继续为 CLI 设置操作创建 action.yml 文件和其他必要文件。
步骤 2:定义 action.yml 元数据文件
创建 action.yml 文件来描述操作。
name: "Setup MyCLI"
description: "Installs MyCLI and adds it to the PATH"
author: "Your Name"
inputs:
version:
description: "The CLI version to install"
required: false
default: "latest"
runs:
using: "node16"
main: "index.js"
为何使用 using: node16?此操作使用 Node.js 16 来运行 JavaScript 代码。
步骤 3:创建 JavaScript 脚本以安装 CLI
在同一目录中,创建一个名为 index.js 的文件并添加以下代码:
const core = require('@actions/core');
const { execSync } = require('child_process');
async function run() {
try {
const version = core.getInput('version') || 'latest';
console.log(`Installing MyCLI version: ${version}...`);
execSync(`curl -fsSL https://cli.example.com/install.sh | sh`, { stdio: 'inherit' });
console.log("MyCLI installed successfully.");
} catch (error) {
core.setFailed(`Installation failed: ${error.message}`);
}
}
run();
上述 JavaScript 代码使用 core.getInput() 检索指定为输入的 CLI 版本。 然后,它会执行 curl 命令来下载并安装 CLI。 如果安装过程失败,该操作使用 core.setFailed() 将工作流标记为“失败”。
步骤 4:在本地测试操作
在工作流中使用操作之前,请在 GitHub 托管的运行程序上对其进行测试。
在存储库中创建工作流文件 (.github/workflows/test.yml):
name: Test MyCLI Setup
on:
push:
branches:
- main
- feature/*
1. 触发工作流
工作流在推送到主分支和与功能/* 模式匹配的任何分支时触发。 可调整此项以匹配存储库的分支策略。
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
2. 克隆存储库
actions/checkout@v4 操作用于将存储库克隆到运行程序。 这可确保工作流有权访问存储库的文件。
- name: Install MyCLI
uses: ./.github/actions/my-cli-action
with:
version: '1.2.3'
3. 运行自定义操作
用法:./.github/actions/my-cli-action 行在本地引用自定义操作。 确保操作目录和 action.yml 文件设置正确。 版本输入指定要安装的 CLI 版本,在本例中为版本 1.2.3。
- name: Verify CLI Installation
run: |
echo "Checking MyCLI version..."
mycli --version
4. 验证 CLI 安装
此步骤运行 shell 命令,验证 CLI 是否已成功安装。 它通过运行 mycli --version 检查已安装 CLI 的版本。
本地测试
若要在本地测试此工作流,请使用 act CLI 工具:
act -j test
这会模拟本地计算机上的 GitHub Actions 环境,以便在推送更改之前调试和验证工作流。
优化提示:缓存
若要提高工作流性能,请使用 actions/cache 操作缓存 CLI 安装目录:
- name: Cache MyCLI
uses: actions/cache@v4
with:
path: ~/.mycli
key: mycli-${{ runner.os }}-${{ inputs.version }}
这可确保后续运行重复使用已缓存的 CLI 安装,从而缩短安装时间。
CLI 设置操作的最佳做法
| 最佳做法 | 说明 |
|---|---|
| 使用版本控制 | 允许用户通过 inputs.version 指定 CLI 版本。 |
| 正确处理错误 | 使用 core.setFailed() 退出错误。 |
| 缓存 CLI 安装 | 使用 actions/cache 优化工作流性能。 |
| 提供文档 | 在 README.md 中解释用法和输入。 |
JavaScript 操作故障排除
使用基于 JavaScript 的 GitHub Actions 时,在工作流执行过程中可能会遇到意外的行为、错误或失败。 本单元提供技术和工具,可帮助你识别和解决 JavaScript 操作中的问题。
常见故障排除方案
| 问题 | 可能的原因 | 建议的解决方法 |
|---|---|---|
| 操作失败并出现堆栈跟踪 |
index.js 中语法或运行时错误 |
使用 console.log() 和检查日志 |
输入为 undefined |
输入名称错误或缺少输入 | 验证 action.yml 及如何传递输入 |
| 未设置环境变量 |
core.exportVariable 或 process.env 未正确使用 |
查看代码设置变量 |
| 找不到文件 | 缺少相对路径 | 使用 __dirname 或完整文件路径 |
| 缓存未还原 |
key 或 path 值不正确 |
检查缓存配置和密钥 |
使用日志记录进行调试
使用 core.info、core.debug 和 console.log 记录日志信息
const core = require('@actions/core');
core.info("This is an info message");
core.debug("This is a debug message");
console.log("This is a raw console log");
✅ 将 core.debug 用于调试日志 - 仅当 ACTIONS_STEP_DEBUG 设置为 true 时,才会显示这些日志。
启用调试日志记录
可通过设置以下机密来启用步骤级调试日志:
ACTIONS_STEP_DEBUG=true
若要启用运行程序诊断日志,请设置:
ACTIONS_RUNNER_DEBUG=true
设置用于调试的机密
- 转到 GitHub 存储库。
- 导航到“设置”“机密和变量”>“操作”。>
- 使用以下名称和值添加新机密:
-
ACTIONS_STEP_DEBUG:
true -
ACTIONS_RUNNER_DEBUG:
true
-
ACTIONS_STEP_DEBUG:
检查工作流日志
工作流失败时,单击“操作”选项卡中失败的作业。请展开每个步骤以:
- 查看详细日志
- 检查标准输出 (stdout)
- 查看脚本的退出代码
- 识别未经处理的异常
🔍 日志输出示例
Error: Cannot find module '@actions/core'
Require stack:
- /home/runner/work/_actions/my-org/my-action/index.js
✅ 修复:运行 npm install @actions/core 并提交node_modules(或使用 ncc 捆绑操作)。
在本地测试操作
使用 act - 一款在本地运行 GitHub Actions 的 CLI 工具。 示例:
act -j test
🛠 请确保在本地测试时正确模拟 GitHub 环境。
正常处理错误
捕获异常失败,并显示有用的消息:
try {
// your logic
} catch (error) {
core.setFailed(`Action failed with error: ${error.message}`);
}
🔁 这可确保 GitHub 停止出错的工作流,并提供可读日志。
调试 JavaScript 操作的最佳做法
| 实践 | 说明 |
|---|---|
| 使用 core.debug() | 除非启用了调试,否则隐藏详细日志。 |
| 验证 action.yml | 确保输入和输出定义正确。 |
| 捆绑代码 | 使用 @vercel/ncc 将 JavaScript 编译为单个文件。 这可减少依赖项并确保包含所有必需的模块,从而防止缺失文件导致的运行时错误。 |
| 使用行为进行测试 | 在本地模拟运行来加快迭代速度。 |
| 使用 try/catch | 防止工作流失败且无提示。 |
排查 Docker 容器操作问题
Docker 容器操作非常强大,可封装 GitHub Actions 工作流中的复杂工具和环境。 但是,由于操作的运行时环境是隔离的,调试这些操作可能比 JavaScript 操作更具挑战性。 本单元将指导你识别、诊断和解决基于 Docker 的操作的问题。
Docker 容器操作中的常见问题
| 问题 | 原因 | 建议的解决方法 |
|---|---|---|
| 操作无法启动 |
ENTRYPOINT 或 CMD misconfigured |
确认 Dockerfile 正确使用 ENTRYPOINT |
| 缺少依赖项 | 依赖项未安装或配置不当 | 确保所有包都安装在映像中 |
| 未接收输入 |
INPUT_ 环境变量无法访问 |
使用 process.env.INPUT_<INPUT_NAME>(或 shell 等效项) |
| 找不到文件 | 容器中的文件路径不正确 | 使用绝对路径或验证目录结构 |
| 权限被拒绝 | 文件或脚本缺少执行权限 | 在 Dockerfile 中添加 RUN chmod +x <script> |
| 与网络相关的故障 | 外部服务不可访问 | 验证网络设置和重试逻辑 |
了解 Docker 操作生命周期
在进行故障排除之前,了解 Docker 容器操作的运行方式很有帮助。
1. 工作流触发器
GitHub Actions 工作流启动来响应配置的事件,例如 push、pull_request 或手动 workflow_dispatch。
2. 运行程序设置
GitHub 预配一个新的虚拟机(运行程序)来执行工作流。 运行程序通过下载操作定义并解析依赖项来准备环境。
3. 操作解决方法
如果操作在其 runs.using: docker 文件中指定 action.yml,GitHub 会将它识别为基于 Docker 的操作。
4. 映像生成或拉取
GitHub 生成在操作的 Dockerfile 中定义的 Docker 映像,或者提取预生成的映像(如果指定)。 此映像定义运行操作代码的环境。
5. 容器执行
运行程序启动 Docker 容器、装载工作区,并注入环境变量(包括工作流中定义的机密和输入)。
6. 入口点运行
GitHub 从容器内的 Dockerfile 执行 entrypoint 命令。 自定义操作逻辑在这里运行,通常是脚本或应用程序。
7. 结果处理
容器操作设置的任何输出都由运行程序捕获,并传递到工作流中的后续步骤。 完成后,容器将关闭并丢弃运行程序。
注意:Docker 容器操作在干净隔离的环境中运行。 文件系统状态、已安装的工具和环境变量都必须在 Dockerfile 中定义。
调试方法
1. 添加日志记录
在入口点脚本中使用 echo、printf 或 logging 语句:
echo "Starting Docker action..."
echo "Input VALUE: $INPUT_VALUE"
记录所有关键输入和步骤,来诊断发生故障的位置。
2. 在本地生成和测试
在计算机上使用 Docker 模拟容器行为:
docker build -t my-action .
docker run -e INPUT_NAME=value my-action
确保环境变量与 GitHub 所设置的保持一致。
3. 使用 act CLI 模拟 GitHub 工作流
安装操作以在本地运行 GitHub 工作流:
act -j test-job
非常适合在工作流中测试 Docker 操作,而无需推送到 GitHub。
4. 验证 Dockerfile 配置
确保定义 ENTRYPOINT 或 CMD。 将脚本复制到映像中,并向其授予执行权限:
COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]
5. 检查 GitHub 日志
单击失败的工作流运行,展开每个步骤并检查:
- Docker 生成日志
- 容器中的标准输出和标准错误
- 退出代码和堆栈跟踪 🔍 日志通常会显示缺少的包、语法问题或权限错误。
示例:入口点错误
Error: container_linux.go:380: starting container process caused "exec: \"/entrypoint.sh\": permission denied"
✅ 修复:在 Dockerfile 中添加 RUN chmod +x /entrypoint.sh。
环境变量映射
| GitHub 输入 | Docker 环境变量 |
|---|---|
with: name: test |
INPUT_NAME=test |
GITHUB_WORKSPACE |
已映射到 /github/workspace |
GITHUB_EVENT_NAME |
触发工作流的事件 |
echo "Input was $INPUT_NAME"
echo "Working dir: $GITHUB_WORKSPACE"
使用排查问题机密
通过将这些机密添加到存储库或组织来启用其他日志记录:
| 机密 | 描述 |
|---|---|
ACTIONS_STEP_DEBUG |
启用调试日志记录。 |
ACTIONS_RUNNER_DEBUG |
启用运行程序诊断 |
Docker 操作调试的最佳做法
| 最佳做法 | 原因 |
|---|---|
| 充分利用日志记录 | 帮助跟踪故障点 |
| 始终设置 chmod +x | 避免 shell 脚本上的权限错误 |
| 验证脚本中的输入 | 提前捕获缺失或格式不正确的输入 |
| 尽量减少容器依赖项 | 较小的映像更易于调试 |
| 使用 docker run 或 act 在本地进行测试 | 加快迭代和即时反馈 |