Exam identity and focus
| Item | Reference |
|---|
| Vendor/provider | GitHub |
| Official exam title | GitHub Actions (GH-200) |
| Official exam code | GH-200 |
| Page purpose | Independent Quick Reference for real-exam preparation and original practice support |
Use this page to quickly review GitHub Actions concepts that are commonly tested through scenario questions: choosing the right trigger, structuring workflows, securing tokens and secrets, reusing automation, selecting runners, and troubleshooting failed or skipped runs.
Core workflow anatomy
A workflow is a YAML file stored in .github/workflows/ and triggered by events, manual dispatch, schedules, or calls from other workflows.
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
workflow_dispatch:
permissions:
contents: read
env:
NODE_VERSION: "20"
jobs:
test:
name: Test on ${{ matrix.os }}
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, windows-latest]
node: ["20", "22"]
steps:
- name: Check out code
uses: actions/checkout@v4
- name: Set up Node
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
- name: Install and test
run: |
npm ci
npm test
Workflow file elements
| Element | Scope | High-yield use |
|---|
name | Workflow | Display name in the Actions UI and checks. |
on | Workflow | Defines events that trigger the workflow. |
permissions | Workflow/job | Sets GITHUB_TOKEN permissions. Prefer least privilege. |
env | Workflow/job/step | Defines environment variables. More specific scopes override broader scopes. |
defaults | Workflow/job | Sets default shell or working directory for run steps. |
jobs | Workflow | Groups independent or dependent units of work. |
runs-on | Job | Selects GitHub-hosted or self-hosted runner. |
needs | Job | Creates job dependencies and enables outputs from prior jobs. |
if | Job/step | Conditionally runs a job or step. |
strategy.matrix | Job | Expands one job definition into multiple job variations. |
steps | Job | Sequential commands or actions inside a job. |
uses | Step/job | Calls an action at step level or reusable workflow at job level. |
run | Step | Executes shell commands. |
with | Step/job call | Supplies action or reusable workflow inputs. |
secrets | Job call/step context | Supplies sensitive values without hard-coding them. |
Trigger selection
Common on events
| Event | Choose when | Important exam traps |
|---|
push | Run after commits or tags are pushed. | Branch and tag filters apply to push refs. Path filters do not apply to tag pushes. |
pull_request | Validate PR changes before merge. | Runs in PR context. Fork PRs usually receive restricted token permissions and no repository secrets. |
pull_request_target | Run automation in the base repository context, such as labeling or commenting. | Dangerous if you check out and execute untrusted PR code while secrets or write token permissions are available. |
workflow_dispatch | Allow manual runs from the UI, API, or CLI. | Inputs are available through the inputs context. |
schedule | Run on a cron schedule. | Uses UTC and runs on the default branch. |
workflow_call | Make a workflow reusable by other workflows. | Called at job level, not as a normal step. |
workflow_run | Run a follow-up workflow after another workflow completes. | Can separate untrusted build/test from privileged publish/deploy, but watch artifact/cache trust boundaries. |
release | Run when release activity occurs. | Use types to narrow activity such as published or created. |
merge_group | Validate merge queue candidates. | Required when branch protection uses merge queue and checks must run on merge groups. |
issues, issue_comment, pull_request_review | Automate repository collaboration. | Confirm event payload fields before writing conditions. |
Trigger filtering patterns
on:
pull_request:
types: [opened, synchronize, reopened]
branches:
- main
- "release/**"
paths:
- "src/**"
- "!src/experimental/**"
| Filter | Applies to | Notes |
|---|
branches | Target branch for PR events; pushed branch for push events | Cannot be used with branches-ignore for the same event. |
branches-ignore | Branch exclusion | Use when exclusion-only logic is clearer. |
tags | Push tag refs | Cannot be used with tags-ignore for the same event. |
paths | Changed files | Cannot be used with paths-ignore for the same event. Order matters when using ! negation. |
paths-ignore | Changed files to ignore | If all changed files match ignored paths, the workflow is skipped. |
types | Event activity types | Reduces unnecessary runs and avoids broad event handling. |
Trigger decision table
| Scenario | Best trigger/pattern |
|---|
Run tests on every PR to main | pull_request with branches: [main] |
| Publish package only after a release is published | release with appropriate types |
| Let maintainers manually run a deployment | workflow_dispatch with inputs and environment protection |
| Reuse the same deployment pipeline across repositories | workflow_call reusable workflow |
| Run nightly dependency checks | schedule |
| Comment on PRs from forks without running their code | pull_request_target, limited permissions, no untrusted checkout execution |
| Perform privileged publish after untrusted PR checks pass | Use workflow_run carefully with trusted artifacts only |
| Validate merge queue entries | merge_group |
Jobs, steps, dependencies, and conditions
Job dependency basics
jobs:
build:
runs-on: ubuntu-latest
outputs:
artifact-name: ${{ steps.meta.outputs.name }}
steps:
- id: meta
run: echo "name=app-${GITHUB_SHA}" >> "$GITHUB_OUTPUT"
deploy:
runs-on: ubuntu-latest
needs: build
if: ${{ github.ref == 'refs/heads/main' }}
steps:
- run: echo "Deploying ${{ needs.build.outputs.artifact-name }}"
| Concept | Behavior |
|---|
Jobs without needs | Run in parallel when runners are available. |
needs | Makes one job wait for another and exposes dependency results/outputs. |
| Failed dependency | Dependent jobs are skipped unless their condition intentionally handles failure. |
| Step order | Steps in a job run sequentially. |
Job if | Evaluated before the job runs. Be careful with matrix expansion and dependency assumptions. |
Step if | Evaluated before the step runs. |
Status functions
| Function | Use |
|---|
success() | True when previous steps/jobs in the dependency chain succeeded. |
failure() | True when a previous step/job failed. |
cancelled() | True when the workflow was cancelled. |
always() | Runs even after failure or cancellation. Use carefully for cleanup/reporting. |
!cancelled() | Common safer condition for cleanup that should not run after cancellation. |
- name: Upload logs on failure
if: ${{ failure() }}
uses: actions/upload-artifact@v4
with:
name: logs
path: logs/
Expressions, contexts, and variables
Contexts to recognize
| Context | Contains | Typical use |
|---|
github | Event, repository, ref, actor, run metadata | Branch checks, event-specific logic, audit info |
env | Environment variables defined in workflow/job/step | Runtime configuration |
vars | Repository, organization, or environment configuration variables | Non-secret configurable values |
secrets | Encrypted secrets available to the workflow | Tokens, passwords, private keys |
inputs | Inputs from workflow_dispatch or workflow_call | Parameterized workflows |
matrix | Current matrix combination | OS, language version, feature flags |
strategy | Matrix strategy metadata | Matrix-related behavior |
runner | Runner OS, architecture, temp paths | Conditional runner behavior |
steps | Prior step outputs and conclusions | Pass data between steps |
needs | Outputs and results from dependency jobs | Pass data between jobs |
job | Current job information | Service container details and job status |
Expression functions and operators
| Item | Example | Notes |
|---|
| Equality | ${{ github.ref == 'refs/heads/main' }} | Used in if, env values, names, inputs. |
| Boolean AND/OR | ${{ startsWith(github.ref, 'refs/tags/') && success() }} | Use parentheses for readability. |
contains | ${{ contains(github.event.pull_request.labels.*.name, 'safe-to-test') }} | Useful with arrays and strings. |
startsWith / endsWith | ${{ startsWith(github.ref, 'refs/heads/release/') }} | Common branch/tag logic. |
format | ${{ format('app-{0}', github.sha) }} | String formatting. |
fromJSON | ${{ fromJSON(needs.plan.outputs.matrix) }} | Converts JSON string to object/array. |
toJSON | ${{ toJSON(github) }} | Debugging contexts; avoid printing secrets. |
hashFiles | ${{ hashFiles('**/package-lock.json') }} | Common cache key component. |
Environment variable precedence
| Scope | Example | Precedence |
|---|
Workflow env | Shared by all jobs and steps | Lowest of these three |
Job env | Shared by steps in one job | Overrides workflow env |
Step env | Applies to one step | Highest |
env:
APP_ENV: test
jobs:
example:
runs-on: ubuntu-latest
env:
APP_ENV: ci
steps:
- run: echo "$APP_ENV"
env:
APP_ENV: step
Passing data between steps and jobs
| Need | Use | Example destination |
|---|
| Step output | $GITHUB_OUTPUT | ${{ steps.id.outputs.name }} |
| Environment variable for later steps in same job | $GITHUB_ENV | $NAME or $env:NAME depending on shell |
Add executable directory to PATH | $GITHUB_PATH | Later steps in same job |
| Human-readable run summary | $GITHUB_STEP_SUMMARY | Actions run summary UI |
| Job output | jobs.<job_id>.outputs | ${{ needs.job.outputs.name }} |
| Files between jobs | Artifacts | upload-artifact / download-artifact |
steps:
- id: version
run: echo "value=1.2.3" >> "$GITHUB_OUTPUT"
- name: Use step output
run: echo "Version is ${{ steps.version.outputs.value }}"
Matrix strategy quick reference
jobs:
test:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest]
node: ["20", "22"]
include:
- os: ubuntu-latest
node: "22"
experimental: true
exclude:
- os: windows-latest
node: "20"
continue-on-error: ${{ matrix.experimental == true }}
steps:
- uses: actions/checkout@v4
- run: npm test
| Feature | Use |
|---|
| Matrix axes | Test combinations such as OS, language version, or dependency version. |
include | Add fields or additional combinations. |
exclude | Remove combinations. |
fail-fast | Cancel in-progress matrix jobs when one fails, or allow all to complete. |
max-parallel | Limit simultaneous matrix jobs. |
continue-on-error | Allow known experimental combinations to fail without failing the workflow. |
| Dynamic matrix | Generate JSON in one job and consume it with fromJSON in another. |
Dynamic matrix pattern:
jobs:
plan:
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.set.outputs.matrix }}
steps:
- id: set
run: echo 'matrix={"package":["api","web"]}' >> "$GITHUB_OUTPUT"
test:
needs: plan
runs-on: ubuntu-latest
strategy:
matrix: ${{ fromJSON(needs.plan.outputs.matrix) }}
steps:
- run: echo "Testing ${{ matrix.package }}"
Actions, reusable workflows, and reuse choices
Reuse decision matrix
| Reuse mechanism | Called from | Best for | Not ideal for |
|---|
| Marketplace or repository action | Step with uses: | Reusable task such as checkout, setup, lint, upload | Full multi-job pipelines |
| Composite action | Step with uses: | Bundling repeated shell/action steps | Separate jobs, services, independent permissions |
| JavaScript action | Step with uses: | Cross-platform custom logic with Node runtime | Heavy OS-specific tooling |
| Docker container action | Step with uses: | Packaged Linux tooling and dependencies | Windows/macOS runner execution needs |
| Reusable workflow | Job with uses: | Standard CI/CD pipeline, governance, deployment flow | One small command inside a job |
Composite action pattern
action.yml:
name: "Setup and lint"
description: "Install dependencies and run lint"
runs:
using: "composite"
steps:
- run: npm ci
shell: bash
- run: npm run lint
shell: bash
Called from a workflow:
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/setup-and-lint
Reusable workflow pattern
Reusable workflow:
name: Reusable Deploy
on:
workflow_call:
inputs:
environment:
required: true
type: string
secrets:
deploy-token:
required: true
jobs:
deploy:
runs-on: ubuntu-latest
environment: ${{ inputs.environment }}
steps:
- run: echo "Deploying to ${{ inputs.environment }}"
Caller workflow:
jobs:
deploy-prod:
uses: org/platform/.github/workflows/deploy.yml@main
with:
environment: production
secrets:
deploy-token: ${{ secrets.PROD_DEPLOY_TOKEN }}
Reusable workflow traps
| Trap | Correct approach |
|---|
| Calling a reusable workflow as a step | Reusable workflows are called at the job level with jobs.<id>.uses. |
| Assuming all secrets automatically pass | Explicitly pass required secrets, or use the supported inheritance pattern where appropriate. |
| Trying to use caller job steps around a reusable workflow job | A job that uses a reusable workflow does not define normal steps. |
| Versioning reusable workflows by mutable branch only | For stronger stability, call a tag or commit SHA. |
| Hiding deployment permissions in reusable workflow | Make required permissions, inputs, and secrets clear to callers. |
Runners and execution environments
Runner selection
| Runner type | Choose when | Watch for |
|---|
| GitHub-hosted runner | You need managed, clean, ephemeral execution with standard OS images. | Image contents can change; install or pin required tool versions. |
| Self-hosted runner | You need private network access, custom hardware, special tools, or controlled environment. | You manage security, patching, cleanup, scaling, and isolation. |
| Larger or specialized runner | You need more resources or specialized hardware. | Availability and configuration depend on account setup. |
| Runner group | You need to restrict which repositories can use specific self-hosted runners. | Misconfigured groups can expose sensitive infrastructure. |
| Custom runner labels | You need jobs to target specific capabilities. | Labels must match available runners. |
jobs:
build:
runs-on: [self-hosted, linux, x64, gpu]
steps:
- run: ./build.sh
Job containers and service containers
| Feature | Use |
|---|
jobs.<job_id>.container | Run job steps inside a container. |
services | Start supporting containers such as databases, queues, or caches. |
| Service label | Hostname used to reach the service from a job container. |
| Port mapping | Needed when the job runs directly on the runner and accesses service through localhost. |
| Linux dependency | Container jobs and Docker-based actions require a compatible Docker environment. |
jobs:
integration:
runs-on: ubuntu-latest
container: node:22
services:
postgres:
image: postgres:16
env:
POSTGRES_PASSWORD: postgres
steps:
- run: npm test
env:
DATABASE_HOST: postgres
Runner security distinctions
| Area | GitHub-hosted | Self-hosted |
|---|
| Lifecycle | Fresh environment per job | Persistent unless you design cleanup/isolation |
| Maintenance | Managed by GitHub | Your responsibility |
| Network access | Public internet and configured services | Can access internal systems if network permits |
| Secrets exposure risk | Lower persistence risk | Higher if untrusted code can run |
| Public repository PRs | Common for validation | Avoid running untrusted fork code on sensitive self-hosted runners |
Security, permissions, secrets, and OIDC
GITHUB_TOKEN permissions
Set token permissions explicitly. Do not rely on repository defaults.
permissions:
contents: read
jobs:
release:
runs-on: ubuntu-latest
permissions:
contents: write
packages: write
steps:
- run: echo "Publish release"
| Permission area | Typical purpose |
|---|
contents | Read or write repository contents, releases, tags. |
pull-requests | Read or modify pull requests. |
issues | Read or modify issues. |
actions | Interact with Actions runs/artifacts where allowed. |
checks | Create or update check runs. |
statuses | Create commit statuses. |
deployments | Create or update deployments. |
packages | Publish or read packages. |
security-events | Upload security scanning results. |
id-token | Request an OIDC token for federated authentication. |
Secrets vs variables
| Store | Sensitive? | Typical examples | Access syntax |
|---|
| Secrets | Yes | API tokens, private keys, passwords | ${{ secrets.NAME }} |
| Configuration variables | No | Region, feature flag, environment name | ${{ vars.NAME }} |
| Environment variables | Not inherently | Runtime values for steps | $NAME, $env:NAME, or ${{ env.NAME }} |
High-yield rules:
- Do not hard-code secrets in workflow YAML, scripts, logs, or action inputs.
- Secrets are masked in logs, but masking is not a substitute for least privilege.
- Secrets are generally not available to workflows triggered from untrusted forks.
- Secrets cannot be directly used in some conditional expressions. A common pattern is to map to
env and test the env value carefully. - Prefer environment secrets for deployment targets with required reviewers or protection rules.
- Use separate secrets for separate environments instead of one broad credential.
OIDC federation pattern
Use OpenID Connect when a cloud or external identity provider trusts GitHub-issued tokens. This avoids long-lived cloud credentials in GitHub secrets.
permissions:
contents: read
id-token: write
jobs:
deploy:
runs-on: ubuntu-latest
environment: production
steps:
- name: Authenticate with cloud provider using OIDC
run: echo "Request short-lived credentials through provider action or CLI"
| OIDC concept | Exam-ready meaning |
|---|
id-token: write | Required permission to request a GitHub OIDC token. |
| Trust policy | Configured in the external provider, not only in the workflow. |
| Claims | Provider can validate repository, branch, environment, workflow, and other token claims. |
| Benefit | Reduces long-lived secret exposure. |
| Trap | OIDC does not automatically grant access; external trust and authorization must be configured. |
Secure action usage
| Risk | Safer pattern |
|---|
| Third-party action changes unexpectedly | Pin to a full commit SHA for high-security workflows. |
| Overbroad token permissions | Set top-level permissions: contents: read, elevate per job only when needed. |
| Running untrusted PR code with secrets | Use pull_request for validation; avoid privileged code execution in pull_request_target. |
| Shell injection from issue/PR data | Treat event context values as untrusted input. Quote variables and avoid direct interpolation into shell. |
| Secret printed by a script | Disable verbose output, avoid set -x, mask derived sensitive values when needed. |
| Self-hosted runner exposed to forks | Restrict runner access and avoid untrusted workloads. |
Unsafe pattern:
- run: echo "${{ github.event.pull_request.title }}" | bash
Safer pattern:
- name: Use PR title as data, not code
env:
PR_TITLE: ${{ github.event.pull_request.title }}
run: |
printf '%s\n' "$PR_TITLE"
Environments and deployments
jobs:
deploy:
runs-on: ubuntu-latest
environment:
name: production
url: https://example.com
steps:
- run: ./deploy.sh
| Feature | Use |
|---|
| Environment | Named deployment target such as dev, staging, or production. |
| Environment secrets | Secrets scoped to that environment. |
| Environment variables | Non-secret configuration scoped to that environment. |
| Required reviewers | Manual approval before a job accesses protected environment secrets and proceeds. |
| Wait timer/protection rules | Add control before deployment jobs run. |
| Deployment URL | Displays deployed app URL in the Actions UI. |
Deployment decision points
| Scenario | Recommended pattern |
|---|
| Deploy to production only after approval | Use environment: production with protection rules. |
| Deploy to staging on every merge to main | Branch condition plus staging environment. |
| Reuse deployment process across many repositories | Reusable workflow with environment input. |
| Need cloud credentials without stored long-lived secret | OIDC plus environment protection. |
| Need separate credentials per target | Environment-scoped secrets. |
Artifacts, caches, and dependency management
Artifacts vs cache
| Feature | Artifacts | Cache |
|---|
| Primary purpose | Preserve or transfer files from a workflow run | Speed up future runs by reusing dependencies/build data |
| Typical data | Test reports, coverage, binaries, logs | Package manager directories, build caches |
| Access pattern | Upload in one job, download later or inspect after run | Restore by key, save for future matching keys |
| Mutability expectation | Run output | Keyed cache content; treat as dependency optimization |
| Security caution | Do not trust artifacts from untrusted workflows for privileged deployment without validation | Cache poisoning is possible if untrusted code can influence cache content/keys |
Artifact pattern:
jobs:
build:
runs-on: ubuntu-latest
steps:
- run: mkdir -p dist && echo app > dist/app.txt
- uses: actions/upload-artifact@v4
with:
name: app
path: dist/
consume:
needs: build
runs-on: ubuntu-latest
steps:
- uses: actions/download-artifact@v4
with:
name: app
path: dist/
- run: ls dist
Cache pattern:
- name: Cache npm dependencies
uses: actions/cache@v4
with:
path: ~/.npm
key: npm-${{ runner.os }}-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
npm-${{ runner.os }}-
Cache key guidance
| Key component | Why it matters |
|---|
| OS or runner label | Avoid restoring incompatible dependencies. |
| Lockfile hash | Invalidates cache when dependencies change. |
| Tool/language version | Avoid mixing incompatible dependency formats. |
| Restore prefix | Allows partial fallback when exact key is unavailable. |
Concurrency and run control
concurrency:
group: deploy-${{ github.ref }}
cancel-in-progress: true
| Feature | Use |
|---|
| Workflow-level concurrency | Prevent duplicate full workflow runs for the same group. |
| Job-level concurrency | Prevent overlapping deployment or mutation jobs. |
group | Defines what counts as the same concurrent unit. |
cancel-in-progress | Cancels older in-progress runs when a newer run in the same group starts. |
Common choices:
| Scenario | Group example |
|---|
| One deployment per environment | deploy-${{ inputs.environment }} |
| One CI run per branch | ci-${{ github.ref }} |
| One release publish per tag | release-${{ github.ref_name }} |
Common workflow commands and GitHub CLI
Workflow file commands
| Command file | Purpose |
|---|
$GITHUB_OUTPUT | Set step outputs. |
$GITHUB_ENV | Set environment variables for later steps in the same job. |
$GITHUB_PATH | Add directories to PATH for later steps in the same job. |
$GITHUB_STEP_SUMMARY | Add Markdown to the run summary. |
- name: Write summary
run: |
echo "### Test summary" >> "$GITHUB_STEP_SUMMARY"
echo "- Passed" >> "$GITHUB_STEP_SUMMARY"
Useful GitHub CLI commands for Actions
gh workflow list
gh workflow run CI
gh run list
gh run view --log
gh run watch
gh run rerun <run-id>
| Command | Use |
|---|
gh workflow list | List workflows. |
gh workflow run | Trigger workflow_dispatch. |
gh run list | Inspect recent workflow runs. |
gh run view --log | View logs from a run. |
gh run watch | Follow a run until completion. |
gh run rerun | Rerun failed or selected jobs depending on options. |
Troubleshooting patterns
Fast diagnosis table
| Symptom | Likely cause | Check |
|---|
| Workflow did not start | Event, branch, tag, path, or activity-type filter did not match | Inspect on filters and event payload. |
| Required check stuck pending | Workflow skipped by branch/path filter or commit message while branch protection expects it | Align required checks with workflows that always run, or adjust filters. |
| Job skipped | if condition false or dependency failed | Review job condition and needs results. |
| Step skipped after failure | Default status condition requires prior success | Use explicit if: ${{ failure() }} or another status function. |
| Secret empty in PR | Fork or untrusted trigger context | Use safer event design; do not expose secrets to untrusted code. |
Permission denied with GITHUB_TOKEN | Missing job/workflow permission | Add least-privilege permissions for that job. |
| Self-hosted job waiting | No online runner matches labels/group | Check runner labels, group access, and runner status. |
| Cache not restored | Key mismatch or path mismatch | Print key inputs; check lockfile hash and path. |
| Service container unreachable | Wrong hostname or port mapping | Use service label from job container; use mapped localhost port when running directly on runner. |
| Reusable workflow fails at parse time | Wrong call location or missing required input/secret | Call at jobs.<id>.uses and supply required values. |
| Matrix produced unexpected jobs | include/exclude logic mismatch | Expand the matrix mentally; verify added fields and exclusions. |
| Deployment waiting | Environment protection rule requires approval or wait | Check environment configuration and reviewers. |
Debugging techniques
- name: Debug selected contexts
run: |
echo "ref=${{ github.ref }}"
echo "event=${{ github.event_name }}"
echo "runner=${{ runner.os }}"
Use toJSON carefully:
- name: Debug event payload
env:
EVENT_JSON: ${{ toJSON(github.event) }}
run: echo "$EVENT_JSON"
Avoid dumping secrets or full contexts that may contain sensitive data.
High-yield exam traps
| Trap | Correct exam response |
|---|
Using pull_request_target to build untrusted PR code with secrets | Use pull_request for untrusted validation; reserve pull_request_target for safe base-context automation. |
Assuming GITHUB_TOKEN always has write permissions | Defaults can vary. Set permissions explicitly. |
| Using secrets in workflows triggered from forks | Secrets are restricted for untrusted fork contexts. Design around that boundary. |
| Confusing artifacts and caches | Artifacts are run outputs; caches are dependency/build acceleration. |
Calling reusable workflows inside steps | Reusable workflows are called as jobs. Actions are called as steps. |
| Expecting jobs to run sequentially by default | Jobs run in parallel unless connected with needs. |
| Forgetting that skipped filtered workflows can affect required checks | Branch protection may wait for checks that never run. |
| Storing non-secret configuration as secrets | Use vars for non-sensitive configuration. |
| Using mutable action references in sensitive pipelines | Pin actions to commit SHA when integrity matters. |
| Running untrusted code on self-hosted runners | Restrict runner access and isolate workloads. |
| Assuming environment secrets are available before approval | Protected environment jobs must pass protection rules first. |
Overusing always() | Prefer precise status checks; use !cancelled() when appropriate. |
Compact scenario review
| Requirement | Strong answer |
|---|
| Build, test, and lint every PR | pull_request workflow with matrix and read-only permissions. |
| Publish only from tags | push with tags filter and job condition for tag refs. |
| Deploy to production with manual approval | Environment protection plus deployment job. |
| Share standard CI across repositories | Reusable workflow with workflow_call. |
| Share five repeated shell steps inside one job | Composite action. |
| Authenticate to cloud without static cloud secret | OIDC with id-token: write and provider trust policy. |
| Speed up package installs | Cache package manager directory keyed by OS, tool version, and lockfile hash. |
| Pass build output to deploy job | Upload artifact in build; download artifact in deploy. |
| Stop overlapping deployments | Job-level concurrency keyed by environment. |
| Run database integration tests | Service container. |
| Target internal network for deployment | Self-hosted runner with restricted repository access. |
| Run safe labeling on fork PRs | pull_request_target with minimal permissions and no untrusted code execution. |
Final preparation checklist
- Can you choose the correct event for
push, pull_request, pull_request_target, workflow_dispatch, schedule, workflow_call, and workflow_run scenarios? - Can you explain why
pull_request_target is powerful and dangerous? - Can you write the difference between an action, a composite action, a Docker action, and a reusable workflow?
- Can you set least-privilege
permissions for GITHUB_TOKEN? - Can you distinguish
secrets, vars, env, inputs, matrix, needs, and steps contexts? - Can you pass data from step to step, job to job, and workflow to workflow?
- Can you choose between artifacts and caches?
- Can you select GitHub-hosted versus self-hosted runners for a scenario?
- Can you diagnose skipped workflows, pending checks, missing secrets, and permission errors?
- Can you secure deployments with environments, approvals, environment secrets, and OIDC?
For the next step, use this Quick Reference to drill scenario-based questions: read a workflow requirement, choose the trigger, permissions, runner type, reuse mechanism, and data-passing pattern before checking your answer.