Browse Certification Practice Tests by Exam Family

GitHub GH-200 Practice Test: GitHub Actions

Prepare for GitHub Actions (GH-200) with free sample questions, a full-length diagnostic, topic drills, timed practice, workflow YAML, CI/CD pipeline behavior, action reuse, troubleshooting, security, scale, optimization, and detailed explanations in IT Mastery.

GitHub Actions validates intermediate skill in automating software development workflows with GitHub Actions, including workflow creation, CI/CD pipeline management, troubleshooting, scale, security, and optimization.

IT Mastery practice for GitHub Actions (GH-200) is live now. Use this page to start the web simulator, review the exam snapshot, work through 24 public sample questions, and continue into full IT Mastery practice with the same IT Mastery account on web, iOS, iPadOS, macOS, or Android.

Interactive Practice Center

Start a practice session for GitHub Actions (GH-200) below, or open the full app in a new tab. For the best experience, open the full app in a new tab and navigate with swipes/gestures or the mouse wheel—just like on your phone or tablet.

Open Full App in a New Tab

A small set of questions is available for free preview. Subscribers can unlock full access by signing in with the same app-family account they use on web and mobile.

Prefer to practice on your phone or tablet? Download the IT Mastery – AWS, Azure, GCP & CompTIA exam prep app for iOS or IT Mastery app on Google Play (Android) and use the same IT Mastery account across web and mobile.

Free diagnostic: Try the GitHub Actions GH-200 full-length practice exam before subscribing. Use it as one workflow-automation baseline, then return to IT Mastery for timed mocks, topic drills, explanations, and the full GH-200 question bank.

What this GH-200 practice page gives you

  • a direct route into IT Mastery practice for GitHub Actions
  • 24 on-page sample questions aligned to the live GH-200 practice route
  • workflow YAML, runner, reusable action, deployment, permissions, and troubleshooting drills
  • a clear free-preview path before you subscribe
  • the same IT Mastery account across web and mobile

GitHub Actions snapshot

  • Vendor: GitHub
  • Credential name: GitHub Actions
  • Microsoft Learn study-guide code: GH-200
  • Level shown by Microsoft Learn: Intermediate
  • Exam time shown by Microsoft Learn: 100 minutes
  • IT Mastery practice bank: 880 questions
  • Current IT Mastery status: live practice available

Topic coverage for Actions practice

AreaWhat to practise
Author workflowstriggers, jobs, steps, runners, matrices, environments, permissions, and reusable workflows
Consume and troubleshoot workflowslogs, artifacts, caching, concurrency, failures, reruns, and debugging strategy
Author and maintain actionscomposite actions, JavaScript actions, Docker actions, inputs, outputs, versioning, and reuse
Enterprise managementpolicies, runner groups, billing awareness, organization settings, and operational governance
Secure and optimize automationsecrets, token permissions, dependency risk, least privilege, performance, and reliability

GitHub Actions workflow map

Use this map to reason through most GitHub Actions questions. Start with the event, then follow the workflow into jobs, runners, permissions, secrets, and deployment controls.

    flowchart LR
	  Event["Event trigger"] --> Workflow["Workflow file"]
	  Workflow --> Jobs["Jobs"]
	  Jobs --> Runner["Runner"]
	  Jobs --> Steps["Steps"]
	  Steps --> Actions["Actions or shell commands"]
	  Workflow --> Permissions["GITHUB_TOKEN permissions"]
	  Workflow --> Secrets["Secrets and variables"]
	  Jobs --> Environment["Environment gates"]
	  Environment --> Deployment["Deployment"]
	  Permissions --> Risk["Least-privilege risk check"]
	  Secrets --> Risk

Actions exhibit patterns to practise

GitHub Actions questions often include a short workflow, log excerpt, policy setting, or runner detail. Read the exhibit from top to bottom: identify the trigger first, then check job scope, permissions, runner context, secrets, and the artifact/cache behavior the question is testing.

Exhibit typeWhat to inspect first
Workflow YAMLon, permissions, jobs, runs-on, strategy, and deployment environment
Failure logfirst failing command, exit code, missing secret, path mismatch, or denied token scope
Permissions blockwhether the GITHUB_TOKEN has only the scopes required for the job
Runner labelswhether the job is using GitHub-hosted or self-hosted infrastructure correctly
Cache keywhether the key changes when dependencies or the runner OS changes
Artifact stepwhether the file is produced before upload and downloaded by the later job

Example workflow exhibit:

on:
  pull_request:
    paths:
      - "src/**"

permissions:
  contents: read
  pull-requests: write

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm ci
      - run: npm test

Best reading: this workflow is pull-request scoped, path-filtered, and uses limited token permissions. It should not be treated as a deployment workflow unless a later job adds environment gates and trusted credentials.

Example failure-log exhibit:

Run npm test
Error: Cannot find module '@acme/shared-config'
Process completed with exit code 1.

Best first check: compare the runner install step, lock file, package registry authentication, workspace path, and cache restore result before changing the test command.

Example deployment-control exhibit:

SettingWhy it matters
environment: productioncan require reviewers and expose production-scoped secrets only after approval
concurrency.group: productionprevents overlapping production deployment runs
cancel-in-progress: falseavoids canceling an already-started production deployment automatically
permissions.contents: readavoids broad repository write access unless a deployment step truly needs it

GH-200 decision filters

Use these filters when a workflow answer looks correct but incomplete:

  • Trigger and scope: inspect on, branch filters, path filters, and event context before changing jobs or steps.
  • Job boundary: separate job-level permissions, runner labels, matrices, dependencies, environments, and artifacts from step-level commands.
  • Reuse mechanism: distinguish reusable workflows, composite actions, JavaScript actions, Docker actions, and shared shell snippets.
  • Security baseline: check GITHUB_TOKEN permissions, secrets, OIDC, environment protection, dependency risk, and untrusted pull request behavior.
  • Troubleshooting evidence: read logs, exit codes, file paths, cache keys, artifact names, and runner context before rewriting workflow logic.

Final 7-day GH-200 practice sequence

DayPractice focus
7Take the free full-length diagnostic and tag misses as authoring, troubleshooting, actions, enterprise, or security.
6Drill workflow syntax, triggers, jobs, matrices, reusable workflows, environments, and permissions.
5Drill logs, cache behavior, artifacts, concurrency, reruns, runner labels, and failure analysis.
4Drill custom actions, inputs, outputs, versioning, and when to use composite, JavaScript, or Docker actions.
3Drill secrets, OIDC, token permissions, untrusted PRs, branch protection, and secure deployment controls.
2Complete a timed mixed set and explain which workflow boundary drove each miss.
1Review weak YAML patterns and exhibit-reading mistakes; avoid late memorization of random syntax.

When GH-200 practice is enough

If you can score above roughly 75% on several unseen mixed attempts and explain the trigger, permission, runner, or troubleshooting signal behind your misses, you are likely ready. More practice should improve workflow reasoning, not make you memorize repeated YAML snippets.

Focused sample questions

Use these child pages when you want focused IT Mastery practice before returning to mixed sets and timed mocks.

Free study resources

Need concept review first? Read the GitHub Actions GH-200 Cheat Sheet on Tech Exam Lexicon, then return here for timed mocks, topic drills, and full IT Mastery practice.

24 GH-200 sample questions with detailed explanations

These are original IT Mastery practice questions aligned to the live GitHub Actions (GH-200) route and the main blueprint areas shown above. Use them to test workflow, runner, security, deployment, and troubleshooting judgment here, then continue in IT Mastery with mixed sets, topic drills, and timed mocks.

Question 1

Topic: Author and Maintain Actions

A team wants to stop duplicating checkout, Node setup, and dependency install steps across several jobs. They created .github/workflows/shared-setup.yml as a reusable workflow, and the next run failed.

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Shared setup
        uses: ./.github/workflows/shared-setup.yml
      - run: npm test
Error: Can't find 'action.yml', 'action.yaml' or 'Dockerfile' under
'/home/runner/work/app/app/.github/workflows/shared-setup.yml'

What is the best explanation for this failure?

  • A. The step is calling a workflow file; shared step reuse should be a composite action.
  • B. The file only needs on: workflow_call; then the current step call will work.
  • C. actions/checkout did not make .github/workflows/shared-setup.yml available on the runner.
  • D. The job needs broader permissions before GitHub can load a local workflow file.

Best answer: A

Explanation: The runner is treating the local path in uses as an action reference, so it looks for action.yml or a Dockerfile. A reusable workflow in .github/workflows cannot be invoked from a step; repeated steps should be packaged as a composite action. uses means different things depending on where it appears. Inside steps, GitHub Actions expects an action. For a local action, the path must point to a directory that contains action.yml (or a Docker action). Because the path points to a workflow file under .github/workflows, the runner searches for action metadata and fails.

  • Use a composite action to bundle repeated steps.
  • Store it in a directory such as .github/actions/shared-setup/action.yml.
  • Call it from a step with uses: ./.github/actions/shared-setup.
  • Use a reusable workflow only when you want to reuse entire jobs, called at the job level with workflow_call.

The closest distractor mixes up reusable workflows and composite actions.


Question 2

Topic: Manage GitHub Actions for the Enterprise

An enterprise workflow deploys to an internal release API protected by a firewall. The API team will allow only a small, stable set of source IP addresses. The platform team wants GitHub to manage runner maintenance and scaling. Which configuration is best for the deployment job?

  • A. Use standard GitHub-hosted ubuntu-latest runners and sync the firewall to GitHub’s published Actions IP ranges each week.
  • B. Use self-hosted runners in the datacenter and target them with a runner label.
  • C. Use GitHub-hosted larger runners with static outbound IPs, and allowlist those IPs on the firewall.
  • D. Use standard GitHub-hosted runners, but require environment approvals before the deployment step.

Best answer: C

Explanation: The best fit is GitHub-hosted larger runners with static outbound IPs. Standard GitHub-hosted runners use changing public IP ranges, so they are a poor match for a firewall that requires a small, stable allow list. The key concept is that standard GitHub-hosted runners do not give you a small fixed set of egress IP addresses for firewall planning. Their outbound addresses come from broader published ranges that can change, so they are not ideal when an internal service only accepts a narrow, predictable allow list. If the team still wants GitHub to manage the runners, larger runners with static outbound IPs are the best match.

  • Standard GitHub-hosted runners: good for general internet access, not for tight source-IP allow lists.
  • GitHub-hosted larger runners: GitHub-managed and suitable when stable outbound IPs are required.
  • Self-hosted runners: also workable for network control, but they add runner operations the stem says the team wants to avoid.

The deciding factor is outbound IP predictability, not deployment approvals or other workflow controls.


Question 3

Topic: Consume and Troubleshoot Workflows

You maintain a repository where all pushes go directly to main. The workflow starts as:

name: CI
on:
  push:
    branches: [main]
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - run: echo ok

Timeline:

  • Push 1 changes only application code.
  • Push 2 renames .github/workflows/ci.yml to .github/workflows/pipeline.yml; the file contents stay the same.
  • After GitHub finishes processing Push 2, a repository admin disables workflow CI.
  • Push 3 changes only application code.
  • Push 4 deletes .github/workflows/pipeline.yml.

Which trace matches GitHub Actions behavior?

  • A. Pushes 1 and 2 create runs; Pushes 3 and 4 do not; earlier completed runs stay in history.
  • B. Push 1 creates a run; Push 2 does not because a renamed workflow takes effect on the next push; earlier completed runs stay in history.
  • C. Pushes 1, 2, and 3 create runs; disabling blocks only manual runs; deleting the file removes prior history.
  • D. Pushes 1 and 2 create runs; Push 3 does not; Push 4 still creates one final run from the previous commit’s workflow definition.

Best answer: A

Explanation: GitHub Actions evaluates workflow files that exist in .github/workflows for the pushed commit. Renaming the file within that directory does not stop the next eligible push from running, but disabling the workflow stops later runs, and deleting the file leaves no workflow to trigger. Completed runs remain in history. The core concept is workflow availability at the time each event is processed. A workflow is eligible only if the workflow file exists in .github/workflows and the workflow is enabled.

  • Push 1 runs because the workflow file exists and is enabled.
  • Push 2 also runs because the file was only renamed, not removed from .github/workflows.
  • After the admin disables the workflow, Push 3 does not start a new run.
  • Push 4 also does not start a run because the workflow file has been deleted.

Disabling or deleting a workflow affects future runs, not completed ones. Previous runs remain in workflow history until they expire by retention policy or are manually deleted. The closest misconception is treating a rename within .github/workflows as if the workflow had been removed.


Question 4

Topic: Author and Manage Workflows

A public repository accepts pull requests from forks. The team needs an automated CI workflow that runs tests against the actual PR code and uses the PR number and head SHA from the event payload. The workflow must safely execute untrusted fork code, with no repository secrets and no write access beyond what is required to read the code. Which configuration is best?

  • A. Trigger on pull_request_target; set permissions: pull-requests: write; checkout github.event.pull_request.head.sha; run tests.
  • B. Trigger on push; use a repository PAT secret to fetch the PR branch and run tests.
  • C. Trigger on pull_request; set permissions: contents: read; checkout github.event.pull_request.head.sha; run tests.
  • D. Trigger on workflow_dispatch; pass the PR number as input and use write-all permissions.

Best answer: C

Explanation: The pull_request event is the best fit for CI that must execute code from forked PRs. It still includes PR-specific context such as the PR number and head SHA, but fork-originated runs do not receive repository secrets and use a restricted token model. The core concept is that the workflow trigger defines both the available event payload and the trust boundary. For testing untrusted code from forked pull requests, pull_request is the safe choice because it exposes github.event.pull_request fields such as the PR number and head.sha, while keeping the run in a lower-trust context with no repository secrets and a restricted GITHUB_TOKEN.

A minimal safe pattern is:

on: pull_request
permissions:
  contents: read

Then the job can check out the PR head commit and run tests with least privilege. The closest distractor is pull_request_target, which does provide PR context, but it runs in the base repository security context, so checking out and executing fork code there creates unnecessary risk.


Question 5

Topic: Secure and Optimize Automation

A team wants newer pushes to main to cancel older in-progress CI runs and reduce runner usage. Instead, every push starts another full run, and none of the earlier runs are canceled.

Exhibit:

name: CI
on:
  push:
    branches: [main]

concurrency:
  group: ci-${{ github.sha }}
  cancel-in-progress: true

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm test

What is the best explanation for the duplicated runs?

  • A. cancel-in-progress works only for pull_request events
  • B. The job needs strategy.fail-fast: true to cancel earlier runs
  • C. actions/checkout must be pinned to a full SHA for concurrency to work
  • D. The concurrency group is unique for each commit

Best answer: D

Explanation: Concurrency cancellation only applies to runs in the same concurrency group. Because the workflow uses github.sha, each push creates a different group, so newer runs do not cancel older ones. The core issue is the concurrency key. GitHub Actions cancels in-progress runs only when a new run starts with the same concurrency.group value and cancel-in-progress: true is set. In this workflow, github.sha is different for every commit, so each push to main creates a brand-new group.

A cost-optimization pattern is to use a stable key for the branch or workflow, such as:

concurrency:
  group: ci-${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

That lets a newer push to the same branch replace the older run instead of consuming more runner time. The closest trap is confusing workflow-level concurrency with job failure behavior; they solve different problems.


Question 6

Topic: Author and Maintain Actions

A team refactored a composite action but wants workflow consumers to keep using the same public output name. No shell commands fail.

# .github/actions/normalize-ref/action.yml
name: normalize-ref
inputs:
  ref_name:
    required: true
outputs:
  normalized:
    value: ${{ steps.prepare.outputs.normalized }}
runs:
  using: composite
  steps:
    - id: slug
      shell: bash
      run: |
        value=$(echo "${{ inputs.ref_name }}" | tr '[:upper:]' '[:lower:]' | tr '/' '-')
        echo "normalized=$value" >> "$GITHUB_OUTPUT"
jobs:
  build:
    runs-on: ubuntu-latest
    outputs:
      ref: ${{ steps.norm.outputs.normalized }}
    steps:
      - id: norm
        uses: ./.github/actions/normalize-ref
        with:
          ref_name: Release/Candidate
      - run: echo "ref=[${{ steps.norm.outputs.normalized }}]"
  deploy:
    needs: build
    if: needs.build.outputs.ref != ''
    runs-on: ubuntu-latest
    steps:
      - run: echo "Deploy ${{ needs.build.outputs.ref }}"

Which execution trace matches GitHub Actions behavior?

  • A. build prints ref=[release-candidate], and deploy runs.
  • B. build prints ref=[], and deploy is skipped.
  • C. build fails before the echo step because steps.prepare does not exist.
  • D. build prints ref=[], and deploy still runs.

Best answer: B

Explanation: Composite actions expose only the outputs declared in action.yml. Here, the public output normalized still maps to steps.prepare.outputs.normalized, but the internal step ID is now slug, so the exposed output is empty and the downstream job condition evaluates to false. The stable interface for a custom action is its declared inputs and outputs, not its internal step IDs. In this scenario, the workflow consumer correctly reads steps.norm.outputs.normalized, but that public output is miswired inside the composite action.

Because outputs.normalized.value references steps.prepare.outputs.normalized and no prepare step exists, the expression resolves to an empty string instead of the transformed value. The inner slug step still writes to GITHUB_OUTPUT, but that output is not exposed unless the metadata maps to it.

So the run behaves like this:

  • The composite action completes successfully.
  • The echo step prints ref=[].
  • The job output ref is empty.
  • deploy is skipped because needs.build.outputs.ref != '' is false.

The closest wrong trace is the one expecting release-candidate, which would require the output mapping to reference steps.slug.outputs.normalized.


Question 7

Topic: Manage GitHub Actions for the Enterprise

A GitHub organization uses three self-hosted Linux runners for production deployments. The payments-api repository is supposed to use them exclusively. After the docs-site repository added a 12-job test matrix, payments-api deployment jobs began queuing for more than 30 minutes.

Exhibit: Recent run history

docs-site / test       -> running on deploy-01, deploy-02, deploy-03
payments-api / deploy  -> queued 31m
Both workflows use: runs-on: [self-hosted, linux, x64, prod-deploy]
Runners are online and healthy.

Which next diagnostic action best identifies why an unrelated repository can consume those runners?

  • A. Review the runner group’s repository access and confirm it is limited to selected repositories.
  • B. Check whether the docs-site matrix should use a lower max-parallel value.
  • C. Verify whether payments-api is waiting for an environment approval instead of a runner.
  • D. Compare label names in both workflows, because labels alone authorize repository access.

Best answer: A

Explanation: The exhibit already shows an unrelated repository running on the same deployment runner names, so the issue is authorization, not runner health. In GitHub Actions, runner groups determine which repositories can use a self-hosted runner set, while labels only help select runners after access is granted. Reviewing runner group repository access is therefore the best next diagnostic step. Self-hosted runner isolation in GitHub Actions is enforced with runner groups and their repository access settings. Because docs-site is executing on the exact deployment runners, the problem is not just high concurrency; that repository was allowed to target the runner set in the first place. The most direct diagnostic step is to inspect the runner group’s access and verify that it is restricted to selected repositories rather than all organization repositories.

  • Runner groups are the authorization boundary.
  • Labels such as prod-deploy are routing filters, not isolation controls.
  • Matrix tuning can reduce queue time, but it cannot prevent cross-repository use by itself.

The key takeaway is that labels place jobs, but runner groups prevent unrelated repositories from sharing sensitive or capacity-constrained runners.


Question 8

Topic: Consume and Troubleshoot Workflows

A push to main triggers this workflow. In the workflow run summary, deploy appears as skipped. Which job and step should you inspect first to locate the actual failure?

name: ci
on:
  push:
    branches: [main]

jobs:
  test:
    strategy:
      fail-fast: false
      matrix:
        os: [ubuntu-latest, windows-latest]
    runs-on: ${{ matrix.os }}
    steps:
      - uses: actions/checkout@v4
      - name: Install
        run: echo "install"
      - name: Run smoke tests
        if: runner.os == 'Windows'
        run: exit 1
  deploy:
    needs: test
    runs-on: ubuntu-latest
    steps:
      - run: echo "deploy"
  • A. The deploy job, at its echo "deploy" step
  • B. The test job on ubuntu-latest, at the Install step
  • C. The actions/checkout@v4 step in either test job
  • D. The test job on windows-latest, at the Run smoke tests step

Best answer: D

Explanation: This workflow creates two test jobs through the matrix, one for Ubuntu and one for Windows. Only the Windows job runs Run smoke tests, and that step exits with code 1, so that is the failed step to inspect; deploy is only skipped because it depends on test. GitHub Actions expands the matrix into separate test jobs, so the run summary will show one job for ubuntu-latest and one for windows-latest. To find the root cause, inspect the failed matrix job rather than the downstream job that was skipped.

Here, Run smoke tests has if: runner.os == 'Windows', so it runs only in the Windows job. That step executes exit 1, which marks the step and that matrix job as failed. The Ubuntu job does not fail from that step because the condition is false there. Since deploy has needs: test, GitHub skips deploy after the failed Windows matrix job.

The key troubleshooting pattern is to open the red failed matrix job entry first, then the failed step inside it.


Question 9

Topic: Author and Manage Workflows

A repository currently runs this workflow only when code is pushed to main:

name: ci
on:
  push:
    branches:
      - main
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - run: echo test

The team also wants the same workflow to run for pull requests that target main, without changing the existing push behavior. Which on: block should replace the current one?

  • A. yaml on: [push, pull_request]
  • B. yaml on: push: branches: [main] pull_request: branches: [main]
  • C. yaml on: push, pull_request: branches: [main]
  • D. yaml on: push: branches: [main] pull_request: branches: [main]

Best answer: B

Explanation: To preserve push on main and add pull requests targeting main, the workflow must use expanded on: syntax with separate event entries. The array shorthand cannot retain the existing branch filter for only one event. The key concept is that GitHub Actions supports both shorthand and expanded on: syntax. Shorthand such as on: [push, pull_request] is useful only when you do not need per-event configuration. Once you need filters like branches, each event must be declared separately under on:.

In this scenario, push must remain limited to main, and pull_request must also target main, so both events need their own configuration:

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

The closest distractor adds both events, but it also broadens push to all branches, which changes the workflow’s original trigger behavior.


Question 10

Topic: Secure and Optimize Automation

Your team maintains a Node.js library. Pull requests need fast feedback on lint and unit tests. Before a version tag publishes the package, the library must still be validated on Ubuntu, Windows, and macOS with Node 20 and 22. Most PR failures are OS-independent, developers use Node 22 day-to-day, and the team wants lower runner cost without adding self-hosted infrastructure. Which workflow configuration is the best fit?

  • A. Run the full Ubuntu/Windows/macOS × Node 20/22 matrix on every pull_request and push, and raise max-parallel to 6.
  • B. Run pull_request checks on ubuntu-latest with Node 22 only; run the full Ubuntu/Windows/macOS × Node 20/22 matrix on version-tag pushes with standard GitHub-hosted runners and max-parallel: 2.
  • C. Keep the full matrix on every pull_request, but move it to self-hosted runners so all jobs can start immediately.
  • D. Replace the matrix with one macos-latest job that runs Node 20 and 22 sequentially for both PRs and releases.

Best answer: B

Explanation: The best optimization is to reduce matrix breadth where it adds little signal and keep full coverage only when it is required. Using ubuntu-latest with the team’s daily Node version on pull requests gives quick feedback, while the capped full matrix on version tags preserves required cross-platform validation before publishing. Matrix optimization in GitHub Actions should match the decision point. For pull requests, the goal is fast feedback, so the best configuration uses the smallest high-signal matrix that catches most regressions: here, ubuntu-latest with Node 22. For publishing, the goal changes to compatibility assurance, so that is the right time to run the full Ubuntu, Windows, and macOS matrix across Node 20 and 22.

Limiting max-parallel on the larger release matrix controls spend and reduces runner contention without removing required coverage. By contrast, running the full matrix on every pull request increases queue time and cost, and moving the same workload to self-hosted runners changes infrastructure rather than fixing matrix scope. A single macOS job also misses required Windows and Ubuntu validation.

The key idea is to size matrix breadth and concurrency to the value of the feedback at each trigger.


Question 11

Topic: Author and Maintain Actions

A team maintains an internal action named octo-org/deploy-action. The action repo currently has a released tag v2.3.1, and v3.0.0 will introduce breaking changes later. Workflow consumers should stay on major version 2, automatically receive future compatible v2 updates, and avoid editing the workflow for each patch release.

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: octo-org/deploy-action@main
        with:
          target: prod

Which edit best meets this goal?

  • A. Change to uses: octo-org/deploy-action@v2.3.1
  • B. Change to a full commit SHA
  • C. Change to uses: octo-org/deploy-action@v2
  • D. Keep uses: octo-org/deploy-action@main

Best answer: C

Explanation: A moving major tag such as v2 is the standard way to express that consumers want the latest backward-compatible release in that major line. Here, the team wants future v2 fixes automatically but does not want v3 changes, so @v2 is the best reference. In GitHub Actions, the ref after uses: tells consumers what level of version stability they are choosing. A major semantic version tag like @v2 is the usual contract when you want automatic updates within a compatible major line.

  • @v2 tracks the maintained major version line.
  • @v2.3.1 targets one exact release.
  • A full commit SHA targets one immutable revision.
  • @main follows a mutable development branch.

Because the requirement is to stay on major version 2 and still receive later compatible fixes, a moving major tag is the best fit. The closest distractors are the exact release tag and commit SHA, but both require changing the workflow again to consume later v2 updates.


Question 12

Topic: Manage GitHub Actions for the Enterprise

A repository uses this workflow:

name: ci
on:
  push:
    branches: [main]
jobs:
  build:
    runs-on: ubuntu-20.04
    steps:
      - uses: actions/checkout@v4
      - run: ./build.sh

GitHub has announced that the ubuntu-20.04 GitHub-hosted runner image used by the organization will be retired soon. The team has already validated the workflow on ubuntu-22.04 and wants the smallest change that keeps the workflow on a supported image without automatically moving again during a future ubuntu-latest migration. Which edit is best?

  • A. Change runs-on to ubuntu-latest
  • B. Change runs-on to ubuntu-22.04
  • C. Add a matrix for ubuntu-20.04 and ubuntu-22.04
  • D. Keep ubuntu-20.04 and install tools manually in a step

Best answer: B

Explanation: The workflow should move from the retired runner label to a supported explicit label. Because the team wants stability across future hosted-image migrations, ubuntu-22.04 is better than the floating ubuntu-latest label. In GitHub Actions, runs-on selects the hosted runner image. When a specific image label is deprecated or retired, the workflow must be updated to another supported label. Here, the team has already tested on ubuntu-22.04 and wants to avoid later environment drift, so the best adaptation is to pin the job to that supported version explicitly.

Using ubuntu-latest would also move off the retired image, but it is intentionally a moving target and may point to a newer Ubuntu image later. A matrix still includes the retiring label, and manually installing packages does not solve a retired runner image. The key takeaway is to use an explicit supported image label when you need predictable behavior during hosted-runner migrations.


Question 13

Topic: Consume and Troubleshoot Workflows

A workflow run completed 1 hour ago. The repository’s artifact retention policy is 7 days. In the run summary, test (ubuntu-latest) has an artifact named coverage-ubuntu-latest, but test (windows-latest) has no artifact and the job still shows as successful.

jobs:
  test:
    strategy:
      matrix:
        os: [ubuntu-latest, windows-latest]
    runs-on: ${{ matrix.os }}
    steps:
      - uses: actions/checkout@v4
      - name: Run tests
        run: echo "tests passed"
      - name: Generate coverage
        if: runner.os == 'Linux'
        run: |
          mkdir coverage
          echo ok > coverage/report.txt
      - name: Upload coverage
        if: always()
        uses: actions/upload-artifact@v4
        with:
          name: coverage-${{ matrix.os }}
          path: coverage/report.txt
          if-no-files-found: warn

Windows job log for Upload coverage:

With the provided path, there will be 0 files uploaded.
Warning: No files were found with the provided path: coverage/report.txt. No artifacts will be uploaded.

Which explanation best matches GitHub Actions behavior?

  • A. The artifact expired before review because retention is evaluated per matrix job.
  • B. The upload step was skipped after the test step, so no upload was attempted.
  • C. upload-artifact failed the Windows job because an empty path always causes a step error.
  • D. The Windows job never created coverage/report.txt, so the upload step ran but found no files to upload.

Best answer: D

Explanation: The missing artifact is caused by a path/input condition inside the Windows matrix leg, not by retention or a skipped upload. Generate coverage only runs on Linux, so the Windows upload step executes with no matching file and only emits a warning. This run is a matrix job, so each OS executes its own independent step flow. On Ubuntu, runner.os == 'Linux' is true, so coverage/report.txt is created and uploaded. On Windows, that step is skipped, but Upload coverage still runs because it uses if: always().

Because the upload step is configured with if-no-files-found: warn, GitHub Actions does not fail the job when coverage/report.txt is missing. It logs a warning and creates no artifact for that matrix leg.

The key takeaway is that a missing artifact can come from a file never being produced in that job, even when the upload step itself ran successfully enough for the job to stay green.


Question 14

Topic: Author and Manage Workflows

A team uses this workflow for integration tests:

name: ci
on: [push]

jobs:
  integration:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm ci
      - run: npm test

The tests now need PostgreSQL 16 during the job. They also call corp-sign, a licensed internal binary that is preinstalled only on organization self-hosted Linux runners with Docker and cannot be redistributed in a container image. Which edit best satisfies these requirements with the least unnecessary change?

  • A. Add container: postgres:16 and keep runs-on: ubuntu-latest.
  • B. Use a job container image with Node, PostgreSQL, and corp-sign.
  • C. Keep ubuntu-latest, add PostgreSQL under services:, and download corp-sign in a step.
  • D. Change to the self-hosted Linux runner labels and add PostgreSQL under services:.

Best answer: D

Explanation: Use a service container for PostgreSQL because it is a supporting service for the job, not the environment that the steps themselves should run inside. Use a self-hosted Linux runner because corp-sign is a runtime dependency that is available only on those approved runners. In GitHub Actions, services: is the right fit for dependencies such as PostgreSQL that need to run alongside the job. A job container: changes the execution environment for all steps, which is useful when every step should run inside the same image, but it is not how you add a database sidecar. Here, the workflow also depends on corp-sign, and the stem says that binary is licensed only on organization-managed self-hosted Linux runners and cannot be redistributed in an image.

  • Use services: for PostgreSQL.
  • Use the self-hosted runner for the host-level corp-sign dependency.
  • Do not move the whole job into a PostgreSQL image.

The closest distractor is the custom job container, but it still fails the stated licensing and distribution constraint for corp-sign.


Question 15

Topic: Secure and Optimize Automation

A team wants production deployment only for a tagged release whose build artifact has GitHub provenance. A maintainer pushes tag v3.1.0 in the same repository. Assume the shell commands succeed. Which run trace matches GitHub Actions behavior?

name: release
on:
  push:
    tags: ['v*']

jobs:
  build:
    runs-on: ubuntu-latest
    permissions:
      contents: read
    outputs:
      file: ${{ steps.meta.outputs.file }}
    steps:
      - id: meta
        run: echo "file=app.tgz" >> "$GITHUB_OUTPUT"
      - run: echo package > app.tgz
      - uses: actions/upload-artifact@v4
        with:
          name: release-bundle
          path: ${{ steps.meta.outputs.file }}

  attest:
    needs: build
    runs-on: ubuntu-latest
    permissions:
      contents: read
      attestations: write
      id-token: write
    outputs:
      ready: ${{ steps.done.outputs.ready }}
    steps:
      - uses: actions/download-artifact@v4
        with:
          name: release-bundle
          path: downloaded
      - uses: actions/attest-build-provenance@v3
        with:
          subject-path: "downloaded/${{ needs.build.outputs.file }}"
      - id: done
        run: echo "ready=true" >> "$GITHUB_OUTPUT"

  deploy:
    needs: attest
    if: ${{ needs.attest.outputs.ready == 'true' }}
    runs-on: ubuntu-latest
    steps:
      - run: echo "Deploying"
  • A. build uploads release-bundle, deploy starts next, and attest finishes later.
  • B. attest creates provenance, but deploy skips because ready cannot cross jobs.
  • C. attest starts, but it fails because contents: write is required.
  • D. build uploads release-bundle, attest creates provenance, and deploy then runs.

Best answer: D

Explanation: Artifact attestations can gate a release when the workflow creates provenance before deployment and exposes a success signal as a job output. Here, attest runs after build, consumes the uploaded artifact, and deploy runs only when needs.attest.outputs.ready is true. Artifact attestations support deployment, release, and supply-chain verification when a trusted build produces an artifact, the workflow generates provenance for that artifact, and later jobs depend on that result. In this workflow, build uploads release-bundle, then attest runs because it has needs: build. The uploaded artifact is available within the same workflow run, so download-artifact can retrieve it for attestation.

The attest job has the required permissions for provenance generation: contents: read, attestations: write, and id-token: write. After the attestation step succeeds, the done step writes ready=true to GITHUB_OUTPUT, and the job maps that step output to the job output ready. The deploy job cannot start early because it needs attest, and its if condition evaluates that job output.

This is how provenance becomes an explicit deployment gate rather than only a post-build record.


Question 16

Topic: Author and Maintain Actions

A platform team needs one shared release component for many repositories. The component must run a build job, wait for approval on the protected production environment before publishing, and return a published value to the caller. They choose a reusable workflow instead of a custom action.

Assume production requires approval, REGISTRY_TOKEN exists in the caller repository, and approval is granted only after build completes.

# caller
on: workflow_dispatch
jobs:
  standard-release:
    uses: org/platform/.github/workflows/reusable-release.yml@v1
    with:
      version: 1.2.3
    secrets:
      registry_token: ${{ secrets.REGISTRY_TOKEN }}

  announce:
    runs-on: ubuntu-latest
    needs: standard-release
    steps:
      - run: echo "${{ needs.standard-release.outputs.published }}"
# callee
on:
  workflow_call:
    inputs:
      version: { type: string, required: true }
    secrets:
      registry_token: { required: true }
    outputs:
      published:
        value: ${{ jobs.publish.outputs.published }}
jobs:
  build:
    runs-on: ubuntu-latest
    outputs:
      image: ${{ steps.pkg.outputs.image }}
    steps:
      - id: pkg
        run: echo "image=app:${{ inputs.version }}" >> $GITHUB_OUTPUT
  publish:
    runs-on: ubuntu-latest
    needs: build
    environment: production
    outputs:
      published: ${{ steps.mark.outputs.published }}
    steps:
      - run: echo "Publishing ${{ needs.build.outputs.image }}"
        env:
          TOKEN: ${{ secrets.registry_token }}
      - id: mark
        run: echo "published=true" >> $GITHUB_OUTPUT

Which statement matches GitHub Actions behavior?

  • A. build runs first, publish waits for approval, and announce prints true only after the called workflow finishes.
  • B. announce starts after build finishes because caller jobs can read callee outputs before later callee jobs complete.
  • C. publish cannot use registry_token because secrets mapped by the caller are unavailable inside callee jobs.
  • D. A custom action would be equally suitable because custom actions can define multiple jobs and protected environments.

Best answer: A

Explanation: Reusable workflows are appropriate when shared automation needs workflow-level features such as multiple jobs, job dependencies, environment approvals, and outputs returned to the caller. Here, build completes first, publish pauses for production approval, and only then can the caller’s announce job read the published output. The key concept is that a reusable workflow behaves like a called workflow, not like a single custom-action step. That makes it the right choice when the shared logic needs multiple jobs, needs relationships, environment protection rules, or outputs passed back to the caller.

In this run, the caller job standard-release represents the entire called workflow. Inside the callee, publish cannot start until build succeeds, and because publish targets the protected production environment, it also waits for approval. After approval, publish runs, writes published=true to the GitHub Actions output file, and the reusable workflow maps that value through on.workflow_call.outputs. Only after the called workflow finishes can the caller’s announce job start and echo true.

The closest wrong idea is treating the reusable workflow like a custom action; custom actions run within one job and cannot create separate jobs or environment-gated stages.


Question 17

Topic: Manage GitHub Actions for the Enterprise

A repository has access to two self-hosted runner groups:

  • prod-runners: runners have labels self-hosted, linux, x64, deploy
  • staging-runners: runners have labels self-hosted, linux, x64, deploy

The deployment job must run only on runners in prod-runners.

jobs:
  deploy:
    if: github.ref == 'refs/heads/main'
    runs-on: [self-hosted, linux, x64, deploy]
    steps:
      - uses: actions/checkout@v4
      - run: ./deploy.sh

Which edit best satisfies the requirement?

  • A. Replace runs-on with self-hosted
  • B. Replace runs-on with prod-runners
  • C. Replace runs-on with [self-hosted, prod-runners, deploy]
  • D. Replace runs-on with { group: prod-runners, labels: deploy }

Best answer: D

Explanation: The current label array can match any accessible self-hosted runner that has those labels, so it does not distinguish production from staging. To target only the intended runners, runs-on must use runner group syntax. Runner labels and runner groups are different routing controls in GitHub Actions. An array such as [self-hosted, linux, x64, deploy] means the runner must have all of those labels, but it does not limit the job to a specific runner group. Because both accessible groups contain runners with the same labels, either group could receive the job.

To constrain execution to one group, use the object form of runs-on:

runs-on:
  group: prod-runners
  labels: deploy

This first narrows selection to runners in prod-runners, then applies the deploy label filter. The closest distractors fail because they treat the group name like a label instead of using the dedicated group selector.


Question 18

Topic: Consume and Troubleshoot Workflows

A pull request run failed, and the downstream package job has needs: test. The workflow run summary shows:

Workflow: validate-and-package
Conclusion: failure

Jobs
- lint ................................ success
- test (os: ubuntu-latest, node: 18) . success
- test (os: ubuntu-latest, node: 20) . success
- test (os: windows-latest, node: 20)  failure
- package ............................. skipped

Failed job steps for `test (os: windows-latest, node: 20)`
- Checkout .............. success
- Setup Node ............ success
- Install dependencies .. success
- Run unit tests ........ failure
- Upload coverage ....... skipped

What is the best next diagnostic action?

  • A. Review logs for test (windows-latest, node 20) at Run unit tests
  • B. Review the skipped package job first
  • C. Compare Ubuntu artifacts before opening failed logs
  • D. Rerun the full workflow immediately

Best answer: A

Explanation: The run summary already identifies both the failing matrix job and the failed step. The fastest diagnostic path is to open that job’s logs at Run unit tests, because the skipped package job is only a downstream result of needs: test. In GitHub Actions, the run summary is the quickest way to locate the root failure: find the first failed job, then inspect the first failed step inside that job. Here, the summary narrows the issue to the test matrix variant running on Windows with Node 20, and it explicitly shows Run unit tests as the failing step.

  • Successful matrix variants can help with comparison later, but they do not explain the failed conclusion.
  • A skipped downstream job usually reflects a dependency such as needs, not the original problem.
  • Reruns are useful after you inspect whether the failure is code-related, environment-specific, or transient.

The key takeaway is to use the summary to jump directly to the failed matrix job and step log.


Question 19

Topic: Author and Manage Workflows

A repository contains these workflows. A maintainer manually runs caller.yml.

# .github/workflows/caller.yml
on:
  workflow_dispatch

jobs:
  release:
    uses: ./.github/workflows/reusable.yml
    with:
      env_name: prod
# .github/workflows/reusable.yml
on:
  workflow_call:
    inputs:
      environment:
        required: true
        type: string

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

Which execution trace occurs?

  • A. deploy runs and prints Deploying to prod.
  • B. deploy runs and prints Deploying to with an empty value.
  • C. The workflow call is rejected before deploy starts because env_name does not match the declared required input.
  • D. The unknown input is ignored, and the reusable workflow job succeeds with a blank default.

Best answer: C

Explanation: workflow_call inputs are strict. The caller must use the exact declared input name, so env_name is not accepted as environment, and the required input is not satisfied. Because the mismatch is detected at the workflow-call boundary, no job in the reusable workflow starts. Reusable workflows act like defined interfaces. Under on.workflow_call.inputs, the called workflow declares the only accepted input names and types. Here, the callee requires environment, but the caller sends env_name. GitHub Actions does not alias or infer that mapping.

  • with keys in the caller must exactly match declared input names.
  • A required input remains missing if the caller uses a different key.
  • When the call is invalid, steps in the called workflow do not execute.

The key point is that this is an interface-validation problem, not a runtime empty-string or default-value behavior.


Question 20

Topic: Secure and Optimize Automation

An organization manually approves the prod environment before deployments. In this run, approval has already occurred, so the deploy job is now executing on a push to main. Because permissions.id-token: write is granted, the Marketplace action in Cloud login can request an OIDC token when that step starts.

jobs:
  deploy:
    environment: prod
    permissions:
      contents: read
      id-token: write
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Cloud login
        uses: <candidate action>
      - run: ./deploy.sh

Which review conclusion is correct?

  • A. Approve an action from the cloud provider’s official organization, pinned to a full commit SHA, with recent maintenance and only the documented permissions this job grants.
  • B. Approve a popular Marketplace action referenced as @v2, because its star count proves the same reviewed code will run.
  • C. Approve an action pinned to a full commit SHA from an archived repository, because immutable pinning removes the need for active maintenance.
  • D. Approve any verified publisher action, even if it asks for broader repository permissions than the job should allow.

Best answer: A

Explanation: Once the environment is approved, the third-party action executes with the job’s granted permissions, including the ability to request an OIDC token. In that situation, the strongest trust signals are reputable source ownership, active maintenance, full-SHA pinning, and least-privilege permission needs. When a third-party action will run in a sensitive step, trust evaluation should focus on four things: who owns the source, what exact code will run, whether it is actively maintained, and whether it needs only the permissions the job should grant. In the stem, the Cloud login step runs after environment approval and has id-token: write, so the action can mint cloud credentials. That makes source quality and exact version pinning especially important. A full commit SHA fixes the precise code that runs; an official or well-governed source repository and recent maintenance reduce supply-chain risk; and documented least-privilege use reduces blast radius. Popularity, download count, or a verified badge are only supporting signals, not proof that the reviewed revision is safe. The key takeaway is to prefer official source + active maintenance + full-SHA pinning + least privilege when an action receives sensitive runtime capabilities.


Question 21

Topic: Author and Maintain Actions

An organization publishes a shared JavaScript action, contoso/deploy-metadata, that other repositories call with uses: contoso/deploy-metadata@.... Most teams should receive backward-compatible fixes automatically within a major version, but a few regulated repositories must use an exact immutable reference. Which release/versioning configuration is the best fit?

  • A. Tell all consumers to reference @main so they always get the newest code.
  • B. Create one moving stable tag for each release and stop publishing semantic version tags.
  • C. Publish v2.4.1, keep v2.4 and v2 tags aligned to compatible releases, and let strict consumers pin the full commit SHA.
  • D. Publish date-based tags such as 2026-04-29 and ask consumers to use the latest date.

Best answer: C

Explanation: The best configuration is to publish semantic version tags and maintain compatibility tags for supported release lines. That lets most workflows follow a stable major or minor stream, while regulated workflows can pin the exact commit SHA for immutability. For GitHub Actions, the version reference should tell consumers how much change they are accepting. A full semantic version tag such as v2.4.1 identifies a specific release, while moving tags such as v2.4 or v2 communicate a compatibility stream for patch or minor updates within that line. When a workflow needs exact repeatability, pinning the released action to a full commit SHA provides an immutable reference.

In this scenario, most teams want safe automatic updates within a major version, so semantic major or minor tags are the clearest signal. The smaller regulated group needs exact stability, so they should use the commit SHA for that release. Branch names and vague moving tags do not communicate compatibility clearly, and date-only tags show chronology rather than upgrade scope.


Question 22

Topic: Manage GitHub Actions for the Enterprise

A nightly internal deployment workflow started failing after the network team allow-listed the source IP from the last successful run. The job still uses runs-on: ubuntu-latest, and the workflow file has not changed.

Run curl -fsS https://inventory.corp.example/health
curl: (7) Failed to connect to inventory.corp.example port 443: Connection timed out

Firewall logs:
- Last successful run source IP: 20.205.92.11
- Current failed run source IP: 20.201.44.18
- Current connection: denied

What is the most likely cause of the failure?

  • A. ubuntu-latest routed the job to a self-hosted runner with a different IP
  • B. Standard GitHub-hosted runners can use different outbound IPs between runs
  • C. The job lost GITHUB_TOKEN permission for the service call
  • D. The service rejected an expired secret before the network request

Best answer: B

Explanation: The curl step ran and then timed out, and the firewall log shows a different source IP than the previous successful run. That pattern matches normal GitHub-hosted runner egress behavior: standard hosted runners do not provide one static outbound IP for every run. Standard GitHub-hosted runners are ephemeral, and their outbound traffic can originate from different GitHub-managed IP addresses on different runs. If a firewall is configured to trust only one IP seen in a prior successful run, a later run can be denied even when the workflow YAML and runner label stay the same.

In this scenario, the key evidence is:

  • the network call actually started
  • the failure is a connection timeout, not an auth error
  • the firewall log shows a new source IP and an explicit deny

That points to egress IP variation on a GitHub-hosted runner, not to token permissions or secret values. For services that require source-IP restrictions, plan around GitHub’s published IP ranges or use runners with more predictable networking.


Question 23

Topic: Consume and Troubleshoot Workflows

An organization stores a shared reusable workflow at octo-org/platform-automation/.github/workflows/ci.yml. The platform team updates the main and release branches regularly and may move the v1 tag to newer tested versions. Compliance requires each release repository to keep using the exact reviewed workflow revision until that repository owner intentionally updates it. Which caller configuration is best?

  • A. uses: octo-org/platform-automation/.github/workflows/ci.yml@main
  • B. uses: octo-org/platform-automation/.github/workflows/ci.yml@release
  • C. uses: octo-org/platform-automation/.github/workflows/ci.yml@8f4b7c2e4d9a6f1c3b2e7a9d0f6c4b1a2e3d5f7c
  • D. uses: octo-org/platform-automation/.github/workflows/ci.yml@v1

Best answer: C

Explanation: The best choice is the full commit SHA because it locks the reusable workflow to an exact reviewed version. Branch references and movable tags are floating references, so updates to shared automation can affect consumers without any change in the caller file. When a repository calls a reusable workflow by branch or by a tag that can be moved, GitHub Actions resolves that reference to whatever commit the ref points to at run time. That means later updates in the shared automation repository can change behavior across consuming repositories automatically. If the requirement is to keep every release repository on the exact reviewed revision until maintainers explicitly adopt a newer one, the caller must pin the reusable workflow to a full commit SHA. This makes version changes deliberate and traceable. The closest distractor is the version tag, but a moved tag is still not an immutable reference.


Question 24

Topic: Author and Manage Workflows

A workflow runs on both events:

on:
  push:
  pull_request:

The publish job must run only when code is pushed directly to the release branch or when a pull request targets release. The team wants to avoid brittle conditions that depend on event-specific ref-string formats. Which if: condition is the best configuration for the job?

  • A. if: contains(github.ref, 'release')
  • B. if: (github.event_name == 'push' && github.ref == 'refs/heads/release') || (github.event_name == 'pull_request' && github.base_ref == 'release')
  • C. if: github.ref_name == 'release'
  • D. if: github.head_ref == 'release' || github.ref == 'refs/heads/release'

Best answer: B

Explanation: The best condition first distinguishes the event and then uses the branch field whose meaning is stable for that event. push evaluates the pushed ref, while pull_request should evaluate base_ref, which is the target branch. GitHub Actions context fields are not interchangeable across events. On a push, github.ref identifies the pushed ref, so refs/heads/release correctly matches only that branch. On a pull_request, the branch being targeted is github.base_ref; by contrast, github.ref points to a PR merge ref and github.head_ref is the source branch.

  • Match the event type first.
  • Use a pushed-branch field for push logic.
  • Use base_ref for PR target-branch logic.
  • Avoid parsing branch intent from ref-string patterns.

That keeps the condition aligned to stable semantics instead of depending on ref formats that differ by event.

Free preview vs premium

  • Free preview: 24 public sample questions on this page plus the web app entry so you can validate the question style and explanation depth.
  • Premium: the full GH-200 practice bank, focused drills, mixed sets, timed mock exams, detailed explanations, and progress tracking across web and mobile.

Quick Cheat Sheet

NeedPattern to recognize
Run only on certain fileson.<event>.paths or paths-ignore
Reduce token riskExplicit permissions with least privilege
Test combinationsstrategy.matrix
Reuse organization workflow logicjobs.<job_id>.uses for reusable workflows
Reuse repeated stepsComposite action
Save build output for another jobArtifact
Speed up dependency installCache with lock-file-aware keys
Protect production deploymentEnvironment reviewers and environment-scoped secrets
Prevent stale preview deploymentsconcurrency with cancel-in-progress
Investigate failuresLogs, runner context, permissions, and debug reruns

Mini Glossary

  • Action: A reusable unit of automation used inside a workflow step.
  • Artifact: A file or bundle uploaded from a workflow run for later jobs, review, or download.
  • Cache: Reused dependency or build data intended to speed up future runs.
  • Environment: A deployment target that can have reviewers, wait timers, and scoped secrets.
  • Job: A set of steps that run on the same runner.
  • Matrix: A strategy that expands a job across combinations such as operating systems or language versions.
  • Reusable workflow: A workflow called by another workflow at the job level.
  • Runner: The machine that executes workflow jobs, either GitHub-hosted or self-hosted.
  • Secret: Encrypted sensitive value exposed to workflows only under allowed contexts.
  • Workflow: A YAML automation file stored under .github/workflows/.

Official sources

In this section

Revised on Friday, May 15, 2026