GH-200 — GitHub Actions (GH-200) Exam Quick Reference

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

Exam identity and focus

ItemReference
Vendor/providerGitHub
Official exam titleGitHub Actions (GH-200)
Official exam codeGH-200
Page purposeIndependent Quick Reference for real-exam preparation and original practice support

Use this page to quickly review GitHub Actions concepts that are commonly tested through scenario questions: choosing the right trigger, structuring workflows, securing tokens and secrets, reusing automation, selecting runners, and troubleshooting failed or skipped runs.

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

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.

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.

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

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 }}"
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

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 }}"

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 }}"

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

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.

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.
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

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"
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"

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.

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

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.

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 Quick Reference 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.

Browse Certification Practice Tests by Exam Family