Files
2026-07-13 13:22:34 +08:00

3.0 KiB

paths
paths
.github/workflows/**/*.yml

GitHub Actions Workflow Guidelines

Use ubuntu-slim for Lightweight Tasks

Prefer ubuntu-slim over ubuntu-latest for simple jobs (e.g., labeling, commenting, notifications).

Note: ubuntu-slim has a 15-minute timeout limit. Use ubuntu-latest for long-running jobs (e.g., polling).

# Bad
runs-on: ubuntu-latest

# Good
runs-on: ubuntu-slim

Use Workflow Context Instead of Fetching

If the trigger event already carries the data, read it from the github context instead of calling gh or actions/github-script. Extra API calls burn rate-limit budget and add a flaky network hop for nothing.

# Bad
- env:
    GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
    PR_NUMBER: ${{ github.event.pull_request.number }}
  run: |
    HEAD_SHA=$(gh pr view "$PR_NUMBER" --json headRefOid -q .headRefOid)

# Good
- env:
    HEAD_SHA: ${{ github.event.pull_request.head.sha }}
  run: echo "$HEAD_SHA"

Only fetch when the data isn't in the payload (e.g., check runs, review threads, changed files on issue_comment).

Prefer gh CLI over actions/github-script

For simple GitHub API operations (commenting, labeling, cancelling runs, etc.), use gh CLI instead of actions/github-script. It avoids the need for actions/checkout and JavaScript boilerplate.

# Bad
- uses: actions/checkout@...
- uses: actions/github-script@...
  with:
    script: |
      const script = require(".github/workflows/my-script.js");
      await script({ context, github });

# Good
- env:
    GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
  run: |
    gh pr comment ...

Use sparse-checkout When Only a Subset of Files Is Needed

When a workflow only needs a small subset of the repo (e.g., a single script under .github/), pass sparse-checkout to actions/checkout instead of cloning the whole tree. A full checkout of this repo takes around 10 seconds on average; a sparse checkout finishes in a fraction of that.

# Bad: clones the entire repo just to run one script
- uses: actions/checkout@...
- run: bash .github/scripts/my-script.sh

# Good: only fetches what the job actually reads
- uses: actions/checkout@...
  with:
    sparse-checkout: |
      .github/scripts/my-script.sh
    sparse-checkout-cone-mode: false
- run: bash .github/scripts/my-script.sh

When listing directories, leave cone mode on (the default):

- uses: actions/checkout@...
  with:
    sparse-checkout: |
      .github/scripts
      dev

Set sparse-checkout-cone-mode: false only when you need to target individual files or non-prefix glob patterns.

pipefail Is Already On

Every workflow in this repo sets top-level defaults.run.shell: bash (enforced by .github/policy.rego). GitHub Actions runs shell: bash as bash --noprofile --norc -eo pipefail {0}, so pipefail is already enabled. Don't ask for set -o pipefail in workflow run: steps. (docs)