github-actions
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseGitHub Actions Skill
GitHub Actions 技能指南
Master GitHub Actions for CI/CD pipelines, automated testing, deployments, and repository automation. This skill covers workflow syntax, triggers, jobs, matrix builds, caching, artifacts, reusable workflows, and secrets management.
掌握GitHub Actions以实现CI/CD流水线、自动化测试、部署及仓库自动化。本技能涵盖工作流语法、触发器、任务、矩阵构建、缓存、制品、可复用工作流及密钥管理。
When to Use This Skill
何时使用本技能
USE when:
适用场景:
- Building CI/CD pipelines for GitHub repositories
- Automating tests across multiple OS/language versions
- Creating release and deployment workflows
- Publishing packages to npm, PyPI, Docker Hub
- Automating issue triage and PR management
- Scheduling periodic maintenance tasks
- Building reusable workflow components
- Implementing GitOps deployment patterns
- 为GitHub仓库构建CI/CD流水线
- 跨多操作系统/语言版本自动化测试
- 创建发布与部署工作流
- 向npm、PyPI、Docker Hub发布包
- 自动化问题分类与PR管理
- 定期调度维护任务
- 构建可复用工作流组件
- 实现GitOps部署模式
DON'T USE when:
不适用场景:
- Repository not hosted on GitHub (use Jenkins, GitLab CI)
- Need complex DAG-based workflow orchestration (use Airflow)
- Require visual workflow design (use n8n, Activepieces)
- Self-hosted runners not available for compute-intensive tasks
- Need real-time event processing (use dedicated message queues)
- 仓库未托管在GitHub(使用Jenkins、GitLab CI)
- 需要复杂的基于DAG的工作流编排(使用Airflow)
- 需要可视化工作流设计(使用n8n、Activepieces)
- 无自托管运行器用于计算密集型任务
- 需要实时事件处理(使用专用消息队列)
Prerequisites
前置条件
GitHub Repository Setup
GitHub仓库设置
bash
undefinedbash
undefinedCreate workflow directory
Create workflow directory
mkdir -p .github/workflows
mkdir -p .github/workflows
Verify GitHub CLI installed
Verify GitHub CLI installed
gh --version
gh --version
Authenticate with GitHub
Authenticate with GitHub
gh auth login
gh auth login
Check workflow permissions
Check workflow permissions
gh api repos/{owner}/{repo}/actions/permissions
undefinedgh api repos/{owner}/{repo}/actions/permissions
undefinedLocal Testing with act
使用act进行本地测试
bash
undefinedbash
undefinedInstall act for local workflow testing
Install act for local workflow testing
macOS
macOS
brew install act
brew install act
Linux
Linux
curl https://raw.githubusercontent.com/nektos/act/master/install.sh | sudo bash
curl https://raw.githubusercontent.com/nektos/act/master/install.sh | sudo bash
Verify installation
Verify installation
act --version
act --version
Run workflow locally
Run workflow locally
act -l # List available workflows
act push # Simulate push event
act pull_request # Simulate PR event
act -j build # Run specific job
undefinedact -l # List available workflows
act push # Simulate push event
act pull_request # Simulate PR event
act -j build # Run specific job
undefinedWorkflow Linting
工作流语法检查
bash
undefinedbash
undefinedInstall actionlint
Install actionlint
brew install actionlint # macOS
go install github.com/rhysd/actionlint/cmd/actionlint@latest # Go
brew install actionlint # macOS
go install github.com/rhysd/actionlint/cmd/actionlint@latest # Go
Lint workflows
Lint workflows
actionlint .github/workflows/*.yml
actionlint .github/workflows/*.yml
YAML validation
YAML validation
pip install yamllint
yamllint .github/workflows/
undefinedpip install yamllint
yamllint .github/workflows/
undefinedCore Capabilities
核心功能
1. Basic Workflow Structure
1. 基础工作流结构
yaml
undefinedyaml
undefined.github/workflows/ci.yml
.github/workflows/ci.yml
name: CI Pipeline
name: CI Pipeline
Workflow triggers
Workflow triggers
on:
push:
branches: [main, develop]
paths:
- 'src/'
- 'tests/'
- 'pyproject.toml'
pull_request:
branches: [main]
types: [opened, synchronize, reopened]
workflow_dispatch:
inputs:
environment:
description: 'Deployment environment'
required: true
default: 'staging'
type: choice
options:
- staging
- production
debug:
description: 'Enable debug mode'
required: false
type: boolean
default: false
on:
push:
branches: [main, develop]
paths:
- 'src/'
- 'tests/'
- 'pyproject.toml'
pull_request:
branches: [main]
types: [opened, synchronize, reopened]
workflow_dispatch:
inputs:
environment:
description: 'Deployment environment'
required: true
default: 'staging'
type: choice
options:
- staging
- production
debug:
description: 'Enable debug mode'
required: false
type: boolean
default: false
Environment variables for all jobs
Environment variables for all jobs
env:
PYTHON_VERSION: '3.11'
NODE_VERSION: '20'
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
env:
PYTHON_VERSION: '3.11'
NODE_VERSION: '20'
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
Concurrency control
Concurrency control
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
Workflow permissions
Workflow permissions
permissions:
contents: read
packages: write
pull-requests: write
jobs:
lint:
name: Code Quality
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: ${{ env.PYTHON_VERSION }}
cache: 'pip'
- name: Install linters
run: |
pip install ruff mypy
- name: Run linting
run: |
ruff check src/
ruff format --check src/
- name: Type checking
run: mypy src/ --ignore-missing-importstest:
name: Test Suite
needs: 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 -e ".[dev]"
- name: Run tests
run: |
pytest tests/ -v --cov=src --cov-report=xml --cov-report=html
- name: Upload coverage
uses: codecov/codecov-action@v4
with:
files: ./coverage.xml
fail_ci_if_error: truebuild:
name: Build Package
needs: test
runs-on: ubuntu-latest
outputs:
version: ${{ steps.version.outputs.version }}
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: Install build tools
run: pip install build
- name: Get version
id: version
run: |
VERSION=$(python -c "import tomllib; print(tomllib.load(open('pyproject.toml', 'rb'))['project']['version'])")
echo "version=$VERSION" >> $GITHUB_OUTPUT
- name: Build package
run: python -m build
- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: dist-${{ steps.version.outputs.version }}
path: dist/
retention-days: 5undefinedpermissions:
contents: read
packages: write
pull-requests: write
jobs:
lint:
name: Code Quality
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: ${{ env.PYTHON_VERSION }}
cache: 'pip'
- name: Install linters
run: |
pip install ruff mypy
- name: Run linting
run: |
ruff check src/
ruff format --check src/
- name: Type checking
run: mypy src/ --ignore-missing-importstest:
name: Test Suite
needs: 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 -e ".[dev]"
- name: Run tests
run: |
pytest tests/ -v --cov=src --cov-report=xml --cov-report=html
- name: Upload coverage
uses: codecov/codecov-action@v4
with:
files: ./coverage.xml
fail_ci_if_error: truebuild:
name: Build Package
needs: test
runs-on: ubuntu-latest
outputs:
version: ${{ steps.version.outputs.version }}
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: Install build tools
run: pip install build
- name: Get version
id: version
run: |
VERSION=$(python -c "import tomllib; print(tomllib.load(open('pyproject.toml', 'rb'))['project']['version'])")
echo "version=$VERSION" >> $GITHUB_OUTPUT
- name: Build package
run: python -m build
- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: dist-${{ steps.version.outputs.version }}
path: dist/
retention-days: 5undefined2. Matrix Builds for Cross-Platform Testing
2. 跨平台测试的矩阵构建
yaml
undefinedyaml
undefined.github/workflows/matrix-test.yml
.github/workflows/matrix-test.yml
name: Cross-Platform Tests
on:
push:
branches: [main]
pull_request:
jobs:
test-matrix:
name: Test (${{ matrix.os }}, Python ${{ matrix.python-version }})
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
max-parallel: 4
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
python-version: ['3.10', '3.11', '3.12']
include:
# Additional configuration for specific combinations
- os: ubuntu-latest
python-version: '3.12'
coverage: true
# Experimental Python version
- os: ubuntu-latest
python-version: '3.13-dev'
experimental: true
exclude:
# Skip Windows + Python 3.10 (known issues)
- os: windows-latest
python-version: '3.10'
continue-on-error: ${{ matrix.experimental || false }}
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'
cache-dependency-path: |
pyproject.toml
requirements*.txt
- name: Install dependencies (Unix)
if: runner.os != 'Windows'
run: |
pip install -e ".[dev]"
- name: Install dependencies (Windows)
if: runner.os == 'Windows'
run: |
pip install -e ".[dev]"
shell: pwsh
- name: Run tests
run: |
pytest tests/ -v --tb=short
env:
CI: true
PLATFORM: ${{ matrix.os }}
- name: Run tests with coverage
if: matrix.coverage
run: |
pytest tests/ -v --cov=src --cov-report=xml
- name: Upload coverage
if: matrix.coverage
uses: codecov/codecov-action@v4
with:
files: ./coverage.xmltest-summary:
name: Test Summary
needs: test-matrix
if: always()
runs-on: ubuntu-latest
steps:
- name: Check matrix results
run: |
if [ "${{ needs.test-matrix.result }}" == "failure" ]; then
echo "Some matrix jobs failed"
exit 1
fi
echo "All matrix jobs passed"
undefinedname: Cross-Platform Tests
on:
push:
branches: [main]
pull_request:
jobs:
test-matrix:
name: Test (${{ matrix.os }}, Python ${{ matrix.python-version }})
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
max-parallel: 4
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
python-version: ['3.10', '3.11', '3.12']
include:
# Additional configuration for specific combinations
- os: ubuntu-latest
python-version: '3.12'
coverage: true
# Experimental Python version
- os: ubuntu-latest
python-version: '3.13-dev'
experimental: true
exclude:
# Skip Windows + Python 3.10 (known issues)
- os: windows-latest
python-version: '3.10'
continue-on-error: ${{ matrix.experimental || false }}
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'
cache-dependency-path: |
pyproject.toml
requirements*.txt
- name: Install dependencies (Unix)
if: runner.os != 'Windows'
run: |
pip install -e ".[dev]"
- name: Install dependencies (Windows)
if: runner.os == 'Windows'
run: |
pip install -e ".[dev]"
shell: pwsh
- name: Run tests
run: |
pytest tests/ -v --tb=short
env:
CI: true
PLATFORM: ${{ matrix.os }}
- name: Run tests with coverage
if: matrix.coverage
run: |
pytest tests/ -v --cov=src --cov-report=xml
- name: Upload coverage
if: matrix.coverage
uses: codecov/codecov-action@v4
with:
files: ./coverage.xmltest-summary:
name: Test Summary
needs: test-matrix
if: always()
runs-on: ubuntu-latest
steps:
- name: Check matrix results
run: |
if [ "${{ needs.test-matrix.result }}" == "failure" ]; then
echo "Some matrix jobs failed"
exit 1
fi
echo "All matrix jobs passed"
undefined3. Caching Strategies
3. 缓存策略
yaml
undefinedyaml
undefined.github/workflows/caching.yml
.github/workflows/caching.yml
name: Build with Caching
on: [push, pull_request]
jobs:
build-with-cache:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# Python pip cache
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
cache: 'pip'
cache-dependency-path: |
requirements.txt
requirements-dev.txt
# Node modules cache
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
cache-dependency-path: package-lock.json
# Custom cache for build artifacts
- name: Cache build outputs
uses: actions/cache@v4
id: build-cache
with:
path: |
build/
dist/
.pytest_cache/
key: build-${{ runner.os }}-${{ hashFiles('src/**', 'pyproject.toml') }}
restore-keys: |
build-${{ runner.os }}-
# Docker layer cache
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Cache Docker layers
uses: actions/cache@v4
with:
path: /tmp/.buildx-cache
key: docker-${{ runner.os }}-${{ hashFiles('Dockerfile') }}
restore-keys: |
docker-${{ runner.os }}-
# Rust cargo cache
- name: Cache Cargo
uses: actions/cache@v4
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
target/
key: cargo-${{ runner.os }}-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
cargo-${{ runner.os }}-
- name: Skip build if cached
if: steps.build-cache.outputs.cache-hit == 'true'
run: echo "Using cached build artifacts"
- name: Build
if: steps.build-cache.outputs.cache-hit != 'true'
run: |
pip install -e ".[dev]"
python -m build
- name: Run tests
run: pytest tests/ -vundefinedname: Build with Caching
on: [push, pull_request]
jobs:
build-with-cache:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# Python pip cache
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
cache: 'pip'
cache-dependency-path: |
requirements.txt
requirements-dev.txt
# Node modules cache
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
cache-dependency-path: package-lock.json
# Custom cache for build artifacts
- name: Cache build outputs
uses: actions/cache@v4
id: build-cache
with:
path: |
build/
dist/
.pytest_cache/
key: build-${{ runner.os }}-${{ hashFiles('src/**', 'pyproject.toml') }}
restore-keys: |
build-${{ runner.os }}-
# Docker layer cache
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Cache Docker layers
uses: actions/cache@v4
with:
path: /tmp/.buildx-cache
key: docker-${{ runner.os }}-${{ hashFiles('Dockerfile') }}
restore-keys: |
docker-${{ runner.os }}-
# Rust cargo cache
- name: Cache Cargo
uses: actions/cache@v4
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
target/
key: cargo-${{ runner.os }}-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
cargo-${{ runner.os }}-
- name: Skip build if cached
if: steps.build-cache.outputs.cache-hit == 'true'
run: echo "Using cached build artifacts"
- name: Build
if: steps.build-cache.outputs.cache-hit != 'true'
run: |
pip install -e ".[dev]"
python -m build
- name: Run tests
run: pytest tests/ -vundefined4. Artifact Management
4. 制品管理
yaml
undefinedyaml
undefined.github/workflows/artifacts.yml
.github/workflows/artifacts.yml
name: Build and Release Artifacts
on:
push:
tags: ['v*']
workflow_dispatch:
jobs:
build-artifacts:
name: Build (${{ matrix.os }})
runs-on: ${{ matrix.os }}
strategy:
matrix:
include:
- os: ubuntu-latest
artifact_name: app-linux-x64
asset_name: app-linux-x64.tar.gz
- os: macos-latest
artifact_name: app-macos-x64
asset_name: app-macos-x64.tar.gz
- os: windows-latest
artifact_name: app-windows-x64
asset_name: app-windows-x64.zip
steps:
- uses: actions/checkout@v4
- name: Build application
run: |
echo "Building for ${{ matrix.os }}"
mkdir -p dist
# Build commands here
- name: Package (Unix)
if: runner.os != 'Windows'
run: |
tar -czvf ${{ matrix.asset_name }} -C dist .
- name: Package (Windows)
if: runner.os == 'Windows'
run: |
Compress-Archive -Path dist/* -DestinationPath ${{ matrix.asset_name }}
shell: pwsh
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.artifact_name }}
path: ${{ matrix.asset_name }}
retention-days: 30
compression-level: 9create-release:
name: Create Release
needs: build-artifacts
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v4
- name: Download all artifacts
uses: actions/download-artifact@v4
with:
path: artifacts/
merge-multiple: false
- name: List artifacts
run: find artifacts/ -type f
- name: Generate changelog
id: changelog
run: |
# Extract changelog for this version
VERSION=${GITHUB_REF#refs/tags/}
echo "version=$VERSION" >> $GITHUB_OUTPUT
# Generate release notes
cat << EOF > release_notes.md
## What's Changed
See [CHANGELOG.md](CHANGELOG.md) for details.
## Assets
- \`app-linux-x64.tar.gz\` - Linux (x64)
- \`app-macos-x64.tar.gz\` - macOS (x64)
- \`app-windows-x64.zip\` - Windows (x64)
EOF
- name: Create Release
uses: softprops/action-gh-release@v2
with:
name: Release ${{ steps.changelog.outputs.version }}
body_path: release_notes.md
draft: false
prerelease: ${{ contains(github.ref, 'alpha') || contains(github.ref, 'beta') }}
files: |
artifacts/**/*
generate_release_notes: trueundefinedname: Build and Release Artifacts
on:
push:
tags: ['v*']
workflow_dispatch:
jobs:
build-artifacts:
name: Build (${{ matrix.os }})
runs-on: ${{ matrix.os }}
strategy:
matrix:
include:
- os: ubuntu-latest
artifact_name: app-linux-x64
asset_name: app-linux-x64.tar.gz
- os: macos-latest
artifact_name: app-macos-x64
asset_name: app-macos-x64.tar.gz
- os: windows-latest
artifact_name: app-windows-x64
asset_name: app-windows-x64.zip
steps:
- uses: actions/checkout@v4
- name: Build application
run: |
echo "Building for ${{ matrix.os }}"
mkdir -p dist
# Build commands here
- name: Package (Unix)
if: runner.os != 'Windows'
run: |
tar -czvf ${{ matrix.asset_name }} -C dist .
- name: Package (Windows)
if: runner.os == 'Windows'
run: |
Compress-Archive -Path dist/* -DestinationPath ${{ matrix.asset_name }}
shell: pwsh
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.artifact_name }}
path: ${{ matrix.asset_name }}
retention-days: 30
compression-level: 9create-release:
name: Create Release
needs: build-artifacts
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v4
- name: Download all artifacts
uses: actions/download-artifact@v4
with:
path: artifacts/
merge-multiple: false
- name: List artifacts
run: find artifacts/ -type f
- name: Generate changelog
id: changelog
run: |
# Extract changelog for this version
VERSION=${GITHUB_REF#refs/tags/}
echo "version=$VERSION" >> $GITHUB_OUTPUT
# Generate release notes
cat << EOF > release_notes.md
## What's Changed
See [CHANGELOG.md](CHANGELOG.md) for details.
## Assets
- \`app-linux-x64.tar.gz\` - Linux (x64)
- \`app-macos-x64.tar.gz\` - macOS (x64)
- \`app-windows-x64.zip\` - Windows (x64)
EOF
- name: Create Release
uses: softprops/action-gh-release@v2
with:
name: Release ${{ steps.changelog.outputs.version }}
body_path: release_notes.md
draft: false
prerelease: ${{ contains(github.ref, 'alpha') || contains(github.ref, 'beta') }}
files: |
artifacts/**/*
generate_release_notes: trueundefined5. Reusable Workflows
5. 可复用工作流
yaml
undefinedyaml
undefined.github/workflows/reusable-python-ci.yml
.github/workflows/reusable-python-ci.yml
name: Reusable Python CI
on:
workflow_call:
inputs:
python-version:
description: 'Python version to use'
required: false
type: string
default: '3.11'
test-command:
description: 'Test command to run'
required: false
type: string
default: 'pytest tests/ -v'
coverage:
description: 'Enable coverage reporting'
required: false
type: boolean
default: true
install-extras:
description: 'Package extras to install'
required: false
type: string
default: 'dev'
secrets:
CODECOV_TOKEN:
description: 'Codecov upload token'
required: false
outputs:
coverage-percent:
description: 'Test coverage percentage'
value: ${{ jobs.test.outputs.coverage }}
jobs:
lint:
name: Lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: ${{ inputs.python-version }}
cache: 'pip'
- name: Install linters
run: pip install ruff
- name: Run ruff
run: ruff check .test:
name: Test
needs: lint
runs-on: ubuntu-latest
outputs:
coverage: ${{ steps.coverage.outputs.percent }}
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: ${{ inputs.python-version }}
cache: 'pip'
- name: Install dependencies
run: pip install -e ".[${{ inputs.install-extras }}]"
- name: Run tests
run: ${{ inputs.test-command }}
- name: Run tests with coverage
if: inputs.coverage
run: |
pip install pytest-cov
pytest tests/ -v --cov=src --cov-report=xml --cov-report=term-missing
- name: Extract coverage
if: inputs.coverage
id: coverage
run: |
COVERAGE=$(python -c "import xml.etree.ElementTree as ET; print(f\"{float(ET.parse('coverage.xml').getroot().get('line-rate')) * 100:.1f}\")")
echo "percent=$COVERAGE" >> $GITHUB_OUTPUT
- name: Upload coverage
if: inputs.coverage && secrets.CODECOV_TOKEN
uses: codecov/codecov-action@v4
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: ./coverage.xml
```yamlname: Reusable Python CI
on:
workflow_call:
inputs:
python-version:
description: 'Python version to use'
required: false
type: string
default: '3.11'
test-command:
description: 'Test command to run'
required: false
type: string
default: 'pytest tests/ -v'
coverage:
description: 'Enable coverage reporting'
required: false
type: boolean
default: true
install-extras:
description: 'Package extras to install'
required: false
type: string
default: 'dev'
secrets:
CODECOV_TOKEN:
description: 'Codecov upload token'
required: false
outputs:
coverage-percent:
description: 'Test coverage percentage'
value: ${{ jobs.test.outputs.coverage }}
jobs:
lint:
name: Lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: ${{ inputs.python-version }}
cache: 'pip'
- name: Install linters
run: pip install ruff
- name: Run ruff
run: ruff check .test:
name: Test
needs: lint
runs-on: ubuntu-latest
outputs:
coverage: ${{ steps.coverage.outputs.percent }}
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: ${{ inputs.python-version }}
cache: 'pip'
- name: Install dependencies
run: pip install -e ".[${{ inputs.install-extras }}]"
- name: Run tests
run: ${{ inputs.test-command }}
- name: Run tests with coverage
if: inputs.coverage
run: |
pip install pytest-cov
pytest tests/ -v --cov=src --cov-report=xml --cov-report=term-missing
- name: Extract coverage
if: inputs.coverage
id: coverage
run: |
COVERAGE=$(python -c "import xml.etree.ElementTree as ET; print(f\"{float(ET.parse('coverage.xml').getroot().get('line-rate')) * 100:.1f}\")")
echo "percent=$COVERAGE" >> $GITHUB_OUTPUT
- name: Upload coverage
if: inputs.coverage && secrets.CODECOV_TOKEN
uses: codecov/codecov-action@v4
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: ./coverage.xml
```yaml.github/workflows/ci.yml - Using the reusable workflow
.github/workflows/ci.yml - Using the reusable workflow
name: CI
on:
push:
branches: [main]
pull_request:
jobs:
python-ci:
uses: ./.github/workflows/reusable-python-ci.yml
with:
python-version: '3.11'
test-command: 'pytest tests/ -v --tb=short'
coverage: true
install-extras: 'dev,test'
secrets:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
undefinedname: CI
on:
push:
branches: [main]
pull_request:
jobs:
python-ci:
uses: ./.github/workflows/reusable-python-ci.yml
with:
python-version: '3.11'
test-command: 'pytest tests/ -v --tb=short'
coverage: true
install-extras: 'dev,test'
secrets:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
undefined6. Composite Actions
6. 复合动作
yaml
undefinedyaml
undefined.github/actions/setup-project/action.yml
.github/actions/setup-project/action.yml
name: 'Setup Project'
description: 'Set up Python environment with dependencies and caching'
inputs:
python-version:
description: 'Python version'
required: false
default: '3.11'
install-dev:
description: 'Install dev dependencies'
required: false
default: 'true'
working-directory:
description: 'Working directory'
required: false
default: '.'
outputs:
python-path:
description: 'Path to Python executable'
value: ${{ steps.setup-python.outputs.python-path }}
cache-hit:
description: 'Whether cache was hit'
value: ${{ steps.pip-cache.outputs.cache-hit }}
runs:
using: 'composite'
steps:
- name: Set up Python
id: setup-python
uses: actions/setup-python@v5
with:
python-version: ${{ inputs.python-version }}
- name: Get pip cache dir
id: pip-cache-dir
shell: bash
run: echo "dir=$(pip cache dir)" >> $GITHUB_OUTPUT
- name: Cache pip
id: pip-cache
uses: actions/cache@v4
with:
path: ${{ steps.pip-cache-dir.outputs.dir }}
key: pip-${{ runner.os }}-${{ inputs.python-version }}-${{ hashFiles('**/pyproject.toml', '**/requirements*.txt') }}
restore-keys: |
pip-${{ runner.os }}-${{ inputs.python-version }}-
pip-${{ runner.os }}-
- name: Install base dependencies
shell: bash
working-directory: ${{ inputs.working-directory }}
run: |
python -m pip install --upgrade pip wheel setuptools
- name: Install project
shell: bash
working-directory: ${{ inputs.working-directory }}
run: |
if [ "${{ inputs.install-dev }}" == "true" ]; then
pip install -e ".[dev]"
else
pip install -e .
fi
```yamlname: 'Setup Project'
description: 'Set up Python environment with dependencies and caching'
inputs:
python-version:
description: 'Python version'
required: false
default: '3.11'
install-dev:
description: 'Install dev dependencies'
required: false
default: 'true'
working-directory:
description: 'Working directory'
required: false
default: '.'
outputs:
python-path:
description: 'Path to Python executable'
value: ${{ steps.setup-python.outputs.python-path }}
cache-hit:
description: 'Whether cache was hit'
value: ${{ steps.pip-cache.outputs.cache-hit }}
runs:
using: 'composite'
steps:
- name: Set up Python
id: setup-python
uses: actions/setup-python@v5
with:
python-version: ${{ inputs.python-version }}
- name: Get pip cache dir
id: pip-cache-dir
shell: bash
run: echo "dir=$(pip cache dir)" >> $GITHUB_OUTPUT
- name: Cache pip
id: pip-cache
uses: actions/cache@v4
with:
path: ${{ steps.pip-cache-dir.outputs.dir }}
key: pip-${{ runner.os }}-${{ inputs.python-version }}-${{ hashFiles('**/pyproject.toml', '**/requirements*.txt') }}
restore-keys: |
pip-${{ runner.os }}-${{ inputs.python-version }}-
pip-${{ runner.os }}-
- name: Install base dependencies
shell: bash
working-directory: ${{ inputs.working-directory }}
run: |
python -m pip install --upgrade pip wheel setuptools
- name: Install project
shell: bash
working-directory: ${{ inputs.working-directory }}
run: |
if [ "${{ inputs.install-dev }}" == "true" ]; then
pip install -e ".[dev]"
else
pip install -e .
fi
```yamlUsing the composite action
Using the composite action
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Project
uses: ./.github/actions/setup-project
with:
python-version: '3.11'
install-dev: 'true'
- name: Run tests
run: pytest tests/ -vundefinedjobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Project
uses: ./.github/actions/setup-project
with:
python-version: '3.11'
install-dev: 'true'
- name: Run tests
run: pytest tests/ -vundefined7. Secrets and Environment Management
7. 密钥与环境管理
yaml
undefinedyaml
undefined.github/workflows/deploy.yml
.github/workflows/deploy.yml
name: Deploy
on:
push:
branches: [main]
workflow_dispatch:
inputs:
environment:
type: environment
description: 'Select environment'
required: true
jobs:
deploy-staging:
name: Deploy to Staging
runs-on: ubuntu-latest
environment:
name: staging
url: https://staging.example.com
steps:
- uses: actions/checkout@v4
- name: Configure AWS credentials
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: us-east-1
- name: Deploy to staging
env:
DATABASE_URL: ${{ secrets.DATABASE_URL }}
API_KEY: ${{ secrets.API_KEY }}
ENVIRONMENT: staging
run: |
echo "Deploying to staging..."
# Deployment commandsdeploy-production:
name: Deploy to Production
needs: deploy-staging
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
environment:
name: production
url: https://example.com
concurrency:
group: production-deploy
cancel-in-progress: false
steps:
- uses: actions/checkout@v4
- name: Configure AWS credentials
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: us-east-1
- name: Deploy to production
env:
DATABASE_URL: ${{ secrets.DATABASE_URL }}
API_KEY: ${{ secrets.API_KEY }}
ENVIRONMENT: production
run: |
echo "Deploying to production..."
# Deployment commands
- name: Notify on failure
if: failure()
uses: slackapi/slack-github-action@v1
with:
payload: |
{
"text": "Production deployment failed!",
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "*Production Deployment Failed*\nWorkflow: ${{ github.workflow }}\nRun: ${{ github.run_id }}"
}
}
]
}
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }}undefinedname: Deploy
on:
push:
branches: [main]
workflow_dispatch:
inputs:
environment:
type: environment
description: 'Select environment'
required: true
jobs:
deploy-staging:
name: Deploy to Staging
runs-on: ubuntu-latest
environment:
name: staging
url: https://staging.example.com
steps:
- uses: actions/checkout@v4
- name: Configure AWS credentials
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: us-east-1
- name: Deploy to staging
env:
DATABASE_URL: ${{ secrets.DATABASE_URL }}
API_KEY: ${{ secrets.API_KEY }}
ENVIRONMENT: staging
run: |
echo "Deploying to staging..."
# Deployment commandsdeploy-production:
name: Deploy to Production
needs: deploy-staging
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
environment:
name: production
url: https://example.com
concurrency:
group: production-deploy
cancel-in-progress: false
steps:
- uses: actions/checkout@v4
- name: Configure AWS credentials
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: us-east-1
- name: Deploy to production
env:
DATABASE_URL: ${{ secrets.DATABASE_URL }}
API_KEY: ${{ secrets.API_KEY }}
ENVIRONMENT: production
run: |
echo "Deploying to production..."
# Deployment commands
- name: Notify on failure
if: failure()
uses: slackapi/slack-github-action@v1
with:
payload: |
{
"text": "Production deployment failed!",
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "*Production Deployment Failed*\nWorkflow: ${{ github.workflow }}\nRun: ${{ github.run_id }}"
}
}
]
}
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }}undefined8. Container Builds and Registry Publishing
8. 容器构建与镜像仓库发布
yaml
undefinedyaml
undefined.github/workflows/docker.yml
.github/workflows/docker.yml
name: Docker Build and Push
on:
push:
branches: [main]
tags: ['v*']
pull_request:
branches: [main]
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
build-and-push:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
id-token: write # For signing
steps:
- name: Checkout
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: Login to Container Registry
if: github.event_name != 'pull_request'
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=ref,event=branch
type=ref,event=pr
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
type=sha,prefix=
- name: Build and push
uses: docker/build-push-action@v5
with:
context: .
platforms: linux/amd64,linux/arm64
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
build-args: |
VERSION=${{ github.ref_name }}
BUILD_DATE=${{ github.event.head_commit.timestamp }}
- name: Sign image
if: github.event_name != 'pull_request'
uses: sigstore/cosign-installer@v3
- name: Sign container image
if: github.event_name != 'pull_request'
run: |
cosign sign --yes ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}@${{ steps.build-and-push.outputs.digest }}undefinedname: Docker Build and Push
on:
push:
branches: [main]
tags: ['v*']
pull_request:
branches: [main]
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
build-and-push:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
id-token: write # For signing
steps:
- name: Checkout
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: Login to Container Registry
if: github.event_name != 'pull_request'
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=ref,event=branch
type=ref,event=pr
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
type=sha,prefix=
- name: Build and push
uses: docker/build-push-action@v5
with:
context: .
platforms: linux/amd64,linux/arm64
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
build-args: |
VERSION=${{ github.ref_name }}
BUILD_DATE=${{ github.event.head_commit.timestamp }}
- name: Sign image
if: github.event_name != 'pull_request'
uses: sigstore/cosign-installer@v3
- name: Sign container image
if: github.event_name != 'pull_request'
run: |
cosign sign --yes ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}@${{ steps.build-and-push.outputs.digest }}undefined9. Scheduled Workflows and Maintenance
9. 定时工作流与维护
yaml
undefinedyaml
undefined.github/workflows/maintenance.yml
.github/workflows/maintenance.yml
name: Repository Maintenance
on:
schedule:
# Run every Monday at 6 AM UTC
- cron: '0 6 * * 1'
workflow_dispatch:
permissions:
contents: write
issues: write
pull-requests: write
jobs:
dependency-update:
name: Update Dependencies
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Update dependencies
run: |
pip install pip-tools
pip-compile --upgrade requirements.in -o requirements.txt
pip-compile --upgrade requirements-dev.in -o requirements-dev.txt
- name: Create PR if changes
uses: peter-evans/create-pull-request@v6
with:
token: ${{ secrets.GITHUB_TOKEN }}
commit-message: 'chore(deps): update dependencies'
title: 'chore(deps): Weekly dependency update'
body: |
Automated weekly dependency update.
Please review the changes and merge if CI passes.
branch: deps/weekly-update
delete-branch: true
labels: dependencies,automatedstale-issues:
name: Close Stale Issues
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v9
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
stale-issue-message: |
This issue has been automatically marked as stale because it has not had recent activity.
It will be closed in 7 days if no further activity occurs.
stale-pr-message: |
This PR has been automatically marked as stale because it has not had recent activity.
It will be closed in 7 days if no further activity occurs.
stale-issue-label: 'stale'
stale-pr-label: 'stale'
days-before-stale: 60
days-before-close: 7
exempt-issue-labels: 'pinned,security,enhancement'
exempt-pr-labels: 'pinned,security'
security-scan:
name: Security Audit
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@master
with:
scan-type: 'fs'
ignore-unfixed: true
format: 'sarif'
output: 'trivy-results.sarif'
severity: 'CRITICAL,HIGH'
- name: Upload Trivy scan results
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: 'trivy-results.sarif'
- name: Python safety check
run: |
pip install safety
safety check --full-report || trueundefinedname: Repository Maintenance
on:
schedule:
# Run every Monday at 6 AM UTC
- cron: '0 6 * * 1'
workflow_dispatch:
permissions:
contents: write
issues: write
pull-requests: write
jobs:
dependency-update:
name: Update Dependencies
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Update dependencies
run: |
pip install pip-tools
pip-compile --upgrade requirements.in -o requirements.txt
pip-compile --upgrade requirements-dev.in -o requirements-dev.txt
- name: Create PR if changes
uses: peter-evans/create-pull-request@v6
with:
token: ${{ secrets.GITHUB_TOKEN }}
commit-message: 'chore(deps): update dependencies'
title: 'chore(deps): Weekly dependency update'
body: |
Automated weekly dependency update.
Please review the changes and merge if CI passes.
branch: deps/weekly-update
delete-branch: true
labels: dependencies,automatedstale-issues:
name: Close Stale Issues
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v9
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
stale-issue-message: |
This issue has been automatically marked as stale because it has not had recent activity.
It will be closed in 7 days if no further activity occurs.
stale-pr-message: |
This PR has been automatically marked as stale because it has not had recent activity.
It will be closed in 7 days if no further activity occurs.
stale-issue-label: 'stale'
stale-pr-label: 'stale'
days-before-stale: 60
days-before-close: 7
exempt-issue-labels: 'pinned,security,enhancement'
exempt-pr-labels: 'pinned,security'
security-scan:
name: Security Audit
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@master
with:
scan-type: 'fs'
ignore-unfixed: true
format: 'sarif'
output: 'trivy-results.sarif'
severity: 'CRITICAL,HIGH'
- name: Upload Trivy scan results
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: 'trivy-results.sarif'
- name: Python safety check
run: |
pip install safety
safety check --full-report || trueundefined10. PR Automation and Checks
10. PR自动化与检查
yaml
undefinedyaml
undefined.github/workflows/pr-checks.yml
.github/workflows/pr-checks.yml
name: PR Checks
on:
pull_request:
types: [opened, synchronize, reopened, edited]
permissions:
contents: read
pull-requests: write
jobs:
validate-pr:
name: Validate PR
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Check PR title
uses: amannn/action-semantic-pull-request@v5
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
types: |
feat
fix
docs
style
refactor
perf
test
build
ci
chore
revert
requireScope: false
subjectPattern: ^.{1,50}$
subjectPatternError: |
PR title must be 50 characters or less
- name: Check for breaking changes
if: contains(github.event.pull_request.title, '!')
run: |
echo "::warning::This PR contains breaking changes"
- name: Add size labels
uses: codelytv/pr-size-labeler@v1
with:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
xs_label: 'size/xs'
xs_max_size: 10
s_label: 'size/s'
s_max_size: 100
m_label: 'size/m'
m_max_size: 500
l_label: 'size/l'
l_max_size: 1000
xl_label: 'size/xl'
fail_if_xl: false
message_if_xl: |
This PR is very large. Please consider breaking it into smaller PRs.auto-assign:
name: Auto Assign
runs-on: ubuntu-latest
steps:
- name: Auto-assign author
uses: kentaro-m/auto-assign-action@v1
with:
configuration-path: '.github/auto-assign.yml'
label-pr:
name: Label PR
runs-on: ubuntu-latest
steps:
- uses: actions/labeler@v5
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
configuration-path: .github/labeler.yml
undefinedname: PR Checks
on:
pull_request:
types: [opened, synchronize, reopened, edited]
permissions:
contents: read
pull-requests: write
jobs:
validate-pr:
name: Validate PR
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Check PR title
uses: amannn/action-semantic-pull-request@v5
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
types: |
feat
fix
docs
style
refactor
perf
test
build
ci
chore
revert
requireScope: false
subjectPattern: ^.{1,50}$
subjectPatternError: |
PR title must be 50 characters or less
- name: Check for breaking changes
if: contains(github.event.pull_request.title, '!')
run: |
echo "::warning::This PR contains breaking changes"
- name: Add size labels
uses: codelytv/pr-size-labeler@v1
with:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
xs_label: 'size/xs'
xs_max_size: 10
s_label: 'size/s'
s_max_size: 100
m_label: 'size/m'
m_max_size: 500
l_label: 'size/l'
l_max_size: 1000
xl_label: 'size/xl'
fail_if_xl: false
message_if_xl: |
This PR is very large. Please consider breaking it into smaller PRs.auto-assign:
name: Auto Assign
runs-on: ubuntu-latest
steps:
- name: Auto-assign author
uses: kentaro-m/auto-assign-action@v1
with:
configuration-path: '.github/auto-assign.yml'
label-pr:
name: Label PR
runs-on: ubuntu-latest
steps:
- uses: actions/labeler@v5
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
configuration-path: .github/labeler.yml
undefinedIntegration Examples
集成示例
Integration with Slack Notifications
与Slack通知集成
yaml
undefinedyaml
undefined.github/workflows/notify.yml
.github/workflows/notify.yml
name: Slack Notifications
on:
workflow_run:
workflows: ["CI Pipeline"]
types: [completed]
jobs:
notify:
runs-on: ubuntu-latest
steps:
- name: Notify Slack
uses: slackapi/slack-github-action@v1
with:
payload: |
{
"blocks": [
{
"type": "header",
"text": {
"type": "plain_text",
"text": "${{ github.event.workflow_run.conclusion == 'success' && 'Build Succeeded' || 'Build Failed' }}"
}
},
{
"type": "section",
"fields": [
{
"type": "mrkdwn",
"text": "Repository:\n${{ github.repository }}"
},
{
"type": "mrkdwn",
"text": "Branch:\n${{ github.event.workflow_run.head_branch }}"
},
{
"type": "mrkdwn",
"text": "Commit:\n<${{ github.event.workflow_run.head_commit.url }}|${{ github.event.workflow_run.head_sha }}>"
},
{
"type": "mrkdwn",
"text": "Author:\n${{ github.event.workflow_run.head_commit.author.name }}"
}
]
},
{
"type": "actions",
"elements": [
{
"type": "button",
"text": {
"type": "plain_text",
"text": "View Run"
},
"url": "${{ github.event.workflow_run.html_url }}"
}
]
}
]
}
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }}
SLACK_WEBHOOK_TYPE: INCOMING_WEBHOOK
undefinedname: Slack Notifications
on:
workflow_run:
workflows: ["CI Pipeline"]
types: [completed]
jobs:
notify:
runs-on: ubuntu-latest
steps:
- name: Notify Slack
uses: slackapi/slack-github-action@v1
with:
payload: |
{
"blocks": [
{
"type": "header",
"text": {
"type": "plain_text",
"text": "${{ github.event.workflow_run.conclusion == 'success' && 'Build Succeeded' || 'Build Failed' }}"
}
},
{
"type": "section",
"fields": [
{
"type": "mrkdwn",
"text": "Repository:\n${{ github.repository }}"
},
{
"type": "mrkdwn",
"text": "Branch:\n${{ github.event.workflow_run.head_branch }}"
},
{
"type": "mrkdwn",
"text": "Commit:\n<${{ github.event.workflow_run.head_commit.url }}|${{ github.event.workflow_run.head_sha }}>"
},
{
"type": "mrkdwn",
"text": "Author:\n${{ github.event.workflow_run.head_commit.author.name }}"
}
]
},
{
"type": "actions",
"elements": [
{
"type": "button",
"text": {
"type": "plain_text",
"text": "View Run"
},
"url": "${{ github.event.workflow_run.html_url }}"
}
]
}
]
}
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }}
SLACK_WEBHOOK_TYPE: INCOMING_WEBHOOK
undefinedIntegration with AWS Deployment
与AWS部署集成
yaml
undefinedyaml
undefined.github/workflows/aws-deploy.yml
.github/workflows/aws-deploy.yml
name: AWS Deployment
on:
push:
branches: [main]
jobs:
deploy-lambda:
runs-on: ubuntu-latest
permissions:
id-token: write
contents: read
steps:
- uses: actions/checkout@v4
- name: Configure AWS credentials (OIDC)
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
aws-region: us-east-1
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install SAM CLI
run: pip install aws-sam-cli
- name: Build
run: sam build
- name: Deploy
run: |
sam deploy \
--no-confirm-changeset \
--no-fail-on-empty-changeset \
--stack-name my-app \
--capabilities CAPABILITY_IAM \
--parameter-overrides Environment=productionundefinedname: AWS Deployment
on:
push:
branches: [main]
jobs:
deploy-lambda:
runs-on: ubuntu-latest
permissions:
id-token: write
contents: read
steps:
- uses: actions/checkout@v4
- name: Configure AWS credentials (OIDC)
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
aws-region: us-east-1
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install SAM CLI
run: pip install aws-sam-cli
- name: Build
run: sam build
- name: Deploy
run: |
sam deploy \
--no-confirm-changeset \
--no-fail-on-empty-changeset \
--stack-name my-app \
--capabilities CAPABILITY_IAM \
--parameter-overrides Environment=productionundefinedBest Practices
最佳实践
1. Security Best Practices
1. 安全最佳实践
yaml
undefinedyaml
undefinedAlways pin action versions with SHA
Always pin action versions with SHA
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
Use minimum required permissions
Use minimum required permissions
permissions:
contents: read
packages: write
permissions:
contents: read
packages: write
Never hardcode secrets
Never hardcode secrets
env:
API_KEY: ${{ secrets.API_KEY }} # Good
API_KEY: "sk-1234567890" # Never do this
env:
API_KEY: ${{ secrets.API_KEY }} # Good
API_KEY: "sk-1234567890" # Never do this
Use OIDC for cloud provider authentication
Use OIDC for cloud provider authentication
- uses: aws-actions/configure-aws-credentials@v4 with: role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
undefined- uses: aws-actions/configure-aws-credentials@v4 with: role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
undefined2. Performance Optimization
2. 性能优化
yaml
undefinedyaml
undefinedUse caching aggressively
Use caching aggressively
- uses: actions/cache@v4 with: path: ~/.cache/pip key: pip-${{ hashFiles('requirements.txt') }}
- uses: actions/cache@v4 with: path: ~/.cache/pip key: pip-${{ hashFiles('requirements.txt') }}
Use matrix fail-fast wisely
Use matrix fail-fast wisely
strategy:
fail-fast: false # Continue other jobs on failure
strategy:
fail-fast: false # Continue other jobs on failure
Limit concurrent runs
Limit concurrent runs
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
undefinedconcurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
undefined3. Maintainability
3. 可维护性
yaml
undefinedyaml
undefinedUse reusable workflows
Use reusable workflows
jobs:
test:
uses: ./.github/workflows/reusable-test.yml
jobs:
test:
uses: ./.github/workflows/reusable-test.yml
Extract common steps into composite actions
Extract common steps into composite actions
- uses: ./.github/actions/setup-project
- uses: ./.github/actions/setup-project
Use environment variables for configuration
Use environment variables for configuration
env:
PYTHON_VERSION: '3.11'
NODE_VERSION: '20'
undefinedenv:
PYTHON_VERSION: '3.11'
NODE_VERSION: '20'
undefined4. Error Handling
4. 错误处理
yaml
undefinedyaml
undefinedUse continue-on-error for non-critical steps
Use continue-on-error for non-critical steps
- name: Optional step continue-on-error: true run: optional-command
- name: Optional step continue-on-error: true run: optional-command
Add timeout to prevent stuck jobs
Add timeout to prevent stuck jobs
jobs:
build:
timeout-minutes: 30
jobs:
build:
timeout-minutes: 30
Always clean up resources
Always clean up resources
- name: Cleanup if: always() run: cleanup-command
undefined- name: Cleanup if: always() run: cleanup-command
undefinedTroubleshooting
故障排除
Common Issues
常见问题
Issue: Workflow not triggering
yaml
undefinedIssue: Workflow not triggering
yaml
undefinedCheck trigger paths
Check trigger paths
on:
push:
paths:
- 'src/**' # Only triggers for src/ changes
on:
push:
paths:
- 'src/**' # Only triggers for src/ changes
Verify branch names match
Verify branch names match
on:
push:
branches:
- main
- 'release/*' # Use quotes for patterns
**Issue: Cache not restoring**
```yamlon:
push:
branches:
- main
- 'release/*' # Use quotes for patterns
**Issue: Cache not restoring**
```yamlVerify cache key matches
Verify cache key matches
- uses: actions/cache@v4 with: path: ~/.cache/pip key: pip-${{ runner.os }}-${{ hashFiles('**/requirements.txt') }} restore-keys: | pip-${{ runner.os }}-
**Issue: Secrets not available**
```yaml- uses: actions/cache@v4 with: path: ~/.cache/pip key: pip-${{ runner.os }}-${{ hashFiles('**/requirements.txt') }} restore-keys: | pip-${{ runner.os }}-
**Issue: Secrets not available**
```yamlSecrets not available in forks
Secrets not available in forks
- name: Deploy if: github.event.pull_request.head.repo.full_name == github.repository env: SECRET: ${{ secrets.MY_SECRET }}
**Issue: Permission denied**
```yaml- name: Deploy if: github.event.pull_request.head.repo.full_name == github.repository env: SECRET: ${{ secrets.MY_SECRET }}
**Issue: Permission denied**
```yamlAdd required permissions
Add required permissions
permissions:
contents: write
packages: write
pull-requests: write
undefinedpermissions:
contents: write
packages: write
pull-requests: write
undefinedDebugging Workflows
调试工作流
yaml
undefinedyaml
undefinedEnable debug logging
Enable debug logging
jobs:
debug:
runs-on: ubuntu-latest
steps:
- name: Dump context
env:
GITHUB_CONTEXT: ${{ toJson(github) }}
JOB_CONTEXT: ${{ toJson(job) }}
STEPS_CONTEXT: ${{ toJson(steps) }}
run: |
echo "GitHub context:"
echo "$GITHUB_CONTEXT"
echo "Job context:"
echo "$JOB_CONTEXT"
undefinedjobs:
debug:
runs-on: ubuntu-latest
steps:
- name: Dump context
env:
GITHUB_CONTEXT: ${{ toJson(github) }}
JOB_CONTEXT: ${{ toJson(job) }}
STEPS_CONTEXT: ${{ toJson(steps) }}
run: |
echo "GitHub context:"
echo "$GITHUB_CONTEXT"
echo "Job context:"
echo "$JOB_CONTEXT"
undefinedVersion History
版本历史
| Version | Date | Changes |
|---|---|---|
| 1.0.0 | 2026-01-17 | Initial release with comprehensive CI/CD patterns |
| 版本 | 日期 | 变更 |
|---|---|---|
| 1.0.0 | 2026-01-17 | 初始版本,包含全面的CI/CD模式 |
Resources
参考资源
- GitHub Actions Documentation
- Workflow Syntax Reference
- GitHub Actions Marketplace
- act - Local Testing
- actionlint - Workflow Linter
This skill provides production-ready patterns for GitHub Actions workflows, tested across multiple repositories and CI/CD pipelines.
本技能提供生产就绪的GitHub Actions工作流模式,已在多个仓库和CI/CD流水线中验证。