github-actions

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

GitHub Actions

GitHub Actions

Activate when creating, modifying, troubleshooting, or optimizing GitHub Actions components. This skill covers action development, marketplace integration, and best practices.
当你需要创建、修改、排查问题或优化GitHub Actions组件时启用此技能。本技能涵盖Action开发、Marketplace集成以及最佳实践。

When to Use This Skill

适用场景

Activate when:
  • Creating custom GitHub Actions (JavaScript, Docker, or composite)
  • Publishing actions to GitHub Marketplace
  • Configuring action metadata and inputs/outputs
  • Implementing action security and permissions
  • Troubleshooting action execution
  • Selecting or evaluating marketplace actions
  • Optimizing action performance and reliability
在以下场景中启用:
  • 创建自定义GitHub Actions(JavaScript、Docker或Composite类型)
  • 将Action发布至GitHub Marketplace
  • 配置Action元数据与输入/输出
  • 实施Action安全与权限控制
  • 排查Action执行问题
  • 选择或评估Marketplace中的Action
  • 优化Action的性能与可靠性

Action Types

Action类型

JavaScript Actions

JavaScript Actions

Execute directly on runners with fast startup and cross-platform compatibility.
Structure:
my-action/
├── action.yml        # Metadata and interface
├── index.js          # Entry point
├── package.json      # Dependencies
└── node_modules/     # Bundled dependencies
Key Requirements:
  • Use
    @actions/core
    for inputs/outputs
  • Use
    @actions/github
    for GitHub API access
  • Bundle all dependencies (use @vercel/ncc)
  • Support Node.js LTS versions
Example action.yml:
yaml
name: 'My JavaScript Action'
description: 'Performs custom task'
inputs:
  token:
    description: 'GitHub token'
    required: true
  config:
    description: 'Configuration file path'
    required: false
    default: 'config.yml'
outputs:
  result:
    description: 'Action result'
runs:
  using: 'node20'
  main: 'dist/index.js'
直接在运行器上执行,启动速度快且具备跨平台兼容性。
结构:
my-action/
├── action.yml        # 元数据与接口定义
├── index.js          # 入口文件
├── package.json      # 依赖配置
└── node_modules/     # 打包后的依赖
核心要求:
  • 使用
    @actions/core
    处理输入/输出
  • 使用
    @actions/github
    访问GitHub API
  • 打包所有依赖(使用@vercel/ncc)
  • 支持Node.js LTS版本
示例action.yml:
yaml
name: 'My JavaScript Action'
description: 'Performs custom task'
inputs:
  token:
    description: 'GitHub token'
    required: true
  config:
    description: 'Configuration file path'
    required: false
    default: 'config.yml'
outputs:
  result:
    description: 'Action result'
runs:
  using: 'node20'
  main: 'dist/index.js'

Docker Container Actions

Docker容器Actions

Provide consistent execution environment with all dependencies packaged.
Structure:
my-action/
├── action.yml
├── Dockerfile
├── entrypoint.sh
└── src/
Key Requirements:
  • Use lightweight base images (Alpine when possible)
  • Set proper file permissions
  • Handle signals gracefully
  • Output to STDOUT/STDERR correctly
Example Dockerfile:
dockerfile
FROM alpine:3.18

RUN apk add --no-cache bash curl jq

COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh

ENTRYPOINT ["/entrypoint.sh"]
提供一致的执行环境,所有依赖均已打包。
结构:
my-action/
├── action.yml
├── Dockerfile
├── entrypoint.sh
└── src/
核心要求:
  • 使用轻量级基础镜像(尽可能使用Alpine)
  • 设置正确的文件权限
  • 优雅处理信号
  • 正确输出至STDOUT/STDERR
示例Dockerfile:
dockerfile
FROM alpine:3.18

RUN apk add --no-cache bash curl jq

COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh

ENTRYPOINT ["/entrypoint.sh"]

Composite Actions

Composite Actions

Combine multiple steps and actions into reusable units.
Structure:
yaml
name: 'Setup Environment'
description: 'Configure development environment'
inputs:
  node-version:
    description: 'Node.js version'
    required: false
    default: '20'
runs:
  using: 'composite'
  steps:
    - uses: actions/setup-node@v4
      with:
        node-version: ${{ inputs.node-version }}
    - run: npm ci
      shell: bash
    - run: npm run build
      shell: bash
将多个步骤和Action组合为可复用的单元。
结构示例:
yaml
name: 'Setup Environment'
description: 'Configure development environment'
inputs:
  node-version:
    description: 'Node.js version'
    required: false
    default: '20'
runs:
  using: 'composite'
  steps:
    - uses: actions/setup-node@v4
      with:
        node-version: ${{ inputs.node-version }}
    - run: npm ci
      shell: bash
    - run: npm run build
      shell: bash

Action Metadata (action.yml)

Action元数据(action.yml)

Required Fields

必填字段

yaml
name: 'Action Name'           # Marketplace display name
description: 'What it does'   # Clear, concise purpose
runs:                         # Execution configuration
  using: 'node20'            # or 'docker' or 'composite'
yaml
name: 'Action Name'           # Marketplace显示名称
description: 'What it does'   # 清晰简洁的用途说明
runs:                         # 执行配置
  using: 'node20'            # 或'docker'或'composite'

Optional Fields

可选字段

yaml
author: 'Your Name'
branding:                    # Marketplace icon/color
  icon: 'activity'
  color: 'blue'
inputs:                      # Define all inputs
  input-name:
    description: 'Purpose'
    required: true
    default: 'value'
outputs:                     # Define all outputs
  output-name:
    description: 'What it contains'
yaml
author: 'Your Name'
branding:                    # Marketplace图标/颜色配置
  icon: 'activity'
  color: 'blue'
inputs:                      # 定义所有输入参数
  input-name:
    description: '用途说明'
    required: true
    default: 'value'
outputs:                     # 定义所有输出结果
  output-name:
    description: '输出内容说明'

Inputs and Outputs

输入与输出

Reading Inputs

读取输入

JavaScript:
javascript
const core = require('@actions/core');
const token = core.getInput('token', { required: true });
const config = core.getInput('config') || 'default.yml';
Shell:
bash
TOKEN="${{ inputs.token }}"
CONFIG="${{ inputs.config }}"
JavaScript:
javascript
const core = require('@actions/core');
const token = core.getInput('token', { required: true });
const config = core.getInput('config') || 'default.yml';
Shell:
bash
TOKEN="${{ inputs.token }}"
CONFIG="${{ inputs.config }}"

Setting Outputs

设置输出

JavaScript:
javascript
core.setOutput('result', 'success');
core.setOutput('artifact-url', artifactUrl);
Shell:
bash
echo "result=success" >> $GITHUB_OUTPUT
echo "artifact-url=$ARTIFACT_URL" >> $GITHUB_OUTPUT
JavaScript:
javascript
core.setOutput('result', 'success');
core.setOutput('artifact-url', artifactUrl);
Shell:
bash
echo "result=success" >> $GITHUB_OUTPUT
echo "artifact-url=$ARTIFACT_URL" >> $GITHUB_OUTPUT

GitHub Actions Toolkit

GitHub Actions工具包

Essential npm packages for JavaScript actions:
JavaScript Action必备的npm包:

@actions/core

@actions/core

javascript
const core = require('@actions/core');

// Inputs/Outputs
const input = core.getInput('name');
core.setOutput('name', value);

// Logging
core.info('Information message');
core.warning('Warning message');
core.error('Error message');
core.debug('Debug message');

// Grouping
core.startGroup('Group name');
// ... operations
core.endGroup();

// Failure
core.setFailed('Action failed: reason');

// Secrets
core.setSecret('sensitive-value');  // Masks in logs

// Environment
core.exportVariable('VAR_NAME', 'value');
javascript
const core = require('@actions/core');

// 输入/输出处理
const input = core.getInput('name');
core.setOutput('name', value);

// 日志输出
core.info('Information message');
core.warning('Warning message');
core.error('Error message');
core.debug('Debug message');

// 分组日志
core.startGroup('Group name');
// ... 操作步骤
core.endGroup();

// 标记失败
core.setFailed('Action failed: reason');

// 敏感信息处理
core.setSecret('sensitive-value');  // 在日志中自动脱敏

// 环境变量导出
core.exportVariable('VAR_NAME', 'value');

@actions/github

@actions/github

javascript
const github = require('@actions/github');

// Context
const context = github.context;
console.log(context.repo);        // { owner, repo }
console.log(context.sha);         // Commit SHA
console.log(context.ref);         // Branch/tag ref
console.log(context.actor);       // Triggering user
console.log(context.payload);     // Webhook payload

// Octokit client
const token = core.getInput('token');
const octokit = github.getOctokit(token);

// API operations
const { data: issues } = await octokit.rest.issues.listForRepo({
  owner: context.repo.owner,
  repo: context.repo.repo,
  state: 'open'
});
javascript
const github = require('@actions/github');

// 上下文信息
const context = github.context;
console.log(context.repo);        // { owner, repo }
console.log(context.sha);         // 提交SHA
console.log(context.ref);         // 分支/标签引用
console.log(context.actor);       // 触发操作的用户
console.log(context.payload);     // Webhook负载

// Octokit客户端
const token = core.getInput('token');
const octokit = github.getOctokit(token);

// API操作示例
const { data: issues } = await octokit.rest.issues.listForRepo({
  owner: context.repo.owner,
  repo: context.repo.repo,
  state: 'open'
});

@actions/exec

@actions/exec

javascript
const exec = require('@actions/exec');

// Execute commands
await exec.exec('npm', ['install']);

// Capture output
let output = '';
await exec.exec('git', ['log', '--oneline'], {
  listeners: {
    stdout: (data) => { output += data.toString(); }
  }
});
javascript
const exec = require('@actions/exec');

// 执行命令
await exec.exec('npm', ['install']);

// 捕获命令输出
let output = '';
await exec.exec('git', ['log', '--oneline'], {
  listeners: {
    stdout: (data) => { output += data.toString(); }
  }
});

Security Best Practices

安全最佳实践

Input Validation

输入验证

Always validate and sanitize inputs:
javascript
const core = require('@actions/core');

function validateInput(input) {
  // Check for command injection
  if (/[;&|`$()]/.test(input)) {
    throw new Error('Invalid characters in input');
  }
  return input;
}

const userInput = core.getInput('user-input');
const safeInput = validateInput(userInput);
始终验证并清理输入:
javascript
const core = require('@actions/core');

function validateInput(input) {
  // 检查是否存在命令注入风险
  if (/[;&|`$()]/.test(input)) {
    throw new Error('输入包含无效字符');
  }
  return input;
}

const userInput = core.getInput('user-input');
const safeInput = validateInput(userInput);

Token Permissions

Token权限

Request minimal required permissions:
yaml
permissions:
  contents: read           # Read repository
  pull-requests: write     # Comment on PRs
  issues: write           # Create issues
请求最小必要权限:
yaml
permissions:
  contents: read           # 读取仓库内容
  pull-requests: write     # 对PR进行评论
  issues: write           # 创建Issue

Secret Handling

敏感信息处理

javascript
// Mask secrets in logs
core.setSecret(sensitiveValue);

// Never log tokens
core.debug(`Token: ${token}`);  // ❌ WRONG
core.debug('Token received');   // ✅ CORRECT

// Secure token usage
const octokit = github.getOctokit(token);
// Token automatically included in requests
javascript
// 在日志中脱敏敏感信息
core.setSecret(sensitiveValue);

// 切勿记录Token
core.debug(`Token: ${token}`);  // ❌ 错误做法
core.debug('已获取Token');   // ✅ 正确做法

// 安全使用Token
const octokit = github.getOctokit(token);
// Token会自动包含在请求中

Dependency Security

依赖安全

bash
undefined
bash
undefined

Audit dependencies

审计依赖包

npm audit
npm audit

Use specific versions

使用特定版本

npm install @actions/core@1.10.0
npm install @actions/core@1.10.0

Bundle dependencies

打包依赖

npm install -g @vercel/ncc ncc build index.js -o dist
undefined
npm install -g @vercel/ncc ncc build index.js -o dist
undefined

Marketplace Publishing

Marketplace发布

Prerequisites

前置条件

  • Public repository
  • action.yml in repository root
  • README.md with usage examples
  • LICENSE file
  • Repository topics (optional)
  • 公开仓库
  • action.yml位于仓库根目录
  • 包含README.md及使用示例
  • 包含LICENSE文件
  • 仓库主题(可选)

Publishing Process

发布流程

  1. Create release with semantic version tag:
bash
git tag -a v1.0.0 -m "Release v1.0.0"
git push origin v1.0.0
  1. Create GitHub Release from tag
  2. Check "Publish this Action to GitHub Marketplace"
  3. Select primary category
  4. Verify branding icon/color
  1. 创建带语义化版本标签的发布:
bash
git tag -a v1.0.0 -m "Release v1.0.0"
git push origin v1.0.0
  1. 基于标签创建GitHub Release
  2. 勾选“Publish this Action to GitHub Marketplace”
  3. 选择主分类
  4. 验证品牌图标/颜色配置

Version Management

版本管理

Use semantic versioning with major version tags:
bash
undefined
使用语义化版本并创建主版本标签:
bash
undefined

Release v1.2.3

发布v1.2.3版本

git tag -a v1.2.3 -m "Release v1.2.3" git tag -fa v1 -m "Update v1 to v1.2.3" git push origin v1.2.3 v1 --force

Users reference by major version:
```yaml
- uses: owner/action@v1  # Tracks latest v1.x.x
git tag -a v1.2.3 -m "Release v1.2.3" git tag -fa v1 -m "将v1更新至v1.2.3" git push origin v1.2.3 v1 --force

用户可通过主版本引用:
```yaml
- uses: owner/action@v1  # 自动跟踪最新v1.x.x版本

Testing Actions Locally

本地测试Action

Use
act
for local testing (see act skill):
bash
undefined
使用
act
进行本地测试(参考act相关技能):
bash
undefined

Test action in current directory

测试当前目录下的Action

act -j test
act -j test

Test with specific event

测试特定事件触发的流程

act push
act push

Test with secrets

携带敏感信息测试

act -s GITHUB_TOKEN=ghp_xxx
undefined
act -s GITHUB_TOKEN=ghp_xxx
undefined

Common Patterns

常见模式

Matrix Testing Action

矩阵测试Action

yaml
undefined
yaml
undefined

action.yml

action.yml

name: 'Matrix Test Runner' description: 'Run tests across multiple configurations' inputs: matrix-config: description: 'JSON matrix configuration' required: true runs: using: 'composite' steps: - run: | echo "Testing with config: ${{ inputs.matrix-config }}" # Parse and execute tests shell: bash
undefined
name: 'Matrix Test Runner' description: 'Run tests across multiple configurations' inputs: matrix-config: description: 'JSON matrix configuration' required: true runs: using: 'composite' steps: - run: | echo "Testing with config: ${{ inputs.matrix-config }}" # 解析并执行测试 shell: bash
undefined

Cache Management Action

缓存管理Action

javascript
const core = require('@actions/core');
const cache = require('@actions/cache');

async function run() {
  const paths = [
    'node_modules',
    '.npm'
  ];

  const key = `deps-${process.platform}-${hashFiles('package-lock.json')}`;

  // Restore cache
  const cacheKey = await cache.restoreCache(paths, key);

  if (!cacheKey) {
    core.info('Cache miss, installing dependencies');
    await exec.exec('npm', ['ci']);
    await cache.saveCache(paths, key);
  } else {
    core.info(`Cache hit: ${cacheKey}`);
  }
}
javascript
const core = require('@actions/core');
const cache = require('@actions/cache');

async function run() {
  const paths = [
    'node_modules',
    '.npm'
  ];

  const key = `deps-${process.platform}-${hashFiles('package-lock.json')}`;

  // 恢复缓存
  const cacheKey = await cache.restoreCache(paths, key);

  if (!cacheKey) {
    core.info('未命中缓存,开始安装依赖');
    await exec.exec('npm', ['ci']);
    await cache.saveCache(paths, key);
  } else {
    core.info(`命中缓存: ${cacheKey}`);
  }
}

Artifact Upload Action

制品上传Action

javascript
const artifact = require('@actions/artifact');

async function uploadArtifact() {
  const artifactClient = artifact.create();
  const files = [
    'dist/bundle.js',
    'dist/styles.css'
  ];

  const rootDirectory = 'dist';
  const options = {
    continueOnError: false
  };

  const uploadResponse = await artifactClient.uploadArtifact(
    'build-artifacts',
    files,
    rootDirectory,
    options
  );

  core.setOutput('artifact-id', uploadResponse.artifactId);
}
javascript
const artifact = require('@actions/artifact');

async function uploadArtifact() {
  const artifactClient = artifact.create();
  const files = [
    'dist/bundle.js',
    'dist/styles.css'
  ];

  const rootDirectory = 'dist';
  const options = {
    continueOnError: false
  };

  const uploadResponse = await artifactClient.uploadArtifact(
    'build-artifacts',
    files,
    rootDirectory,
    options
  );

  core.setOutput('artifact-id', uploadResponse.artifactId);
}

Troubleshooting

问题排查

Action Not Found

Action未找到

  • Verify repository is public or accessible
  • Check action.yml exists in repository root
  • Confirm version tag exists
  • 验证仓库是否公开或可访问
  • 检查action.yml是否存在于仓库根目录
  • 确认版本标签已创建

Permission Denied

权限拒绝

yaml
undefined
yaml
undefined

Add required permissions to workflow

为工作流添加所需权限

permissions: contents: write pull-requests: write
undefined
permissions: contents: write pull-requests: write
undefined

Node Modules Missing

Node Modules缺失

  • Bundle dependencies with ncc
  • Check dist/ folder is committed
  • Verify node_modules excluded from .gitignore for dist/
  • 使用ncc打包依赖
  • 确认dist/文件夹已提交
  • 验证.gitignore中是否排除了dist/下的node_modules

Docker Action Fails

Docker Action执行失败

  • Check Dockerfile syntax
  • Verify entrypoint has execute permissions
  • Test container locally:
    docker build -t test . && docker run test
  • 检查Dockerfile语法
  • 验证entrypoint文件具备执行权限
  • 本地测试容器:
    docker build -t test . && docker run test

Anti-Fabrication Requirements

防伪造要求

  • Execute Read or Glob tools to verify action files exist before claiming structure
  • Use Bash to test commands before documenting syntax
  • Validate action.yml schema against actual files using tool analysis
  • Execute actual API calls with @actions/github before documenting responses
  • Test permission configurations in real workflows before recommending settings
  • Never claim action capabilities without reading actual implementation code
  • Report actual npm audit results when discussing security, not fabricated vulnerability counts
  • 在声明文件结构前,使用Read或Glob工具验证Action文件是否存在
  • 在记录命令语法前,使用Bash测试命令
  • 使用工具分析验证action.yml schema与实际文件是否匹配
  • 在记录API响应前,使用@actions/github执行实际API调用
  • 在推荐权限配置前,在真实工作流中测试权限设置
  • 未阅读实际实现代码前,切勿宣称Action具备某项功能
  • 讨论安全问题时,报告实际的npm audit结果,而非伪造的漏洞数量