Skip to content

Latest commit

Β 

History

History
400 lines (305 loc) Β· 16 KB

File metadata and controls

400 lines (305 loc) Β· 16 KB

πŸ“Œ Lecture 3 β€” CI/CD: From it works on my machine to it works on every machine


πŸ“ Slide 1 – πŸ’₯ The Commit That Got Caught

  • πŸ—“οΈ Friday, 11:47 a.m. β€” a developer pushes a "small refactor" to QuickNotes
  • πŸ€– 90 seconds later, the CI pipeline flags a unit test failure plus a go vet warning
  • πŸ“© PR is auto-blocked. The developer fixes both before lunch
  • πŸͺ¦ Alternative timeline: no CI. The bug ships on Monday. Customer reports /notes returning empty arrays on Wednesday. Postmortem on Friday
  • πŸŽ“ Lesson: CI doesn't make engineers smarter. It makes the consequence of being wrong fast and cheap

πŸ€” Think: The same human, the same commit. One world catches it in 90 seconds. The other catches it in 7 days. What changed?


πŸ“ Slide 2 – 🎯 Learning Outcomes

# πŸŽ“ Outcome
1 βœ… Distinguish Continuous Integration, Delivery, and Deployment
2 βœ… Describe a four-stage pipeline: trigger β†’ build β†’ test β†’ publish
3 βœ… Write a GitHub Actions workflow for a Go project
4 βœ… Write the equivalent GitLab CI pipeline
5 βœ… Use caching, matrix builds, and reusable workflows safely
6 βœ… Identify common CI supply-chain risks (tj-actions, secret leaks)

πŸ“ Slide 3 – πŸ—ΊοΈ Lecture Overview

graph LR
    A["πŸ“œ CI History<br/>Fowler 2006"] --> B["🎯 CI vs CD vs CD"]
    B --> C["πŸ› οΈ Pipeline Shape"]
    C --> D["πŸ™ GitHub Actions"]
    D --> E["🦊 GitLab CI"]
    E --> F["πŸ”’ Secrets & OIDC"]
    F --> G["πŸ’₯ Real Incidents"]
Loading
  • πŸ“ Slides 1-5 β€” History and what CI/CD actually means
  • πŸ“ Slides 6-11 β€” GitHub Actions in detail
  • πŸ“ Slides 12-15 β€” GitLab CI in detail, and a comparison
  • πŸ“ Slides 16-19 β€” Caching, matrices, secrets, OIDC
  • πŸ“ Slides 20-22 β€” Real incidents and what Lab 3 asks of you

πŸ“ Slide 4 – πŸ“œ The Origin of CI

  • πŸ—οΈ 1991 β€” Grady Booch coins "Continuous Integration" in Object-Oriented Design with Applications
  • 🟒 2000 β€” Kent Beck makes it a practice of Extreme Programming: integrate at least daily, automated tests
  • πŸ“ 2006 β€” Martin Fowler publishes the canonical Continuous Integration article β€” still the best 20-minute read on the topic
  • πŸ“š 2010 β€” Jez Humble & Dave Farley publish Continuous Delivery (winner, Jolt Award)
  • πŸ€– 2011-2018 β€” Travis CI, CircleCI, Jenkins X, then GitHub Actions (Nov 2019, GA Feb 2020) and modern GitLab CI/CD (2012, integrated since)

πŸ’¬ "If it hurts, do it more often, and bring the pain forward." β€” Jez Humble


πŸ“ Slide 5 – 🎯 CI vs CD vs CD

Acronym What it means Trigger Risk per change
CI β€” Continuous Integration Merge changes to mainline frequently; auto-build + test on each merge git push Low β€” only build
CD β€” Continuous Delivery Every CI-passing build is deployable (manual click to ship) Same β€” human approval Medium β€” gated
CD β€” Continuous Deployment Every CI-passing build is automatically shipped Same β€” no human High β€” full automation
graph LR
    M["πŸ‘¨β€πŸ’» Merge"] --> CI["βœ… CI passes"]
    CI -- "manual approve" --> Del["πŸš€ Delivery"]
    CI -- "no approval" --> Dep["πŸ€– Deployment"]
Loading
  • 🎯 This course practices CI + Delivery. Continuous Deployment shows up in SRE-Intro
  • πŸͺͺ The word "CD" is overloaded β€” always clarify Delivery or Deployment

πŸ“ Slide 6 – πŸ› οΈ The Shape of Every Pipeline

graph LR
    T["πŸ”” Trigger<br/>(push, PR, tag, schedule)"] --> B["πŸ”¨ Build<br/>(compile, package)"]
    B --> Te["πŸ§ͺ Test<br/>(unit, integration, lint)"]
    Te --> S["πŸ” Scan<br/>(deps, image, secrets)"]
    S --> P["πŸ“¦ Publish<br/>(artifact, image, release)"]
    P --> D["πŸš€ Deploy<br/>(staging, prod)"]
Loading
  • πŸ”” Trigger β€” what starts it (a git push, a PR, a tag, a cron, a manual click)
  • πŸ”¨ Build β€” produce the artifact (Go binary, container image, JAR)
  • πŸ§ͺ Test β€” fail fast (run unit tests in parallel; integration tests in a service-up step)
  • πŸ” Scan β€” Trivy on the image, gitleaks on the diff (Lab 9)
  • πŸ“¦ Publish β€” push to a registry, attach to a release, tag
  • πŸš€ Deploy β€” into staging always; prod with approval

πŸ“ Slide 7 – πŸ™ GitHub Actions Anatomy

# .github/workflows/ci.yml
name: ci
on:
  push:        { branches: [main] }
  pull_request: { branches: [main] }

permissions:
  contents: read           # βœ… least privilege

jobs:
  test:
    runs-on: ubuntu-24.04
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-go@v5
        with: { go-version: '1.24', cache: true }
      - run: go vet ./...
      - run: go test -race ./...
  • 🧱 A workflow = a YAML file in .github/workflows/
  • ▢️ A job = a set of steps run on one runner
  • πŸͺœ A step = one shell command or one action (uses:)
  • πŸ” Workflows are triggered by events (on:)

πŸ“ Slide 8 – πŸ™ GitHub Actions: Events & Runners

Event When it fires Common use
push Any push to a branch Build + test
pull_request PR opened/synced Block merge if it fails
release New release published Build & upload artifacts
schedule Cron Nightly scans, dependency bumps
workflow_dispatch Manual click One-off jobs
Runner Where Notes
ubuntu-24.04 GitHub-hosted Fresh VM per job; free minutes per plan
ubuntu-latest GitHub-hosted Always points at current LTS; don't pin to this in 2026 if you need stability
Self-hosted Your hardware Persistent disks, GPUs, ARM β€” but you maintain them

⚠️ Pin runner versions (ubuntu-24.04, not ubuntu-latest) β€” GitHub's auto-upgrade has bitten everyone at least once.


πŸ“ Slide 9 – 🦊 GitLab CI Anatomy

# .gitlab-ci.yml
stages: [test, scan, publish]

variables:
  GO_VERSION: "1.24"

test:
  stage: test
  image: golang:${GO_VERSION}-alpine
  script:
    - go vet ./...
    - go test -race ./...

scan:
  stage: scan
  image: aquasec/trivy:0.59.1
  script:
    - trivy fs --severity HIGH,CRITICAL .
  • 🧱 One file: .gitlab-ci.yml at repo root
  • 🎬 Stages run sequentially; jobs within a stage run in parallel
  • 🐳 Each job runs in a container image β€” no need for actions/setup-X
  • πŸƒ Runners can be GitLab-hosted (saas-linux-*) or self-hosted

πŸ“ Slide 10 – πŸ™ vs 🦊 GitLab CI vs GitHub Actions

Capability GitHub Actions GitLab CI
Config location .github/workflows/*.yml .gitlab-ci.yml (or includes)
Reuse Composite + reusable workflows extends: + include:
Secrets Repo / Org / Env secrets Project / Group / Instance vars
Cloud OIDC βœ… first-class βœ… first-class
Self-hosted runners βœ… βœ…
Free minutes Yes, generous on public repos Yes, similar tier
Manual approval environments + reviewers when: manual + protected env
  • 🟰 Feature parity is high β€” concepts transfer in both directions
  • 🚫 If you can't use GitHub (sanctions, banned account), the same ci.yml ports to .gitlab-ci.yml in an afternoon β€” Lab 3 Bonus asks you to do exactly this

πŸ“ Slide 11 – πŸš€ Matrix Builds: Same Test, Many Versions

jobs:
  test:
    runs-on: ${{ matrix.os }}
    strategy:
      fail-fast: false           # βœ… run them all even if one fails
      matrix:
        os: [ubuntu-24.04, macos-14, windows-2022]
        go: ['1.23', '1.24']
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-go@v5
        with: { go-version: ${{ matrix.go }} }
      - run: go test ./...
  • πŸ§ͺ Same workflow, 6 combinations β†’ 6 parallel jobs
  • πŸ” Catches "works on my machine" β€” and which machine
  • ⚠️ Cost: 6Γ— the runner minutes. Use it for projects, not for every PR

πŸ“ Slide 12 – πŸ’Ύ Caching: From 4 Minutes to 30 Seconds

- uses: actions/setup-go@v5
  with:
    go-version: '1.24'
    cache: true       # βœ… caches $GOMODCACHE and $GOCACHE keyed by go.sum
# manual cache (e.g., Python venv, node_modules)
- uses: actions/cache@v4
  with:
    path: ~/.cache/pip
    key: ${{ runner.os }}-pip-${{ hashFiles('requirements.txt') }}
  • πŸͺ€ Cache inputs (downloaded modules), never outputs (your build artifacts β€” those should be reproducible from inputs)
  • ⚠️ A cache poisoned by a malicious PR is a real attack vector β€” GitHub now restricts cache writes to the default branch

πŸ“ Slide 13 – πŸ” Secrets: How Not to Leak Them

βœ… Do ❌ Don't
Use repo / org / environment secrets Hard-code in .yml
Scope by environment (production requires manual approval) Reuse one "deploy" secret everywhere
Use OIDC to obtain short-lived cloud credentials at runtime Store long-lived AWS keys as secrets
Audit secret access in the workflow run log Echo secrets in run: lines
  • πŸͺͺ OIDC (OpenID Connect) lets your workflow exchange a GitHub-issued token for a temporary AWS/GCP/Azure credential β€” no long-lived keys at rest
  • πŸ” Secret scanning (Lab 9) finds leaks already in history
  • 🚫 GitHub Actions redacts known secrets from logs β€” but redaction is best-effort, not perfect

πŸ“ Slide 14 – ☁️ OIDC in One Picture

sequenceDiagram
    participant W as πŸ€– Workflow
    participant G as πŸ™ GitHub OIDC IDP
    participant A as ☁️ AWS STS
    W->>G: requestToken(audience=sts.amazonaws.com)
    G-->>W: signed JWT (sub=repo:org/proj:ref:main)
    W->>A: AssumeRoleWithWebIdentity(JWT)
    A-->>W: temp creds (15 min)
    W->>A: aws s3 cp ...
Loading
  • πŸ†” The JWT contains repo, branch, event, actor β€” your IAM trust policy can pin on any of these
  • ⏳ Credentials are typically 15 minutes β€” a stolen one is near-worthless
  • βœ… This is how Lab 10 will push images and deploy to Cloud Run without committing a key

πŸ“ Slide 15 – 🧰 Reusable Workflows & Composite Actions

Approach Use it when Where it lives
Reusable workflow (workflow_call) You want to share whole pipelines across many repos .github/workflows/build.yml
Composite action You want to share a sequence of steps (no separate runner) .github/actions/setup-go/action.yml
include: (GitLab) Same idea: pull config from another repo / file .gitlab-ci.yml include:
  • βœ… DRY pays off after ~3 repos; for one project, inline is fine
  • πŸ›‘οΈ Pin reusable workflows by SHA, not by branch β€” see next slide

πŸ“ Slide 16 – πŸ›‘οΈ Supply Chain: Pin Actions by SHA

# ❌ BAD β€” moving target
- uses: actions/checkout@v4

# βœ… BETTER β€” pinned tag (still mutable!)
- uses: actions/checkout@v4.2.2

# βœ… BEST β€” pinned commit SHA (immutable)
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11  # v4.2.2
  • πŸͺ€ Tags are mutable β€” anyone with push to the action's repo can move v4 to malicious code
  • πŸ’₯ In March 2025, the popular tj-actions/changed-files action was compromised; the attacker rewrote all tags to a malicious version, leaking secrets from thousands of public CI runs
  • πŸ›‘οΈ Tools: GitHub's dependabot bumps SHAs; pinact auto-pins your workflow files

πŸ’¬ "If you trust a tag, you trust its maintainer's GitHub account forever."


πŸ“ Slide 17 – ⚑ Build Speed Antipatterns

πŸ”₯ Antipattern βœ… Fix
One job that does build + test + scan + deploy Split into stages; parallel where possible
No cache β†’ re-download deps every run Use setup-X cache: true, or actions/cache@v4
Tests serial: 18-min run go test -p N, pytest -n auto, matrix
Building Docker image without layer cache docker buildx build --cache-from --cache-to
30-minute integration test as PR gate Move to nightly; PR gate runs unit only
One mega-workflow, 90% no-op for most PRs Path filters: on.push.paths: ['app/**']

πŸ“ Slide 18 – πŸ“œ Real Story: A Build So Brittle It Quit

  • πŸ—“οΈ 2014-2018 β€” many startups stand up Jenkins, then realize the team spends more time fixing the build server than writing code
  • πŸͺ¦ Jenkins on a single VM with no backups, plugins upgraded ad-hoc, secrets in environment variables, deploy scripts maintained by "the one engineer who knows it"
  • πŸ’₯ Single point of failure for every release. When Jenkins is down, the company can't ship
  • πŸš€ Hosted CI (GitHub Actions, GitLab.com, CircleCI) eliminated the "CI as a service we host" problem for most teams from ~2020 onward
  • πŸ€” But hosted CI has a new failure mode: a global outage means the whole industry stops shipping at once (GitHub Actions, October 2025)

πŸ“ Slide 19 – πŸ§ͺ Lab 3 Preview: CI for QuickNotes

You'll write ci.yml (and a .gitlab-ci.yml mirror for the Bonus):

jobs:
  test:
    runs-on: ubuntu-24.04
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-go@v5
        with: { go-version: '1.24', cache: true }
      - run: go vet ./...
      - run: go test -race -count=1 ./...

  lint:
    runs-on: ubuntu-24.04
    steps:
      - uses: actions/checkout@v4
      - uses: golangci/golangci-lint-action@v6
        with: { version: 'v2.5.0' }
  • 🟒 PR-gated: every PR runs go vet, go test, golangci-lint
  • 🚫 PR is auto-blocked if any fail (branch protection)
  • 🦊 Bonus: same idea as .gitlab-ci.yml β€” for students using GitLab instead

πŸ“ Slide 20 – 🧠 Key Takeaways

  1. 🎯 CI catches what humans miss β€” by making the consequence of a typo fast and cheap
  2. πŸ› οΈ The four-stage pipeline shape β€” trigger β†’ build β†’ test β†’ publish β€” is universal
  3. πŸ™ GitHub Actions and 🦊 GitLab CI are conceptually equivalent β€” feature parity is high
  4. πŸͺͺ OIDC kills the "long-lived cloud keys as secrets" antipattern
  5. πŸ›‘οΈ Pin actions by SHA β€” tags move, and tj-actions/changed-files proved it
  6. 🚦 The same lab works on either platform β€” the discipline is what counts

πŸ’¬ "Continuous Integration doesn't get rid of bugs, but it does make them dramatically easier to find and remove." β€” Martin Fowler


πŸ“ Slide 21 – πŸš€ What's Next + πŸ“š Resources

graph LR
    P["🌳 Week 2<br/>Git Internals"] --> Y["πŸ“ You Are Here<br/>CI/CD"]
    Y --> N["πŸ’» Week 4<br/>OS & Networking"]
    N --> M["πŸ“¦ Week 5<br/>Virtualization"]
Loading

🎯 Remember: CI/CD is a culture, not a tool. The pipeline is just where the culture's discipline becomes executable.