2.1.4. Choosing Scope, Permissions, and Event Filters
💡 First Principle: A trigger decision is incomplete until you've also decided how much authority the resulting run gets and how many runs are allowed to pile up — event, permissions:, and concurrency: are three dials on the same control panel.
Scope down first with filters, because runs you never start cost nothing and can't misbehave. paths:/paths-ignore: limit a build to the code it actually cares about (note you cannot use both a paths and paths-ignore filter for the same event — likewise for branches/branches-ignore). Filter patterns support *, **, +, ?, !, and character ranges; ** is the one that crosses directory separators.
Then scope authority with permissions:, which sets the GITHUB_TOKEN's access at the workflow or job level. Declaring permissions: at all switches the token from the repository default to only what you list, so the idiomatic secure pattern is a restrictive workflow-level block with narrow per-job widening:
permissions:
contents: read # applies to all jobs
jobs:
release:
permissions:
contents: write # this job only
id-token: write # OIDC, see 6.1.3
Finally, control pile-up with concurrency:. A group key plus cancel-in-progress: true is the standard "only the newest commit on a PR matters" pattern, and the standard answer to "how do we stop redundant runs from burning minutes":
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
For deployments you usually want the inverse — a concurrency group without cancellation, so production deploys queue rather than trample each other.
⚠️ Exam Trap: Adding a permissions: block to grant one scope silently revokes every scope you didn't list. If a scenario shows a job that could push commits before someone added permissions: {id-token: write} and now fails on git push, the fix is adding contents: write back — not a PAT.
Reflection Question: For a PR-validation workflow, cancel-in-progress: true is almost always right; for a production deploy workflow it's almost always wrong. What property of the two workloads flips the answer?