GitHub Actions cheat sheet
GitHub Actions ยท Keep the key ideas within reach.
Workflow creation
A workflow is a YAML file in .github/workflows/ of the repository; GitHub discovers every .yml/.yaml file there on the default branch and on branches that contain it for push/pull_request events.
Top-level keys: name, run-name, on, permissions, env, defaults, concurrency, jobs. Unknown top-level keys fail validation with an 'Invalid workflow file' error.
onaccepts a single event, a list of events, a schedule cron list, or a map with filters such as branches, branches-ignore, paths, paths-ignore, and types.Job identifiers must start with a letter or underscore and contain only alphanumeric characters, '-' and '_'; the identifier, not
name, is whatneedsreferences.Steps run sequentially inside a job; each
runstep is a separate shell process, so shell variables do not persist between steps - use $GITHUB_ENV or $GITHUB_OUTPUT to pass values.Expression syntax
${{ }}is evaluated by the runner before the step executes; contexts include github, env, vars, secrets, inputs, matrix, needs, steps, job, and runner.permissionscan be set workflow-wide or per job; setting any permission switches all unspecified scopes tonone, so list every scope the job needs.Reusable workflows are called with
uses: owner/repo/.github/workflows/file.yml@refat job level and receive values throughwith:mapped toon.workflow_call.inputs.
Automation
Event payload is available at $GITHUB_EVENT_PATH as JSON;
github.event_nameandgithub.event.actionidentify the trigger, andtypes:filters which actions fire the workflow.scheduleuses POSIX cron in UTC; the shortest interval is 5 minutes, and scheduled runs on the default branch are disabled automatically after 60 days of repository inactivity.workflow_dispatchsupports typedinputs(string, choice, boolean, environment) exposed via theinputscontext;repository_dispatchaccepts an arbitraryclient_payload.Concurrency group
${{ github.workflow }}-${{ github.ref }}withcancel-in-progress: truesupersedes stale runs; without cancel-in-progress, at most one running and one pending run are kept.GITHUB_TOKEN scopes come from the
permissionsblock, are limited to the repository, and expire when the job finishes; a job cannot use GITHUB_TOKEN to trigger another workflow run.GITHUB_STEP_SUMMARYwrites Markdown to the run summary page;::warning::,::error::, and::notice::workflow commands create annotations.Path and branch filters are ANDed across categories but ORed within a list; a workflow skipped by filters leaves the check in a pending state that can block required status checks.
Self-hosted runners are selected by
runs-onlabels; they do not receive automatic security updates and keep no state between jobs unless configured otherwise.
CI/CD pipeline management
Job graph is defined by
needs; jobs without dependencies run in parallel, and a failed job skips dependents unless they useif: always()orif: failure().strategy.matrixexpands a job per combination;includeadds or augments combinations,excluderemoves them, andfail-fast: falselets other legs finish after one fails.actions/cacherestores by key with optionalrestore-keysprefix fallback; caches are immutable once written, so the key must encode a hash of the lockfile to refresh.actions/upload-artifactanddownload-artifactmove files between jobs; artifacts persist for a retention period (default 90 days) and are scoped to the run.ifconditions are evaluated before matrix expansion for job-level conditions and can referencesuccess(),failure(),cancelled(),always(), andgithub.ref.Environments add protection rules (required reviewers, wait timers, deployment branch policies); secrets scoped to an environment are only available to jobs that declare
environment:.continue-on-error: trueat step or job level marks the result as a failure that does not fail the run;steps.<id>.outcomerecords the raw result whileconclusionrecords the post-continue result.Required status checks match by job name, so renaming a job breaks branch protection until the rule is updated; skipped jobs report success for path-filtered workflows only when configured as such.
Intermediate practice for DevOps and developers
Secrets are not passed to workflows triggered from forks;
pull_request_targetruns in the base repository context with secrets, so never check out and execute untrusted PR code under it.Pin third-party actions to a full commit SHA for supply-chain safety; a tag such as
@v4is mutable and can be repointed.Default
GITHUB_TOKENpermissions depend on repository/org settings; declare an explicit least-privilegepermissions:block rather than relying on the default.OIDC federation (
permissions: id-token: write) lets a job assume a cloud role without long-lived credentials; the token is short-lived and bound to the workflow ref and environment.actions/checkoutdefaults tofetch-depth: 1; setfetch-depth: 0for tools that need history or tags, and setpersist-credentials: falsewhen the token must not remain in git config.Composite actions group steps in
action.ymlwithruns.using: composite; they cannot useusesat the top level of the action but can call other actions inside steps.Debugging: re-run with debug logging enabled by setting the
ACTIONS_STEP_DEBUGsecret totrue;ACTIONS_RUNNER_DEBUGadds runner-level diagnostics.Job containers and service containers share a network namespace, so a service is reachable on its label hostname (for example
postgres:5432) from steps in the job.
CI/CD pipeline management
Workflow run statuses progress queued โ in_progress โ completed with conclusion success, failure, cancelled, skipped, or timed_out;
if:functions read these states within a run.timeout-minutesdefaults to 360 per job and 6 hours per step is not configurable; a hung step consumes the job timeout and is cancelled at the limit.strategy.max-parallelcaps concurrent matrix legs independently of the total matrix size, which matters on limited self-hosted capacity.A job that is skipped because of
needsreportsskipped, which satisfies required checks only if the branch protection rule allows skipped jobs; otherwise the PR stays blocked.workflow_runtriggers a follow-up workflow after another completes and can readgithub.event.workflow_run.conclusionto gate deployment on success.Rollback strategy: deploy by immutable artifact or image digest produced in the build job, so re-deploying a prior digest requires no rebuild.
environmentdeployments record a deployment history and URL;steps.deployment.outputs.page_urlstyle outputs come from the action performing the deploy, not from GitHub itself.Manual approval gates live in environment protection rules, not in workflow YAML; the YAML only declares the environment name.
Intermediate practice for DevOps and developers
actions/setup-node,setup-python, andsetup-javaread version files (package.jsonengines,.python-version,pom.xml) whennode-version-file,python-version-file, orjava-version-fileis supplied.Dependency caching built into setup actions keys on the lockfile hash; for monorepos set
cache-dependency-pathto the relevant lockfile.GITHUB_OUTPUT,GITHUB_ENV,GITHUB_PATH, andGITHUB_STEP_SUMMARYare per-step files; writingname=valueto GITHUB_OUTPUT creates a step output, while multiline values need a delimiter heredoc.envprecedence: step-level overrides job-level, which overrides workflow-level; thevarscontext holds non-secret configuration variables set at repo, environment, or org level.actruns workflows locally in Docker but does not replicate GitHub-hosted runner images or secret scoping exactly, so treat local results as indicative only.github.shaon apull_requestevent is the merge commit, not the head commit; usegithub.event.pull_request.head.shawhen the PR head is required.Masking: any value matching a registered secret is redacted in logs, but derived or transformed values (base64, substrings) are not masked automatically.
Job-level
ifruns before the runner is allocated;runnercontext is unavailable there, so conditions on runner OS must usematrixorruns-onvalues instead.