CI verifies changes on pull requests; release workflows should operate from trusted code and immutable commit identities.
Course navigationPhase 33⌄
CI/CD with GitHub Actions
Build a trustworthy GitHub Actions pipeline that verifies pull requests, creates immutable release artifacts, uses least privilege, caches safely, and controls deployment concurrency.
Phase outcome and project increment
Learning outcome
Build a trustworthy GitHub Actions pipeline that verifies pull requests, creates immutable release artifacts, uses least privilege, caches safely, and controls deployment concurrency.
Concrete deliverable
.github/workflows/ci.yml plus release/deploy workflows that build once, tag by commit SHA, and gate promotion.
Principles to keep while coding
Use frozen lockfile installs so CI fails when dependency manifests and the lockfile disagree.
Give GitHub Actions tokens the minimum permissions each job requires.
Build once and promote the same artifact instead of rebuilding different binaries for each environment.
Guided phase lab
Do these in order. Each step should end with evidence before the next one starts.
- 01
Create CI triggered by pull_request with read-only contents permission and explicit concurrency cancellation.
- 02
Set up Node and pnpm, then install with --frozen-lockfile so manifests cannot drift from the lockfile.
- 03
Run lint/typecheck/tests/build as separate clear steps or jobs with understandable failure ownership.
- 04
Add dependency/package cache only after correctness works; key it from lockfile inputs.
- 05
Create trusted-main release workflow that tags an image/artifact with the commit SHA rather than latest only.
- 06
Add environment protection/permissions for deployment and ensure stale production runs are not silently canceled mid-release.
Failures you should know how to diagnose
Using pull_request_target with untrusted checkout/secrets
Predict the symptom, capture evidence at the nearest boundary, change one variable, and verify the same reproduction after the fix.
Broad write permissions for all jobs
Predict the symptom, capture evidence at the nearest boundary, change one variable, and verify the same reproduction after the fix.
Rebuilding different artifacts in each environment
Predict the symptom, capture evidence at the nearest boundary, change one variable, and verify the same reproduction after the fix.
CI green because important workspace checks never ran
Predict the symptom, capture evidence at the nearest boundary, change one variable, and verify the same reproduction after the fix.
Lessons
The source chapters come first. Every phase now ends with a guided build, a debugging workshop, and an independent challenge so reading turns into transferable skill.
CI/CD Mental Model and Pipeline Architecture
CI/CD Mental Model and Pipeline Architecture Continuous Integration answers: can this change safely join the main branch? Continuous Delivery answers: do we have a verified artifact that cou
GitHub Actions Workflow Triggers Permissions and Concurrency
Workflow Triggers, Permissions, and Concurrency Use pull_request for untrusted PR verification and push/main for trusted release work. Give GITHUB_TOKEN the minimum permissions required. Con
Node pnpm Monorepo Cache and Deterministic Install
Node, pnpm, Monorepo Cache, and Deterministic Install CI reproduces the repository using the committed lockfile. pnpm install --frozen-lockfile should fail if package manifests and the lockf
Quality Gates Lint Typecheck Test and Build
Quality Gates: Lint, Typecheck, Test, and Build A pull request should fail before merge when static analysis, typechecking, tests, or a production build fails. These checks answer different
PostgreSQL Service Containers and Prisma Migrations in CI
PostgreSQL Service Containers and Prisma Migrations in CI Repository and HTTP integration tests need a real PostgreSQL database. GitHub Actions service containers provide an isolated test da
Test Matrices Artifacts and Failure Diagnostics
Test Matrices, Artifacts, and Failure Diagnostics Artifacts should help diagnose failed or expensive jobs, not become an uncontrolled data dump. Upload browser traces, coverage, release mani
Docker Buildx Cache and Immutable Image Tags
Docker Buildx, Cache, and Immutable Image Tags Phase 32 created reproducible images. Phase 33 builds them in CI using Buildx and GitHub Actions cache. Every release image receives an immutab
GHCR GITHUB TOKEN and Release Manifests
GHCR, GITHUB_TOKEN, and Release Manifests GitHub Container Registry can be authenticated with the workflow-scoped GITHUB_TOKEN. Only the image-publishing job needs packages: write. A release
Secrets OIDC and Protected Environments
Secrets, OIDC, and Protected Environments Long-lived cloud keys in repository secrets are avoidable on providers that support GitHub OIDC. The deployment job requests a short-lived identity
Supply Chain Hardening Action Pinning and Dependabot
Supply-Chain Hardening: Action Pinning and Dependabot A GitHub Action executes code inside your workflow. GitHub recommends full-length commit SHA pinning as the immutable reference. The rea
Branch Protection Release Gates and Rollback Friendly Design
Branch Protection, Release Gates, and Rollback-Friendly Design CI becomes meaningful when protected branches require its checks before merge. Release and deployment pipelines should serializ
Hands On Lab and Phase Exit
Hands-On Lab and Phase Exit Implement CI, integration testing, and release-image workflows in a branch. Break each gate deliberately and prove it fails for the expected reason. Then restore
Guided Build Lab
Purpose Build a trustworthy GitHub Actions pipeline that verifies pull requests, creates immutable release artifacts, uses least privilege, caches safely, and controls deployment concurrency
Debugging Workshop
Goal Learn to diagnose this phase's failures systematically instead of reinstalling packages, changing random configuration, or copying a fix without understanding it.
Phase Synthesis & Independent Challenge
Mission Rebuild the capability of Phase 33 without following the exact code line-by-line. The goal is transfer: can you use the ideas when the variable names, UI, or route shape changes?
Mastery Capstone Lab
Integrate Phase 33 without following a transcript.
Exercises that increase independence
Create CI triggered by pull_request with read-only contents permission and explicit concurrency cancellation.
Run lint/typecheck/tests/build as separate clear steps or jobs with understandable failure ownership.
reproduce and diagnose — Using pull_request_target with untrusted checkout/secrets.
deliver .github/workflows/ci.yml plus release/deploy workflows that build once, tag by commit SHA, and gate promotion.
Can read/write a GitHub Actions workflow and Can explain trigger trust boundaries.
Do not leave the phase only because you reached the bottom
Phase exit criteria
Scenario and concept quiz
These questions test engineering choices rather than whether you remember a lesson title.
Reference files
Attempt the work first. Use snapshots to compare a checkpoint, not as a substitute for understanding.
reference/TaskFlow_GitHub_Actions_Snapshot/.github/dependabot.yml
version: 2
updates:
- package-ecosystem: npm
directory: /
schedule:
interval: weekly
- package-ecosystem: github-actions
directory: /
schedule:
interval: weeklyreference/TaskFlow_GitHub_Actions_Snapshot/.github/workflows/ci.yml
name: CI
on:
pull_request:
branches: [main]
push:
branches: [main]
permissions:
contents: read
concurrency:
group: ci-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
verify:
runs-on: ubuntu-latest
timeout-minutes: 20
reference/TaskFlow_GitHub_Actions_Snapshot/.github/workflows/deployment-gate.example.yml
name: Deployment Gate Example
on:
workflow_dispatch:
inputs:
image_sha:
description: Full commit SHA already published by release-images
required: true
permissions:
contents: read
id-token: write
concurrency:
group: production
cancel-in-progress: false
jobs:
gate:
runs-on: ubuntu-latestreference/TaskFlow_GitHub_Actions_Snapshot/.github/workflows/integration.yml
name: Integration Tests
on:
pull_request:
branches: [main]
push:
branches: [main]
permissions:
contents: read
jobs:
api-integration:
runs-on: ubuntu-latest
timeout-minutes: 25
services:
postgres:
image: postgres:18
env:reference/TaskFlow_GitHub_Actions_Snapshot/.github/workflows/release-images.yml
name: Build Release Images
on:
push:
branches: [main]
workflow_dispatch:
permissions:
contents: read
packages: write
concurrency:
group: release-images-${{ github.ref }}
cancel-in-progress: false
jobs:
images:
runs-on: ubuntu-latest
timeout-minutes: 30
reference/TaskFlow_GitHub_Actions_Snapshot/CI_CD_ARCHITECTURE.md
# TaskFlow CI/CD Architecture Pull request -> CI + integration tests -> merge protection -> main -> build immutable web/api images -> GHCR -> protected environment -> Phase 34 deployment. Rules: 1. CI proves source. 2. Release builds one immutable artifact per commit. 3. Deployment promotes an already-built artifact; it does not rebuild source. 4. Production environment may require approval and exposes secrets only after protection rules pass. 5. Prefer OIDC to long-lived cloud credentials. 6. `GITHUB_TOKEN` gets only the permissions required by each workflow/job.
reference/TaskFlow_GitHub_Actions_Snapshot/README.md
# TaskFlow GitHub Actions Snapshot This snapshot is educational and intentionally stops before actual cloud deployment. Phase 33 produces tested, immutable container candidates. Phase 34 connects those candidates to the selected hosting platform. Workflow examples use current major action tags for readability. GitHub's security guidance recommends full-length commit SHA pinning for immutable third-party action references in hardened production repositories.
reference/TaskFlow_GitHub_Actions_Snapshot/SOURCES.md
# Sources used for Phase 33 - GitHub Docs: PostgreSQL service containers - GitHub Docs: Secure use reference - GitHub Docs: workflow syntax and GITHUB_TOKEN permissions - GitHub Docs: OpenID Connect reference - GitHub Docs: deployments, environments, concurrency - Official action release pages for checkout, setup-node, upload-artifact - Official Docker action release pages for setup-buildx, login, metadata, build-push - pnpm/action-setup official releases
reference/TaskFlow_GitHub_Actions_Snapshot/VERSION_LOCK.md
# Phase 33 verified action lines (2026-09-18) - actions/checkout: v7 line (latest observed v7.0.1) - actions/setup-node: v7 line (latest observed v7.0.0) - pnpm/action-setup: v6 line (latest observed v6.0.9) - actions/upload-artifact: v7 line (latest observed v7.0.1) - docker/setup-buildx-action: v4 line (latest observed v4.3.0) - docker/login-action: v4 line (latest observed v4.6.0) - docker/metadata-action: v6 line (latest observed v6.2.0) - docker/build-push-action: v7 line (latest observed v7.2.0) For production hardening, pin third-party actions to verified full commit SHAs and let Dependabot maintain them. The workflow examples use major tags for readability.
reference/TaskFlow_GitHub_Actions_Snapshot/tests/ci-cd-test-matrix.md
# Phase 33 verification matrix - PR lint failure blocks CI. - Type error blocks CI. - Unit test failure blocks CI. - PostgreSQL integration test uses committed migrations. - Missing migration causes integration failure rather than schema drift. - Docker build succeeds for web and API. - Release image is tagged with full commit SHA. - Release workflow has `packages: write` but CI does not. - Production deployment concurrency prevents overlapping releases. - Production environment can require approval before secrets are exposed. - Workflow action dependencies are reviewed/pinned for production. - Secrets are never echoed. - Fork PRs do not receive production secrets. - OIDC-based cloud auth is preferred over static cloud access keys.