Workflow creation: authoring valid, correctly-triggered workflows
What this domain tests
Workflow creation covers the file itself: where it lives, which event starts it, how jobs and steps are wired, and which contexts and permissions each key may legally use. Exam items usually show a short YAML fragment and ask why it does not run, runs too often, or fails validation.
File anatomy and triggers
- Workflows live in
.github/workflows/as YAML files; a repository can hold many, each with its ownonblock. onaccepts one event, a list, or a map with filters. Forpull_request,branchesfilters the base branch, andpathsaccepts globs plus negations such as!docs/**.scheduleuses five-field POSIX cron in UTC, has a minimum interval of five minutes, and always runs the default branch version of the workflow.workflow_dispatchadds a manual Run button;workflow_callmakes the file reusable from another workflow's job.- Actions are referenced as
owner/repo@ref,./local-path, ordocker://image.
Contexts, expressions, permissions
${{ }}is evaluated before the step executes. Availability depends on the key:secretsis rejected in a job-levelif, andenvis unavailable insideon.- Declare
permissions:explicitly at workflow or job level. Default token scope depends on repository settings, so never depend on it implicitly. - A step-level
ifimplicitly includessuccess()unless you writealways(),failure(), orcancelled().
Worked example
A test workflow that runs on source changes only:
on:
push:
branches: [main]
paths: ['src/**', 'package*.json']
pull_request:
concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true
permissions:
contents: read
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: {node-version: 20, cache: npm}
- run: npm ci && npm test
Reasoning: the paths filter keeps documentation-only pushes off the runners; concurrency cancels superseded runs on the same ref; setup-node's built-in cache: npm makes a separate actions/cache step unnecessary; contents: read is the minimum checkout needs.
Traps that cost marks
- Indentation errors and duplicated keys break parsing silently; lint with
actionlintbefore pushing. - Referring to
secretsinside a job-levelifis a validation error - map the secret intoenvfirst. - A
paths-filtered workflow that never runs leaves a required check pending, which blocks merging rather than passing it. continue-on-errorchanges a step's conclusion, not its outcome; later steps still readsteps.<id>.outcome == 'failure'.- Cron times are best-effort; scheduled runs are delayed under load and skipped rather than queued.