Try 10 focused GitHub Actions GH-200 questions on Author and Maintain Actions, with explanations, then continue with IT Mastery.
Open the matching IT Mastery practice page for timed mocks, topic drills, progress tracking, explanations, and full practice.
Try GitHub Actions GH-200 on Web View full GitHub Actions GH-200 practice page
| Field | Detail |
|---|---|
| Exam route | GitHub Actions GH-200 |
| Topic area | Author and Maintain Actions |
| Blueprint weight | 18% |
| Page purpose | Focused sample questions before returning to mixed practice |
Use this page to isolate Author and Maintain Actions for GitHub Actions GH-200. Work through the 10 questions first, then review the explanations and return to mixed practice in IT Mastery.
| Pass | What to do | What to record |
|---|---|---|
| First attempt | Answer without checking the explanation first. | The fact, rule, calculation, or judgment point that controlled your answer. |
| Review | Read the explanation even when you were correct. | Why the best answer is stronger than the closest distractor. |
| Repair | Repeat only missed or uncertain items after a short break. | The pattern behind misses, not the answer letter. |
| Transfer | Return to mixed practice once the topic feels stable. | Whether the same skill holds up when the topic is no longer obvious. |
Blueprint context: 18% of the practice outline. A focused topic score can overstate readiness if you recognize the pattern too quickly, so use it as repair work before timed mixed sets.
These questions are original IT Mastery practice items aligned to this topic area. They are designed for self-assessment and are not official exam questions.
Topic: Author and Maintain Actions
An organization has 60 private repositories that all need the same internally supported signing logic. Each team must insert that logic at different points in different jobs, security prohibits public distribution, and the platform team wants one codebase with independent versioning and centralized support. Which configuration is best?
Options:
A. Move the logic into a central reusable workflow that every repository calls.
B. Create a dedicated private action repository and let organization repositories consume versioned releases from it.
C. Publish the action from a public repository to GitHub Marketplace.
D. Copy the composite action into each consuming repository.
Best answer: B
Explanation: A dedicated private action repository best fits because the shared logic is a reusable step sequence, not an entire standardized job. It also keeps the action internal while giving the platform team one supported codebase with its own release lifecycle.
Choose the distribution boundary that matches both the reuse unit and the trust boundary. Here, teams need the same logic inserted at different places inside different jobs, so an action is the right reusable component. Because many private repositories will use it and the platform team wants separate ownership, support, and versioning, that action should live in its own private repository rather than inside each application repository.
A public Marketplace publication would break the internal-only requirement. A reusable workflow is better when you want to standardize an entire job or workflow-call boundary, not when teams need a shared step sequence they can place flexibly. The key takeaway is to use a private, centrally maintained action repository when reuse spans many repositories but must remain internal.
Topic: Author and Maintain Actions
A team maintains a local composite action that should make a CLI available to later workflow steps.
# .github/actions/setup-tool/action.yml
runs:
using: composite
steps:
- shell: bash
run: |
TOOL_DIR="$GITHUB_ACTION_PATH/bin"
export TOOL_HOME="$TOOL_DIR"
export PATH="$TOOL_DIR:$PATH"
# .github/workflows/build.yml
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: ./.github/actions/setup-tool
- run: |
echo "$TOOL_HOME"
mytool --version
The second step prints an empty TOOL_HOME and mytool: command not found. Which edit best fixes the action safely so later steps in the same job can use both values?
Options:
A. Replace the export lines with ::set-env and ::add-path workflow commands.
B. Replace the export lines by writing TOOL_HOME to GITHUB_ENV and TOOL_DIR to GITHUB_PATH.
C. Keep the action unchanged and add an env: block on the uses: ./.github/actions/setup-tool step.
D. Replace the export lines by writing both values to GITHUB_OUTPUT so following steps inherit them automatically.
Best answer: B
Explanation: The action fails because export only changes the environment of that single shell process. To make an environment variable and a PATH entry available to later steps in the same job, the action must write to the supported environment files: GITHUB_ENV and GITHUB_PATH.
The key concept is step scope. In a composite action, export TOOL_HOME=... and export PATH=... affect only the shell running that one run step. When the workflow moves to the next step, GitHub Actions starts a new process, so those exported values are gone.
- shell: bash
run: |
TOOL_DIR="$GITHUB_ACTION_PATH/bin"
echo "TOOL_HOME=$TOOL_DIR" >> "$GITHUB_ENV"
echo "$TOOL_DIR" >> "$GITHUB_PATH"
GITHUB_ENV makes TOOL_HOME available to later steps in the same job, and GITHUB_PATH safely adds the tool directory to command lookup. Using step outputs would require explicit consumption and still would not update PATH automatically.
GITHUB_OUTPUT creates step outputs, not automatic job-wide environment variables or PATH updates.env: block on the uses step applies to that step invocation and does not make later workflow steps inherit the action’s exported shell state.::set-env and ::add-path are deprecated; environment files are the supported safe mechanism.Topic: Author and Maintain Actions
Your JavaScript action is still on major version 1, and consumers use uses: contoso/cache-action@v1. The team sometimes pushes prerelease tags such as v1.5.0-rc.1 before publishing a GitHub release. The v1 tag must move only after a non-prerelease release is published.
name: update-major
on:
push:
tags:
- 'v1.*'
jobs:
move-tag:
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v4
- run: |
git tag -fa v1 -m "Update v1"
git push origin refs/tags/v1 --force
Which edit best satisfies the requirement?
Options:
A. Keep the tag-push trigger, but add if: ${{ !contains(github.ref_name, '-') }} before updating v1.
B. Change the trigger to release types: [created] so v1 moves when the release entry is first made.
C. Replace the trigger with release types: [published], add if: ${{ !github.event.release.prerelease }}, and checkout ${{ github.event.release.tag_name }} before updating v1.
D. Change the trigger to release types: [published], but checkout ref: main before updating v1.
Best answer: C
Explanation: A floating major tag such as v1 should advance only when the stable release is actually published, not when any matching tag is pushed. Using release with types: [published], checking github.event.release.prerelease, and checking out the released tag keeps v1 aligned to the correct backward-compatible release commit.
Major-version tags for custom actions are movable aliases, while full semantic version tags such as v1.4.2 should remain fixed release points. In this workflow, push on tags is too early because a matching tag can be pushed before the GitHub release is published, and prerelease tags can match the same pattern.
The safe pattern is:
release with types: [published]github.event.release.prerelease is true${{ github.event.release.tag_name }}v1 to that released commitThe existing contents: write permission is what allows the workflow to update the tag. Checking out main would make v1 follow a branch tip instead of the immutable commit associated with the published release. The key point is that the floating major tag should follow the published stable release, not tag pushes or branch heads.
v1 too early.v1 follow the latest branch commit rather than the published release tag commit.Topic: Author and Maintain Actions
A team created a local JavaScript action at .github/actions/label-release/ and calls it from a workflow with uses: ./.github/actions/label-release. The job fails before any script output appears.
Exhibit:
# .github/actions/label-release/action.yml
name: Release labeler
description: Adds a label based on release type
inputs:
release-type:
description: Release channel
required: true
outputs:
applied-label:
description: Label added to the issue
Log excerpt:
Error: action metadata is invalid
Required property is missing: runs
What is the best explanation for the failure?
Options:
A. The workflow needs permissions: issues: write before GitHub can load the local action.
B. The local action must be stored under .github/workflows instead of .github/actions.
C. The action output should be defined only through GITHUB_ENV, not in action.yml.
D. The action metadata is missing a runs section that declares the runtime and entry point.
Best answer: D
Explanation: The failure occurs during action metadata validation, not during script execution. For a JavaScript action, action.yml must include a runs block that tells GitHub which runtime to use and which file to execute.
Custom actions need an action.yml (or action.yaml) metadata file. For a JavaScript action, that file can include name, description, inputs, and outputs, but it also must include a runs section so GitHub knows how to execute the action.
A minimal JavaScript action metadata file includes:
runs.using for the runtimeruns.main for the entry fileBecause the log says the required property runs is missing, GitHub stops before the action code starts. Permissions problems or output-writing mistakes would happen later, after the action had already been loaded and executed.
.github/workflows is incorrect because that folder is for workflow files, while local actions are commonly stored in paths such as .github/actions.issues: write is a later runtime concern; missing permissions would not cause a metadata validation error about runs.GITHUB_ENV is incorrect because action outputs are declared in metadata and runtime output values are written with GITHUB_OUTPUT, not GITHUB_ENV.Topic: Author and Maintain Actions
You are authoring a composite action named package-report. The action should behave as follows:
report-path.format is optional and should default to sarif.include-summary is optional and should default to true.action.yml.Which inputs definition is the best configuration?
Options:
- B. ```yaml
inputs:
report-path:
description: Path to the generated report file
required: true
format:
description: Output format for the published report
required: false
default: sarif
include-summary:
description: Whether to add a workflow summary
required: false
default: 'true'
- D. ```yaml
inputs:
report-path:
description: Path to the generated report file
required: true
format:
description: Output format for the published report
required: true
default: sarif
include-summary:
description: Whether to add a workflow summary
required: true
default: 'true'
Best answer: B
Explanation: The best metadata definition matches the action contract exactly: one required caller-supplied input and two optional inputs with explicit defaults. That keeps the action interface clear and prevents callers from being forced to pass values the action already knows how to default.
In action.yml, input metadata should mirror how the action is meant to be used. A value the caller must always supply should be marked required: true and normally should not rely on a fallback. Optional behavior should be marked required: false and given a default when the action has a known fallback value.
Here, report-path must come from the caller, so it is the only required input. format and include-summary are optional because the action is supposed to use sarif and true when the caller omits them. Including descriptions for each input makes the contract clear to anyone consuming the action.
The closest distractors either change which values are required or change the defaults, which means the metadata no longer matches the stated behavior.
report-path optional changes the contract because the caller is no longer required to supply the file path.format and include-summary as required contradicts the requirement that both are optional with defaults.json and 'false' changes the declared fallback behavior of the action.Topic: Author and Maintain Actions
A team rewrote a composite action so invalid caller inputs fail with targeted guidance. The action defines environment as required. approver, token, and use_oidc are optional, and omitted optional inputs resolve to empty strings. In this run, secrets.DEPLOY_TOKEN exists.
# action.yml (fragment)
runs:
using: composite
steps:
- shell: bash
env:
ENVIRONMENT: ${{ inputs.environment }}
APPROVER: ${{ inputs.approver }}
USE_OIDC: ${{ inputs.use_oidc }}
TOKEN: ${{ inputs.token }}
run: |
if [[ "$ENVIRONMENT" == "prod" && -z "$APPROVER" ]]; then
echo "::error title=Invalid action configuration::For production, set input 'approver' in the calling workflow."
exit 1
fi
if [[ "$USE_OIDC" == "true" && -n "$TOKEN" ]]; then
echo "::error title=Invalid action configuration::Use either 'token' or 'use_oidc: true', not both."
exit 1
fi
# caller step
- uses: org/deploy-action@v1
with:
environment: prod
use_oidc: 'true'
token: ${{ secrets.DEPLOY_TOKEN }}
Which message is emitted by the validation step?
Options:
A. For production, set input approver in the calling workflow.
B. Input required and not supplied: approver.
C. No error is emitted; validation passes.
D. Use either token or use_oidc: true, not both.
Best answer: A
Explanation: The validation logic runs top to bottom. Because the caller sets environment to prod and omits approver, the first condition is true and the script exits immediately with the targeted production guidance.
This item tests how a custom action surfaces a helpful configuration error during execution. The caller has two bad settings, but the script stops at the first failing check. Here, ENVIRONMENT is prod, APPROVER is empty, and the first if emits a message that tells the workflow author exactly what to add in the caller.
ENVIRONMENT=prodAPPROVER=""exit 1 stops the step immediatelyBecause the step exits on that first branch, the later token plus use_oidc conflict is never evaluated.
exit 1.approver were defined as required in the action metadata.Topic: Author and Maintain Actions
A platform team maintains an internal action used as uses: org/release-note@v2. Consuming workflows run on self-hosted Linux runners that have no outbound internet except GitHub and no Docker daemon.
The current action.yml is:
runs:
using: node16
main: src/index.js
The action repo contains package.json, but its npm dependencies are not bundled or committed. Runs now fail with:
Error: Cannot find module '@actions/core'
The node16 runtime is not supported on this runner
The team wants the most reliable configuration with no extra setup in consuming workflows. Which configuration is best?
Options:
A. Convert it to a composite action that runs node src/index.js.
B. Bundle dependencies into dist, commit it, and use node20.
C. Keep src/index.js and add npm ci before each use.
D. Convert it to a Docker action built from node:20.
Best answer: B
Explanation: A JavaScript action should ship executable code and dependencies, typically as a bundled dist/index.js, because consuming workflows do not install the action’s npm packages automatically. Updating runs.using to a supported runtime such as node20 fixes the Node compatibility failure at the same time.
This failure has two causes: packaging and runtime selection. For a JavaScript action, the consumer runner does not run npm install for the action repository, so pointing main to src/index.js while leaving dependencies only in package.json can produce Cannot find module errors. The reliable fix is to build or bundle the action and commit the generated output, commonly dist/index.js, then set main to that file.
The second error comes from declaring an unsupported Node runtime in action.yml. Updating runs.using to a supported value such as node20 lets the runner execute the action on current GitHub Actions infrastructure. Requiring each workflow to bootstrap the action, or switching to Docker on runners without Docker, adds fragility instead of fixing the action package itself.
node plus the action’s local npm packages being present, so it does not solve the missing dependency problem.Topic: Author and Maintain Actions
A team is troubleshooting a local composite action used by a matrix job on GitHub-hosted runners. The repository contains .github/actions/pkg/scripts/build.sh, and build.sh has the executable bit committed in Git.
# workflow.yml
jobs:
pack:
strategy:
matrix:
os: [ubuntu-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/pkg
# .github/actions/pkg/action.yml
name: pkg
runs:
using: composite
steps:
- name: Run build
shell: bash
working-directory: ${{ github.action_path }}\scripts
run: ./build.sh
Which execution trace matches GitHub Actions behavior?
Options:
A. Both runners run build.sh because the path is normalized.
B. Windows runs build.sh; Ubuntu fails before bash starts.
C. Ubuntu runs build.sh; Windows fails because bash is unsupported.
D. Both runners fail because build.sh still needs chmod +x.
Best answer: B
Explanation: github.action_path resolves to the action folder on each runner, but the appended \scripts hard-codes a Windows path separator. On Ubuntu, the runner cannot resolve that working directory, so the step fails before bash runs. On Windows, the directory is valid and the script can run.
In a composite action, working-directory is resolved by the runner before the run command starts. Here, ${{ github.action_path }} expands to the absolute path of the action directory, but \scripts makes the final path Windows-specific. On Ubuntu, that becomes a path ending in pkg\scripts, which Linux treats as a different name rather than the scripts subfolder, so the runner cannot change into the directory and the step stops before shell execution.
On Windows, the same path is valid. Because the step explicitly uses shell: bash, the Windows GitHub-hosted runner can launch Bash for that step. Since build.sh already has its executable bit committed in Git, there is no extra permission fix required in this scenario.
For cross-platform action authoring, use an OS-neutral path such as ${{ github.action_path }}/scripts.
chmod +x step is required here.shell: bash.Topic: Author and Maintain Actions
Consider this workflow job:
jobs:
lint-notes:
runs-on: ubuntu-latest
steps:
- id: notes
run: |
NOTES=$(cat notes.txt)
echo "::warning::$NOTES"
echo "notes=$NOTES" >> "$GITHUB_OUTPUT"
- run: echo "${{ steps.notes.outputs.notes }}"
notes.txt can contain multiple lines and text such as ::notice::review me. Which edit best preserves the full value for steps.notes.outputs.notes and avoids malformed workflow command parsing?
Options:
A. Replace both lines with echo "::set-output name=notes::$NOTES".
B. Escape %, CR, and LF before ::warning::, and write notes to GITHUB_OUTPUT with the multiline notes<<EOF format.
C. Replace GITHUB_OUTPUT with GITHUB_ENV and reference ${{ env.notes }} in the next step.
D. Keep ::warning:: unchanged, but write the output with printf 'notes=%s\n' "$NOTES" >> "$GITHUB_OUTPUT".
Best answer: B
Explanation: The value is unsafe in two different ways: annotation commands parse special characters, and GITHUB_OUTPUT single-line syntax does not safely carry multiline data. The fix is to escape the warning message and use the multiline environment-file form for the step output.
GitHub Actions still parses lines such as ::warning::... as workflow commands, so arbitrary text inserted into that command must escape %, carriage returns, and newlines. Otherwise, the runner can misread the message or treat later lines as separate commands. For step outputs, the current pattern is the GITHUB_OUTPUT file, and multiline values must use the delimiter form rather than name=value.
A safe pattern is:
esc=${NOTES//'%'/'%25'}
esc=${esc//$'\n'/'%0A'}
esc=${esc//$'\r'/'%0D'}
echo "::warning::$esc"
{
echo 'notes<<EOF'
printf '%s\n' "$NOTES"
echo 'EOF'
} >> "$GITHUB_OUTPUT"
Using GITHUB_ENV changes the data to an environment variable for later steps in the same job, while ::set-output is deprecated and should not be used.
printf 'notes=%s\n' still produces invalid multiline output when the value itself contains line breaks.GITHUB_ENV changes the mechanism; it does not create steps.notes.outputs.notes for step-output references.::set-output is obsolete and still relies on workflow-command parsing instead of the recommended environment file.Topic: Author and Maintain Actions
A workflow generates Markdown release notes and needs them as an environment variable in a later step of the same job. The notes can contain blank lines.
jobs:
publish:
runs-on: ubuntu-latest
steps:
- name: Generate notes
run: |
NOTES=$(printf 'Fix login bug\n\n- add tests\n- update docs\n')
echo "RELEASE_NOTES=$NOTES" >> "$GITHUB_ENV"
- name: Use notes
run: printf '%s\n' "$RELEASE_NOTES"
Which edit best fixes this so the full multiline value is available in Use notes?
Options:
A. Move RELEASE_NOTES to the job-level env: block.
B. Write the notes to GITHUB_STEP_SUMMARY and read them later.
C. Write RELEASE_NOTES to GITHUB_ENV with the <<DELIMITER multiline format.
D. Use export RELEASE_NOTES="$NOTES" in the first step instead.
Best answer: C
Explanation: GITHUB_ENV can pass values to later steps in the same job, but raw NAME=value syntax only works for single-line content. For multiline data, you must use the NAME<<DELIMITER format so GitHub Actions treats the entire block as one value.
When a step writes to GITHUB_ENV, GitHub Actions parses that file as environment-file commands. A line like RELEASE_NOTES=$NOTES is valid only if the value stays on one line. If the value contains embedded newlines, those extra lines are parsed separately, which breaks the environment file or loses part of the content.
{
echo 'RELEASE_NOTES<<EOF'
printf '%s\n' "$NOTES"
echo 'EOF'
} >> "$GITHUB_ENV"
This delimiter form tells Actions to read everything until the closing token as one multiline value. Choose a delimiter that will not appear by itself in the content. The variable then becomes available to later steps in the same job, which is exactly what this workflow needs.
env: does not solve runtime generation, because the value is being created inside the step’s shell.export only affects the current step process and does not persist to later steps.GITHUB_STEP_SUMMARY is for run summary output, not for creating environment variables for subsequent steps.Use the GitHub Actions GH-200 Practice Test page for the full IT Mastery route, mixed-topic practice, timed mock exams, explanations, and web/mobile app access.
Try GitHub Actions GH-200 on Web View GitHub Actions GH-200 Practice Test
Read the GitHub Actions GH-200 Cheat Sheet on Tech Exam Lexicon, then return to IT Mastery for timed practice.