Last updated: May 2026
GH-200 — GitHub Certified: GitHub Actions
Test your knowledge with official exam-style questions
Questions and options are shuffled each attempt
▶GitHub Actions — Practice Set 1: All Questions & Explanations
Full question text, answer options, and explanations for this practice set — a spoiler-free alternative is the interactive quiz above for scored, shuffled practice.
1. A developer wants a workflow to run every day at 08:00 UTC and also allow manual triggering from the GitHub UI. Which combination of triggers should they use in the `on:` block?
- A. `push` and `pull_request`
- B. `schedule` with a cron expression and `workflow_dispatch`(correct)
- C. `repository_dispatch` and `cron`
- D. `workflow_call` and `schedule`
Explanation: `schedule` with a cron expression (e.g., `cron: '0 8 * * *'`) handles time-based execution, while `workflow_dispatch` enables manual triggering from the Actions tab. `push` and `pull_request` are event-based, not time-based. `repository_dispatch` requires an API call, not a UI button. `workflow_call` is for reusable workflows invoked by other workflows.
2. A team has a build job and a deploy job in the same workflow. The deploy job must only run after the build job completes successfully. Which workflow keyword enforces this ordering?
- A. `if:` with `success()`
- B. `needs:` referencing the build job(correct)
- C. `depends-on:` referencing the build job
- D. `require:` in the deploy job definition
Explanation: The `needs:` keyword creates an explicit dependency between jobs; the downstream job waits for all listed upstream jobs to complete successfully before it starts. `if: success()` controls conditional execution but does not establish ordering on its own. `depends-on:` and `require:` are not valid GitHub Actions workflow keywords.
3. A workflow needs to run integration tests against a PostgreSQL database. The tests run inside the default runner environment and must connect to the database on port 5432. What is the correct approach?
- A. Install PostgreSQL as a step using `apt-get` and start it manually
- B. Use the `services:` key to define a PostgreSQL container with the port mapped to the host(correct)
- C. Deploy a PostgreSQL instance to Azure and store the connection string as a secret
- D. Use a matrix strategy with one dimension set to `postgres`
Explanation: The `services:` key spins up Docker containers alongside the runner, making dependent services like databases available during job execution. Configuring `ports:` and optionally `options:` for health checks is the idiomatic GitHub Actions pattern. Installing via `apt-get` works but is slower and less reproducible. An external Azure database introduces network latency and cost. A matrix strategy generates job variants and is unrelated to service dependencies.
4. A workflow tests a library against Node.js versions 18, 20, and 22 on both Ubuntu and Windows. The team wants to exclude the `windows-latest` + Node 18 combination. Which configuration elements achieve this?
- A. Add an `if:` condition on the job that evaluates `matrix.os != 'windows-latest' || matrix.node != 18`
- B. Use `strategy.matrix.exclude` with `os: windows-latest` and `node: 18`(correct)
- C. Use `strategy.matrix.include` to add a `skip: true` flag and check it with `if:`
- D. Set `fail-fast: false` so the excluded combination is silently skipped
Explanation: `strategy.matrix.exclude` lets you remove specific combinations from the generated matrix. Adding an entry with `os: windows-latest` and `node: 18` will prevent that combination from being scheduled. An `if:` condition on the whole job fires per-variant but does not remove the variant from the matrix — it would cancel the job, which is semantically different. The `include` approach with a flag is a workaround, not the idiomatic solution. `fail-fast` controls behaviour on failure and has nothing to do with skipping combinations.
5. A platform team maintains a monorepo workflow file with three jobs that each share the same 12-step database migration setup block. An engineer proposes using YAML anchors to avoid the repetition. Which YAML anchor syntax correctly defines a reusable block named `db-setup` and merges it into a job?
- A. Define `&db-setup` on the block, then reference `*db-setup` with `<<:` merge key in each job's steps(correct)
- B. Define `@db-setup` on the block, then reference `@db-setup` wherever needed
- C. Define `!include db-setup` in each job that requires it
- D. Use `uses: ./.github/workflows/db-setup.yml` in each job as a step reference
Explanation: YAML anchors use `&name` to declare a reusable node and `*name` to reference (alias) it. The merge key `<<:` is used to merge a mapping into another mapping, which is exactly how you inline a shared block of steps. `@` is not valid YAML anchor syntax. `!include` is not part of the YAML 1.2 spec and is not supported by GitHub Actions. `uses:` with a local path invokes a reusable workflow or action, not an inline YAML block, and appears at job level, not step level for this purpose.
6. An engineer is reviewing a workflow that passes data between jobs. Which of the following are valid mechanisms for sharing data between jobs in GitHub Actions? Choose 3.
- A. Setting `GITHUB_OUTPUT` in one job and reading the output via `needs.<job>.outputs.<name>` in a downstream job(correct)
- B. Uploading an artifact in one job with `actions/upload-artifact` and downloading it in another with `actions/download-artifact`(correct)
- C. Writing a value to `GITHUB_ENV` in one job and reading `${{ env.MY_VAR }}` directly in a different job
- D. Declaring job outputs under `jobs.<job>.outputs` and referencing them in downstream jobs via `needs`(correct)
- E. Writing to a shared in-memory cache that all jobs in the same workflow can read simultaneously
Explanation: Job outputs (`GITHUB_OUTPUT` + `jobs.<job>.outputs`) and artifacts are the two main cross-job data sharing patterns. Option D (declaring `outputs:` at the job level) is actually the same mechanism as A — you must both write to `GITHUB_OUTPUT` and declare the output for it to be available to downstream jobs via `needs`. `GITHUB_ENV` (C) sets environment variables only within the same job — it does not persist across job boundaries. There is no shared in-memory cache between jobs (E); the actions cache is filesystem-based and requires explicit save/restore steps.
7. A workflow run failed and a developer needs to see the raw log output for a specific step. Where in the GitHub UI can they find this?
- A. The repository's Insights > Traffic page
- B. The Actions tab > the specific workflow run > expand the failed job and step(correct)
- C. The repository Settings > Webhooks log
- D. The Security tab > Code scanning alerts
Explanation: Workflow run logs are accessible via the Actions tab. Selecting a specific run shows each job, and expanding a job reveals individual steps with their log output. Insights > Traffic shows page views and clones. The Webhooks log shows HTTP delivery attempts, not workflow step output. The Security tab surfaces vulnerability alerts, not CI logs.
8. A matrix workflow tests 6 OS/language combinations. Combination `ubuntu-latest` + `python-3.10` failed. The developer wants to rerun only that specific failing combination without rerunning all 6 jobs. What should they do?
- A. Delete the workflow run and push a new empty commit
- B. In the failed run, select the specific failed job and use 'Re-run job' to rerun only that matrix variant(correct)
- C. Set `fail-fast: false` and push a new commit
- D. Edit the matrix to include only `ubuntu-latest` + `python-3.10` and push a commit
Explanation: GitHub Actions supports selectively rerunning individual matrix jobs from the Actions UI — you can expand the job list on a failed run and choose 'Re-run job' (or 'Re-run failed jobs') for a specific variant. Deleting the run wastes all passing results. `fail-fast: false` prevents early cancellation but does not help rerun a specific variant. Editing the matrix and pushing a commit creates a new run and loses context of the original run.
9. An organization uses starter workflows stored in its `.github` repository. A developer in one of the org's repositories cannot see these templates when creating a new workflow. What is the most likely cause?
- A. Starter workflows are only available in public repositories
- B. The `.github` repository storing the templates is set to private and the developer's repository does not have access(correct)
- C. Starter workflows are not supported for organizations, only for enterprises
- D. The developer must first fork the `.github` repository before templates appear
Explanation: Organization starter workflows are stored in the special `.github` repository under the organization. If that repository is private (non-public), only repositories and members with access can consume the templates. Making the `.github` repository internal or public (for non-enterprise) is the standard fix. Starter workflows work in both public and private repos within the org. They are available at organization level, not just enterprise. No forking is required.
10. A team needs to stop a workflow from running without permanently deleting it. Which action achieves this?
- A. Delete the workflow YAML file from the repository
- B. Disable the workflow from the Actions tab using 'Disable workflow'(correct)
- C. Set `on: {}` in the workflow file to remove all triggers
- D. Archive the repository to prevent any further runs
Explanation: Disabling a workflow via the Actions tab pauses all future runs while keeping the YAML file intact in the repository. It can be re-enabled at any time. Deleting the file is irreversible without a git revert. Setting `on: {}` is technically possible but requires a commit and leaves a confusing file in the repo. Archiving the repository is an extreme action that also prevents code changes and collaboration.
11. A workflow uses a reusable workflow for deployment and a composite action for setting up the build environment. After a refactor, the CI pipeline passes on the first run but a teammate's fork produces different step names in the log. The teammate is using the same workflow file. What is the most likely explanation?
- A. The composite action is versioned and the fork is pinned to a different version that has different step names(correct)
- B. Reusable workflows expose their internal step names to callers, which differ when called from a fork
- C. The runner image is different on the fork, causing different step rendering
- D. The GITHUB_TOKEN has different permissions in the fork, hiding some steps
Explanation: Composite actions and reusable workflows are referenced by a specific version (tag, SHA, or branch). If the fork's workflow pins a different version (or an unpinned floating reference that has since moved), it may execute a different revision of the action with different step names. Reusable workflow internals are not exposed differently per fork — the caller sees only job-level names. Runner image differences affect installed software, not log step names. GITHUB_TOKEN permission differences affect API calls, not step visibility.
12. Which of the following action types runs directly on the runner without a Docker container and typically has the fastest startup time?
- A. Docker container action
- B. JavaScript action(correct)
- C. Composite action using only shell scripts
- D. Reusable workflow
Explanation: JavaScript actions run directly on the runner using the pre-installed Node.js runtime, requiring no container pull or spin-up, making them the fastest to start. Docker actions require pulling and initializing a container image, adding latency. Composite actions using shell scripts also run on the runner but compose existing steps rather than executing a Node.js bundle. Reusable workflows are not actions — they are entire workflow definitions invoked by other workflows.
13. A developer is building a custom JavaScript action and needs to define its inputs, outputs, and entry point. Which file is required and what format must it use?
- A. `action.yml` (or `action.yaml`) in YAML format at the root of the action directory(correct)
- B. `action.json` in JSON format at the root of the action directory
- C. `package.json` with an `action` key describing inputs and outputs
- D. `workflow.yml` with an `action:` top-level key
Explanation: Every GitHub Action must have an `action.yml` (or `action.yaml`) metadata file at the root of the action repository or directory. This YAML file declares inputs, outputs, and the `runs` section specifying the action type and entry point. `action.json` is not a supported format. `package.json` describes Node.js package metadata, not action metadata. `workflow.yml` is for workflow definitions, not action definitions.
14. A team wants to publish an internal action and share it only within their organization's repositories. Which distribution model achieves this without listing the action on the GitHub Marketplace?
- A. Publish the action repository as public and add a `private: true` flag in `action.yml`
- B. Keep the action repository internal (GitHub Enterprise) or private, and reference it from workflows using the repository path(correct)
- C. Publish the action to the GitHub Marketplace with an 'Organization only' visibility toggle
- D. Copy the action files into each repository that needs it
Explanation: Actions in private or internal repositories are accessible to workflows in repositories that have been granted access — this is the standard pattern for internal distribution. There is no `private: true` key in `action.yml`. The Marketplace does not have an 'Organization only' visibility option. Copying action files into each repo creates maintenance debt and divergence over time.
15. An action developer releases v2.1.0 of their action with a breaking change from v2.0.0. To follow GitHub's recommended versioning strategy and allow consumers to pin to the major version, what tags should the developer maintain?
- A. Only the full semantic version tag `v2.1.0`
- B. A full version tag `v2.1.0` and a floating major version tag `v2` pointing to the same commit(correct)
- C. A branch named `v2` and a tag named `v2.1.0-stable`
- D. Overwrite the existing `v2.0.0` tag to point to the new commit
Explanation: GitHub recommends maintaining both an immutable full SemVer tag (e.g., `v2.1.0`) and a mutable major version tag (e.g., `v2`) that always points to the latest patch/minor within the major line. Consumers who want automatic non-breaking updates reference `@v2`, while those who want a pinned version reference `@v2.1.0`. Providing only the full version (A) forces all consumers to update references manually. Using a branch (C) is non-standard and can cause confusion. Overwriting an existing tag (D) breaks reproducibility for consumers already pinned to `v2.0.0`.
16. A JavaScript action uses `@actions/core` to read an input named `api-key` and passes it to an external API. During debugging, a developer accidentally logs the value with `core.info(apiKey)`. What is the correct fix to prevent the secret from appearing in logs?
- A. Replace `core.info(apiKey)` with `core.debug(apiKey)` to move the log to the debug tier
- B. Call `core.setSecret(apiKey)` before logging, then remove the `core.info(apiKey)` call entirely(correct)
- C. Store the key in `GITHUB_ENV` so it is automatically masked in all logs
- D. Set `no-log: true` on the input in `action.yml` to prevent any logging of that input
Explanation: `core.setSecret(value)` registers a value as a secret so the Actions runner will mask it (replace with `***`) in all subsequent log output. However, the root fix is to remove the explicit log call. Using `core.debug()` (A) only hides the line at the default log level but the value is still logged and visible when debug logging is enabled. Writing to `GITHUB_ENV` (C) does not auto-mask values. There is no `no-log:` key in `action.yml` input definitions (D).
17. An enterprise wants to allow repositories to use only actions from verified creators and actions within their own organization. Which setting achieves this?
- A. Set each repository's branch protection rule to require Actions approval
- B. Configure the enterprise Actions policy to allow only actions created by GitHub and verified Marketplace creators, plus actions in the enterprise's own organizations(correct)
- C. Enable required status checks on the default branch
- D. Create a CODEOWNERS file listing approved action repositories
Explanation: GitHub's enterprise (and organization) Actions policies let administrators restrict which actions can run: options include 'All', 'GitHub-created', 'Verified Marketplace creators', or specific allow-listed repositories. Combining 'verified Marketplace creators' with 'own organization' actions is a common enterprise posture. Branch protection rules control merge requirements, not action usage. Status checks enforce CI gates. CODEOWNERS controls code review requirements, not action execution.
18. A self-hosted runner registered to an organization is processing jobs from multiple repositories. The security team wants to restrict it so it only processes jobs from a specific set of repositories. What should the administrator do?
- A. Set an IP allow list on the runner machine's firewall to block other repositories' runner agents
- B. Move the runner into a runner group and configure the group's repository access to include only the allowed repositories(correct)
- C. Add a label to the runner and require workflows to specify that label — unauthorized repositories cannot guess the label
- D. Register the runner at repository level instead of organization level for each allowed repository
Explanation: Runner groups are the standard mechanism for controlling which repositories can use a set of runners. An administrator creates a group, adds runners to it, and restricts group access to selected repositories. IP allow lists control network-level access to GitHub.com, not job routing. Labels route workflows to runners with matching labels but do not restrict which repositories can target a runner — any workflow in the org can reference a label. Registering per-repository scales poorly and doesn't centralize management.
19. A workflow step needs a database password that is stored as an organization-level secret named `DB_PASSWORD`. The repository belongs to that organization. How should the workflow reference this secret?
- A. `${{ env.DB_PASSWORD }}` after exporting it in a prior step
- B. `${{ secrets.DB_PASSWORD }}` in the workflow YAML(correct)
- C. `${{ vars.DB_PASSWORD }}` in the workflow YAML
- D. `${{ github.secret.DB_PASSWORD }}` in the workflow YAML
Explanation: Organization and repository secrets are accessed via the `secrets` context using `${{ secrets.SECRET_NAME }}`. Organization-level secrets are automatically available to repositories that have been granted access, with no additional configuration needed in the workflow. `env` holds environment variables, not secrets. `vars` holds configuration variables (non-sensitive). `github.secret` is not a valid context path.
20. An enterprise administrator needs to identify which software tools are pre-installed on GitHub-hosted `ubuntu-latest` runners without running a workflow. Which resources provide this information? Choose 2.
- A. The runner image release notes published in the `actions/runner-images` GitHub repository(correct)
- B. The `toolcache` directory listing printed at the start of every GitHub-hosted runner job
- C. GitHub's REST API endpoint `GET /repos/{owner}/{repo}/actions/runners`
- D. The runner's included software list in the GitHub documentation for hosted runners(correct)
- E. The `GITHUB_RUNNER_VERSION` environment variable available in any workflow
Explanation: The `actions/runner-images` repository on GitHub publishes detailed release notes and included software manifests for each hosted runner image — this is the authoritative source. GitHub's documentation for hosted runners also lists the major tools available. The toolcache listing (B) appears during a live run, not before. The REST API endpoint (C) returns runner registration metadata (name, OS, status), not installed software. `GITHUB_RUNNER_VERSION` (E) is not a standard built-in environment variable in GitHub Actions.
21. An organization wants a standardized deployment workflow that all its repositories must use, defined centrally and updated from one place. Updates should be reflected in all consuming repositories immediately without requiring them to pull changes. What feature best meets this requirement?
- A. A starter workflow copied to each repository's `.github/workflows/` folder
- B. A reusable workflow referenced via `uses:` with a version tag, hosted in a central repository(correct)
- C. A composite action published to the Marketplace
- D. A GitHub App that submits workflow runs via the REST API
Explanation: Reusable workflows (invoked via `uses: org/repo/.github/workflows/deploy.yml@main`) are centrally versioned — when the central workflow is updated, all consuming workflows pick up the changes on their next run without any local changes. Starter workflows are copied once and become independent, so updates to the source do not propagate. Composite actions encapsulate steps but not full job-level deployment orchestration. A GitHub App adds complexity and doesn't use the native workflow reuse mechanism.
22. A self-hosted runner in an air-gapped environment fails to pick up jobs. The organization's IT team has an IP allow list configured on GitHub Enterprise Cloud. A network trace shows the runner cannot reach `api.github.com`. What is the most likely fix?
- A. Add the GitHub Actions service IP ranges to the organization's IP allow list so the runner's egress traffic is permitted(correct)
- B. Register the runner at the enterprise level instead of the organization level
- C. Install the `gh` CLI on the runner and configure it with a PAT to poll for jobs
- D. Enable GitHub-hosted runners as a fallback so jobs route to them when the self-hosted runner is unreachable
Explanation: Self-hosted runners must be able to reach GitHub's API and Actions service endpoints outbound. GitHub publishes the IP ranges used by its services in the meta API. When an IP allow list is active on an organization, the runner's outbound IP must be added to the allow list — or the runner must go through a proxy that is on the allow list. Changing registration level (B) does not resolve network connectivity. The `gh` CLI (C) is not how runners poll for jobs. Enabling GitHub-hosted runners (D) doesn't fix the self-hosted runner's connectivity.
23. A workflow receives a pull request title via `github.event.pull_request.title` and passes it to a `run:` step using string interpolation: `run: echo "Title is ${{ github.event.pull_request.title }}". A security reviewer flags this as a script injection risk. What is the recommended mitigation?
- A. Wrap the expression in double quotes inside the shell command
- B. Pass the value through an environment variable in the step and reference the env var in the shell script(correct)
- C. Use `secrets.GITHUB_TOKEN` to sign the input before interpolation
- D. Set `permissions: read-all` on the workflow to prevent execution of injected commands
Explanation: Inline `${{ }}` expressions are expanded before the shell interprets the command, so a malicious pull request title could inject shell commands. The safe pattern is to assign the expression to an environment variable at the step level (`env: TITLE: ${{ github.event.pull_request.title }}`) and then reference `$TITLE` in the shell script — environment variable expansion happens after shell parsing, preventing injection. Quoting alone (A) is insufficient for complex payloads. Signing with GITHUB_TOKEN (C) is unrelated to script injection. Read-only permissions (D) limit what the injected code can do but do not prevent injection.
24. A team wants to eliminate long-lived cloud credentials from their GitHub Actions workflows that deploy to Azure. Which approaches achieve this goal? Choose 2.
- A. Store the Azure service principal client secret as an encrypted GitHub secret and rotate it every 90 days
- B. Use OIDC (OpenID Connect) federation: configure Azure to trust GitHub's OIDC provider and use `id-token: write` permission in the workflow(correct)
- C. Use `actions/azure-login` with OIDC — the workflow requests a short-lived token at runtime and Azure validates the token claims(correct)
- D. Encode the service principal credentials in Base64 and store them in a repository variable
- E. Use a self-hosted runner inside the Azure VNet with a managed identity assigned, so the runner authenticates without any stored credentials
Explanation: OIDC federation (B and C are two perspectives on the same mechanism) allows GitHub Actions to request a short-lived, workflow-scoped OIDC token and exchange it with Azure for access — no long-lived secret is ever stored in GitHub. B describes the Azure-side configuration, C describes the workflow-side implementation; together they fully describe the OIDC approach. Rotating a stored secret (A) reduces risk but does not eliminate long-lived credentials. Base64-encoding credentials (D) provides no security benefit and is worse than encrypted secrets. A self-hosted runner with managed identity (E) also eliminates stored credentials and is valid, but the question's OIDC options (B+C) are the most direct answer pairing.
25. An enterprise security policy requires that all third-party actions used in workflows are pinned to immutable references. Which of the following are correct reasons for pinning actions to full commit SHAs rather than tags or branches? Choose 2.
- A. A tag like `v3` is a mutable pointer that an action author (or an attacker who compromises the author's account) can move to a different commit(correct)
- B. Commit SHAs are immutable — once a commit exists on GitHub it cannot be altered, so a SHA pin always refers to the same code(correct)
- C. Pinning to a SHA improves workflow performance because GitHub can cache the action more efficiently
- D. GitHub requires SHA pinning for all actions used in workflows targeting protected branches
- E. SHA pinning is mandatory when using GitHub-hosted runners but optional for self-hosted runners
Explanation: The security rationale for SHA pinning is that tags and branch references are mutable — they can be moved (intentionally or through a supply-chain attack) to point to malicious code. A full commit SHA is a content-addressable, cryptographic reference that cannot be altered; the same SHA always refers to the same tree. Caching efficiency (C) is not affected by the reference type. GitHub does not mandate SHA pinning for protected branches (D) — that is a recommended policy, not an enforced platform rule. SHA pinning requirements do not differ between GitHub-hosted and self-hosted runners (E).