After this lesson, you will be able to: Write multi-job GitHub Actions workflows with matrix builds, dependency caching, secrets, and deploy steps.
GitHub Actions is the default CI/CD platform for GitHub repos. This lesson takes you from 'I once ran a hello-world workflow' to 'I can ship a production-grade pipeline'.
A workflow is a YAML file in `.github/workflows/`. It has: triggers (when to run), jobs (parallel or dependent units of work), and steps (commands or reusable actions). Each job runs in a fresh ephemeral VM (the 'runner'). Jobs in the same workflow can share dependencies via the `needs` keyword.
Drop this in .github/workflows/ci.yml. It handles lint + typecheck + test + build with caching.
name: CIon:pull_request:push:branches: [main]concurrency:group: ci-${{ github.ref }}cancel-in-progress: truejobs:lint-typecheck:runs-on: ubuntu-lateststeps:- uses: actions/checkout@v4- uses: actions/setup-node@v4with:node-version: 20cache: npm- run: npm ci- run: npm run lint- run: npx tsc --noEmittest:runs-on: ubuntu-latestneeds: lint-typecheckstrategy:matrix:node: [18, 20, 22]steps:- uses: actions/checkout@v4- uses: actions/setup-node@v4with:node-version: ${{ matrix.node }}cache: npm- run: npm ci- run: npm test -- --runbuild:runs-on: ubuntu-latestneeds: teststeps:- uses: actions/checkout@v4- uses: actions/setup-node@v4with:node-version: 20cache: npm- run: npm ci- run: npm run build
Matrix lets one job spec spawn N parallel runs across versions of a dimension (Node 18 / 20 / 22, or OS ubuntu / macos / windows, or both). Catches version-specific bugs your dev machine misses. Costs more minutes; balance breadth against budget (matrix only on PRs, run a single version on push?).
`actions/setup-node` with `cache: npm` caches node_modules between runs based on package-lock.json hash. For other languages: `actions/cache` is the generic primitive. Cache pip / Maven / Gradle / Cargo / your own build output. Without caching, every CI run reinstalls 200MB+ of dependencies; with caching, the install step is near-zero.
Store secrets in Settings > Secrets and variables > Actions. Never hardcode in YAML.
# .github/workflows/deploy.ymlname: Deploy to Vercelon:push:branches: [main]jobs:deploy:runs-on: ubuntu-latestenvironment:name: productionurl: https://your-app.vercel.appsteps:- uses: actions/checkout@v4- uses: actions/setup-node@v4with: { node-version: 20, cache: npm }- run: npm ci- name: Deployrun: npx vercel deploy --prod --token=${{ secrets.VERCEL_TOKEN }}env:VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}# 'environment: production' adds a manual approval step in GitHub if you# configure 'Required reviewers' under Settings > Environments. Use this# for any prod deploy where a human should rubber-stamp.
Echoing secrets to logs. Use `${{ secrets.X }}` directly in env; never `echo $X`. GitHub masks secrets but only if it sees them named. Skipping `concurrency:` to cancel in-progress runs on new pushes. Wasted minutes are the silent budget killer. Reinventing actions instead of using marketplace ones (actions/checkout, actions/setup-node, etc.). Vetted, fast, free. Workflows that take 20 minutes. Cache aggressively, parallelize jobs, split large tests. Forgetting to set `permissions:` explicitly on workflows that need write access. Default permissions changed in 2023; explicit is safer.
Pick the most impactful single change.
Sign in and purchase access to unlock this lesson.