GitHub Actions 自动化:从 CI/CD 到自动发版

GitHub Actions 自动化:从 CI/CD 到自动发版

GitHub Actions 自动化:从 CI/CD 到自动发版

引言

在现代软件开发中,持续集成和持续部署(CI/CD) 已经成为提升交付效率的关键。GitHub Actions 是 GitHub 原生的自动化平台,能够将你的工作流程自动化,从代码提交到测试、构建、部署全流程。

为什么选择 GitHub Actions?

❌ 传统 CI/CD 工具的挑战:
  • 需要额外配置服务器
  • 管理复杂,成本高
  • 与 GitHub 集成度低
  • 学习曲线陡峭
✅ GitHub Actions 的优势:
  • 零配置开箱即用
  • 免费额度充足(2000 分钟/月)
  • 与 GitHub 深度集成
  • 丰富的社区 Action 生态
  • 支持私有仓库

适用场景:

  • 🚀 自动构建和测试
  • 📦 自动发布包(npm、PyPI、Docker)
  • 🌐 自动部署到云服务商
  • 📝 自动发布 Changelog
  • 🔔 自动发送通知(Slack、邮件)

适用读者: 有一定 Git 和 CI/CD 概念的后端、前端、全栈开发工程师

核心概念

1. GitHub Actions 架构

┌─────────────────────────────────────────────────────────────┐
│                      GitHub Actions 架构                      │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  Workflow (工作流)                                          │
│  └─ YAML 配置文件 (.github/workflows/*.yml)                 │
│      │                                                      │
│      ├─ Job (作业)                                          │
│      │   └─ 在 Runner 上运行的任务集合                       │
│      │       │                                               │
│      │       └─ Step (步骤)                                  │
│      │           └─ 可执行的命令或 Action                    │
│      │                                                       │
│      └─ Runner (运行器)                                     │
│          └─ 执行工作流的服务器                               │
│                                                             │
│  Action (动作)                                              │
│  └─ 可重用的代码单元                                        │
│                                                             │
└─────────────────────────────────────────────────────────────┘

2. 概念详解

Workflow(工作流):

  • 一个完整的自动化任务
  • 存储在 `.github/workflows/` 目录
  • YAML 格式定义

Job(作业):

  • 工作流中的一个执行单元
  • 可以并行运行多个 Job
  • 每个 Job 有自己的 Runner 环境

Step(步骤):

  • Job 中的最小执行单元
  • 可以是命令或 Action
  • 支持顺序或并行执行

Runner(运行器):

  • 执行工作流的服务器
  • GitHub 提供:ubuntu-latest, windows-latest, macos-latest
  • 也可以自建 Runner

Action(动作):

  • 可重用的代码单元
  • 可以是官方 Action 或自定义 Action
  • 简化重复工作

3. 工作流文件结构

“`yaml
name: CI Pipeline # 工作流名称

触发条件

on:
push:
branches: [main, develop]
pull_request:
branches: [main]

工作流设置

env:
NODE_VERSION: ’18’

jobs:
build: # 作业名称
runs-on: ubuntu-latest # 运行器

# 环境变量
env:
ENVIRONMENT: production

# 步骤
steps:

  • name: 检查代码

uses: actions/checkout@v4

  • name: 设置 Node.js

uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}


---

基础实践:创建第一个 CI 工作流

1. 基础 CI 工作流

yaml

.github/workflows/ci.yml

name: CI

on:
push:
branches: [main, develop]
pull_request:
branches: [main]

env:
NODE_VERSION: ’18’

jobs:
build-and-test:
runs-on: ubuntu-latest

steps:
# 步骤 1: 检出代码

  • name: Checkout code

uses: actions/checkout@v4

# 步骤 2: 设置 Node.js

  • name: Setup Node.js

uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: ‘npm’

# 步骤 3: 安装依赖

  • name: Install dependencies

run: npm ci

# 步骤 4: 运行 lint

  • name: Run lint

run: npm run lint

# 步骤 5: 运行测试

  • name: Run tests

run: npm test
env:
CI: true

# 步骤 6: 构建项目

  • name: Build

run: npm run build

# 步骤 7: 上传构建产物

  • name: Upload artifacts

uses: actions/upload-artifact@v4
with:
name: build-artifacts
path: dist/
retention-days: 7


2. 多步骤并行工作流

yaml

.github/workflows/ci-parallel.yml

name: CI Parallel

on:
push:
branches: [main]
pull_request:
branches: [main]

jobs:
lint:
runs-on: ubuntu-latest
steps:

  • uses: actions/checkout@v4
  • uses: actions/setup-node@v4

with:
node-version: ’18’
cache: ‘npm’

  • run: npm ci
  • run: npm run lint

test:
runs-on: ubuntu-latest
steps:

  • uses: actions/checkout@v4
  • uses: actions/setup-node@v4

with:
node-version: ’18’
cache: ‘npm’

  • run: npm ci
  • run: npm test
  • name: Upload coverage

uses: codecov/codecov-action@v4

build:
needs: [lint, test] # 依赖前面两个作业
runs-on: ubuntu-latest
steps:

  • uses: actions/checkout@v4
  • uses: actions/setup-node@v4

with:
node-version: ’18’
cache: ‘npm’

  • run: npm ci
  • run: npm run build
  • uses: actions/upload-artifact@v4

with:
name: dist
path: dist/


3. 工作流运行结果

运行结果示例:

✅ CI – Build and Test
✓ Checkout code (ubuntu-latest)
✓ Setup Node.js (ubuntu-latest)
✓ Install dependencies (ubuntu-latest)
✓ Run lint (ubuntu-latest)
✓ Run tests (ubuntu-latest)
✓ Build (ubuntu-latest)

耗时:3 分 42 秒
内存使用:1.2GB


---

进阶技巧

1. 环境变量和 Secrets

yaml

.github/workflows/with-secrets.yml

name: Deploy with Secrets

on:
push:
tags:

  • ‘v*’

env:
NODE_ENV: production
DEPLOY_ENV: staging

jobs:
deploy:
runs-on: ubuntu-latest
environment: production

steps:

  • uses: actions/checkout@v4
  • name: Setup Node.js

uses: actions/setup-node@v4
with:
node-version: ’18’
registry-url: ‘https://registry.npmjs.org’

  • name: Install and build

run: |
npm ci
npm run build

  • name: Deploy to AWS

uses: aws-actions/configure-aws-credentials@v4
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ap-northeast-1

  • name: Deploy to AWS

run: |
aws s3 sync dist/ s3://my-app-bucket/
aws cloudfront create-invalidation \
–distribution-id ${{ secrets.CLOUDFRONT_DISTRIBUTION_ID }} \
–paths “/*”

  • name: Deploy to NPM

run: npm publish
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}


2. 矩阵构建

yaml

.github/workflows/matrix-build.yml

name: Matrix Build

on:
push:
branches: [main]
pull_request:
branches: [main]

jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [16, 18, 20]
os: [ubuntu-latest, windows-latest, macos-latest]
exclude:

  • node-version: 16

os: windows-latest

steps:

  • uses: actions/checkout@v4
  • name: Setup Node.js ${{ matrix.node-version }}

uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}

  • name: Install dependencies

run: npm ci

  • name: Run tests

run: npm test

  • name: Upload coverage

uses: codecov/codecov-action@v4
if: matrix.node-version == 20

results:
needs: test
runs-on: ubuntu-latest
steps:

  • name: Show matrix results

run: |
echo “Matrix completed successfully!”
echo “Total matrix configurations: ${{ needs.test.result }}”


3. 条件触发

yaml

.github/workflows/conditional.yml

name: Conditional Workflow

on:
push:
branches: [main, develop]
pull_request:
branches: [main]

jobs:
lint:
runs-on: ubuntu-latest
if: github.event_name == ‘pull_request’
steps:

  • uses: actions/checkout@v4
  • run: npm run lint

test:
runs-on: ubuntu-latest
if: always()
steps:

  • uses: actions/checkout@v4
  • run: npm test

deploy-staging:
runs-on: ubuntu-latest
if: github.ref == ‘refs/heads/develop’
environment: staging
steps:

  • uses: actions/checkout@v4
  • run: npm run build:staging
  • name: Deploy to staging

run: echo “Deploying to staging…”

deploy-production:
runs-on: ubuntu-latest
if: github.ref == ‘refs/heads/main’
environment: production
steps:

  • uses: actions/checkout@v4
  • run: npm run build:production
  • name: Deploy to production

run: echo “Deploying to production…”

notify:
runs-on: ubuntu-latest
needs: [deploy-staging, deploy-production]
if: always()
steps:

  • name: Send notification

run: |
if [ “${{ needs.deploy-staging.result }}” == “success” ]; then
echo “Staging deployed”
fi
if [ “${{ needs.deploy-production.result }}” == “success” ]; then
echo “Production deployed”
fi


---

实战案例:多项目自动化

1. Node.js 项目

yaml

.github/workflows/nodejs-ci-cd.yml

name: Node.js CI/CD

on:
push:
branches: [main, develop]
paths:

  • ‘src/**’
  • ‘package.json’

pull_request:
branches: [main]

env:
NODE_VERSION: ’18’
NPM_REGISTRY: ‘https://registry.npmjs.org’

jobs:
test:
name: Test on Node ${{ matrix.node }}
runs-on: ubuntu-latest
strategy:
matrix:
node: [16, 18, 20]

steps:

  • uses: actions/checkout@v4
  • name: Setup Node.js

uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
cache: ‘npm’

  • name: Install dependencies

run: npm ci

  • name: Lint

run: npm run lint

  • name: Run tests

run: npm test
env:
CI: true

  • name: Upload coverage

uses: codecov/codecov-action@v4

build:
needs: test
runs-on: ubuntu-latest
steps:

  • uses: actions/checkout@v4
  • name: Setup Node.js

uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: ‘npm’

  • name: Install and build

run: |
npm ci
npm run build

  • name: Upload build artifact

uses: actions/upload-artifact@v4
with:
name: build
path: dist/
retention-days: 30

publish:
needs: build
runs-on: ubuntu-latest
if: startsWith(github.ref, ‘refs/tags/’)
environment: npm
steps:

  • uses: actions/checkout@v4
  • name: Setup Node.js

uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
registry-url: ${{ env.NPM_REGISTRY }}

  • name: Publish to NPM

run: npm publish
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}


2. Python 项目

yaml

.github/workflows/python-ci-cd.yml

name: Python CI/CD

on:
push:
branches: [main, develop]
pull_request:
branches: [main]

env:
PYTHON_VERSION: ‘3.11’

jobs:
lint:
runs-on: ubuntu-latest
steps:

  • uses: actions/checkout@v4
  • name: Set up Python

uses: actions/setup-python@v5
with:
python-version: ${{ env.PYTHON_VERSION }}
cache: ‘pip’

  • name: Install dependencies

run: |
pip install -r requirements.txt
pip install flake8 mypy pytest

  • name: Run flake8

run: |
flake8 src –count –select=E9,F63,F7,F82 –show-source –statistics
flake8 src –count –exit-zero –max-complexity=10 –max-line-length=127 –statistics

  • name: Run mypy

run: mypy src/

test:
runs-on: ubuntu-latest
needs: lint
strategy:
matrix:
python-version: [‘3.9’, ‘3.10’, ‘3.11’, ‘3.12’]

steps:

  • uses: actions/checkout@v4
  • name: Set up Python ${{ matrix.python-version }}

uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
cache: ‘pip’

  • name: Install dependencies

run: |
pip install -r requirements.txt
pip install pytest pytest-cov

  • name: Run tests

run: pytest –cov=src –cov-report=xml

  • name: Upload coverage

uses: codecov/codecov-action@v4

build-docker:
runs-on: ubuntu-latest
needs: test
if: github.event_name == ‘push’ && github.ref == ‘refs/heads/main’
steps:

  • uses: actions/checkout@v4
  • name: Set up Docker Buildx

uses: docker/setup-buildx-action@v3

  • name: Login to Docker Hub

uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}

  • name: Build and push

uses: docker/build-push-action@v5
with:
context: .
push: true
tags: |
${{ secrets.DOCKER_USERNAME }}/myapp:latest
${{ secrets.DOCKER_USERNAME }}/myapp:${{ github.sha }}

deploy:
runs-on: ubuntu-latest
needs: build-docker
environment: production
steps:

  • name: Deploy to Kubernetes

uses: Azure/k8s-deploy@v4
with:
action: set
manifests: k8s/
images: |
${{ secrets.DOCKER_USERNAME }}/myapp:${{ github.sha }}


3. Go 项目

yaml

.github/workflows/go-ci-cd.yml

name: Go CI/CD

on:
push:
branches: [main, develop]
pull_request:
branches: [main]

env:
GO_VERSION: ‘1.21’

jobs:
test:
runs-on: ubuntu-latest
steps:

  • uses: actions/checkout@v4
  • name: Set up Go

uses: actions/setup-go@v5
with:
go-version: ${{ env.GO_VERSION }}

  • name: Run tests

run: |
go test -v -cover ./…

  • name: Upload coverage

uses: codecov/codecov-action@v4

build:
runs-on: ubuntu-latest
needs: test
outputs:
version: ${{ steps.meta.outputs.version }}
steps:

  • uses: actions/checkout@v4
  • name: Set up Go

uses: actions/setup-go@v5
with:
go-version: ${{ env.GO_VERSION }}

  • name: Run tests

run: go build -v ./…

  • name: Get version

id: meta
run: |
echo “version=$(git describe –tags –always –dirty)” >> $GITHUB_OUTPUT

  • name: Build binary

run: |
go build -ldflags=”-s -w” -o myapp
tar czf myapp-${{ steps.meta.outputs.version }}.tar.gz myapp

  • name: Upload artifacts

uses: actions/upload-artifact@v4
with:
name: binaries
path: |
myapp-${{ steps.meta.outputs.version }}.tar.gz
retention-days: 30

release:
needs: build
runs-on: ubuntu-latest
if: startsWith(github.ref, ‘refs/tags/’)
steps:

  • uses: actions/checkout@v4
  • name: Download artifacts

uses: actions/download-artifact@v4
with:
name: binaries

  • name: Create GitHub Release

uses: softprops/action-gh-release@v1
with:
draft: false
prerelease: false
files: |
myapp-*.tar.gz


---

自动发版

1. Tag 触发版本发布

yaml

.github/workflows/release.yml

name: Release

on:
push:
tags:

  • ‘v*’

env:
APP_NAME: myapp
NODE_VERSION: ’18’

jobs:
create-release:
runs-on: ubuntu-latest
outputs:
upload_url: ${{ steps.create_release.outputs.upload_url }}
version: ${{ steps.get_version.outputs.version }}
steps:

  • name: Get version from tag

id: get_version
run: echo “version=${GITHUB_REF#refs/tags/}” >> $GITHUB_OUTPUT

  • name: Create Release

id: create_release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ steps.get_version.outputs.version }}
release_name: Release ${{ steps.get_version.outputs.version }}
draft: false
prerelease: false

build-and-publish:
needs: create-release
runs-on: ubuntu-latest
steps:

  • uses: actions/checkout@v4
  • name: Setup Node.js

uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
registry-url: ‘https://registry.npmjs.org’

  • name: Install and build

run: |
npm ci
npm run build

  • name: Publish to NPM

run: npm publish
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

  • name: Upload build artifacts

uses: actions/upload-artifact@v4
with:
name: dist
path: dist/

  • name: Upload to Release

uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ needs.create-release.outputs.upload_url }}
asset_path: dist/dist.zip
asset_name: myapp-${{ needs.create-release.outputs.version }}.zip
asset_content_type: application/zip

generate-changelog:
needs: create-release
runs-on: ubuntu-latest
steps:

  • uses: actions/checkout@v4

with:
fetch-depth: 0

  • name: Generate Changelog

id: changelog
uses: requarks/changelog-action@v1
with:
token: ${{ secrets.GITHUB_TOKEN }}
fromTag: ${{ github.event.repository.default_branch }}
toTag: ${{ github.ref }}
includeInvalidCommits: false
writeToFile: false

  • name: Update Release with Changelog

uses: actions/github-script@v6
env:
RELEASE_TAG: ${{ github.ref }}
CHANGELOG: ${{ steps.changelog.outputs.changes }}
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const { data: release } = await github.rest.repos.getReleaseByTag({
owner: context.repo.owner,
repo: context.repo.repo,
tag: process.env.RELEASE_TAG.replace(‘refs/tags/’, ”)
})
await github.rest.repos.updateRelease({
owner: context.repo.owner,
repo: context.repo.repo,
release_id: release.id,
body: process.env.CHANGELOG
})


2. 自动发布到 Docker Hub

yaml

.github/workflows/docker-publish.yml

name: Docker Publish

on:
push:
tags:

  • ‘v*’

env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}

jobs:
build-and-push:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write

steps:

  • name: Checkout repository

uses: actions/checkout@v4

  • name: Set up QEMU

uses: docker/setup-qemu-action@v3

  • name: Set up Docker Buildx

uses: docker/setup-buildx-action@v3

  • name: Log in to Container Registry

uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

  • name: Extract metadata

id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
type=sha

  • name: Build and push

uses: docker/build-push-action@v5
with:
context: .
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max


---

最佳实践

1. 缓存策略

yaml

使用缓存加速依赖安装

cache-dependency-path: |
package-lock.json
yarn.lock

Node.js 缓存

  • uses: actions/cache@v4

with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles(‘**/package-lock.json’) }}
restore-keys: |
${{ runner.os }}-node-

Python pip 缓存

  • uses: actions/cache@v4

with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles(‘**/requirements.txt’) }}
restore-keys: |
${{ runner.os }}-pip-

Go module 缓存

  • uses: actions/cache@v4

with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles(‘**/go.sum’) }}
restore-keys: |
${{ runner.os }}-go-


2. 超时控制

yaml
jobs:
slow-job:
runs-on: ubuntu-latest
timeout-minutes: 30 # 设置超时时间

steps:

  • name: Install dependencies

timeout-minutes: 10
run: npm ci

  • name: Run tests

timeout-minutes: 15
run: npm test

  • name: Build

timeout-minutes: 10
run: npm run build


3. 错误处理

yaml
jobs:
build:
runs-on: ubuntu-latest
steps:

  • uses: actions/checkout@v4
  • name: Install dependencies

run: npm ci
continue-on-error: false # 失败时中止工作流

  • name: Build

run: npm run build
continue-on-error: true # 即使失败也继续后续步骤

  • name: Notify on failure

if: failure()
run: |
echo “Build failed!”
# 发送通知到 Slack/钉钉等


4. 工作流组织

yaml

将通用步骤提取为复用模板

.github/workflows/ reusable-workflows/nodejs-base.yml

name: Node.js Base

on:
workflow_call:
inputs:
node-version:
required: true
type: string
cache-key:
required: true
type: string

jobs:
setup:
runs-on: ubuntu-latest
steps:

  • uses: actions/checkout@v4
  • name: Setup Node.js

uses: actions/setup-node@v4
with:
node-version: ${{ inputs.node-version }}
cache: ${{ inputs.cache-key }}

  • name: Install dependencies

run: npm ci

  • name: Lint

run: npm run lint

  • name: Test

run: npm test


---

总结

核心要点回顾

GitHub Actions 核心概念:Workflow、Job、Step、Runner、Action ✅ 触发器配置:push、pull_request、schedule、workflow_dispatch ✅ 环境变量和 Secrets:安全存储敏感信息 ✅ 矩阵构建:多平台、多版本并行测试 ✅ 自动发版:Tag 触发、版本发布、Changelog 生成

性能优化清单

✅ 缓存优化:

  • 依赖缓存(npm、pip、go mod)
  • 构建产物缓存
  • Docker 镜像缓存

✅ 超时控制:

  • 设置合理的 timeout-minutes
  • 区分各步骤的超时时间
  • 避免无限等待

✅ 并行处理:

  • 使用 matrix 并行测试
  • 分离 lint、test、build
  • 利用 needs 依赖关系

✅ 错误处理:

  • 使用 if/failure() 条件
  • 设置 continue-on-error
  • 添加通知步骤

推荐工作流

完整 CI/CD 工作流:

  1. 代码提交
  2. ├─ 触发条件(push、PR)
    └─ 运行 lint 检查

    1. 自动测试
    2. ├─ 多版本测试(矩阵)
      ├─ 单元测试
      └─ 代码覆盖率

      1. 构建发布
      2. ├─ 构建产物
        ├─ Docker 镜像
        └─ 上传到仓库

        1. 部署
        2. ├─ 测试环境
          ├─ 生产环境
          └─ 回滚机制

          1. 通知
          2. ├─ Slack/钉钉
            ├─ 邮件
            └─ Webhook
            “`

            *本文档最后更新时间:2026 年 04 月 28 日*
            *作者:creator | 适用 GitHub Actions 2024+*

            ![](https://img.freepik.com/free-vector/ci-cd-pipeline-illustration_114360-10348.jpg)

标签

发表评论