# Repo map — CI/CD

Ground truth for the course. Paths from the repo root. Line numbers drift — treat them as "look
near here." Counts: **99 workflows**, **21 `tbd.*`**, **76 composite actions**.

---

## 0. The two facts that reframe the course

1. **A merge does NOT deploy directly.** The chain is:
   `push to develop` → `tbd.create_release_tag` (make a release tag) → a **scheduled cron
   (every 2h, Mon–Fri)** picks the latest tag → `tbd.build.yaml` (with `auto_deploy=true`) →
   `tbd.deploy.yml`. **All prod/human deploys are `workflow_dispatch`.** There is no `on: push`
   deploy job. So: cron-driven auto-deploy to *staging*, manual dispatch to *prod*.
2. **Binary authorization is audit-only.** Attestations are created and signed, but every
   admission rule is `enforcement_mode = "DRYRUN_AUDIT_LOG_ONLY"` — it does *not* block deploys
   yet. (Like the Istio mTLS-off pattern: the machinery exists, enforcement is not turned on.)

`tbd.` = **trunk-based deploy/development**.

---

## 1. Workflow inventory *(Lessons 1–2)*
- Primary **CI** = <span class="filepath">.github/workflows/tiered.pre_merge.yml</span> (740 lines) —
  `on: pull_request: types:[labeled]`, gated on the **`Ok to test`** label (`:2-4,13`).
- Primary **build** = <span class="filepath">.github/workflows/tbd.build.yaml</span> —
  `repository_dispatch` + `workflow_dispatch` (`:11-16`).
- Primary **deploy** = <span class="filepath">.github/workflows/tbd.deploy.yml</span> —
  `repository_dispatch` (`tbd.deploy_be/_fe/_me/_all`) + `workflow_dispatch` (`:11-15`); env ∈
  {staging, uat, preproduction(=dorp), production, trial} (`:18-27`), orgs {manabie, jprep, tokyo,
  synersia, renseikai, ga} (`:29-32`).
- Release tagging: `tbd.create_release_tag.yaml` (`on: push` develop/hotfix, `:3-24`).
- Staging auto-deploy cron: `tbd.auto-deploy-staging.yml` — `cron: "0 0-13/2 * * 1-5"` (`:5-7`).

## 2. The CI pipeline — `tiered.pre_merge.yml` *(Lesson 3)*
- **`requirements` router** (`:12-108`) — runs a prebuilt **`diff` binary** (`:82-108`) that
  inspects the PR diff (+ squad membership + PR labels like `force-test`) to decide *which jobs
  run*, and emits a per-job **runner-label map** consumed as
  `runs-on: ${{ fromJson(needs.requirements.outputs.runners)['<job>'] }}`.
- `unit-test` (`:146-216`) — `setup-go` (go-version-file), GCS Go cache, **`go test -count=3
  ./internal/... -cover`** (`:186-192`) + coverage-diff vs base.
- `lint` (`:266-309`) — **reviewdog** `action-golangci-lint`, `golangci_lint_version: v2.8.0`,
  `reporter: github-pr-review` (`:296-309`). *(Not `make lint`.)*
- `build-test` (`:328-359`) — `CGO_ENABLED=0 GOOS=linux go build -o backend ./cmd/server/`.
- `proto-check` (`:526-545`) — buf lint + **buf breaking** vs base.
- `dbschema-test` (`:361-384`) → `.github/scripts/ensure-make-gen-db-schema.bash` (drift guard).
- `gen-dags-test` (`:399-452`) → `.github/scripts/ensure-make-generate-dags.bash` (drift guard).
- `gitleaks-scan` (`:625-643`), `check-commit-messages` (`:547-594`), `skaffold-test` (`:218-264`).
- `conclusion` (`:645-679`) — a `statuscheck` binary aggregates required checks
  (`--require=skaffold-test,…,unit-test,build-test,dbschema-test,lint,…,gitleaks-scan`).
- Post-merge unit test (separate): `tiered.post_merge_unit_test.yml` — `on: push`
  develop/release, `paths: '**.go'`, runs `make test-unit-for-coverage`.

## 3. Build & push images *(Lesson 5)*
- <span class="filepath">.github/actions/tbd.build-backend/action.yml</span> (called from
  `tbd.build.yaml:213-233`):
  - Builder = **docker buildx** + `docker/build-push-action@v6` (`:91-111`) — *not*
    skaffold/kaniko/bazel.
  - Dockerfile = <span class="filepath">developments/release.Dockerfile</span>, `target: runner`
    (`:104-108`).
  - Registry = **`asia.gcr.io/student-coach-e1e95/backend`** (`PREFIX_IMAGE`,
    `tbd.build.yaml:108-109`); Artifact Registry `asia-docker.pkg.dev` also logged in.
  - **Tag = the git release tag** (`be_release_tag`, `:107`); `docker manifest inspect` skips the
    build if the tag already exists (`:69-83`); cache `type=gha` (`:110-111`).
  - Then → **security-scan-image** (`:121-127`) → **binauthz-sign** (`:129-135`).
- The learner's 3 services compile into **one `backend` binary** (`cmd/server/`) → **one `backend`
  image** → deployed as separate Helm subcharts.

## 4. Deploy flow *(Lessons 6, 8)*
- `deploy-k8s` job in <span class="filepath">.github/workflows/tbd.deploy.yml:360-429</span> —
  matrix over orgs (`:368-372`), per-org concurrency `cancel-in-progress: false` (`:373-375`),
  approval via the **`tbd.privileges-check`** action (`:213`) — *not* native GitHub Environments.
- Real mechanics: <span class="filepath">.github/actions/tbd.deploy-backend/action.yml</span> —
  install skaffold (`:18-22`), `setup-k8s` (auth + GKE creds), checkout the release tag,
  `get-deployment-config`, preprod/trial **clone prod config** (`clone_prod_configs.bash`, the
  `dorp` path, `:44-49`), enable Hasura RLS, then the deploy command (`:80-97`):
  `skaffold deploy -f skaffold.backend.yaml` (= `helm upgrade --install`), `TAG=${be_release_tag}`,
  `SKAFFOLD_STATUS_CHECK=true`. **Push-based, not GitOps.**

## 5. Self-hosted runners (ARC) *(Lesson 7)*
- **Legacy ARC (summerwind):** `deployments/runner/runners/*.yaml` — `RunnerDeployment` +
  `HorizontalRunnerAutoscaler` + PDB; `dockerdWithinRunnerContainer: true` (**dind** — the
  docker/kind-in-runner setup); autoscale on `TotalNumberOfQueuedAndInProgressWorkflowRuns`.
- **New ARC scale sets:** <span class="filepath">deployments/helm/platforms/actions-runner-controller/</span>
  — `gha-runner-scale-set-controller-0.10.1.tgz` + `gha-runner-scale-set-0.10.1.tgz`.
- **`runs-on:` is dynamic** — `.github/actions/runners/runners.js` maps
  {repo→workflow→job→label[]}, e.g. `build-backend → ["4-8-large-runner"]` (`:69`),
  `run-e2e → ["8-32-non-persistent-large-runner"]` (`:46`); `dynamicRunners()` prefers the
  self-hosted HCM scale set. Per-size values: `…/actions-runner-controller/{1-4,2-4,4-8-large,8-32}-runner-values.yaml`.

## 6. Binary Authorization / supply chain *(Lesson 10)*
- **Sign (CI):** <span class="filepath">.github/actions/binauthz-sign/action.yml</span> —
  `gcloud beta container binauthz attestations sign-and-create` (`:88-97`), KMS keyring
  `binauthz-attestors`, attestor `stag-deploy`/`prod-deploy` by env (`:54-85`). It's **GCP Binary
  Authorization**, *not* cosign/SLSA (no cosign in repo). Preceded by `security-scan-image`.
- **Policy (Terraform):** <span class="filepath">deployments/terraform/modules/binary-authorization-attestor/policy.tf</span>
  — `google_binary_authorization_policy` with `cluster_admission_rules` + `require_attestations_by`
  (`:14-36`). GKE enable: `modules/platforms/gke.tf:111` (`enable_binary_authorization`).
- **AUDIT-ONLY:** every admission rule (stag + prod) is `evaluation_mode = "ALWAYS_ALLOW"`,
  `enforcement_mode = "DRYRUN_AUDIT_LOG_ONLY"`
  (`live/stag-manabie/…/terragrunt.hcl:46-54`, `live/prod-tokyo/…:46-69`) → **does not block deploys**.

## 7. Composite actions (76) & reuse *(Lesson 4)*
- `auth-oidc/` (GCP OIDC/WIF), `setup-k8s/` (auth + GKE creds), `tbd.build-backend/`,
  `tbd.deploy-backend/`, `binauthz-sign/`, `runners/` (+`runners.js`, dynamic `runs-on`),
  `gcs-cache/` (GCS-backed cache), `tool-install/` (skaffold/helm/jq/yq), `slack-alert-*/`,
  `get-deployment-config/`, `tbd.privileges-check/` (deploy authorization gate).
- **No reusable workflows (`workflow_call`)** — the repo composes via **composite actions +
  `repository_dispatch`**, not `workflow_call`.

## 8. CI → GCP auth *(Lesson 9)*
- **Workload Identity Federation / OIDC everywhere; no static SA JSON keys.** Jobs declare
  `permissions: id-token: "write"` (e.g. `tiered.pre_merge.yml:152-156`, `tbd.deploy.yml:362-364`).
- `google-github-actions/auth@v2` with a WIF provider + a purpose-specific SA:
  build → provider `…/gh-action-pool/providers/build-bot-provider`, SA
  `prod-build-bot@student-coach-e1e95` (`tbd.build-backend/action.yml:47-53`); deploy → resolved
  per env/org (`auth-oidc/action.yaml:83-87`); integration-test → `integration-test-bot@…`.
- Secrets: GitHub Actions secrets (`BUILD_GITHUB_TOKEN`, `SLACK_TOKEN`, …) + in-cluster **SOPS**
  (Course 3) applied at deploy.

## 9. Concurrency / matrix / cache / path filters *(Lesson 12)*
- Concurrency: CI `group: workflow-head_ref, cancel-in-progress: true` (`tiered.pre_merge.yml:7-9`);
  deploy per-org `cancel-in-progress: false` (never cancel a live deploy, `tbd.deploy.yml:373-375`).
- Matrix: org fan-out with dynamic `max-parallel`, `fail-fast: false` (`tbd.deploy.yml:368-372`).
- Cache: **GCS-backed** `gcs-cache` action (bucket `stag-manabie-cache`), *not* `actions/cache`;
  buildx GHA cache for images.
- Path filters (monorepo): (1) static `paths`/`paths-ignore` on triggers; (2) the **dynamic `diff`
  binary** in `requirements` turns individual jobs on/off, so most PRs skip most jobs.

## 10. Integration / E2E & drift guards *(Lesson 11)*
- **kind-in-runner integration:** `tiered.post_merge_integration_test.yml` (currently
  `workflow_dispatch` only) — tools install, WIF auth, GCR/AR login, then
  **`./deployments/sk.bash`** (skaffold-in-kind bring-up) + `deployer` binary; waits on rollouts;
  teardown `./deployments/sk.bash -d`. The `GODOG_TAGS=@critical` BDD run exists but is currently
  **commented** (`:142-165`).
- **Drift guards:** `ensure-make-gen-db-schema.bash` (`:9-16`) and `ensure-make-generate-dags.bash`
  (`:5-18`, takes `AIRFLOW_ENV`) — regenerate then fail on `git diff`. Proto drift via `buf-breaking`.

---

## Anchors cheat-list

| Thing | Where |
|---|---|
| Primary CI (PR gate) | `.github/workflows/tiered.pre_merge.yml` (`Ok to test` label; `diff` router) |
| Primary build | `.github/workflows/tbd.build.yaml` + `.github/actions/tbd.build-backend/action.yml` (buildx) |
| Primary deploy | `.github/workflows/tbd.deploy.yml` + `.github/actions/tbd.deploy-backend/action.yml` (`skaffold deploy`) |
| Deploy trigger chain | develop → `tbd.create_release_tag` → 2h cron → build → deploy |
| Registry / tag | `asia.gcr.io/student-coach-e1e95/backend` : `be_release_tag` |
| ARC runners | `deployments/runner/runners/` (dind) + `helm/platforms/actions-runner-controller/`; `runners.js` |
| Keyless auth | `.github/actions/auth-oidc/`; `id-token: write` + `google-github-actions/auth@v2` |
| Binauthz (audit-only) | `.github/actions/binauthz-sign/`; policy `terraform/modules/binary-authorization-attestor/` |
| Drift guards | `.github/scripts/ensure-make-{gen-db-schema,generate-dags}.bash` |
| Dynamic gating | the `diff` binary in `tiered.pre_merge.yml:82-108` |
