CI/CD Patterns
Patterns for GitHub Actions, PR automation, and deployment workflows.
When to Use
- Setting up or fixing GitHub Actions workflows
- Automating PR checks (lint, test, build)
- Configuring deployment pipelines
- Monitoring PR status and retrying flaky CI
- Setting up multi-environment deployment (dev, staging, prod)
GitHub Actions --- Common Patterns
Basic CI Workflow
name: CI
on:
push:
branches: [main, develop]
pull_request:
branches: [main, develop]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run tests
run: ./gradlew test
PR Check Workflow
name: PR Check
on: pull_request
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: ./gradlew ktlintCheck
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: ./gradlew test
build:
runs-on: ubuntu-latest
needs: [lint, test]
steps:
- uses: actions/checkout@v4
- run: ./gradlew build
PR Babysitting Pattern
Monitor a PR through CI, handle common failures:
- Check CI status ---
gh pr checks <number>
- Identify failure type --- flaky test, lint error, build failure
- Fix and push --- for lint/build errors, fix locally and push
- Retry flaky tests --- re-run the workflow:
gh run rerun <run-id> --failed
- Resolve merge conflicts --- rebase onto target branch
- Enable auto-merge ---
gh pr merge <number> --auto --squash
See workflows.md for ready-to-use GitHub Actions YAML templates.
Deployment Checklist
Before deploying:
Gotchas
- Caching saves minutes per run. Always cache dependencies (
actions/cache or actions/setup-java with cache). A cold Gradle build takes 3-5 minutes, cached takes 30 seconds.
needs: creates sequential dependencies. Without it, all jobs run in parallel. Use needs: [lint, test] to make build wait for checks.
- Secret names are case-sensitive.
secrets.DB_PASSWORD and secrets.db_password are different. Match the exact name from Settings > Secrets.
- Don't use
actions/checkout@v3 --- use v4. v3 uses Node 16 which is deprecated. v4 uses Node 20.
- Flaky tests need investigation, not just retry. If you re-run a workflow more than twice for the same test, fix the test. Common causes: race conditions, time-dependent assertions, shared test state.
- Force-pushing during CI review resets the check suite. Wait for CI to finish before force-pushing, or you'll waste runner minutes.
Rules
- Every PR must pass CI before merge
- Don't skip CI checks (
[skip ci]) unless it's docs-only
- Keep workflows under 10 minutes total
- Use matrix builds for multi-version testing
- Store secrets in GitHub Secrets, never in code
1---2name: ci-cd-patterns3description: CI/CD pipeline patterns for GitHub Actions, PR automation, and deployment workflows. Use when setting up CI, fixing broken pipelines, automating PR checks, or configuring deployment.4---56# CI/CD Patterns78Patterns for GitHub Actions, PR automation, and deployment workflows.910## When to Use1112- Setting up or fixing GitHub Actions workflows13- Automating PR checks (lint, test, build)14- Configuring deployment pipelines15- Monitoring PR status and retrying flaky CI16- Setting up multi-environment deployment (dev, staging, prod)1718## GitHub Actions --- Common Patterns1920### Basic CI Workflow21```yaml22name: CI23on:24 push:25 branches: [main, develop]26 pull_request:27 branches: [main, develop]2829jobs:30 test:31 runs-on: ubuntu-latest32 steps:33 - uses: actions/checkout@v434 - name: Run tests35 run: ./gradlew test36```3738### PR Check Workflow39```yaml40name: PR Check41on: pull_request4243jobs:44 lint:45 runs-on: ubuntu-latest46 steps:47 - uses: actions/checkout@v448 - run: ./gradlew ktlintCheck4950 test:51 runs-on: ubuntu-latest52 steps:53 - uses: actions/checkout@v454 - run: ./gradlew test5556 build:57 runs-on: ubuntu-latest58 needs: [lint, test]59 steps:60 - uses: actions/checkout@v461 - run: ./gradlew build62```6364## PR Babysitting Pattern6566Monitor a PR through CI, handle common failures:67681. **Check CI status** --- `gh pr checks <number>`692. **Identify failure type** --- flaky test, lint error, build failure703. **Fix and push** --- for lint/build errors, fix locally and push714. **Retry flaky tests** --- re-run the workflow: `gh run rerun <run-id> --failed`725. **Resolve merge conflicts** --- rebase onto target branch736. **Enable auto-merge** --- `gh pr merge <number> --auto --squash`7475> See `workflows.md` for ready-to-use GitHub Actions YAML templates.7677## Deployment Checklist7879Before deploying:80- [ ] All CI checks pass81- [ ] No merge conflicts82- [ ] Database migrations reviewed (if any)83- [ ] Environment variables set in target environment84- [ ] Rollback plan identified8586## Gotchas8788- **Caching saves minutes per run.** Always cache dependencies (`actions/cache` or `actions/setup-java` with cache). A cold Gradle build takes 3-5 minutes, cached takes 30 seconds.89- **`needs:` creates sequential dependencies.** Without it, all jobs run in parallel. Use `needs: [lint, test]` to make build wait for checks.90- **Secret names are case-sensitive.** `secrets.DB_PASSWORD` and `secrets.db_password` are different. Match the exact name from Settings > Secrets.91- **Don't use `actions/checkout@v3` --- use `v4`.** v3 uses Node 16 which is deprecated. v4 uses Node 20.92- **Flaky tests need investigation, not just retry.** If you re-run a workflow more than twice for the same test, fix the test. Common causes: race conditions, time-dependent assertions, shared test state.93- **Force-pushing during CI review resets the check suite.** Wait for CI to finish before force-pushing, or you'll waste runner minutes.9495## Rules9697- Every PR must pass CI before merge98- Don't skip CI checks (`[skip ci]`) unless it's docs-only99- Keep workflows under 10 minutes total100- Use matrix builds for multi-version testing101- Store secrets in GitHub Secrets, never in code