尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

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

【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.shaction.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 statussuccess $GITHUB_OUTPUT else echo statusfailed $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/checkoutv4 # 使用本地 Action同一仓库内 - uses: ./.github/actions/deploy with: image: ghcr.io/myorg/myapp:latest namespace: production deployment-name: myapp # 使用其他仓库的 Action - uses: myorg/my-deploy-actionv1 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.jsonaction.ymlname: 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.jsTypeScript 实现// src/main.ts import * as core from actions/core; import * as github from actions/github; import * as fs from fs; async function run(): Promisevoid { 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-actionv1 # 自动获取最新 1.x.x - uses: myorg/my-actionv1.2 # 自动获取最新 1.2.x - uses: myorg/my-actionv1.2.3 # 固定版本 - uses: myorg/my-actionmain # 跟随主分支不推荐生产用发布到 GitHub Marketplace1. 在 Action 仓库 → Releases → Create a new release2. 选择 Publish this Action to the GitHub Marketplace3. 填写 Category如 Continuous Integration4. 发布发布后用户可以在 GitHub Marketplace 搜索到你的 Action# 用户使用 - uses: your-name/my-deploy-actionv1 with: image: myapp:latest版本发布的最佳实践# .github/workflows/release.yml name: Release on: push: tags: [v*] jobs: release: runs-on: ubuntu-latest steps: - uses: actions/checkoutv4 # 自动创建/更新 major version tag (v1) - name: Update major tag uses: actions/publish-actionv0.3 with: source-tag: ${{ github.ref_name }} # 如 v1.2.3五、Reusable Workflow可复用工作流什么是 Reusable WorkflowComposite 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/checkoutv4 - uses: actions/setup-javav4 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-artifactv4 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.ymlv1 with: java-version: 21 run-tests: true secrets: sonar-token: ${{ secrets.SONAR_TOKEN }} # 调用后继续自己的步骤 deploy: needs: build runs-on: [self-hosted] steps: - uses: actions/download-artifactv4 - 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.ymlv1 with: java-version: 17 deploy: needs: build if: github.ref refs/heads/main uses: myorg/ci-templates/.github/workflows/reusable-deploy-k8s.ymlv1 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 类型CompositeYAML 组合最简单、JavaScript自定义逻辑、Docker特定环境2. Composite Action 用using: composite定义通过inputs接收参数3. JavaScript Action 必须用vercel/ncc打包成单文件4. 版本管理用语义化 Tagv1自动跟随、v1.2.3固定版本5. Reusable Workflow 封装整个 Job适合组织级 CI/CD 标准化下一篇预告进入 CI/CD 进阶篇下一篇《安全实践密钥管理、镜像签名与供应链安全》。
返回列表