当前位置: 首页 > news >正文

【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 进阶篇,下一篇:《安全实践:密钥管理、镜像签名与供应链安全》。

http://www.cnnetsun.cn/news/3951243.html

相关文章:

  • Windows系统安全终极指南:OpenArk完整使用教程
  • 如何计算化妆品和香水的保质期?一看就懂的实用指南
  • Mac Mouse Fix终极指南:让你的普通鼠标变身macOS专业输入设备
  • 如何快速掌握具身智能核心技术:面向初学者的完整学习指南
  • OpenCensus-Java指标收集指南:从理论到实践的完整教程
  • 3分钟快速上手:Python免费获取通达信实时行情数据的终极指南
  • 技术方案:OpenCore Legacy Patcher实现旧款Mac现代化升级
  • YDoc 与其他静态站点生成器对比:为什么选择 YDoc?
  • FreeShow:打破语言壁垒,让演示文稿与世界对话
  • 终极招聘时间助手:5大优势让你轻松把握最佳求职时机
  • 鸿蒙6.1 arkui.observer UI观察器坑:uiObserver namespace真名不是observer
  • C++笔记之实现多态的所有方法
  • 教育辅导Agent:构建一个能出题、批改、讲解的AI家教
  • Jellyfin字幕管理终极指南:5步解决多语言字幕难题
  • 建筑工程消防施工质量验收规范-幕墙相关摘录
  • 基于STM32F4的心电监护仪
  • 3步解锁网易云音乐NCM格式:免费开源ncmdump完整指南
  • 高性能AI视频生成架构设计与实现:基于FramePack Studio的技术演进路线
  • 计算机毕业设计之基于spring boot的高校读书分享系统的设计与实现
  • 西安社区服务软件开发实战指南:从需求到上线全流程
  • 3大核心解决方案:OpCore Simplify如何让Hackintosh配置效率提升87%
  • 基于WebGL与Three.js实现移动端三维装配体交互查看与操作
  • FPS游戏瞄准训练:从无效练习到科学系统的提升指南
  • 如何让老旧Mac焕发新生:3步使用OpenCore Legacy Patcher完整指南
  • Pixelle-Video:AI全自动短视频生成终极指南
  • UE5 WorldPartition底层机制:从网格单元到运行时流送的开放世界架构
  • Unity物理系统实战:从碰撞检测到爆炸效果的完整实现与优化
  • Swift编译性能优化:从泛型开销到宏展开的深度实践
  • 逆向分析协作:评审结论怎样落地
  • 专业Android设备管理实战指南:5个高效技巧提升多设备控制效率