📖 [Docs]: add job-vs-step and action-vs-reusable-workflow guidance to the GitHub Actions standard#17
Merged
Marius Storhaug (MariusStorhaug) merged 9 commits intoJul 5, 2026
Conversation
… the GitHub Actions standard Add two decision sections to the GitHub Actions coding standard: - "Structure work into jobs and steps" — when a step suffices and when a new job is warranted (parallelism, permission boundary, distinct runner or deployment environment, merge gating, calling a reusable workflow), with the cost of a job (fresh runner, no shared filesystem) called out. - "Choose an action or a reusable workflow" — an action as a reusable unit of steps vs a reusable workflow as a reusable unit of jobs, when to reach for each, and cross-links to SHA pinning, local-then-promote, and explicit secrets passing.
…ons standard Steps can now run concurrently within a single job (the background / wait / wait-all / cancel / parallel keywords, GitHub Actions changelog 2026-06-25). Reflect this in "Structure work into jobs and steps": - Soften the "steps run in order" claim to "in order by default". - Frame a job as the way to spread independent work across separate runners. - Add a "Parallel steps are new and not yet a default" note: the four keywords, the patterns they enable, and guidance to prefer a separate job for ordinary parallelism and confirm toolchain (actionlint/zizmor) support before relying on the feature.
…valid YAML The "Distinguish vars from secrets" example packed a Correct and an Avoid snippet into one fenced block, repeating the top-level `jobs:` key — which is not a valid YAML document (duplicate mapping key, and rejected by GitHub Actions / yamllint). Split it into two fenced blocks so the snippet parses on its own. Validated every fenced example in the standard: all 13 YAML blocks parse under a strict duplicate-key-aware loader, and both bash blocks pass `bash -n`. The one remaining non-code block is an illustrative directory tree (text).
Copilot started reviewing on behalf of
Marius Storhaug (MariusStorhaug)
July 5, 2026 14:40
View session
There was a problem hiding this comment.
Pull request overview
This PR extends the GitHub Actions coding standard with decision-oriented guidance to help authors choose (1) when to structure work as steps vs separate jobs and (2) when to package reuse as an action vs a reusable workflow, placed in the document’s security → structure → authoring flow.
Changes:
- Adds a new “Structure work into jobs and steps” section, including tradeoffs of job boundaries and an example showing permission isolation via a separate job.
- Adds a new “Choose an action or a reusable workflow” section clarifying the appropriate reuse unit and common anti-patterns.
- Fixes the
secrets: inheritdiscussion formatting by separating the “Correct” vs “Avoid” YAML examples into distinct fenced blocks.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
The standard's own "Pin the runner and name everything" rule asks for a `name:` on every job. The action/reusable-workflow example's `docs` and `ci` jobs were unnamed — unlike every other example that shows a job with steps (the job/step, logging, and status-check examples all name their jobs). Add `name: Publish docs` and `name: CI` so the example follows the standard it documents. Audited every fenced example against the standard: SHA-pinned actions, pinned runners (ubuntu-24.04), least-privilege permissions, and secrets-by-name all hold in the Correct/neutral snippets; the only unpinned action, `secrets: inherit`, and inline-untrusted-input appear solely in the labelled Avoid snippets.
Copilot started reviewing on behalf of
Marius Storhaug (MariusStorhaug)
July 5, 2026 15:38
View session
Strengthen the naming rule from "every job and every non-trivial step" to every job AND every step, with the rationale: the name in the code is the label shown in the portal (traceability / code-portal parity); it is a stable handle that does not drift when the underlying `run`/`uses` changes; it names a failure by intent rather than by a decoded command line; and — because we pin actions to full SHAs — an unnamed action step would otherwise wear its 40-character SHA as its UI label. Add a companion rule: separate each job and each step with a single blank line. Apply both across every example that contains steps (pinning, untrusted-input, job/step, action/reusable-workflow, logging report, status check). Verified: all 15 code examples still parse, every step now starts with `- name:`, markdownlint clean, links resolve.
Copilot started reviewing on behalf of
Marius Storhaug (MariusStorhaug)
July 5, 2026 16:15
View session
The 'Choose an action or a reusable workflow' summary said both are 'consumed the same disciplined way: pinned by full commit SHA', which over-generalised: a local action or workflow referenced by path (./.github/...) runs at the checked-out commit and is not SHA-pinned — only contradicting the local-action examples. Reframe as a lifecycle: start local (by path), promote to a standalone repo once a second consumer appears, and only then consume it by full commit SHA like any third-party dependency.
Copilot started reviewing on behalf of
Marius Storhaug (MariusStorhaug)
July 5, 2026 16:20
View session
Copilot review: the intro said 'four workflow keywords' but the list names five distinct keys (background, wait, wait-all, cancel, parallel), and the toolchain caveat named only a three-key subset. Drop the count ('new workflow keywords') and make the caveat refer to 'the new keywords' so it covers all of them.
Copilot started reviewing on behalf of
Marius Storhaug (MariusStorhaug)
July 5, 2026 16:24
View session
PowerShell is the standard's default glue language, but the three examples in
"Build in logging and diagnostics" used bash `echo` — contradicting that rule,
and each one also contradicted its own bullet (which advocates the PowerShell
helpers). Convert them:
- grouped logging -> `LogGroup 'phase' { ... }`
- annotations -> `Write-GitHubNotice` / `Write-GitHubError` (which escape dynamic
data for you, so the manual %25/%0D/%0A note stays in prose but drops from code)
- step summary -> `Set-GitHubStepSummary` with the PSModule `Markdown` module
(`Heading`, `Table`, `Details`)
Verified helper signatures against the installed PSModule `GitHub` module
(v0.43.1) and the `Markdown` module's `Heading`/`Table`/`Details` aliases; both
PowerShell blocks pass the PowerShell language parser, and no `echo`/`shell: bash`
examples remain.
Copilot started reviewing on behalf of
Marius Storhaug (MariusStorhaug)
July 5, 2026 16:51
View session
Copilot review: the "Never expand untrusted input inline" example still used bash (`echo`, `$TITLE`) with no `shell:`, so a reader pasting it into a pwsh step would find `$TITLE` empty. Set `shell: pwsh` and use `Write-Host $env:TITLE` (safe, the value arrives as an env var) versus an inline expansion of the untrusted title (unsafe). Now every example step declares `shell: pwsh`; the only remaining "echo" is the prose "never echo a secret".
Copilot started reviewing on behalf of
Marius Storhaug (MariusStorhaug)
July 5, 2026 16:57
View session
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Adds two decision-oriented sections to the GitHub Actions coding standard
(
src/docs/Coding-Standards/GitHub-Actions.md), answering two questions thatwere not previously covered:
Structure work into jobs and steps
When a step is enough and when a new job is warranted. Default to steps
within one job for sequential, state-sharing work; reach for a new job only when
you need one of:
It also spells out what a job costs — a fresh runner with no shared filesystem,
so data crosses a job boundary only through
outputsor artifacts — so jobs arenot created where ordered steps would do.
Choose an action or a reusable workflow
When to package reuse as an action (a reusable unit of steps, composed
inside a job) versus a reusable workflow (a reusable unit of jobs — a whole
multi-job pipeline). Includes the anti-patterns (don't wrap a single task in a
reusable workflow; don't force a multi-job pipeline into one action) and
cross-links the shared discipline: SHA pinning, start-local-then-promote, and
passing secrets explicitly by name.
Placement
Both sections sit together after the security rules and before the authoring
mechanics (PowerShell glue, script extraction), giving a top-down flow:
security → structure decisions → authoring details.
Notes
conventions (SHA-pinned
checkout,permissions: {}default-deny floor,PowerShell steps).
Test-DocumentationLink.ps1(all links/anchors resolve),markdownlint-cli2with the repo config (0 errors).