【CI/CD·Actions篇】进阶实战:复合 Action 开发与 Marketplace 利用

前言

上一篇文章你学会了用别人的 Action,本篇教你写自己的 Action------把重复的步骤封装成可复用的组件,发布到 Marketplace 让全世界的项目都能用。这是 GitHub Actions 生态的核心能力。


一、Action 的三种类型

| 类型 | 实现方式 | 适用场景 | 复杂度 |

|------|---------|---------|--------|

| Composite Action | YAML 组合 | 组合多个步骤为一个 Action | 低 |

| JavaScript Action | TypeScript/JS | 需要自定义逻辑和 UI 交互 | 中 |

| Docker Action | Dockerfile | 需要特定运行环境 | 中 |

**培训要点**:90% 的场景用 Composite Action 就够了------不需要写代码,纯 YAML 组合。先学这种。


二、Composite Action 开发

创建 Action 仓库

复制代码
my-deploy-action/
├── action.yml       # Action 定义文件
├── README.md
└── scripts/
    └── deploy.sh

action.yml 核心配置

复制代码
# action.yml
name: 'Deploy to Kubernetes'
description: 'Deploy a Docker image to Kubernetes with health check and auto-rollback'
author: 'Your Name'

# 定义输入参数
inputs:
  image:
    description: 'Docker image to deploy'
    required: true
  namespace:
    description: 'Kubernetes namespace'
    required: false
    default: 'default'
  deployment-name:
    description: 'Kubernetes Deployment name'
    required: true
  timeout:
    description: 'Rollout timeout in seconds'
    required: false
    default: '180'

# 定义输出
outputs:
  status:
    description: 'Deployment status (success/failed)'
    value: ${{ steps.deploy.outputs.status }}

runs:
  using: composite
  steps:
    # 第一步:部署
    - name: Deploy
      id: deploy
      shell: bash
      env:
        IMAGE: ${{ inputs.image }}
        NAMESPACE: ${{ inputs.namespace }}
        DEPLOYMENT: ${{ inputs.deployment-name }}
        TIMEOUT: ${{ inputs.timeout }}
        KUBECONFIG: ${{ env.KUBECONFIG }}
      run: |
        # 更新镜像
        kubectl set image deployment/${DEPLOYMENT} \
          app=${IMAGE} -n ${NAMESPACE}
        
        # 等待滚动更新完成
        if kubectl rollout status deployment/${DEPLOYMENT} \
          -n ${NAMESPACE} --timeout=${TIMEOUT}s; then
          echo "status=success" >> $GITHUB_OUTPUT
        else
          echo "status=failed" >> $GITHUB_OUTPUT
          # 自动回滚
          kubectl rollout undo deployment/${DEPLOYMENT} -n ${NAMESPACE}
          exit 1
        fi

    # 第二步:健康检查
    - name: Health check
      if: ${{ steps.deploy.outputs.status == 'success' }}
      shell: bash
      env:
        NAMESPACE: ${{ inputs.namespace }}
        DEPLOYMENT: ${{ inputs.deployment-name }}
      run: |
        for i in $(seq 1 12); do
          READY=$(kubectl get deployment ${DEPLOYMENT} -n ${NAMESPACE} \
            -o jsonpath='{.status.readyReplicas}')
          DESIRED=$(kubectl get deployment ${DEPLOYMENT} -n ${NAMESPACE} \
            -o jsonpath='{.status.replicas}')
          if [ "$READY" = "$DESIRED" ]; then
            echo "All pods ready ($READY/$DESIRED)"
            exit 0
          fi
          sleep 10
        done
        echo "Health check failed"
        exit 1

使用自己的 Action

复制代码
# 在其他项目的 Workflow 中使用
jobs:
  deploy:
    runs-on: [self-hosted, k8s]
    steps:
      - uses: actions/checkout@v4
      
      # 使用本地 Action(同一仓库内)
      - uses: ./.github/actions/deploy
        with:
          image: ghcr.io/myorg/myapp:latest
          namespace: production
          deployment-name: myapp
      
      # 使用其他仓库的 Action
      - uses: myorg/my-deploy-action@v1
        with:
          image: ghcr.io/myorg/myapp:${{ github.sha }}
          namespace: production
          deployment-name: myapp
          timeout: '300'

三、JavaScript Action 开发

项目结构

复制代码
js-action/
├── action.yml
├── package.json
├── tsconfig.json
├── src/
│   └── main.ts
├── dist/
│   └── index.js    # 编译后的 JS
└── .eslintrc.json

action.yml

复制代码
name: 'PR Comment Bot'
description: 'Add a comment to a PR with build status and test results'

inputs:
  pr-number:
    description: 'PR number'
    required: true
  status:
    description: 'Build status (success/failure)'
    required: true
  report-path:
    description: 'Path to test report'
    required: false

runs:
  using: node20
  main: dist/index.js

TypeScript 实现

复制代码
// src/main.ts
import * as core from '@actions/core';
import * as github from '@actions/github';
import * as fs from 'fs';

async function run(): Promise<void> {
  try {
    const prNumber = core.getInput('pr-number');
    const status = core.getInput('status');
    const reportPath = core.getInput('report-path');
    
    const token = core.getInput('github-token') || process.env.GITHUB_TOKEN;
    const octokit = github.getOctokit(token);
    
    // 读取测试报告
    let reportSummary = 'No report available';
    if (reportPath && fs.existsSync(reportPath)) {
      const report = fs.readFileSync(reportPath, 'utf-8');
      const parsed = JSON.parse(report);
      reportSummary = `Tests: ${parsed.total}, Passed: ${parsed.passed}, Failed: ${parsed.failed}`;
    }
    
    // 构建评论内容
    const statusEmoji = status === 'success' ? '✅' : '❌';
    const body = [
      `## ${statusEmoji} Build ${status}`,
      ``,
      `**Report:** ${reportSummary}`,
      ``,
      `**Commit:** ${github.context.sha.substring(0, 7)}`,
    ].join('\n');
    
    // 发表评论到 PR
    await octokit.rest.issues.createComment({
      ...github.context.repo,
      issue_number: parseInt(prNumber),
      body: body,
    });
    
    core.setOutput('comment-url', `PR #${prNumber}`);
    
  } catch (error) {
    core.setFailed((error as Error).message);
  }
}

run();

package.json

复制代码
{
  "name": "pr-comment-bot",
  "version": "1.0.0",
  "main": "dist/index.js",
  "scripts": {
    "build": "ncc build src/main.ts -o dist --source-map",
    "lint": "eslint src/**/*.ts"
  },
  "dependencies": {
    "@actions/core": "^1.10.0",
    "@actions/github": "^6.0.0"
  },
  "devDependencies": {
    "@vercel/ncc": "^0.38.0",
    "typescript": "^5.3.0"
  }
}

编译

复制代码
npm install
npm run build  # 用 ncc 打包成单文件

**踩坑提示**:JavaScript Action 必须用 `@vercel/ncc` 打包成单文件,不能直接引用 node_modules。因为 GitHub Actions 运行时只下载你的仓库,不会自动 npm install。


四、Action 版本管理

Tag 策略

复制代码
# 语义化版本 Tag
v1         → 指向最新的 1.x.x 版本(自动跟随 minor/patch 更新)
v1.0       → 指向最新的 1.0.x 版本
v1.0.0     → 固定版本

# 使用时:
- uses: myorg/my-action@v1        # 自动获取最新 1.x.x
- uses: myorg/my-action@v1.2      # 自动获取最新 1.2.x
- uses: myorg/my-action@v1.2.3    # 固定版本
- uses: myorg/my-action@main      # 跟随主分支(不推荐生产用)

发布到 GitHub Marketplace

  1. 在 Action 仓库 → Releases → Create a new release

  2. 选择 "Publish this Action to the GitHub Marketplace"

  3. 填写 Category(如 Continuous Integration)

  4. 发布

发布后,用户可以在 GitHub Marketplace 搜索到你的 Action:

复制代码
# 用户使用
- uses: your-name/my-deploy-action@v1
  with:
    image: myapp:latest

版本发布的最佳实践

复制代码
# .github/workflows/release.yml
name: Release

on:
  push:
    tags: ['v*']

jobs:
  release:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      # 自动创建/更新 major version tag (v1)
      - name: Update major tag
        uses: actions/publish-action@v0.3
        with:
          source-tag: ${{ github.ref_name }}  # 如 v1.2.3

五、Reusable Workflow(可复用工作流)

什么是 Reusable Workflow

Composite Action 封装的是步骤,Reusable Workflow 封装的是整个 Job------可以跨仓库复用完整的 CI/CD 流程。

定义 Reusable Workflow

复制代码
# .github/workflows/reusable-build.yml
name: Reusable Build

on:
  workflow_call:
    inputs:
      java-version:
        type: string
        required: false
        default: '17'
      run-tests:
        type: boolean
        required: false
        default: true
    secrets:
      sonar-token:
        required: false

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-java@v4
        with:
          java-version: ${{ inputs.java-version }}
          distribution: temurin
          cache: maven
      - run: mvn clean package -DskipTests
      - run: mvn test
        if: ${{ inputs.run-tests }}
      - uses: actions/upload-artifact@v4
        with:
          name: app-jar
          path: target/*.jar

调用 Reusable Workflow

复制代码
# .github/workflows/ci.yml
name: CI

on: [push, pull_request]

jobs:
  # 调用可复用工作流
  build:
    uses: myorg/ci-templates/.github/workflows/reusable-build.yml@v1
    with:
      java-version: '21'
      run-tests: true
    secrets:
      sonar-token: ${{ secrets.SONAR_TOKEN }}
  
  # 调用后继续自己的步骤
  deploy:
    needs: build
    runs-on: [self-hosted]
    steps:
      - uses: actions/download-artifact@v4
      - run: ./deploy.sh

组织级模板仓库

创建一个专门的 CI 模板仓库:

复制代码
ci-templates/
├── .github/workflows/
│   ├── reusable-build-java.yml     # Java 构建模板
│   ├── reusable-build-go.yml       # Go 构建模板
│   ├── reusable-build-node.yml     # Node 构建模板
│   ├── reusable-deploy-k8s.yml     # K8s 部署模板
│   └── reusable-security-scan.yml  # 安全扫描模板
└── README.md

所有项目只需几行调用:

复制代码
# 项目的 .github/workflows/ci.yml
name: CI
on: [push, pull_request]

jobs:
  build:
    uses: myorg/ci-templates/.github/workflows/reusable-build-java.yml@v1
    with:
      java-version: '17'
  
  deploy:
    needs: build
    if: github.ref == 'refs/heads/main'
    uses: myorg/ci-templates/.github/workflows/reusable-deploy-k8s.yml@v1
    with:
      namespace: production
    secrets:
      kubeconfig: ${{ secrets.KUBECONFIG_PROD }}

**培训要点**:Reusable Workflow 比 Composite Action 更强大------它可以定义完整的 Job(含 runs-on、services、environment),而 Composite Action 只是 Step 级别的复用。对于组织级的 CI/CD 标准化,用 Reusable Workflow 建立模板仓库。


六、本篇要点回顾

  1. 三种 Action 类型:Composite(YAML 组合,最简单)、JavaScript(自定义逻辑)、Docker(特定环境)

  2. Composite Action 用 using: composite 定义,通过 inputs 接收参数

  3. JavaScript Action 必须用 @vercel/ncc 打包成单文件

  4. 版本管理用语义化 Tag:@v1 自动跟随、@v1.2.3 固定版本

  5. Reusable Workflow 封装整个 Job,适合组织级 CI/CD 标准化

下一篇预告:进入 CI/CD 进阶篇,下一篇:《安全实践:密钥管理、镜像签名与供应链安全》。

相关推荐
智脑API20 小时前
Claude Code sandbox.failIfUnavailable 怎么用?沙箱启动失败、拒绝降级和 CI 安全
安全·ci/cd
小小测试开发1 天前
Trace 驱动回归:把线上 Agent 故障变成 CI 门禁的四步管道
人工智能·ci/cd·数据挖掘·回归
汪海游龙2 天前
告别 Play Console 手动上传:从模拟器截图到自动发版的完整流水线
android·ci/cd·github
minhuan2 天前
大模型应用CI/CD全流程解析:打通模型训练、评估、部署自动化,应用持续迭代实践23.7
ci/cd·自动化·大模型应用·大模型应用ci/cd全流程解析·大模型应用工程体系
凌云拓界3 天前
NodeVerdict | 性能门禁:把追踪数据变成 CI 规则
ci/cd·信息可视化·开源·node.js·bug·数据可视化·安全架构
空谷有来人4 天前
springcloud中配置ci/cd集成docker的配置文件
spring cloud·ci/cd·docker
key_3_feng5 天前
AI Native 下企业 CI/CD 最佳实践:AI 驱动的流水线设计与落地细节
人工智能·ci/cd
龙智DevSecOps解决方案5 天前
CI/CD | AI Agent 自主配置 TeamCity 构建链实战:从文档读取到流水线跑通全过程
ci/cd·持续集成·ai agent·teamcity
heimeiyingwang5 天前
【CI/CD·Jenkins篇】多节点构建:Agent 配置与任务分发策略
ci/cd·jenkins