GitHub Actions
GitHub Actions automates CI/CD natively in GitHub. Master workflows, jobs and steps, hosted runners, expression evaluation, matrix builds, environment protection rules and reusable workflows to build production-grade pipelines.
What is this?
GitHub Actions is GitHub's CI/CD platform. Workflows are YAML files in .github/workflows triggered by events such as push or pull_request. Jobs run on hosted or self-hosted runners and are composed of steps that run shell commands or community actions. Expressions, matrix builds, environment protection rules and reusable workflows scale it from simple tests to full deployment automation.
Why do you need it?
- CI/CD lives beside the code, so checks and deployments are automatic for every push and PR.
- A huge marketplace of versioned actions composes into pipelines without writing boilerplate.
- Matrix builds verify one job across OSes, languages and versions in parallel.
- Environments add approval gates, protection rules and scoped secrets before production deploys.
- Reusable workflows turn common logic into callable, centrally maintained definitions.
Mental model
A factory floor with interchangeable tool stations
A GitHub Actions workflow
An event is the order that starts production. Each station (job/step) does one task; some stations run simultaneously (parallel jobs), some only after others finish (needs). The floor supervisor (GITHUB_TOKEN) grants access to materials (secrets) only where needed.
How it works
- An event (push, pull_request, schedule, workflow_dispatch) triggers a workflow file in .github/workflows.
- The workflow defines jobs; each job runs in a fresh environment on a runner such as ubuntu-latest.
- Each job runs steps sequentially — shell commands with run: or marketplace actions with uses:.
- Expressions like ${{ github.ref }} and ${{ secrets.X }} are evaluated before a step runs, and needs: links dependent jobs.
- A matrix strategy expands one job across combinations of os/version; the final status reports on the commit and PR.
A workflow run: event, runner, jobs, matrix, results.
Key commands
gh workflow listList all workflows in the repository.
gh run watch <run-id>Watch a workflow run until it completes.
gh run view --log-failedShow only the logs of failed steps in the latest run.
gh run rerun <run-id>Rerun a failed or partially completed run.
gh pr checksShow the status of all checks required on a pull request.
Examples
A matrix build with a gated deployment environment
name: CIon: push: branches: [main] pull_request: jobs: test: runs-on: ubuntu-latest strategy: matrix: node-version: [18, 20, 22] steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: ${{ matrix.node-version }} cache: npm - run: npm ci - run: npm test deploy: needs: test if: github.ref == 'refs/heads/main' runs-on: ubuntu-latest environment: production steps: - run: echo "Deploying with ${{ secrets.DEPLOY_TOKEN }}"The test job expands into three parallel runs across Node 18, 20 and 22, with npm cached. Deployment waits for all of them, runs only on main, and requires the production environment — so it obeys protection rules and uses production-scoped secrets.
In production
- CI gates every PR with lint, test and build jobs; required checks block merges.
- Main-branch workflows build, scan and deploy to staging; a release tag triggers production.
- Matrix builds verify compatibility across Node, Python and OS combinations before shipping.
- Scheduled workflows run dependency updates, security scanning and backups.
- Environment protection rules require approvals and deploy restrictions for production jobs.
Best practices
- Pin actions to a commit SHA (or a signed release tag), never to a floating branch.
- Cache dependency installs with actions/cache or the native cache input of setup actions.
- Grant GITHUB_TOKEN the minimum permissions for each job's scope.
- Split large workflows into small jobs joined by needs: so failures are cheap to isolate.
- Extract repeated logic into composite actions or reusable workflows instead of copying YAML.
Common mistakes
- Hardcoding credentials in workflow files instead of using encrypted secrets.
- Using unpinned action versions that change behavior without notice.
- Exposing secrets to pull requests from forks, where untrusted code could exfiltrate them.
- Building one monolithic workflow with every step, which is slow to iterate and hard to debug.
Common issues
File not found on the runnerA step expected the repository or a generated file but checkout ran after it, or it was in a different job.
Run actions/checkout@v4 as the first step, and use upload-artifact and download-artifact to pass files between jobs.
Workflow file is invalid / job failed at validationYAML syntax error, wrong indentation, or an unknown key.
Validate with a YAML parser and check the workflow's jobs section against the schema; GitHub shows a syntax check in the Actions tab.
403: secrets are not available to this jobThe job runs for a fork pull request, where secrets are deliberately withheld.
Use pull_request_target with extreme caution, or gate secret-using jobs to trusted contexts and approved branches.
Debugging tips
- gh run view --log-failed jumps straight to the failing step's output.
- Enable the ACTIONS_STEP_DEBUG repository secret to see masked values and expanded context.
- Validate YAML and expressions locally with yamllint and act before pushing.
- Rerun just the failed job (gh run rerun --failed) to avoid rerunning the whole pipeline.
Interview checkpoint
Hands-on lab
- Create a workflow file with a CI job
- Commit and push it to trigger a run
- Query workflow run status via the GitHub REST API
- Inspect run results without the gh CLI
Author and Observe a GitHub Actions Workflow
30 min · advanced
Quick quiz
Where must GitHub Actions workflow files be stored?
A matrix strategy runs a single job definition across multiple configured combinations in parallel.
How do you safely inject a production credential into a workflow?
Cheat sheet
on:Triggering eventsjobs:Top-level job definitionsruns-on:Runner image (ubuntu-latest etc.)needs:Declare job dependenciesuses: actions/checkout@v4Call an actionstrategy.matrixRun a job across combinationsenvironment:Gate with protection rules + secretsreusable workflowsworkflow_call to reuse YAMLactions/upload-artifactShare files between jobsif: github.ref == ...Conditional job execution