Skip to content

Commit 04c4804

Browse files
Copilotsofthack007
andauthored
Add cicd.instructions.md style guide for GitHub Actions workflows
Agent-Logs-Url: https://github.com/MoonModules/WLED-MM/sessions/2198b03d-660a-40a6-ad60-17f948a038a2 Co-authored-by: softhack007 <91616163+softhack007@users.noreply.github.com>
1 parent 908f35a commit 04c4804

2 files changed

Lines changed: 154 additions & 1 deletion

File tree

.github/cicd.instructions.md

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
---
2+
applyTo: ".github/workflows/*.yml,.github/workflows/*.yaml"
3+
---
4+
# CI/CD Conventions — GitHub Actions Workflows
5+
6+
## YAML Style
7+
8+
- Indent with **2 spaces** (no tabs)
9+
- Every workflow, job, and step must have a `name:` field that clearly describes its purpose
10+
- Group related steps logically; separate unrelated groups with a blank line
11+
- Comments (`#`) are encouraged for non-obvious decisions (e.g., why `fail-fast: false` is set, what a cron expression means)
12+
13+
## Workflow Structure
14+
15+
### Triggers
16+
17+
- Declare `on:` triggers explicitly; avoid bare `on: push` without branch filters on long-running or expensive jobs
18+
- Prefer `workflow_call` for shared build logic (see `build.yml`) to avoid duplicating steps across workflows
19+
- Document scheduled triggers (`cron:`) with a human-readable comment:
20+
21+
```yaml
22+
schedule:
23+
- cron: '0 2 * * *' # run at 2 AM UTC daily
24+
```
25+
26+
### Jobs
27+
28+
- Express all inter-job dependencies with `needs:` — never rely on implicit ordering
29+
- Use job `outputs:` + step `id:` to pass structured data between jobs (see `get_default_envs` in `build.yml`)
30+
- Set `fail-fast: false` on matrix builds so that a single failing environment does not cancel others
31+
32+
### Runners
33+
34+
- Pin to a specific Ubuntu version (`ubuntu-22.04`, `ubuntu-24.04`) rather than `ubuntu-latest` for reproducible builds
35+
- Only use `ubuntu-latest` in jobs where exact environment reproducibility is not required (e.g., trivial download/publish steps)
36+
37+
### Tool and Language Versions
38+
39+
- Pin tool versions explicitly:
40+
```yaml
41+
python-version: '3.12'
42+
```
43+
- Do not rely on the runner's pre-installed tool versions — always install via a versioned setup action
44+
45+
### Caching
46+
47+
- Always cache package managers and build tool directories when the job installs dependencies:
48+
```yaml
49+
- uses: actions/cache@v4
50+
with:
51+
path: ~/.cache/pip
52+
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
53+
restore-keys: |
54+
${{ runner.os }}-pip-
55+
```
56+
- Include the environment name or a relevant identifier in cache keys when building multiple targets
57+
58+
### Artifacts
59+
60+
- Name artifacts with enough context to be unambiguous (e.g., `firmware-${{ matrix.environment }}`)
61+
- Avoid uploading artifacts that will never be consumed downstream
62+
63+
---
64+
65+
## Security
66+
67+
### Permissions — Least Privilege
68+
69+
Declare explicit `permissions:` blocks. The default token permissions are broad; scope them to the minimum required:
70+
71+
```yaml
72+
permissions:
73+
contents: read # for checkout
74+
```
75+
76+
For jobs that publish releases or write to the repository:
77+
78+
```yaml
79+
permissions:
80+
contents: write # create/update releases
81+
```
82+
83+
A common safe baseline for build-only jobs:
84+
85+
```yaml
86+
permissions:
87+
contents: read
88+
```
89+
90+
### Supply Chain — Action Pinning
91+
92+
**Third-party actions** (anything outside the `actions/` and `github/` namespaces) should be pinned to a specific release tag. Branch pins (`@main`, `@master`) are **not allowed** — they can be updated by the action author at any time without notice:
93+
94+
```yaml
95+
# ✅ Acceptable — specific version tag
96+
uses: softprops/action-gh-release@v2
97+
98+
# ❌ Not acceptable — mutable branch reference
99+
uses: andelf/nightly-release@main
100+
```
101+
102+
SHA pinning (e.g., `uses: someorg/some-action@abc1234`) is the most secure option for third-party actions; it is recommended when auditing supply-chain risk is a priority. At minimum, always use a specific version tag.
103+
104+
**First-party actions** (`actions/checkout`, `actions/cache`, `actions/upload-artifact`, etc.) pinned to a major version tag (e.g., `@v4`) are acceptable because GitHub maintains and audits these.
105+
106+
When adding a new third-party action:
107+
1. Check that the action's repository is actively maintained
108+
2. Review the action's source before adding it
109+
3. Prefer well-known, widely-used actions over obscure ones
110+
111+
### Credentials and Secrets
112+
113+
- Use `${{ secrets.GITHUB_TOKEN }}` for operations within the same repository — it is automatically scoped and rotated
114+
- Never commit secrets, tokens, or passwords into workflow files or any tracked file
115+
- Never print secrets in `run:` steps, even with `echo` — GitHub masks known secrets but derived values are not automatically masked
116+
- Scope secrets to the narrowest step that needs them using `env:` at the step level, not at the workflow level:
117+
118+
```yaml
119+
# ✅ Scoped to the step that needs it
120+
- name: Create release
121+
uses: softprops/action-gh-release@v2
122+
with:
123+
token: ${{ secrets.GITHUB_TOKEN }}
124+
125+
# ❌ Unnecessarily broad
126+
env:
127+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
128+
```
129+
130+
- Personal Access Tokens (PATs, stored as repository secrets) should have the minimum required scopes and should be rotated periodically
131+
132+
### Script Injection
133+
134+
`${{ }}` expressions are evaluated before the shell script runs. If an expression comes from untrusted input (PR titles, issue bodies, branch names from forks), it can inject arbitrary shell commands.
135+
136+
**Never** interpolate `github.event.*` values directly into a `run:` step:
137+
138+
```yaml
139+
# ❌ Injection risk — PR title is attacker-controlled
140+
- run: echo "${{ github.event.pull_request.title }}"
141+
142+
# ✅ Safe — value passed through an environment variable
143+
- env:
144+
PR_TITLE: ${{ github.event.pull_request.title }}
145+
run: echo "$PR_TITLE"
146+
```
147+
148+
This rule applies to any value that originates outside the repository (issue bodies, labels, comments, commit messages from forks).
149+
150+
### Pull Request Workflows
151+
152+
- Workflows triggered by `pull_request` from a fork run with **read-only** token permissions and no access to repository secrets — this is intentional and correct
153+
- Do not use `pull_request_target` unless you fully understand the security implications; it runs in the context of the base branch and *does* have secret access, making it a common attack surface

.github/copilot-instructions.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,4 +69,4 @@ Main development branch: `mdev`
6969
- **PR reviews:** Authors do not need to commit `html_*.h` files; they are generated during firmware builds.
7070
- **Provide references** when making analyses or recommendations. Base them on the correct branch or PR.
7171
- **C++ formatting available**: `clang-format` is installed but not in CI
72-
- No automated linting is configured — match existing code style in files you edit. See `cpp.instructions.md` and `web.instructions.md` for language-specific conventions.
72+
- No automated linting is configured — match existing code style in files you edit. See `cpp.instructions.md` and `web.instructions.md` for language-specific conventions, and `cicd.instructions.md` for GitHub Actions workflows.

0 commit comments

Comments
 (0)