Skip to content

Chapter 2 of 7

GitHub Actions in Depth

GitHub Actions is a CI/CD platform built directly into GitHub, where every push, pull request, or scheduled event can trigger an automated workflow defined in YAML under `.github/workflows/`. A workflow is composed of one or more jobs, each of which is an independent unit of work that runs on a designated runner. By default, jobs run in parallel, but the `needs` keyword creates explicit dependencies so that, for example, a deploy job can wait for both build and test to complete first. Inside each job, steps execute sequentially and either run a shell command via `run:` or invoke a reusable action via `uses:`. Reusable actions can come from the GitHub Marketplace or from your own repositories, and a composite action lets you package several shell steps into a single reusable unit. The distinction between composite actions and reusable workflows matters: composites are step-level reuse, while reusable workflows invoked through `workflow_call` are whole-workflow reuse with their own jobs.

The runner is the server that actually executes a job. GitHub-hosted runners are managed virtual machines that are provisioned fresh for each job, available on Ubuntu, Windows, and macOS, and selected through the `runs-on:` key. Self-hosted runners are machines you operate yourself, giving you full control over the execution environment; they are valuable for specialized hardware, private network access, GPU workloads, or cost optimization at scale. Either flavor supports ephemeral mode — a runner that handles one job and then unregisters — which improves isolation. When maintaining self-hosted runners, the safe drain procedure is to remove the runner from the repository or organization, wait for any in-flight job to complete, and only then stop the runner service to avoid leaving a ghost entry in the UI.

Matrix builds amplify the value of a single job definition by running it across multiple combinations of variables such as operating system and language version. The `strategy.matrix` keyword defines those combinations, and GitHub automatically creates one job per cell. To control cost and feedback speed, `fail-fast: true` cancels pending matrix jobs when any one fails, while `max-parallel:` caps how many run concurrently. For monorepos or cases where the matrix depends on repository state, a dynamic matrix can be computed at runtime by emitting the matrix as an output from a setup job and consuming it in a downstream job. This pattern lets you build only the targets affected by a particular change.

Workflows respond to a rich set of events. `push` and `pull_request` are the daily drivers; `pull_request` runs in an isolated concurrency group so it does not affect a production deployment in flight. `workflow_dispatch` lets a human trigger the workflow from the GitHub UI, CLI, or REST API, optionally with typed input parameters, while `schedule` runs on a cron expression with a minimum interval of five minutes. `workflow_run` triggers one workflow after another finishes, enabling clean separation of CI and CD responsibilities with different permissions and approvers. For security, `pull_request_target` must be handled with care because it runs in the context of the base branch with access to secrets — any workflow that uses it must never echo attacker-controlled content. Path filters (`on.push.paths`) restrict runs to changes in specific directories, saving CI minutes in monorepos.

A few cross-cutting concepts give Actions its flexibility. Concurrency groups ensure that only one in-flight run per branch or workflow exists at a time, with `cancel-in-progress: true` aborting older superseded runs. `continue-on-error: true` allows a step or job to fail without halting the pipeline, useful for surfacing warnings without blocking merges. `timeout-minutes` enforces an upper bound on a job's wall-clock duration with a default of 360 minutes, and the workflow-run timeout is configurable up to 43,200 minutes as well. The automatically provisioned `secrets.GITHUB_TOKEN` carries the least-privilege scopes you grant in a `permissions:` block, and OIDC lets Actions assume short-lived cloud IAM roles without storing long-lived keys. Service containers provide sidecar dependencies such as a database for integration tests, and actions like `actions/checkout@v4` clone the repository with options like `fetch-depth: 0` for full git history when needed.

All chapters
  1. 1Foundations of CI/CD
  2. 2GitHub Actions in Depth
  3. 3Jenkins and Alternative CI/CD Platforms
  4. 4Pipeline Internals, Caching, and Configuration
  5. 5Testing, Code Quality, and Security Scanning
  6. 6Deployment Strategies and Progressive Delivery
  7. 7Supply Chain Security and Engineering Excellence

Drill it

Reading is not remembering. These come from the Cicd Pipelines deck:

Q

What is Continuous Integration (CI)?

Continuous Integration is a development practice where developers frequently merge code changes into a shared repository, often multiple times a day. Each merge...

Q

What is the difference between Continuous Delivery and Continuous Deployment?

Continuous Delivery ensures code is always in a deployable state but requires a manual approval step before production release.Continuous Deployment goes furthe...

Q

What are the typical stages of a CI/CD pipeline?

A standard CI/CD pipeline includes these stages:Source – code checkout from version controlBuild – compile code and resolve dependenciesTest – run unit, integra...

Q

What is a GitHub Actions workflow?

A GitHub Actions workflow is an automated process defined in a YAML file under .github/workflows/. It is triggered by events such as push, pull_request, or sche...