|
| 1 | +--- |
| 2 | +applyTo: ".github/workflows/**" |
| 3 | +--- |
| 4 | + |
| 5 | +# GitHub Actions — instructions for Copilot coding agent |
| 6 | + |
| 7 | +> **Skill reference:** for the full best-practices guide, action-pinning decision table, composite action patterns and PR comment strategies read `.github/skills/github-actions.md` first. |
| 8 | +
|
| 9 | +Follow these steps when creating or modifying GitHub Actions workflows in this repository. |
| 10 | + |
| 11 | +## 1. File conventions |
| 12 | + |
| 13 | +- All workflow files use the **`.yaml`** extension (not `.yml`). |
| 14 | +- File names use **kebab-case**: `a11y.yaml`, `lighthouse.yaml`, `docs.yaml`. |
| 15 | +- Every workflow must start with a block comment explaining its purpose, triggers and strategy. |
| 16 | + |
| 17 | +## 2. Pinning third-party actions |
| 18 | + |
| 19 | +**Every third-party action (anything not `actions/*` from GitHub itself) MUST be pinned |
| 20 | +to an exact commit SHA, never to a tag or a branch.** |
| 21 | + |
| 22 | +```yaml |
| 23 | +# ✅ correct — SHA-pinned with tag comment |
| 24 | +uses: owner/repo@<40-char-sha> # v1.2.3 |
| 25 | + |
| 26 | +# ❌ wrong — mutable tag |
| 27 | +uses: owner/repo@v1 |
| 28 | + |
| 29 | +# ❌ wrong — branch (can be force-pushed) |
| 30 | +uses: owner/repo@main |
| 31 | +``` |
| 32 | +
|
| 33 | +To resolve a tag to its commit SHA: |
| 34 | +
|
| 35 | +```bash |
| 36 | +git ls-remote https://github.com/<owner>/<repo>.git refs/tags/<tag> refs/tags/<tag>^{} |
| 37 | +# The ^{} peeled SHA (second line, if present) is the actual commit. Use that one. |
| 38 | +``` |
| 39 | + |
| 40 | +Add a comment `# v1.2.3` to the right of the hash so the version is human-readable. |
| 41 | + |
| 42 | +**First-party GitHub actions** (`actions/checkout`, `actions/upload-artifact`, etc.) should |
| 43 | +also be pinned. Always resolve the latest released tag before using any action. |
| 44 | + |
| 45 | +## 3. Coordinated version upgrades |
| 46 | + |
| 47 | +When bumping an action version, update **every workflow file** that uses it in the same commit. |
| 48 | +Actions that appear in multiple workflows (e.g. `actions/checkout`, `oven-sh/setup-bun`) must |
| 49 | +always reference **the same commit SHA** across all files. Use `grep` to find all usages before |
| 50 | +opening a PR. |
| 51 | + |
| 52 | +```bash |
| 53 | +grep -r "oven-sh/setup-bun" .github/workflows/ |
| 54 | +``` |
| 55 | + |
| 56 | +## 4. DRY — composite action for shared steps |
| 57 | + |
| 58 | +If two or more workflows share identical steps (e.g. starting a preview server, waiting for it |
| 59 | +to be ready), extract them into a **composite action** under `.github/actions/<name>/action.yml`. |
| 60 | + |
| 61 | +```yaml |
| 62 | +# In a workflow: |
| 63 | +- name: Start VitePress preview server |
| 64 | + uses: ./.github/actions/start-preview-server |
| 65 | +``` |
| 66 | +
|
| 67 | +The composite action must declare all inputs with defaults and document each one. Never |
| 68 | +duplicate multi-step shell sequences across workflows. |
| 69 | +
|
| 70 | +## 5. PR comment reports |
| 71 | +
|
| 72 | +Every workflow that produces a structured report (audit scores, coverage, test results) **must**: |
| 73 | +
|
| 74 | +1. Generate a formatted Markdown summary (table preferred). |
| 75 | +2. Post it as a **sticky PR comment** using |
| 76 | + `marocchino/sticky-pull-request-comment@<sha> # v2.9.4` with a unique `header:` value |
| 77 | + so the comment is updated (not duplicated) on each push to the PR. |
| 78 | +3. Use `actions/github-script` to build the comment body when the data comes from JSON files |
| 79 | + produced by the workflow (avoids shelling out to `jq` / Python). |
| 80 | + |
| 81 | +```yaml |
| 82 | +- name: Generate scores comment |
| 83 | + id: comment-body |
| 84 | + if: github.event_name == 'pull_request' |
| 85 | + uses: actions/github-script@<sha> # v7 |
| 86 | + with: |
| 87 | + result-encoding: string |
| 88 | + script: | |
| 89 | + // ... parse JSON, build Markdown table, return body |
| 90 | +
|
| 91 | +- name: Post / update comment on PR |
| 92 | + if: github.event_name == 'pull_request' |
| 93 | + uses: marocchino/sticky-pull-request-comment@<sha> # v2.9.4 |
| 94 | + with: |
| 95 | + header: <unique-identifier> |
| 96 | + message: ${{ steps.comment-body.outputs.result }} |
| 97 | +``` |
| 98 | + |
| 99 | +## 6. Minimum required permissions |
| 100 | + |
| 101 | +Set `permissions: contents: read` at the **workflow level** as the default. |
| 102 | +Override to `write` **at the job level** only for the exact permissions needed: |
| 103 | + |
| 104 | +```yaml |
| 105 | +permissions: |
| 106 | + contents: read # workflow-level default (principle of least privilege) |
| 107 | +
|
| 108 | +jobs: |
| 109 | + audit: |
| 110 | + permissions: |
| 111 | + contents: read |
| 112 | + pull-requests: write # needed only to post the PR comment |
| 113 | +``` |
| 114 | + |
| 115 | +## 7. Concurrency |
| 116 | + |
| 117 | +Every workflow that produces side-effects (deploy, comment, upload) must define a |
| 118 | +`concurrency` block to cancel stale runs: |
| 119 | + |
| 120 | +```yaml |
| 121 | +concurrency: |
| 122 | + group: <workflow-name>-${{ github.ref }} |
| 123 | + cancel-in-progress: true |
| 124 | +``` |
| 125 | + |
| 126 | +## 8. Validation checklist before opening a PR |
| 127 | + |
| 128 | +```bash |
| 129 | +# Lint all workflow files locally (requires actionlint): |
| 130 | +actionlint .github/workflows/*.yaml .github/actions/**/*.yml |
| 131 | +
|
| 132 | +# Confirm every action is pinned: |
| 133 | +grep -rn "uses:" .github/workflows/ | grep -v "@[a-f0-9]\{40\}" |
| 134 | +# → must return nothing |
| 135 | +``` |
| 136 | + |
| 137 | +Also verify: |
| 138 | + |
| 139 | +- [ ] All `.yml` workflow files have been renamed to `.yaml` |
| 140 | +- [ ] Every new third-party action has a `# vX.Y.Z` comment after its SHA |
| 141 | +- [ ] New reusable composite actions are under `.github/actions/<name>/action.yml` |
| 142 | +- [ ] The workflow self-references the correct filename (e.g. in `paths:`) |
| 143 | +- [ ] `pull-requests: write` is set only on jobs that post comments |
| 144 | +- [ ] No `bunx` in workflow `run:` steps — use binaries from `node_modules/.bin/` or |
| 145 | + scripts defined in `package.json` via `bun run <script>` |
0 commit comments