engineering-handbook/cicd.md
Adam Moussa 132e4fe51d
Document README badges, repo topics, and PR auto-labeler conventions (INFRA-56/57/70) (#14)
Capture the org conventions rolled out in the INFRA-47 hygiene pass:
- github-standards.md: static-only README badges (dynamic shields break on
  private repos; CI badge is member-only) and a lowercase-hyphenated repo
  topic vocabulary, both part of new-repo provisioning.
- cicd.md: the central inline-config reusable PR labeler — pull_request
  trigger, the three required caller permissions, no per-repo labeler.yml.
2026-06-11 14:25:12 -04:00

3.8 KiB

CI/CD Pipelines

Requirement

Every deployable repo must have a CI/CD pipeline. No manual deploys to production. If it deploys to AWS, it needs a pipeline.

Platform

GitHub Actions is the standard CI/CD platform. All pipelines use reusable workflows from the Sea-Haven-Industries/.github org repo (.github/workflows/).

Workflow Structure

Every repo gets two thin workflow files in .github/workflows/:

File Trigger Purpose
ci.yaml pull_request on main Lint, typecheck, test, synth/validate
deploy.yaml push on main Deploy to AWS

CDK Stacks (TypeScript)

# .github/workflows/ci.yaml
name: CI
on:
  pull_request:
    branches: [main]
jobs:
  ci:
    uses: Sea-Haven-Industries/.github/.github/workflows/ci-typescript-cdk.yaml@main
    with:
      node-version: "24"

# .github/workflows/deploy.yaml
name: Deploy
on:
  push:
    branches: [main]
jobs:
  deploy:
    uses: Sea-Haven-Industries/.github/.github/workflows/cd-cdk.yaml@main
    with:
      node-version: "24"
    secrets:
      deploy-role-arn: ${{ secrets.AWS_DEPLOY_ROLE_ARN }}

SAM Stacks (Python)

# .github/workflows/ci.yaml
name: CI
on:
  pull_request:
    branches: [main]
jobs:
  ci:
    uses: Sea-Haven-Industries/.github/.github/workflows/ci-python-sam.yaml@main

# .github/workflows/deploy.yaml
name: Deploy
on:
  push:
    branches: [main]
jobs:
  deploy:
    uses: Sea-Haven-Industries/.github/.github/workflows/cd-sam.yaml@main
    with:
      stack-name: "your-stack-name"
    secrets:
      cfn-role-arn: ${{ secrets.AWS_DEPLOY_ROLE_ARN }}

Authentication

Deploy workflows authenticate to AWS via OIDC (no long-lived credentials). Each repo needs:

  1. An IAM role named githubdeploy-<repo-name> with:
    • OIDC trust policy for token.actions.githubusercontent.com
    • Subject condition: repo:Sea-Haven-Industries/<repo>:ref:refs/heads/main
    • Inline policy allowing sts:AssumeRole on CDK/SAM bootstrap roles
  2. A repo secret AWS_DEPLOY_ROLE_ARN containing the role ARN

Node.js Version

Always pass node-version: "24" to reusable workflows. Local dev uses Node 24 / npm 11 which generates lockfileVersion 3. The workflow defaults match this, but be explicit to avoid drift.

Naming

  • All workflow files: kebab-case
  • Reusable workflow references: @main branch

When to Add a Pipeline

  • When creating a new deployable project — the pipeline is part of the initial setup, not a follow-up
  • When working on an existing project that lacks one — flag it and add it as part of the current work

A project is not production-ready without CI/CD.

PR Auto-Labeling

Pull requests are auto-labeled org-wide by a reusable workflow in .github. The label rules live once, centrally, inside the reusable workflow itself (written to the runner at execution time), so each repo needs only a short caller and no per-repo labeler.yml:

# .github/workflows/labeler.yml — the per-repo caller
name: Labeler
on:
  pull_request:
    branches: [main]
permissions:
  contents: read
  pull-requests: write
  issues: write
jobs:
  label:
    uses: Sea-Haven-Industries/.github/.github/workflows/callable-labeler.yaml@main
  • The trigger is plain pull_request, not pull_request_target: private repos take no fork PRs, so the lower-privilege event is sufficient and avoids the pwn-request surface. Because pull_request runs the workflow from the merge commit, the Labeler check appears on the PR that first adds the caller — an absent or failed check means a missing permission, not expected behaviour.
  • The caller MUST grant all three permissions. Reusable-workflow permissions can only be downgraded from the caller, so omitting issues: write (needed to create labels that don't exist yet) or any other grant causes a silent startup_failure.
  • Adding the caller is part of new-repo provisioning.