Instiq
Chapter 1 · Author and Manage Workflows·v1.0.0·Updated 6/14/2026·~18 min

What's changed: New GH-200 Chapter 1 (triggers & events = push/pull_request/schedule/workflow_dispatch/repository_dispatch/workflow_call with inputs/secrets; workflow structure = jobs/steps/needs/if, env/$GITHUB_ENV, service containers, strategy.matrix, contexts/${{ }}, YAML anchors; execution mgmt = artifacts/$GITHUB_OUTPUT/cache/$GITHUB_STEP_SUMMARY/badges/environment protections/retention REST API)

1.2Workflow Structure, Matrix, Contexts, and Expressions

Key points

Understand jobs and steps, job dependencies (needs) and conditionals (if), choosing runners, workflow commands and environment variables, service containers, parallel job expansion via strategy/matrix (include/exclude, fail-fast, max-parallel), YAML anchors/aliases, and the github/env/matrix/needs contexts with ${{ }} expression evaluation.

A workflow has one or more jobs, and each job is a sequence of steps. A job selects its runner via runs-on (GitHub-hosted like ubuntu-latest, or self-hosted) and, by default, jobs run in parallel with each other. Steps run top to bottom, using run: for shell commands and uses: for actions. Crucially, each job runs in its own isolated (clean) virtual environment—so passing data between jobs requires explicit mechanisms (artifacts or outputs, Section 3).

1.2.1Dependencies (needs) and conditionals (if)

To run jobs in order, use needs to declare "job B runs after A completes" (multiple dependencies allowed). needs both serializes jobs and provides the path for a later job to read an earlier job’s outputs. Control whether something runs with an if expression—e.g., "deploy only on main," "always run even if a prior step failed (if: always())," or "notify only on failure (if: failure())." Knowing the status functions success(), failure(), cancelled(), and always() lets you build robust pipelines.

1.2.2Environment variables and workflow commands

Environment variables are defined with env: at the workflow, job, or step level, with narrower scopes taking precedence. To compute a value at runtime and pass it to later steps, write to the special file $GITHUB_ENV (e.g., echo "KEY=value" >> "$GITHUB_ENV")—it becomes an env var in subsequent steps. Likewise, workflow commands (e.g., echo "::notice::...") emit log annotations and control output or masking. Organization/repository/environment-level variables (vars) are read via the vars context for sharing non-secret configuration.

1.2.3Service containers

When tests need dependent services like databases or queues, use services: to spin up sidecar containers (PostgreSQL, Redis, etc.) only for the job’s duration. You can specify port mapping, health checks, and container options, and steps in the job connect via localhost:<port>. This runs integration tests reproducibly without provisioning an external test DB. Services are torn down when the job ends, keeping each job clean.

1.2.4Expanding jobs with strategy and matrix

strategy.matrix auto-expands one job definition into multiple variations. For example, OS (ubuntu-latest/windows-latest/macos-latest) × language version (Node 18/20/22) produces a parallel job per combination. Use include to add a specific combination (or attach values to existing ones) and exclude to drop unneeded ones. By default fail-fast: true cancels the rest when any variant fails; set it to false to run all combinations to completion and compare results. max-parallel caps concurrency to manage cost and runner capacity. Also watch runner image updates such as the Windows Server 2025 migration for windows-latest and the deprecation of ubuntu-20.04.

SettingMeaningWhen to use
includeAdd a combination / attach valuesAdd a special case only
excludeDrop unneeded combinationsRemove unsupported OS×version
fail-fasttrue = cancel rest / false = run allUse false to diagnose
max-parallelCap on concurrencyManage cost/capacity

1.2.5Contexts, ${{ }} expressions, and YAML anchors

Workflows access runtime metadata via contexts—event info in github (github.ref, github.event, etc.), env/vars/secrets/inputs, matrix values in matrix, dependency results in needs, plus runner/job/steps. Expressions are evaluated inside ${{ }} for if: and value interpolation. Mind the difference between expressions evaluated at workflow parse time (static) vs at runtime (dynamic), and never leak secrets into logs or expressions. Repeated YAML fragments can be reused within a single file via anchors (&) and aliases (*) and merge keys (<<)—note this is not cross-file sharing.

Exam point

Common: (1) jobs are parallel by default; order with needs (also the path to read prior outputs). (2) Conditionals use if + status functions success()/failure()/cancelled()/always(). (3) Pass a runtime value forward by appending to $GITHUB_ENV. (4) Dependent services like DBs = services (service containers) + health checks. (5) matrix with include/exclude, fail-fast (false runs all), max-parallel. (6) Evaluate expressions in ${{ }} with contexts github/env/matrix/needs; YAML anchors/aliases reuse within a single file.

Warning

Watch out: (1) Jobs are separate clean environments—variables/files are not auto-shared (pass explicitly via outputs/artifacts). (2) env: is environment variables; with: is inputs to an action/reusable workflow—different things. (3) fail-fast: true (default) cancels the rest on failure—use false if you want all results. (4) YAML anchors/aliases work only within one file—use a reusable workflow to share across workflows. (5) Don’t put secrets in echo or expressions (mind leak paths even when masked).

Diagram: event → jobs (parallel, needs) → steps → matrix expansion.
Jobs parallel; order via needs

1.2.6Section summary

  • Jobs select runners via runs-on and run in parallel by default; steps execute run:/uses: sequentially; jobs are isolated
  • Order = needs (+ reading prior outputs); conditions = if + success()/failure()/cancelled()/always()
  • env: for variables, $GITHUB_ENV to pass runtime values forward, services for dependent DBs/queues (health checks)
  • matrix expands OS×versions (include/exclude, fail-fast, max-parallel); ${{ }} expressions and contexts; YAML anchors within one file

Sign in to track progress — Log in.

Quick check

(just a quick review)

Q1. You want job deploy to run only after both build and test succeed. How do you express it?

Q2. You computed a value (e.g., a version number) in one step and want later steps in the same job to use it as an env var. Which is correct?

Q3. Your integration tests need PostgreSQL. Which GitHub Actions feature starts a DB only during the job, without provisioning an external one?

Q4. You test all OS×Node combinations, but one failure cancels the rest, making diagnosis hard. How do you run every combination to completion?

Q5. You want to reuse an identical group of steps within a single workflow file. Which YAML feature fits best?

Q6. You want a cleanup/notify job that always runs, even if a prior job failed, to report failures. Which function is appropriate in the if expression?

Check your understandingPractice questions for Chapter 1: Author and Manage Workflows