Skip to content

Commit 9e2ce02

Browse files
Add Style Guides section with GitHub Actions, Markdown, and PowerShell guides
1 parent 5f70d47 commit 9e2ce02

5 files changed

Lines changed: 1425 additions & 0 deletions

File tree

Lines changed: 232 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,232 @@
1+
---
2+
title: GitHub Actions
3+
description: GitHub Actions style guidelines for consistency and security across workflows and composite actions.
4+
---
5+
6+
# GitHub Actions style guidelines
7+
8+
This document defines the GitHub Actions style guidelines for all workflow files (`.github/workflows/*.yml`), composite actions (`action.yml`), and reusable workflows in PSModule repositories. These rules follow GitHub Actions best practices and the security guidance enforced by [zizmor](https://github.com/woodruffw/zizmor).
9+
10+
## Scope
11+
12+
- Applies to `.github/workflows/*.{yml,yaml}`, `action.yml`, `.github/dependabot.yml`, and `CODEOWNERS` entries for workflows.
13+
- Application source code stays out of scope. If a fix requires changes outside `.github/`, surface it as a recommendation instead.
14+
15+
## Naming
16+
17+
- Give every job and every step a `name:` field
18+
- Use short, human-readable names that describe what the job or step does, not how
19+
- Keep naming consistent with existing workflows in the repository
20+
21+
**Good:**
22+
23+
```yaml
24+
jobs:
25+
build:
26+
name: Build module
27+
steps:
28+
- name: Checkout repository
29+
uses: actions/checkout@<sha> # vX.Y.Z
30+
```
31+
32+
**Bad:**
33+
34+
```yaml
35+
jobs:
36+
build:
37+
steps:
38+
- uses: actions/checkout@<sha> # vX.Y.Z
39+
```
40+
41+
## Quoting
42+
43+
- Only quote scalar values when YAML would otherwise misinterpret them
44+
- Quote values starting with `{`, containing `:`, boolean-like strings (`true`/`false`), or numeric strings
45+
- Omit quotes everywhere else
46+
47+
**Good:**
48+
49+
```yaml
50+
run-name: Release ${{ github.ref_name }}
51+
concurrency:
52+
group: ${{ github.workflow }}-${{ github.ref }}
53+
```
54+
55+
**Bad:**
56+
57+
```yaml
58+
run-name: 'Release ${{ github.ref_name }}'
59+
concurrency:
60+
group: '${{ github.workflow }}-${{ github.ref }}'
61+
```
62+
63+
## Pinning actions
64+
65+
- Pin every `uses:` to a full 40-character commit SHA
66+
- Add a patch-level version comment (`# vX.Y.Z`) so Dependabot can update SHA and comment together
67+
- Applies to reusable workflows too (`uses: org/repo/.github/workflows/foo.yml@<sha>`)
68+
- Exception: first-party actions in the same repository (`uses: ./.github/actions/...`)
69+
- Resolve SHAs via `gh api` — never invent or guess a commit SHA. Always use the commit SHA, never the annotated-tag object SHA.
70+
71+
**Good:**
72+
73+
```yaml
74+
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
75+
```
76+
77+
**Bad:**
78+
79+
```yaml
80+
- uses: actions/checkout@v4
81+
- uses: actions/checkout@main
82+
```
83+
84+
Resolving SHAs:
85+
86+
```bash
87+
gh api repos/<owner>/<repo>/git/refs/tags/<vX.Y.Z> --jq '.object.sha'
88+
# If .object.type == "tag", dereference:
89+
gh api repos/<owner>/<repo>/git/tags/<sha> --jq '.object.sha'
90+
```
91+
92+
## Permissions
93+
94+
- Declare workflow-level `permissions:` set to the strictest needed (default `contents: read`)
95+
- Override per-job when one job needs more
96+
- Never use `permissions: write-all` or omit `permissions:` entirely
97+
- Add an inline comment justifying any non-`read` grant
98+
99+
**Good:**
100+
101+
```yaml
102+
permissions:
103+
contents: read
104+
105+
jobs:
106+
deploy:
107+
permissions:
108+
contents: read
109+
id-token: write # OIDC federation to AWS
110+
```
111+
112+
## Secrets and configuration
113+
114+
- Use `vars.*` for configuration: region, account ID, role ARN, environment name
115+
- Use `secrets.*` for credentials: API tokens, passwords, signing keys
116+
- Never hardcode account IDs, role ARNs, or region names
117+
- Authenticate to cloud providers with OIDC, never long-lived keys
118+
- In reusable workflows, pass secrets explicitly — never use `secrets: inherit`
119+
120+
**Good:**
121+
122+
```yaml
123+
permissions:
124+
id-token: write
125+
contents: read
126+
127+
steps:
128+
- name: Configure AWS credentials
129+
uses: aws-actions/configure-aws-credentials@<sha> # vX.Y.Z
130+
with:
131+
aws-region: ${{ vars.AWS_REGION }}
132+
role-to-assume: ${{ vars.AWS_ROLE_ARN_CONTINUOUS_DEPLOYMENT }}
133+
```
134+
135+
**Bad:**
136+
137+
```yaml
138+
jobs:
139+
call:
140+
uses: ./.github/workflows/reusable.yml
141+
secrets: inherit
142+
```
143+
144+
## Untrusted input
145+
146+
- Never interpolate untrusted context directly into shell commands
147+
- Untrusted contexts include `github.event.issue.title`, `.body`, `.comment.body`, `.pull_request.title`, `.pull_request.body`, `.pull_request.head.ref`, `.head_commit.message`, `.review.body`, `.review_comment.body`, and `.head_ref`
148+
- Pass untrusted values through an `env:` variable and quote them in the shell
149+
150+
**Good:**
151+
152+
```yaml
153+
- run: echo "Title: $TITLE"
154+
env:
155+
TITLE: ${{ github.event.issue.title }}
156+
```
157+
158+
**Bad:**
159+
160+
```yaml
161+
- run: echo "Title: ${{ github.event.issue.title }}"
162+
```
163+
164+
## Triggers and isolation
165+
166+
- Default to `pull_request` for PR validation
167+
- Avoid `pull_request_target` and `workflow_run` unless required — they run with write tokens and secrets while potentially checking out attacker-controlled code
168+
- If `pull_request_target` is unavoidable, never check out the PR head, or check out into a sandbox without secrets
169+
- Use `actions/checkout` with `persist-credentials: false` unless the job pushes commits
170+
171+
**Good:**
172+
173+
```yaml
174+
- uses: actions/checkout@<sha> # vX.Y.Z
175+
with:
176+
persist-credentials: false
177+
```
178+
179+
## Runners and concurrency
180+
181+
- Pin `runs-on:` to a specific OS version (`ubuntu-24.04`) over a floating label (`ubuntu-latest`)
182+
- Add `concurrency:` to deploy and release workflows
183+
- Use `cancel-in-progress: true` for CI and `false` for deploys
184+
185+
**Good:**
186+
187+
```yaml
188+
concurrency:
189+
group: ${{ github.workflow }}-${{ github.ref }}
190+
cancel-in-progress: false
191+
```
192+
193+
## Operating protocol
194+
195+
Before writing or reviewing a workflow:
196+
197+
1. Read existing workflows and match shell choice, naming, job structure, and comment style
198+
1. Check `.github/dependabot.yml` for `package-ecosystem: github-actions` and propose adding it if absent
199+
1. Check `CODEOWNERS` for `.github/workflows/` ownership and recommend it if absent
200+
1. Resolve SHAs via `gh api` — never invent a commit SHA
201+
202+
## Security checklist
203+
204+
Run mentally (and via [zizmor](https://github.com/woodruffw/zizmor) when available) before declaring done.
205+
206+
### High severity — must fix
207+
208+
- `template-injection` — no `${{ ... }}` from untrusted context inside `run:` or `script:`
209+
- `dangerous-triggers` — `pull_request_target` / `workflow_run` justified and hardened
210+
- `unpinned-uses` — every `uses:` has a 40-char SHA and `# vX.Y.Z` comment
211+
- `excessive-permissions` — workflow- and job-level `permissions:` minimal
212+
- `secrets-inherit` — no `secrets: inherit`
213+
- `known-vulnerable-actions` — no pinned versions in GHSA
214+
- `github-env` — no untrusted writes to `$GITHUB_ENV` / `$GITHUB_PATH`
215+
216+
### Medium severity — fix unless justified
217+
218+
- `overprovisioned-secrets` — no `${{ toJSON(secrets) }}` or wholesale `${{ secrets }}`
219+
- `cache-poisoning` — no `actions/cache` (or `setup-*` cache) in tag-triggered release workflows
220+
- `ref-confusion` — pinned ref is not a name shared by both a tag and a branch
221+
- `ref-version-mismatch` — the `# vX.Y.Z` comment matches what the SHA actually is
222+
223+
### Low severity — fix when reasonable
224+
225+
- `stale-action-refs` — pinned commit corresponds to a real tag
226+
- `impostor-commit` — pinned SHA exists in the action repo's history
227+
228+
## Related resources
229+
230+
- [GitHub Actions documentation](https://docs.github.com/en/actions)
231+
- [zizmor — static analysis for GitHub Actions](https://github.com/woodruffw/zizmor)
232+
- [GitHub Actions Standard](../GitHub-Actions/Standards.md)

0 commit comments

Comments
 (0)