GitHub Actions 第一个工作流 #
本节将通过一个完整的实践案例,带你创建一个真实的CI工作流,涵盖代码检查、测试和构建全过程。
项目准备 #
创建示例项目 #
我们创建一个简单的Node.js项目作为示例:
bash
# 创建项目目录
mkdir my-ci-demo
cd my-ci-demo
# 初始化项目
npm init -y
# 安装依赖
npm install --save-dev jest eslint prettier
# 创建项目结构
mkdir -p src tests
创建源代码 #
创建 src/index.js:
javascript
function add(a, b) {
return a + b;
}
function subtract(a, b) {
return a - b;
}
function multiply(a, b) {
return a * b;
}
function divide(a, b) {
if (b === 0) {
throw new Error('Division by zero');
}
return a / b;
}
module.exports = { add, subtract, multiply, divide };
创建测试文件 #
创建 tests/index.test.js:
javascript
const { add, subtract, multiply, divide } = require('../src/index');
describe('Calculator', () => {
test('adds 1 + 2 to equal 3', () => {
expect(add(1, 2)).toBe(3);
});
test('subtracts 5 - 3 to equal 2', () => {
expect(subtract(5, 3)).toBe(2);
});
test('multiplies 2 * 3 to equal 6', () => {
expect(multiply(2, 3)).toBe(6);
});
test('divides 6 / 2 to equal 3', () => {
expect(divide(6, 2)).toBe(3);
});
test('throws error when dividing by zero', () => {
expect(() => divide(1, 0)).toThrow('Division by zero');
});
});
配置package.json #
更新 package.json:
json
{
"name": "my-ci-demo",
"version": "1.0.0",
"scripts": {
"test": "jest",
"lint": "eslint src/",
"format": "prettier --write src/",
"format:check": "prettier --check src/"
},
"devDependencies": {
"eslint": "^8.57.0",
"jest": "^29.7.0",
"prettier": "^3.2.5"
}
}
创建ESLint配置 #
创建 .eslintrc.json:
json
{
"env": {
"node": true,
"es2021": true,
"jest": true
},
"extends": "eslint:recommended",
"parserOptions": {
"ecmaVersion": "latest",
"sourceType": "module"
}
}
创建Prettier配置 #
创建 .prettierrc:
json
{
"semi": true,
"singleQuote": true,
"tabWidth": 2,
"trailingComma": "es5"
}
创建CI工作流 #
基础版本 #
创建 .github/workflows/ci.yml:
yaml
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install dependencies
run: npm ci
- name: Run linter
run: npm run lint
- name: Run tests
run: npm test
进阶版本 - 添加缓存 #
yaml
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run linter
run: npm run lint
- name: Check formatting
run: npm run format:check
- name: Run tests
run: npm test -- --coverage
- name: Upload coverage
uses: actions/upload-artifact@v4
with:
name: coverage
path: coverage/
高级版本 - 矩阵构建 #
yaml
name: CI
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: '20'
cache: 'npm'
- run: npm ci
- run: npm run lint
- run: npm run format:check
test:
needs: lint
runs-on: ${{ matrix.os }}
strategy:
matrix:
node: [16, 18, 20]
os: [ubuntu-latest, windows-latest, macos-latest]
fail-fast: false
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
cache: 'npm'
- run: npm ci
- name: Run tests
run: npm test -- --coverage
- name: Upload coverage
if: matrix.node == '20' && matrix.os == 'ubuntu-latest'
uses: actions/upload-artifact@v4
with:
name: coverage
path: coverage/
build:
needs: test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- run: npm ci
- run: npm run build
if: ${{ hashFiles('package.json') != '' && contains(fromJson('{"scripts": {}}'), 'build') }}
- uses: actions/upload-artifact@v4
with:
name: build
path: dist/
工作流详解 #
触发条件 #
yaml
on:
push:
branches: [main] # 推送到main分支时触发
pull_request:
branches: [main] # 针对main分支的PR时触发
作业依赖 #
yaml
jobs:
lint:
# 第一个运行的作业
test:
needs: lint # 等待lint作业完成
build:
needs: test # 等待test作业完成
矩阵策略 #
yaml
strategy:
matrix:
node: [16, 18, 20] # 测试多个Node版本
os: [ubuntu-latest, windows-latest, macos-latest] # 测试多个操作系统
fail-fast: false # 一个失败不取消其他
条件执行 #
yaml
- name: Upload coverage
if: matrix.node == '20' && matrix.os == 'ubuntu-latest'
# 只在特定配置下上传覆盖率
添加状态徽章 #
在README.md中添加工作流状态徽章:
markdown

完整示例:
markdown
# My CI Demo

A simple Node.js project with GitHub Actions CI.
本地测试工作流 #
使用act工具 #
act 可以在本地运行GitHub Actions:
bash
# 安装act (macOS)
brew install act
# 列出工作流
act -l
# 运行push事件
act push
# 运行特定作业
act -j test
# 使用特定事件
act pull_request
运行结果示例 #
text
[CI/test] 🚀 Start image=node:16
[CI/test] 🐳 docker run image=node:16
[CI/test] ⭐ Run actions/checkout@v4
[CI/test] ✅ Success - actions/checkout@v4
[CI/test] ⭐ Run actions/setup-node@v4
[CI/test] ✅ Success - actions/setup-node@v4
[CI/test] ⭐ Run npm ci
[CI/test] ✅ Success - npm ci
[CI/test] ⭐ Run npm test
[CI/test] ✅ Success - npm test
查看运行结果 #
GitHub网页 #
- 进入仓库的 “Actions” 标签
- 选择工作流运行
- 查看每个步骤的日志
使用GitHub CLI #
bash
# 列出最近的工作流运行
gh run list --limit 5
# 查看特定运行
gh run view <run-id>
# 实时查看日志
gh run watch
# 下载日志
gh run download <run-id>
添加更多功能 #
代码覆盖率报告 #
yaml
- name: Run tests with coverage
run: npm test -- --coverage --reporters=default --reporters=jest-junit
env:
JEST_JUNIT_OUTPUT_DIR: ./coverage
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v4
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: ./coverage/lcov.info
依赖安全检查 #
yaml
- name: Run security audit
run: npm audit --audit-level=moderate
continue-on-error: true
通知 #
yaml
- name: Notify on failure
if: failure()
uses: slackapi/slack-github-action@v1
with:
channel-id: 'C0123456789'
slack-message: 'CI failed for ${{ github.repository }}'
env:
SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}
完整工作流示例 #
yaml
name: CI
on:
push:
branches: [main, develop]
paths-ignore:
- '**.md'
- 'docs/**'
pull_request:
branches: [main]
workflow_dispatch:
env:
NODE_VERSION: '20'
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
lint:
name: Lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run ESLint
run: npm run lint
- name: Check formatting
run: npm run format:check
test:
name: Test (Node ${{ matrix.node }} on ${{ matrix.os }})
needs: lint
runs-on: ${{ matrix.os }}
strategy:
matrix:
node: [16, 18, 20]
os: [ubuntu-latest]
include:
- node: 20
os: windows-latest
- node: 20
os: macos-latest
fail-fast: false
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm test -- --coverage
- name: Upload coverage
if: matrix.node == '20' && matrix.os == 'ubuntu-latest'
uses: actions/upload-artifact@v4
with:
name: coverage
path: coverage/
retention-days: 7
build:
name: Build
needs: test
runs-on: ubuntu-latest
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Build
run: npm run build
- uses: actions/upload-artifact@v4
with:
name: build
path: dist/
retention-days: 7
notify:
name: Notify
needs: [lint, test, build]
if: always()
runs-on: ubuntu-latest
steps:
- name: Determine status
id: status
run: |
if [ "${{ needs.build.result }}" == "success" ]; then
echo "status=success" >> $GITHUB_OUTPUT
echo "emoji=✅" >> $GITHUB_OUTPUT
elif [ "${{ needs.build.result }}" == "skipped" ]; then
echo "status=skipped" >> $GITHUB_OUTPUT
echo "emoji=⏭️" >> $GITHUB_OUTPUT
else
echo "status=failure" >> $GITHUB_OUTPUT
echo "emoji=❌" >> $GITHUB_OUTPUT
fi
- name: Log result
run: |
echo "## Workflow Result" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "${{ steps.status.outputs.emoji }} Status: ${{ steps.status.outputs.status }}" >> $GITHUB_STEP_SUMMARY
最佳实践总结 #
1. 使用缓存加速 #
yaml
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm' # 自动缓存npm依赖
2. 合理使用矩阵 #
yaml
strategy:
matrix:
include:
- node: 20
os: ubuntu-latest
experimental: false
3. 添加作业依赖 #
yaml
jobs:
test:
needs: lint
4. 使用并发控制 #
yaml
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
5. 条件执行 #
yaml
if: github.ref == 'refs/heads/main'
下一步学习 #
- 工作流Workflow - 深入理解工作流概念
- 作业Job - 学习作业的高级配置
- 触发器 - 了解更多触发方式
小结 #
- 创建完整的项目结构,包含源代码、测试和配置
- 工作流应包含lint、test、build等关键步骤
- 使用矩阵策略测试多版本和多平台
- 添加缓存加速构建过程
- 使用作业依赖控制执行顺序
- 添加状态徽章展示项目质量
最后更新:2026-03-28