GH-200 — GitHub Actions Cheat Sheet

Compact GitHub Actions (GH-200) Cheat sheet for workflow syntax, triggers, runners, security, reuse, artifacts, caching, and troubleshooting.

Use the tables for a quick pre-exam check. Expand a topic’s notes for explanations, examples, and additional distinctions.

Scope and study context
ItemReference
Vendor/providerGitHub
Official exam titleGitHub Actions (GH-200)
Official exam codeGH-200
Page purposeIndependent Cheat Sheet for real-exam preparation and original practice support

The exam mindset is practical: know how a workflow is triggered, how jobs and steps execute, how data moves between jobs, how permissions and secrets are controlled, and which GitHub Actions feature solves a specific automation problem.

Core workflow anatomy

A workflow is a YAML file stored in .github/workflows/ and triggered by events, manual dispatch, schedules, or calls from other workflows.

name: CI

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]
  workflow_dispatch:

permissions:
  contents: read

env:
  NODE_VERSION: "20"

jobs:
  test:
    name: Test on ${{ matrix.os }}
    runs-on: ${{ matrix.os }}

    strategy:
      matrix:
        os: [ubuntu-latest, windows-latest]
        node: ["20", "22"]

    steps:
      - name: Check out code
        uses: actions/checkout@v4

      - name: Set up Node
        uses: actions/setup-node@v4
        with:
          node-version: ${{ matrix.node }}

      - name: Install and test
        run: |
          npm ci
          npm test
Notes and examples

Workflow file elements

ElementScopeHigh-yield use
nameWorkflowDisplay name in the Actions UI and checks.
onWorkflowDefines events that trigger the workflow.
permissionsWorkflow/jobSets GITHUB_TOKEN permissions. Prefer least privilege.
envWorkflow/job/stepDefines environment variables. More specific scopes override broader scopes.
defaultsWorkflow/jobSets default shell or working directory for run steps.
jobsWorkflowGroups independent or dependent units of work.
runs-onJobSelects GitHub-hosted or self-hosted runner.
needsJobCreates job dependencies and enables outputs from prior jobs.
ifJob/stepConditionally runs a job or step.
strategy.matrixJobExpands one job definition into multiple job variations.
stepsJobSequential commands or actions inside a job.
usesStep/jobCalls an action at step level or reusable workflow at job level.
runStepExecutes shell commands.
withStep/job callSupplies action or reusable workflow inputs.
secretsJob call/step contextSupplies sensitive values without hard-coding them.

Workflow File Anatomy

A workflow is a YAML file placed under .github/workflows/. It normally includes an event trigger and at least one job.

KeyScopePurpose
nameWorkflowDisplay name for the workflow
run-nameWorkflow runDynamic display name for a specific run
onWorkflowDefines triggering events
permissionsWorkflow or jobSets GITHUB_TOKEN permissions
envWorkflow, job, or stepDefines environment variables
defaultsWorkflow or jobSets defaults such as shell or working directory
concurrencyWorkflow or jobControls overlapping runs or jobs
jobsWorkflowDefines one or more jobs
runs-onJobChooses the runner
needsJobCreates job dependencies
ifJob or stepConditionally runs a job or step
strategy.matrixJobExpands a job across combinations
stepsJobDefines the commands/actions in a job
usesStep or reusable workflow jobInvokes an action or reusable workflow
runStepRuns shell commands
withStep or reusable workflow callSupplies inputs
secretsReusable workflow callPasses secrets to a reusable workflow
environmentJobAssociates job with a deployment environment
outputsJob or step/actionExposes values for later use

Workflow Structure Traps

TrapCorrect understanding
Jobs run in the order listedJobs run in parallel unless linked with needs
A step can have both run and usesA normal step uses one or the other
uses always means “action”At step level it invokes an action; at job level it can call a reusable workflow
Job-level if works exactly like step-level ifJob-level if is evaluated before matrix expansion
All workflow data is available everywhereContext availability depends on workflow phase and scope
Environment variables set in one job are available in anotherJobs are isolated; use outputs or artifacts

Trigger selection

Common on events

EventChoose whenImportant exam traps
pushRun after commits or tags are pushed.Branch and tag filters apply to push refs. Path filters do not apply to tag pushes.
pull_requestValidate PR changes before merge.Runs in PR context. Fork PRs usually receive restricted token permissions and no repository secrets.
pull_request_targetRun automation in the base repository context, such as labeling or commenting.Dangerous if you check out and execute untrusted PR code while secrets or write token permissions are available.
workflow_dispatchAllow manual runs from the UI, API, or CLI.Inputs are available through the inputs context.
scheduleRun on a cron schedule.Uses UTC and runs on the default branch.
workflow_callMake a workflow reusable by other workflows.Called at job level, not as a normal step.
workflow_runRun a follow-up workflow after another workflow completes.Can separate untrusted build/test from privileged publish/deploy, but watch artifact/cache trust boundaries.
releaseRun when release activity occurs.Use types to narrow activity such as published or created.
merge_groupValidate merge queue candidates.Required when branch protection uses merge queue and checks must run on merge groups.
issues, issue_comment, pull_request_reviewAutomate repository collaboration.Confirm event payload fields before writing conditions.
Notes and examples

Trigger filtering patterns

on:
  pull_request:
    types: [opened, synchronize, reopened]
    branches:
      - main
      - "release/**"
    paths:
      - "src/**"
      - "!src/experimental/**"
FilterApplies toNotes
branchesTarget branch for PR events; pushed branch for push eventsCannot be used with branches-ignore for the same event.
branches-ignoreBranch exclusionUse when exclusion-only logic is clearer.
tagsPush tag refsCannot be used with tags-ignore for the same event.
pathsChanged filesCannot be used with paths-ignore for the same event. Order matters when using ! negation.
paths-ignoreChanged files to ignoreIf all changed files match ignored paths, the workflow is skipped.
typesEvent activity typesReduces unnecessary runs and avoids broad event handling.

Trigger decision table

ScenarioBest trigger/pattern
Run tests on every PR to mainpull_request with branches: [main]
Publish package only after a release is publishedrelease with appropriate types
Let maintainers manually run a deploymentworkflow_dispatch with inputs and environment protection
Reuse the same deployment pipeline across repositoriesworkflow_call reusable workflow
Run nightly dependency checksschedule
Comment on PRs from forks without running their codepull_request_target, limited permissions, no untrusted checkout execution
Perform privileged publish after untrusted PR checks passUse workflow_run carefully with trusted artifacts only
Validate merge queue entriesmerge_group

Events and Triggers

The on key defines when a workflow runs. Be comfortable with simple triggers, filtered triggers, manual triggers, reusable workflow triggers, and security-sensitive pull request triggers.

EventUse whenKey review points
pushCode is pushed to branches or tagsCan filter by branches, tags, and paths
pull_requestValidate PR changes safelyFork PRs usually have restricted token/secrets access
pull_request_targetRun in base repository contextSecurity-sensitive; do not run untrusted head code with secrets/write token
workflow_dispatchManual runSupports typed inputs; workflow file must be available for dispatch
workflow_callReusable workflowCalled from another workflow at the job level
scheduleCron-based automationUses UTC cron; runs from the default branch context
repository_dispatchExternal/custom triggerUsually sent through the API
workflow_runReact to another workflowUseful but can create privilege-escalation risk if artifacts/input are untrusted
releaseRelease lifecycle automationUse activity types to limit when it runs
issues, pull_request_review, labelRepository collaboration automationUnderstand the event payload and permissions needed

Trigger Filters

FilterApplies toNotes
branchesBranch namesFor pull_request, usually filters target/base branches
branches-ignoreBranch namesCannot be combined with branches for the same event
tagsTag namesUsed with push tag events
tags-ignoreTag namesCannot be combined with tags for the same event
pathsChanged file pathsIf branch and path filters both exist, both must match
paths-ignoreChanged file pathsCannot be combined with paths for the same event
typesEvent activity typesExample: PR opened, synchronized, closed

High-yield filter rules:

  • If both branch filters and path filters are configured, the workflow runs only when both conditions match.
  • Negative patterns using ! are order-sensitive.
  • You cannot use branches and branches-ignore together for the same event.
  • You cannot use paths and paths-ignore together for the same event.
  • Path filters are not evaluated for tag pushes.
  • Workflows skipped by branch, path, or commit-message filters can leave required checks pending, which may block merging if that check is required.
  • Events created by the default GITHUB_TOKEN generally do not trigger new workflow runs, which helps prevent accidental recursion. Use a suitable GitHub App token or personal access token only when workflow chaining is intentionally required.

Pull Request Trigger Decision Point

ScenarioSafer choiceWhy
Build and test untrusted PR codepull_requestRuns with restricted access for forked PRs
Label/comment on a PR using base repo permissionspull_request_targetRuns in base context, but avoid executing untrusted code
Need secrets for deploymentAvoid fork PR execution; use protected branches/environmentsSecrets should not be exposed to untrusted code
Need to inspect PR metadata onlypull_request_target may fitMetadata automation is different from checking out and running PR code

The key exam trap: pull_request_target is powerful but dangerous. It runs in the context of the base repository. If you check out and execute code from an untrusted fork in that context, secrets or write permissions may be exposed.

Jobs, steps, dependencies, and conditions

Job dependency basics

jobs:
  build:
    runs-on: ubuntu-latest
    outputs:
      artifact-name: ${{ steps.meta.outputs.name }}
    steps:
      - id: meta
        run: echo "name=app-${GITHUB_SHA}" >> "$GITHUB_OUTPUT"

  deploy:
    runs-on: ubuntu-latest
    needs: build
    if: ${{ github.ref == 'refs/heads/main' }}
    steps:
      - run: echo "Deploying ${{ needs.build.outputs.artifact-name }}"
Notes and examples
ConceptBehavior
Jobs without needsRun in parallel when runners are available.
needsMakes one job wait for another and exposes dependency results/outputs.
Failed dependencyDependent jobs are skipped unless their condition intentionally handles failure.
Step orderSteps in a job run sequentially.
Job ifEvaluated before the job runs. Be careful with matrix expansion and dependency assumptions.
Step ifEvaluated before the step runs.

Status functions

FunctionUse
success()True when previous steps/jobs in the dependency chain succeeded.
failure()True when a previous step/job failed.
cancelled()True when the workflow was cancelled.
always()Runs even after failure or cancellation. Use carefully for cleanup/reporting.
!cancelled()Common safer condition for cleanup that should not run after cancellation.
- name: Upload logs on failure
  if: ${{ failure() }}
  uses: actions/upload-artifact@v4
  with:
    name: logs
    path: logs/

Expressions, contexts, and variables

Contexts to recognize

ContextContainsTypical use
githubEvent, repository, ref, actor, run metadataBranch checks, event-specific logic, audit info
envEnvironment variables defined in workflow/job/stepRuntime configuration
varsRepository, organization, or environment configuration variablesNon-secret configurable values
secretsEncrypted secrets available to the workflowTokens, passwords, private keys
inputsInputs from workflow_dispatch or workflow_callParameterized workflows
matrixCurrent matrix combinationOS, language version, feature flags
strategyMatrix strategy metadataMatrix-related behavior
runnerRunner OS, architecture, temp pathsConditional runner behavior
stepsPrior step outputs and conclusionsPass data between steps
needsOutputs and results from dependency jobsPass data between jobs
jobCurrent job informationService container details and job status
Notes and examples

Expression functions and operators

ItemExampleNotes
Equality${{ github.ref == 'refs/heads/main' }}Used in if, env values, names, inputs.
Boolean AND/OR${{ startsWith(github.ref, 'refs/tags/') && success() }}Use parentheses for readability.
contains${{ contains(github.event.pull_request.labels.*.name, 'safe-to-test') }}Useful with arrays and strings.
startsWith / endsWith${{ startsWith(github.ref, 'refs/heads/release/') }}Common branch/tag logic.
format${{ format('app-{0}', github.sha) }}String formatting.
fromJSON${{ fromJSON(needs.plan.outputs.matrix) }}Converts JSON string to object/array.
toJSON${{ toJSON(github) }}Debugging contexts; avoid printing secrets.
hashFiles${{ hashFiles('**/package-lock.json') }}Common cache key component.

Environment variable precedence

ScopeExamplePrecedence
Workflow envShared by all jobs and stepsLowest of these three
Job envShared by steps in one jobOverrides workflow env
Step envApplies to one stepHighest
env:
  APP_ENV: test

jobs:
  example:
    runs-on: ubuntu-latest
    env:
      APP_ENV: ci
    steps:
      - run: echo "$APP_ENV"
        env:
          APP_ENV: step

Passing data between steps and jobs

NeedUseExample destination
Step output$GITHUB_OUTPUT${{ steps.id.outputs.name }}
Environment variable for later steps in same job$GITHUB_ENV$NAME or $env:NAME depending on shell
Add executable directory to PATH$GITHUB_PATHLater steps in same job
Human-readable run summary$GITHUB_STEP_SUMMARYActions run summary UI
Job outputjobs.<job_id>.outputs${{ needs.job.outputs.name }}
Files between jobsArtifactsupload-artifact / download-artifact
steps:
  - id: version
    run: echo "value=1.2.3" >> "$GITHUB_OUTPUT"

  - name: Use step output
    run: echo "Version is ${{ steps.version.outputs.value }}"

Contexts, Variables, and Expressions

Contexts expose workflow data. The correct context depends on where the expression is evaluated.

ContextContainsCommon use
githubEvent, repository, actor, ref, run metadataBranch/event decisions
envEnvironment variables defined in workflow/job/stepShell configuration
varsConfiguration variablesNon-secret configuration
secretsEncrypted secretsTokens, passwords, credentials
inputsManual or reusable workflow inputsworkflow_dispatch, workflow_call
matrixMatrix valuesOS/version-specific logic
needsUpstream job results and outputsDownstream job decisions
stepsStep outputs and outcomesUse output from earlier step in same job
runnerRunner OS/temp/tool informationOS-specific behavior
strategyMatrix strategy metadataMatrix-related conditions
jobCurrent job informationJob services/container information

Variables and Secrets

MechanismUse forSecurity levelNotes
envRuntime environment valuesNot inherently secretScope can be workflow, job, or step
varsPlain configurationNot secretOrganization, repository, or environment-level configuration
secretsSensitive valuesEncrypted and maskedNot available in many untrusted fork scenarios
GITHUB_TOKENGitHub API access for the workflowScoped by permissionsPrefer least privilege
OIDC tokenFederated cloud authShort-livedRequires id-token: write and external trust config

Environment variable precedence generally becomes more specific as scope narrows: step-level env overrides job-level env, which overrides workflow-level env.

Workflow Command Files

File/environment variablePurposeExam trap
GITHUB_ENVSet environment variables for later steps in the same jobDoes not affect the current step
GITHUB_OUTPUTSet outputs for the current stepStep needs an id for later reference
GITHUB_PATHAdd directories to PATH for later stepsJob-scoped, not workflow-global
GITHUB_STEP_SUMMARYAdd Markdown summary for the jobUseful for reports and diagnostics
GITHUB_STATEShare state within an action’s lifecycleMainly relevant to action authoring

Expression Traps

TrapCorrect approach
Treating all values as strongly typedUse fromJSON when booleans/numbers matter
Assuming secrets can be used directly in all if expressionsUse safe patterns; secrets are restricted in conditionals
Dumping entire contexts into logsAvoid exposing sensitive metadata; masking is not a full security design
Forgetting expression delimitersUse ${{ }} where expression evaluation is required
Using untrusted input directly in shell scriptsPass values through environment variables and quote carefully
Assuming outputs are typedOutputs are strings unless parsed/converted
Using hashFiles without matching filesEmpty hash results can cause unexpected cache keys

Useful expression functions to recognize:

FunctionCommon use
containsCheck list/string membership
startsWithBranch or tag prefix logic
endsWithFile or ref suffix checks
formatBuild strings
joinCombine array values
toJSONDebug or serialize context data
fromJSONParse JSON into structured data
hashFilesBuild cache keys from lockfiles

Matrix strategy quick reference

jobs:
  test:
    runs-on: ${{ matrix.os }}

    strategy:
      fail-fast: false
      matrix:
        os: [ubuntu-latest, windows-latest]
        node: ["20", "22"]
        include:
          - os: ubuntu-latest
            node: "22"
            experimental: true
        exclude:
          - os: windows-latest
            node: "20"

    continue-on-error: ${{ matrix.experimental == true }}

    steps:
      - uses: actions/checkout@v4
      - run: npm test
FeatureUse
Matrix axesTest combinations such as OS, language version, or dependency version.
includeAdd fields or additional combinations.
excludeRemove combinations.
fail-fastCancel in-progress matrix jobs when one fails, or allow all to complete.
max-parallelLimit simultaneous matrix jobs.
continue-on-errorAllow known experimental combinations to fail without failing the workflow.
Dynamic matrixGenerate JSON in one job and consume it with fromJSON in another.

Dynamic matrix pattern:

jobs:
  plan:
    runs-on: ubuntu-latest
    outputs:
      matrix: ${{ steps.set.outputs.matrix }}
    steps:
      - id: set
        run: echo 'matrix={"package":["api","web"]}' >> "$GITHUB_OUTPUT"

  test:
    needs: plan
    runs-on: ubuntu-latest
    strategy:
      matrix: ${{ fromJSON(needs.plan.outputs.matrix) }}
    steps:
      - run: echo "Testing ${{ matrix.package }}"
Notes and examples

Matrix Strategy

A matrix expands one job definition into multiple job runs.

Matrix featurePurpose
matrixDefines combinations such as OS and language version
includeAdds or modifies matrix combinations
excludeRemoves combinations
fail-fastCancels in-progress matrix jobs when one fails
max-parallelLimits simultaneous matrix jobs
continue-on-errorCan allow experimental combinations to fail

High-yield matrix rules:

  • Matrix jobs are separate job runs.
  • Matrix variables are accessed with the matrix context.
  • Use include for special combinations or extra variables.
  • Use exclude to remove invalid combinations.
  • Use fail-fast: false when you want all matrix combinations to finish even if one fails.
  • Use max-parallel when runner capacity, rate limits, or deployment constraints matter.
  • Dynamic matrices often use a previous job output plus fromJSON.

Actions, reusable workflows, and reuse choices

Reuse decision matrix

Reuse mechanismCalled fromBest forNot ideal for
Marketplace or repository actionStep with uses:Reusable task such as checkout, setup, lint, uploadFull multi-job pipelines
Composite actionStep with uses:Bundling repeated shell/action stepsSeparate jobs, services, independent permissions
JavaScript actionStep with uses:Cross-platform custom logic with Node runtimeHeavy OS-specific tooling
Docker container actionStep with uses:Packaged Linux tooling and dependenciesWindows/macOS runner execution needs
Reusable workflowJob with uses:Standard CI/CD pipeline, governance, deployment flowOne small command inside a job
Notes and examples

Composite action pattern

action.yml:

name: "Setup and lint"
description: "Install dependencies and run lint"
runs:
  using: "composite"
  steps:
    - run: npm ci
      shell: bash
    - run: npm run lint
      shell: bash

Called from a workflow:

steps:
  - uses: actions/checkout@v4
  - uses: ./.github/actions/setup-and-lint

Reusable workflow pattern

Reusable workflow:

name: Reusable Deploy

on:
  workflow_call:
    inputs:
      environment:
        required: true
        type: string
    secrets:
      deploy-token:
        required: true

jobs:
  deploy:
    runs-on: ubuntu-latest
    environment: ${{ inputs.environment }}
    steps:
      - run: echo "Deploying to ${{ inputs.environment }}"

Caller workflow:

jobs:
  deploy-prod:
    uses: org/platform/.github/workflows/deploy.yml@main
    with:
      environment: production
    secrets:
      deploy-token: ${{ secrets.PROD_DEPLOY_TOKEN }}

Reusable workflow traps

TrapCorrect approach
Calling a reusable workflow as a stepReusable workflows are called at the job level with jobs.<id>.uses.
Assuming all secrets automatically passExplicitly pass required secrets, or use the supported inheritance pattern where appropriate.
Trying to use caller job steps around a reusable workflow jobA job that uses a reusable workflow does not define normal steps.
Versioning reusable workflows by mutable branch onlyFor stronger stability, call a tag or commit SHA.
Hiding deployment permissions in reusable workflowMake required permissions, inputs, and secrets clear to callers.

Actions

An action is a reusable unit invoked from a step with uses.

Action typeRuns asGood for
JavaScript actionNode-based actionFast startup, API integrations
Docker actionContainerized actionConsistent runtime dependencies
Composite actionCollection of stepsReusing shell/action steps across workflows
Local actionAction in the same repositoryRepo-specific automation
Marketplace/third-party actionExternal action referenceCommon tasks; review and pin carefully

Security review for actions:

  • Prefer trusted actions and review what they do.
  • Pin actions to a full commit SHA when security and reproducibility matter most.
  • Pinning to a major version tag is common but less strict than SHA pinning.
  • Avoid using unreviewed actions with secrets or write permissions.
  • Use organization or enterprise policies where available to restrict allowed actions.

Composite Action vs Reusable Workflow

FeatureComposite actionReusable workflow
Invoked fromA step using usesA job using jobs.<job_id>.uses
Defines jobsNoYes
Chooses runnerNo, uses caller job runnerYes, inside called workflow
Good forRepeated step logicStandardized CI/CD workflows
InputsSupportedSupported through workflow_call
SecretsUses caller context carefullyPassed explicitly or inherited where supported
OutputsSupportedSupported
Can contain multiple jobsNoYes

Decision rule:

  • Use a composite action when you want to reuse a set of steps inside a job.
  • Use a reusable workflow when you want to reuse an entire job or multi-job process.
  • Use a workflow template when you want a starter workflow pattern for repositories, not runtime reuse.

Reusable Workflow Calling Patterns

Reusable workflows are triggered by workflow_call and called from another workflow at the job level.

Caller needPattern
Pass simple valuesDefine inputs in called workflow; use with in caller
Pass secrets explicitlyDefine secrets in called workflow; pass with secrets in caller
Pass available secrets broadlyUse secrets: inherit only when appropriate and supported
Return valuesDefine outputs in called workflow
Version reusable workflowReference a branch, tag, or SHA
Apply matrix to reusable workflowUse strategy.matrix on the calling job

Reusable workflow traps:

  • A reusable workflow is called as a job, not as a step.
  • The called workflow must define on: workflow_call.
  • The caller does not define the internal steps of the called workflow.
  • Permissions and secrets should be explicitly considered across the call boundary.
  • Pinning reusable workflows by SHA improves reproducibility and security.

Runners and execution environments

Runner selection

Runner typeChoose whenWatch for
GitHub-hosted runnerYou need managed, clean, ephemeral execution with standard OS images.Image contents can change; install or pin required tool versions.
Self-hosted runnerYou need private network access, custom hardware, special tools, or controlled environment.You manage security, patching, cleanup, scaling, and isolation.
Larger or specialized runnerYou need more resources or specialized hardware.Availability and configuration depend on account setup.
Runner groupYou need to restrict which repositories can use specific self-hosted runners.Misconfigured groups can expose sensitive infrastructure.
Custom runner labelsYou need jobs to target specific capabilities.Labels must match available runners.
Notes and examples
jobs:
  build:
    runs-on: [self-hosted, linux, x64, gpu]
    steps:
      - run: ./build.sh

Job containers and service containers

FeatureUse
jobs.<job_id>.containerRun job steps inside a container.
servicesStart supporting containers such as databases, queues, or caches.
Service labelHostname used to reach the service from a job container.
Port mappingNeeded when the job runs directly on the runner and accesses service through localhost.
Linux dependencyContainer jobs and Docker-based actions require a compatible Docker environment.
jobs:
  integration:
    runs-on: ubuntu-latest
    container: node:22

    services:
      postgres:
        image: postgres:16
        env:
          POSTGRES_PASSWORD: postgres

    steps:
      - run: npm test
        env:
          DATABASE_HOST: postgres

Runner security distinctions

AreaGitHub-hostedSelf-hosted
LifecycleFresh environment per jobPersistent unless you design cleanup/isolation
MaintenanceManaged by GitHubYour responsibility
Network accessPublic internet and configured servicesCan access internal systems if network permits
Secrets exposure riskLower persistence riskHigher if untrusted code can run
Public repository PRsCommon for validationAvoid running untrusted fork code on sensitive self-hosted runners

Runners

A runner is the machine that executes a job.

Runner typeBest forKey considerations
GitHub-hosted runnerCommon CI/CD tasksFresh environment per job; choose OS with runs-on
Larger GitHub-hosted runnerMore CPU/RAM or specialized capacityUseful for heavier workloads
Self-hosted runnerCustom tools, private network, specialized hardwareYou manage security, updates, cleanup, and availability
Runner groupAccess control for self-hosted runnersHelps restrict which repos can use which runners

Runner Selection

runs-on patternMeaning
Single labelMatch a runner image or label, such as Linux, Windows, or macOS runner labels
Multiple labelsRunner must match all listed labels
Self-hosted labelsInclude self-hosted plus OS/architecture/custom labels
Runner groupRestrict selection to a configured group

Runner traps:

  • Each normal job runs on its own runner instance. Do not assume job filesystem state carries over.
  • GitHub-hosted runners are ephemeral; self-hosted runners may persist files unless cleaned.
  • Shell defaults differ by operating system.
  • Container jobs and service containers are primarily Linux-runner features.
  • Self-hosted runners in public repositories require special caution because untrusted code may execute on your infrastructure.
  • If a self-hosted runner is offline, jobs can remain queued.

Security, permissions, secrets, and OIDC

GITHUB_TOKEN permissions

Set token permissions explicitly. Do not rely on repository defaults.

permissions:
  contents: read

jobs:
  release:
    runs-on: ubuntu-latest
    permissions:
      contents: write
      packages: write
    steps:
      - run: echo "Publish release"
Notes and examples
Permission areaTypical purpose
contentsRead or write repository contents, releases, tags.
pull-requestsRead or modify pull requests.
issuesRead or modify issues.
actionsInteract with Actions runs/artifacts where allowed.
checksCreate or update check runs.
statusesCreate commit statuses.
deploymentsCreate or update deployments.
packagesPublish or read packages.
security-eventsUpload security scanning results.
id-tokenRequest an OIDC token for federated authentication.

Secrets vs variables

StoreSensitive?Typical examplesAccess syntax
SecretsYesAPI tokens, private keys, passwords${{ secrets.NAME }}
Configuration variablesNoRegion, feature flag, environment name${{ vars.NAME }}
Environment variablesNot inherentlyRuntime values for steps$NAME, $env:NAME, or ${{ env.NAME }}

High-yield rules:

  • Do not hard-code secrets in workflow YAML, scripts, logs, or action inputs.
  • Secrets are masked in logs, but masking is not a substitute for least privilege.
  • Secrets are generally not available to workflows triggered from untrusted forks.
  • Secrets cannot be directly used in some conditional expressions. A common pattern is to map to env and test the env value carefully.
  • Prefer environment secrets for deployment targets with required reviewers or protection rules.
  • Use separate secrets for separate environments instead of one broad credential.

OIDC federation pattern

Use OpenID Connect when a cloud or external identity provider trusts GitHub-issued tokens. This avoids long-lived cloud credentials in GitHub secrets.

permissions:
  contents: read
  id-token: write

jobs:
  deploy:
    runs-on: ubuntu-latest
    environment: production
    steps:
      - name: Authenticate with cloud provider using OIDC
        run: echo "Request short-lived credentials through provider action or CLI"
OIDC conceptExam-ready meaning
id-token: writeRequired permission to request a GitHub OIDC token.
Trust policyConfigured in the external provider, not only in the workflow.
ClaimsProvider can validate repository, branch, environment, workflow, and other token claims.
BenefitReduces long-lived secret exposure.
TrapOIDC does not automatically grant access; external trust and authorization must be configured.

Secure action usage

RiskSafer pattern
Third-party action changes unexpectedlyPin to a full commit SHA for high-security workflows.
Overbroad token permissionsSet top-level permissions: contents: read, elevate per job only when needed.
Running untrusted PR code with secretsUse pull_request for validation; avoid privileged code execution in pull_request_target.
Shell injection from issue/PR dataTreat event context values as untrusted input. Quote variables and avoid direct interpolation into shell.
Secret printed by a scriptDisable verbose output, avoid set -x, mask derived sensitive values when needed.
Self-hosted runner exposed to forksRestrict runner access and avoid untrusted workloads.

Unsafe pattern:

- run: echo "${{ github.event.pull_request.title }}" | bash

Safer pattern:

- name: Use PR title as data, not code
  env:
    PR_TITLE: ${{ github.event.pull_request.title }}
  run: |
    printf '%s\n' "$PR_TITLE"

Permissions, Tokens, and Security

Security is a major decision area for GitHub Actions. Know how to reduce privileges and prevent untrusted code from reaching secrets.

GITHUB_TOKEN

GitHub automatically creates a GITHUB_TOKEN for workflow runs. Its access is controlled with the permissions key.

Permissions practiceWhy it matters
Set permissions at workflow or job levelAvoid unnecessary default access
Grant only required scopesLeast privilege reduces blast radius
Use job-level permissions for sensitive jobsDifferent jobs may need different access
Use contents: read for checkout-only jobsMany CI jobs do not need write access
Use id-token: write only for OIDC jobsNeeded to request an OIDC token
Avoid broad write permissions on untrusted triggersPR workflows are a common attack surface

Important nuance: when you explicitly configure permissions, unspecified permissions are generally not granted, aside from required metadata access. This is a common exam-style trap.

OIDC

OpenID Connect allows workflows to request short-lived tokens from a cloud provider or external system without storing long-lived cloud credentials as GitHub secrets.

OIDC requirementMeaning
permissions: id-token: writeAllows workflow to request an OIDC token
External trust policyCloud/provider must trust the GitHub identity claims
Short-lived credential exchangeOIDC token is exchanged for provider credentials
Claim restrictionsLimit by repository, branch, environment, workflow, or other claims where appropriate

OIDC trap: granting id-token: write does not automatically grant cloud access. The external provider must be configured to trust the workflow identity.

Secrets

Secret behaviorReview point
Encrypted at restUsed for sensitive values
Masked in logsExact secret values are masked, but do not rely on logs as a security boundary
ScopedOrganization, repository, and environment scopes exist
Restricted for fork PRsDo not expect secrets in untrusted fork workflows
Not plain configurationUse vars for non-sensitive config
Can be environment-specificDeployment environments can have separate secrets

Common secret mistakes:

  • Printing secrets or derived credentials.
  • Passing secrets to untrusted third-party actions.
  • Using secrets in workflows triggered by untrusted code.
  • Confusing vars and secrets.
  • Assuming masking prevents all forms of leakage.
  • Forgetting that generated sensitive values may need explicit masking with add-mask.

Environments and deployments

jobs:
  deploy:
    runs-on: ubuntu-latest
    environment:
      name: production
      url: https://example.com
    steps:
      - run: ./deploy.sh
FeatureUse
EnvironmentNamed deployment target such as dev, staging, or production.
Environment secretsSecrets scoped to that environment.
Environment variablesNon-secret configuration scoped to that environment.
Required reviewersManual approval before a job accesses protected environment secrets and proceeds.
Wait timer/protection rulesAdd control before deployment jobs run.
Deployment URLDisplays deployed app URL in the Actions UI.

Deployment decision points

ScenarioRecommended pattern
Deploy to production only after approvalUse environment: production with protection rules.
Deploy to staging on every merge to mainBranch condition plus staging environment.
Reuse deployment process across many repositoriesReusable workflow with environment input.
Need cloud credentials without stored long-lived secretOIDC plus environment protection.
Need separate credentials per targetEnvironment-scoped secrets.
Notes and examples

Environments and Deployments

GitHub Actions environments help control deployment jobs.

Environment featureUse
Environment nameAssociates a job with a deployment target
Environment secretsSecrets scoped to that environment
Environment variablesNon-secret configuration scoped to that environment
Protection rulesAdd controls before a deployment job proceeds
Deployment historyTrack deployment activity

Deployment review rules:

  • Use environments for deployment gates and environment-specific secrets.
  • Use concurrency to prevent overlapping deployments.
  • Build once, then deploy the same artifact through later stages when possible.
  • Keep CI validation separate from privileged deployment jobs.
  • Avoid exposing production secrets to PR validation workflows.
  • Use least-privilege permissions for each deployment job.

Artifacts, caches, and dependency management

Artifacts vs cache

FeatureArtifactsCache
Primary purposePreserve or transfer files from a workflow runSpeed up future runs by reusing dependencies/build data
Typical dataTest reports, coverage, binaries, logsPackage manager directories, build caches
Access patternUpload in one job, download later or inspect after runRestore by key, save for future matching keys
Mutability expectationRun outputKeyed cache content; treat as dependency optimization
Security cautionDo not trust artifacts from untrusted workflows for privileged deployment without validationCache poisoning is possible if untrusted code can influence cache content/keys
Notes and examples

Artifact pattern:

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - run: mkdir -p dist && echo app > dist/app.txt
      - uses: actions/upload-artifact@v4
        with:
          name: app
          path: dist/

  consume:
    needs: build
    runs-on: ubuntu-latest
    steps:
      - uses: actions/download-artifact@v4
        with:
          name: app
          path: dist/
      - run: ls dist

Cache pattern:

- name: Cache npm dependencies
  uses: actions/cache@v4
  with:
    path: ~/.npm
    key: npm-${{ runner.os }}-${{ hashFiles('**/package-lock.json') }}
    restore-keys: |
      npm-${{ runner.os }}-

Cache key guidance

Key componentWhy it matters
OS or runner labelAvoid restoring incompatible dependencies.
Lockfile hashInvalidates cache when dependencies change.
Tool/language versionAvoid mixing incompatible dependency formats.
Restore prefixAllows partial fallback when exact key is unavailable.

Concurrency and run control

concurrency:
  group: deploy-${{ github.ref }}
  cancel-in-progress: true
FeatureUse
Workflow-level concurrencyPrevent duplicate full workflow runs for the same group.
Job-level concurrencyPrevent overlapping deployment or mutation jobs.
groupDefines what counts as the same concurrent unit.
cancel-in-progressCancels older in-progress runs when a newer run in the same group starts.

Common choices:

ScenarioGroup example
One deployment per environmentdeploy-${{ inputs.environment }}
One CI run per branchci-${{ github.ref }}
One release publish per tagrelease-${{ github.ref_name }}

Common workflow commands and GitHub CLI

Workflow file commands

Command filePurpose
$GITHUB_OUTPUTSet step outputs.
$GITHUB_ENVSet environment variables for later steps in the same job.
$GITHUB_PATHAdd directories to PATH for later steps in the same job.
$GITHUB_STEP_SUMMARYAdd Markdown to the run summary.
- name: Write summary
  run: |
    echo "### Test summary" >> "$GITHUB_STEP_SUMMARY"
    echo "- Passed" >> "$GITHUB_STEP_SUMMARY"

Useful GitHub CLI commands for Actions

gh workflow list
gh workflow run CI
gh run list
gh run view --log
gh run watch
gh run rerun <run-id>
CommandUse
gh workflow listList workflows.
gh workflow runTrigger workflow_dispatch.
gh run listInspect recent workflow runs.
gh run view --logView logs from a run.
gh run watchFollow a run until completion.
gh run rerunRerun failed or selected jobs depending on options.

Troubleshooting patterns

Fast diagnosis table

SymptomLikely causeCheck
Workflow did not startEvent, branch, tag, path, or activity-type filter did not matchInspect on filters and event payload.
Required check stuck pendingWorkflow skipped by branch/path filter or commit message while branch protection expects itAlign required checks with workflows that always run, or adjust filters.
Job skippedif condition false or dependency failedReview job condition and needs results.
Step skipped after failureDefault status condition requires prior successUse explicit if: ${{ failure() }} or another status function.
Secret empty in PRFork or untrusted trigger contextUse safer event design; do not expose secrets to untrusted code.
Permission denied with GITHUB_TOKENMissing job/workflow permissionAdd least-privilege permissions for that job.
Self-hosted job waitingNo online runner matches labels/groupCheck runner labels, group access, and runner status.
Cache not restoredKey mismatch or path mismatchPrint key inputs; check lockfile hash and path.
Service container unreachableWrong hostname or port mappingUse service label from job container; use mapped localhost port when running directly on runner.
Reusable workflow fails at parse timeWrong call location or missing required input/secretCall at jobs.<id>.uses and supply required values.
Matrix produced unexpected jobsinclude/exclude logic mismatchExpand the matrix mentally; verify added fields and exclusions.
Deployment waitingEnvironment protection rule requires approval or waitCheck environment configuration and reviewers.
Notes and examples

Debugging techniques

- name: Debug selected contexts
  run: |
    echo "ref=${{ github.ref }}"
    echo "event=${{ github.event_name }}"
    echo "runner=${{ runner.os }}"

Use toJSON carefully:

- name: Debug event payload
  env:
    EVENT_JSON: ${{ toJSON(github.event) }}
  run: echo "$EVENT_JSON"

Avoid dumping secrets or full contexts that may contain sensitive data.

High-yield exam traps

TrapCorrect exam response
Using pull_request_target to build untrusted PR code with secretsUse pull_request for untrusted validation; reserve pull_request_target for safe base-context automation.
Assuming GITHUB_TOKEN always has write permissionsDefaults can vary. Set permissions explicitly.
Using secrets in workflows triggered from forksSecrets are restricted for untrusted fork contexts. Design around that boundary.
Confusing artifacts and cachesArtifacts are run outputs; caches are dependency/build acceleration.
Calling reusable workflows inside stepsReusable workflows are called as jobs. Actions are called as steps.
Expecting jobs to run sequentially by defaultJobs run in parallel unless connected with needs.
Forgetting that skipped filtered workflows can affect required checksBranch protection may wait for checks that never run.
Storing non-secret configuration as secretsUse vars for non-sensitive configuration.
Using mutable action references in sensitive pipelinesPin actions to commit SHA when integrity matters.
Running untrusted code on self-hosted runnersRestrict runner access and isolate workloads.
Assuming environment secrets are available before approvalProtected environment jobs must pass protection rules first.
Overusing always()Prefer precise status checks; use !cancelled() when appropriate.

Compact scenario review

RequirementStrong answer
Build, test, and lint every PRpull_request workflow with matrix and read-only permissions.
Publish only from tagspush with tags filter and job condition for tag refs.
Deploy to production with manual approvalEnvironment protection plus deployment job.
Share standard CI across repositoriesReusable workflow with workflow_call.
Share five repeated shell steps inside one jobComposite action.
Authenticate to cloud without static cloud secretOIDC with id-token: write and provider trust policy.
Speed up package installsCache package manager directory keyed by OS, tool version, and lockfile hash.
Pass build output to deploy jobUpload artifact in build; download artifact in deploy.
Stop overlapping deploymentsJob-level concurrency keyed by environment.
Run database integration testsService container.
Target internal network for deploymentSelf-hosted runner with restricted repository access.
Run safe labeling on fork PRspull_request_target with minimal permissions and no untrusted code execution.

Final preparation checklist

  • Can you choose the correct event for push, pull_request, pull_request_target, workflow_dispatch, schedule, workflow_call, and workflow_run scenarios?
  • Can you explain why pull_request_target is powerful and dangerous?
  • Can you write the difference between an action, a composite action, a Docker action, and a reusable workflow?
  • Can you set least-privilege permissions for GITHUB_TOKEN?
  • Can you distinguish secrets, vars, env, inputs, matrix, needs, and steps contexts?
  • Can you pass data from step to step, job to job, and workflow to workflow?
  • Can you choose between artifacts and caches?
  • Can you select GitHub-hosted versus self-hosted runners for a scenario?
  • Can you diagnose skipped workflows, pending checks, missing secrets, and permission errors?
  • Can you secure deployments with environments, approvals, environment secrets, and OIDC?

For the next step, use this Cheat Sheet to drill scenario-based questions: read a workflow requirement, choose the trigger, permissions, runner type, reuse mechanism, and data-passing pattern before checking your answer.

Core Mental Model

GitHub Actions automation usually follows this path:

  1. An event occurs, such as push, pull_request, workflow_dispatch, or schedule.
  2. GitHub finds a matching workflow file in .github/workflows/.
  3. The workflow creates one or more jobs.
  4. Each job runs on a runner.
  5. Each job executes steps, which either run shell commands with run or invoke actions with uses.
  6. Logs, outputs, artifacts, caches, checks, deployments, and summaries are produced.
LayerWhat it doesHigh-yield exam cueCommon mistake
WorkflowYAML automation definitionStored in .github/workflows/*.yml or .yamlExpecting a workflow outside that directory to run
EventStarts a workflowon: push, on: pull_request, on: workflow_dispatchConfusing event filters with job if conditions
JobUnit of executionHas runs-on, steps, optional needsAssuming jobs run sequentially by default
RunnerMachine that executes a jobGitHub-hosted or self-hostedAssuming files persist across jobs
StepIndividual command or actionUses run or usesTrying to use both run and uses in one step
ActionReusable automation unitJavaScript, Docker, or composite actionConfusing an action with a reusable workflow
ContextRuntime datagithub, env, secrets, matrix, needsUsing the wrong context for the execution phase
Token/permissionsAccess controlGITHUB_TOKEN, permissions, OIDCLeaving broad write permissions enabled

Jobs, Steps, and Control Flow

Jobs

A job is the main execution unit in a workflow. Each job gets a runner and runs its steps in order.

FeatureUse forExam cue
runs-onSelect runnerRequired for normal jobs
needsOrder jobsWithout needs, jobs run in parallel
ifConditional job executionJob skipped if condition is false
strategy.matrixRun variantsOS/language/version combinations
continue-on-errorAllow failure without failing workflow/jobOften used for experimental matrix entries
timeout-minutesStop long-running jobsAvoids stuck jobs consuming time
concurrencyPrevent overlapping workUseful for deployments and branch-specific runs
environmentDeployment environmentCan add protection rules and environment-scoped secrets
permissionsToken scopePrefer least privilege at workflow or job level
Notes and examples

Steps

Steps run sequentially inside a job.

Step typeSyntax cueUse for
Shell commandrunBuild, test, script, CLI command
ActionusesReusable task such as checkout, setup, upload artifact
Conditional stepifRun only under specific status/event/context conditions
Identified stepidRequired if later steps read that step’s outputs
Input-driven stepwithPass inputs to an action
Environment stepenvPass environment variables to the command/action

Status Functions

FunctionMeaningCommon use
success()Previous steps/jobs succeededDefault behavior for most steps
failure()A previous step/job failedUpload logs after failure
cancelled()Workflow was cancelledSkip cleanup that should not run after cancellation
always()Run regardless of previous resultCleanup, diagnostics, summaries
!cancelled()Run unless cancelledOften safer than unconditional always() for long operations

Common control-flow traps:

  • A dependent job is skipped if a required job is skipped or fails, unless the condition explicitly handles it.
  • Step-level if conditions are evaluated at runtime inside the job.
  • Job-level if conditions are evaluated before matrix expansion.
  • continue-on-error changes failure behavior but does not mean the command succeeded.
  • Use needs.<job_id>.result when downstream jobs must react to upstream success, failure, cancellation, or skip states.

Artifacts, Caches, Outputs, and Data Passing

Different data-sharing mechanisms solve different problems.

NeedBest toolWhy
Pass a small value between steps in one jobStep outputSimple and direct
Pass a small value between jobsJob output plus needsDesigned for job dependencies
Pass files between jobs in a workflowArtifactDownload in another job
Preserve dependencies between workflow runsCacheSpeeds repeated installs/builds
Publish human-readable run resultsStep summaryAppears in workflow UI
Persist deployment package for releaseArtifact or release assetCache is not a release mechanism
Notes and examples

Outputs

Output typeHow it is used
Step outputWritten to GITHUB_OUTPUT; referenced through steps.<id>.outputs.<name>
Job outputMaps step outputs to jobs.<job_id>.outputs; consumed through needs.<job_id>.outputs.<name>
Reusable workflow outputExposed by the called workflow and consumed by the caller

Output traps:

  • A step must have an id if later steps need its outputs.
  • Job outputs are only available to jobs that declare needs.
  • Outputs are strings unless converted.
  • Outputs are not a safe place for secrets unless the flow is carefully controlled.

Artifacts vs Cache

FeatureArtifactCache
Primary purposeStore workflow files/resultsReuse dependencies/build data
Typical examplesTest reports, build packages, coverage filesPackage manager directories, tool caches
Shared across jobsYes, by upload/downloadNot the main purpose
Shared across workflow runsArtifacts can be retained; caches are restored by keyYes, if key/scope matches
DeterminismGood for exact files from a runCache hit is helpful but not guaranteed
Security cautionDo not blindly trust artifacts from untrusted workflowsDo not cache secrets or sensitive files

Cache key best practices:

  • Include operating system or platform.
  • Include dependency lockfile hash with hashFiles.
  • Use restore keys for fallback matches.
  • Do not depend on cache for correctness.
  • Do not put credentials, tokens, or secrets in cached paths.

Concurrency

Concurrency prevents overlapping workflow runs or jobs from interfering with each other.

Use caseConcurrency pattern
Cancel older CI runs on same branchGroup by workflow and ref; enable cancel-in-progress
Prevent simultaneous production deploymentsUse a stable production deployment group
Serialize environment-specific deploymentsGroup by environment name
Avoid duplicate scheduled workUse one group for the scheduled process

Review points:

  • concurrency can be set at workflow or job level.
  • A concurrency group allows at most one running and one pending run/job in that group.
  • cancel-in-progress: true cancels the currently running item in the group.
  • Group names are case-insensitive.
  • Ordering of queued items is not guaranteed.

Self-Hosted Runner Security

Self-hosted runners are powerful because they can access internal tools and networks, but that also increases risk.

RiskMitigation
Untrusted PR code runs on internal machineAvoid self-hosted runners for untrusted public PRs
Files persist between jobsClean workspace and use ephemeral runners where possible
Runner has broad network accessSegment network access
Tooling becomes outdatedPatch runner host and runner application
Secrets remain in logs/files/processesMinimize secrets, clean aggressively, use least privilege
Any repo can target runnerUse runner groups and labels carefully

Exam decision rule: if a scenario involves private infrastructure, custom hardware, or special internal dependencies, a self-hosted runner may be the answer. If the scenario involves untrusted public contributions, self-hosted runners are often the risk to avoid.

Debugging and Observability

Tool/featureUse
Workflow run logsStep-by-step execution details
AnnotationsWarnings/errors surfaced in the UI
Step summariesMarkdown reports for a job
Re-run jobs/workflowsRetry transient failures
Debug loggingMore detailed runner and step logs
toJSONInspect context data carefully
GitHub CLIInspect and trigger workflow runs from the command line
ArtifactsPreserve logs, reports, screenshots, build outputs
Notes and examples

Debug logging review:

  • ACTIONS_STEP_DEBUG enables detailed step debug logging.
  • ACTIONS_RUNNER_DEBUG enables additional runner diagnostic logging.
  • Use debug logging carefully because logs may contain sensitive operational details.

Common failure patterns:

SymptomLikely cause
Workflow does not runWrong event, filter mismatch, workflow file location issue
Required check stays pendingWorkflow skipped by branch/path/commit-message filter
Job queued foreverNo matching runner or self-hosted runner unavailable
Step cannot access repoMissing checkout or insufficient contents permission
API call gets 403GITHUB_TOKEN permissions too restrictive
Secret is emptySecret not available for trigger/scope/environment
Output is blankMissing step id, wrong output name, or wrong context
Cache never hitsKey changes too often or path/key mismatch
Matrix creates too many jobsMatrix combinations not limited with exclude or include
Deployment overlapsMissing concurrency or environment controls

YAML and Syntax Gotchas

GitHub Actions workflows are YAML, so small syntax mistakes can change meaning.

GotchaReview point
IndentationYAML structure is indentation-sensitive
Special charactersQuote patterns beginning with characters like *, !, or [
Multi-event syntaxIf one event has configuration, use mapping syntax consistently
Glob patternsNegative patterns are order-sensitive
Strings vs booleansUse expression conversion when type matters
Shell quotingTreat untrusted input carefully
Step IDsRequired for referencing step outputs
Context spellingContext and property names must be exact
File pathsLinux, Windows, and macOS path syntax differs

High-Yield Decision Rules

Use these as quick “if the question says X, think Y” rules.

ScenarioLikely answer
Run workflow manuallyworkflow_dispatch
Reuse an entire workflowworkflow_call reusable workflow
Reuse repeated steps inside a jobComposite action
Pass files between jobsUpload/download artifact
Reuse dependencies across runsCache
Sequence jobsneeds
Run jobs on several OS/language versionsMatrix strategy
Limit overlapping deploymentsconcurrency
Require approval or protected deployment controlsEnvironment
Reduce token accesspermissions
Authenticate to cloud without long-lived secretOIDC
Safely validate fork PR codepull_request with restricted permissions
Automate PR labels/comments from base contextCarefully designed pull_request_target
Run cleanup after failureStatus functions such as always() or failure()
Set environment variable for later stepsWrite to GITHUB_ENV
Set step outputWrite to GITHUB_OUTPUT
Get upstream job outputUse needs.<job_id>.outputs
Inspect event payloadUse github.event or event payload file
Use internal network/hardwareSelf-hosted runner
Avoid running untrusted code on infrastructurePrefer GitHub-hosted or restricted patterns

Common Candidate Mistakes

Confusing Reuse Mechanisms

Candidate mistakeCorrect distinction
“Reusable workflow” called from a stepReusable workflows are called at job level
“Composite action” defines runners/jobsComposite actions run within the caller’s job
“Workflow template” runs dynamicallyTemplates help create workflow files; they are not runtime calls
Notes and examples

Misusing Security Features

Candidate mistakeCorrect distinction
OIDC replaces all permissionsOIDC needs id-token: write and external trust
Secrets are safe in any workflowSecrets must be protected from untrusted code
Masking equals full protectionMasking helps logs but is not a complete security boundary
pull_request_target is just a stronger pull_requestIt changes trust context and can be dangerous
Broad GITHUB_TOKEN permissions are harmlessUse least privilege

Misunderstanding Data Flow

Candidate mistakeCorrect distinction
Cache is for passing build artifactsArtifacts are for exact files/results
Environment variables cross jobsJobs are isolated
Step output is available without idThe step needs an id
Job output is available everywhereDownstream job must use needs
$GITHUB_ENV affects current shell lineIt affects later steps

Quick Concept Check

PromptExpected answer
Where must workflow files live?.github/workflows/
What makes jobs run sequentially?needs
What selects the runner?runs-on
What runs a shell command?run
What invokes an action?Step-level uses
What invokes a reusable workflow?Job-level uses
What trigger enables manual runs?workflow_dispatch
What trigger enables reusable workflows?workflow_call
What is used for cloud federation?OIDC with id-token: write
What controls GITHUB_TOKEN scopes?permissions
What is best for dependency reuse?Cache
What is best for test reports/build files?Artifact
What context reads matrix values?matrix
What context reads upstream outputs?needs
What context reads encrypted secrets?secrets
What feature prevents overlapping deploys?concurrency
What feature can require deployment approval?Environment
What should be avoided with untrusted fork code?Secrets, write tokens, unsafe pull_request_target patterns

Put the review into practice