CI/CD Expert
Quick Decision Tree
What need?
├── Platform selection → references/platforms.md
├── GitHub Actions architecture → references/github-actions.md
├── GitLab CI optimization → references/gitlab-ci.md
├── Monorepo pipeline (JS/TS) → references/monorepo-frontend.md
├── Monorepo pipeline (compiled) → references/monorepo-bazel.md
├── Container builds (K8s) → references/container-builds.md
├── GitOps + ArgoCD deploy → references/gitops-argocd.md
├── DB migrations in pipeline → references/db-migrations.md
├── Canary / progressive delivery → references/progressive-delivery.md
├── Mobile CI/CD (iOS/Android) → references/mobile-devops.md
├── Android affected modules & testing → references/android-affected-testing.md
├── Android signing & release → references/android-signing-release.md
├── Android Gradle tuning & caching → references/android-gradle-tuning.md
├── Android pre-merge & emulator → references/android-emulator-validation.md
├── Android runner memory & swap → references/android-runners-memory.md
├── Pipeline security (OIDC/PPE) → references/security-oidc.md
└── Full config samples → references/samples.md
Core Principles
- DAG > Sequential. Never chain jobs linearly. Use
needs (GitLab) or job dependencies (GHA) for parallel fan-out/fan-in.
- Cache everything. Hash lock files for cache keys. Use fallback keys. Distribute cache via S3/NFS at scale.
- Impact radius only. Never rebuild world. Use affected/changed detection (Nx, Turborepo, Bazel, dropbox/affectedmoduledetector,
git diff).
- Immutable tags. Use Git SHA tags (
v1.2.3-a3f5b2c), never latest. Pin 3rd-party actions to commit SHAs.
- Zero static secrets. OIDC federation for cloud access. Short-lived JWT tokens. No long-lived IAM keys.
- GitOps pull > CI push. ArgoCD/Flux pull from Git. Never
kubectl apply from CI runner.
- Fail fast. Lint/syntax first.
interruptible: true on superseded commits. Kill redundant jobs.
- Build once, promote. Single artifact through all environments. No per-env rebuilds.
Platform Selection Cheat Sheet
| Platform |
Best For |
Avoid When |
| GitHub Actions |
Teams <50, OSS, rapid setup |
Need self-hosted control, complex loops |
| GitLab CI/CD |
Enterprise compliance, self-hosted, DAGs |
Already deep in GitHub ecosystem |
| Jenkins |
Legacy integration, multi-VCS, extreme flex |
Greenfield projects (maintenance overhead) |
| CircleCI |
Startups needing raw speed |
Need native SCM integration |
| Bazel |
Polyglot monorepo >1M LOC, hermetic builds |
Small projects (setup overhead) |
GitHub Actions: Reusable Workflows vs Composite Actions
|
Reusable Workflows |
Composite Actions |
| Level |
Job-level orchestration |
Step-level encapsulation |
| Jobs |
Multiple jobs, own runs-on |
No jobs, runs on caller's runner |
| Secrets |
Native secrets: passing |
Must pass via env vars |
| Logging |
Per-step visibility |
Single collapsed step |
| Matrix |
Own strategy.matrix |
None (caller controls) |
Rule: Workflows = pipeline templates. Actions = task templates. Never mix.
GitLab CI: DAG + Dynamic Pipelines
# Fan-out with needs (bypass stage waits)
test-unit:
stage: test
needs: [build]
script: make test-unit
test-integration:
stage: test
needs: [build]
script: make test-integration
deploy:
stage: deploy
needs: [test-unit, test-integration] # fan-in
rules:
- if: $CI_COMMIT_BRANCH == "main"
interruptible: false
Dynamic parent-child for monorepos:
generate-config:
stage: setup
script: python generate_pipeline.py --base origin/main --output child.yml
artifacts:
paths: [child.yml]
trigger-child:
stage: trigger
needs: [generate-config]
trigger:
include:
- artifact: child.yml
strategy: depend
Caching Strategy
| Platform |
Cache Key Pattern |
Fallback |
| GHA |
${{ runner.os }}-node-${{ hashFiles('**/pnpm-lock.yaml') }} |
${{ runner.os }}-node- |
| GitLab |
cache:key:files: [yarn.lock] |
Default branch cache |
| Turborepo |
globalDependencies + outputs arrays |
Remote cache (Vercel) |
| Nx |
namedInputs + inputs per target |
Nx Cloud remote cache |
| Bazel |
--disk_cache + --remote_cache |
N/A (hermetic) |
Container Build Decision
Need privileged access?
├── YES (trusted runners) → Docker BuildKit (fast, local cache)
└── NO (shared K8s, multi-tenant)
└── Kaniko (daemonless, userspace, --cache=true --cache-repo=...)
Always use multi-stage Dockerfiles: heavy builder → minimal runtime (scratch/alpine).
GitOps Flow
CI Pipeline K8s Cluster
┌──────────┐ commit SHA tag ┌────────────┐
│ Build + │ ──→ Update manifest │ ArgoCD / │
│ Test │ in infra repo │ Flux │
│ └──────────┘ (Kustomize edit) │ (pull) │
│ └────────────┘
ArgoCD Sync Wave order: Wave -5 (secrets/PVCs) → Wave 0 (DB) → Wave 1 (backend) → Wave 2 (frontend).
Zero-Downtime DB Migration
Phase 1: EXPAND → ALTER TABLE ADD COLUMN (additive only)
Phase 2: DUAL-WRITE → App writes both old + new columns
Phase 3: BACKFILL → Background worker migrates legacy data
Phase 4: CONTRACT → DROP old column (days later, after telemetry confirms)
Execute via ArgoCD PreSync hooks with backoffLimit: 3 + activeDeadlineSeconds: 300.
OIDC Federation (Zero Static Secrets)
CI Job starts → Platform generates JWT (aud + sub claims)
→ Cloud provider validates JWT signature
→ Evaluates sub claim against IAM trust policy
→ Returns short-lived STS credentials
→ Credentials auto-expire post-job
Lock sub claim to exact repo + branch: repo:org/repo:ref:refs/heads/main.
Reference Files
| File |
Read When |
| platforms.md |
Comparing/selecting CI/CD platforms |
| github-actions.md |
Writing GHA workflows, reusable workflows, composite actions |
| gitlab-ci.md |
GitLab DAGs, rules, dynamic pipelines, caching |
| monorepo-frontend.md |
Turborepo/Nx config, affected logic, cache tuning |
| monorepo-bazel.md |
Bazel setup, presubmit.yml, remote exec, Gazelle |
| container-builds.md |
Kaniko vs BuildKit, multi-stage, K8s pod specs |
| gitops-argocd.md |
ArgoCD sync waves, hooks, Kustomize, Helm |
| db-migrations.md |
Expand-Contract, Flyway/Liquibase, PreSync hooks |
| progressive-delivery.md |
Canary, Spinnaker, Kayenta config, statistical analysis |
| mobile-devops.md |
Fastlane Match, Fastfile, iOS/Android signing |
| android-affected-testing.md |
Android affected module detection, test-only modules, Roborazzi |
| android-signing-release.md |
Android GHA keystore signing, GPG/base64, Play Store upload |
| android-gradle-tuning.md |
Android compiler tuning (KSP), configuration/remote cache |
| android-emulator-validation.md |
Android git hooks auto-install, act dry-run, KVM emulator permissions |
| android-runners-memory.md |
Android runner comparison, swap space expansion (17GB RAM) |
| security-oidc.md |
OIDC federation, PPE prevention, trust policies |
| samples.md |
Full working config samples for all platforms |
| hyperscale.md |
Stripe STE, Uber/Airbnb Bazel, Netflix Spinnaker |
Source: JosephSanjaya/skills — distributed by TomeVault.
1---2name: josephsanjaya-skills-ci-cd-expert3description: CI/CD Expert4---56# CI/CD Expert78<instructions>910## Quick Decision Tree1112```13What need?14├── Platform selection → references/platforms.md15├── GitHub Actions architecture → references/github-actions.md16├── GitLab CI optimization → references/gitlab-ci.md17├── Monorepo pipeline (JS/TS) → references/monorepo-frontend.md18├── Monorepo pipeline (compiled) → references/monorepo-bazel.md19├── Container builds (K8s) → references/container-builds.md20├── GitOps + ArgoCD deploy → references/gitops-argocd.md21├── DB migrations in pipeline → references/db-migrations.md22├── Canary / progressive delivery → references/progressive-delivery.md23├── Mobile CI/CD (iOS/Android) → references/mobile-devops.md24├── Android affected modules & testing → references/android-affected-testing.md25├── Android signing & release → references/android-signing-release.md26├── Android Gradle tuning & caching → references/android-gradle-tuning.md27├── Android pre-merge & emulator → references/android-emulator-validation.md28├── Android runner memory & swap → references/android-runners-memory.md29├── Pipeline security (OIDC/PPE) → references/security-oidc.md30└── Full config samples → references/samples.md31```3233## Core Principles34351. **DAG > Sequential.** Never chain jobs linearly. Use `needs` (GitLab) or job dependencies (GHA) for parallel fan-out/fan-in.362. **Cache everything.** Hash lock files for cache keys. Use fallback keys. Distribute cache via S3/NFS at scale.373. **Impact radius only.** Never rebuild world. Use affected/changed detection (Nx, Turborepo, Bazel, dropbox/affectedmoduledetector, `git diff`).384. **Immutable tags.** Use Git SHA tags (`v1.2.3-a3f5b2c`), never `latest`. Pin 3rd-party actions to commit SHAs.395. **Zero static secrets.** OIDC federation for cloud access. Short-lived JWT tokens. No long-lived IAM keys.406. **GitOps pull > CI push.** ArgoCD/Flux pull from Git. Never `kubectl apply` from CI runner.417. **Fail fast.** Lint/syntax first. `interruptible: true` on superseded commits. Kill redundant jobs.428. **Build once, promote.** Single artifact through all environments. No per-env rebuilds.4344## Platform Selection Cheat Sheet4546| Platform | Best For | Avoid When |47|----------|----------|------------|48| **GitHub Actions** | Teams <50, OSS, rapid setup | Need self-hosted control, complex loops |49| **GitLab CI/CD** | Enterprise compliance, self-hosted, DAGs | Already deep in GitHub ecosystem |50| **Jenkins** | Legacy integration, multi-VCS, extreme flex | Greenfield projects (maintenance overhead) |51| **CircleCI** | Startups needing raw speed | Need native SCM integration |52| **Bazel** | Polyglot monorepo >1M LOC, hermetic builds | Small projects (setup overhead) |5354## GitHub Actions: Reusable Workflows vs Composite Actions5556| | Reusable Workflows | Composite Actions |57|-|-------------------|-------------------|58| **Level** | Job-level orchestration | Step-level encapsulation |59| **Jobs** | Multiple jobs, own `runs-on` | No jobs, runs on caller's runner |60| **Secrets** | Native `secrets:` passing | Must pass via env vars |61| **Logging** | Per-step visibility | Single collapsed step |62| **Matrix** | Own `strategy.matrix` | None (caller controls) |6364**Rule:** Workflows = pipeline templates. Actions = task templates. Never mix.6566## GitLab CI: DAG + Dynamic Pipelines6768```yaml69# Fan-out with needs (bypass stage waits)70test-unit:71 stage: test72 needs: [build]73 script: make test-unit7475test-integration:76 stage: test77 needs: [build]78 script: make test-integration7980deploy:81 stage: deploy82 needs: [test-unit, test-integration] # fan-in83 rules:84 - if: $CI_COMMIT_BRANCH == "main"85 interruptible: false86```8788Dynamic parent-child for monorepos:89```yaml90generate-config:91 stage: setup92 script: python generate_pipeline.py --base origin/main --output child.yml93 artifacts:94 paths: [child.yml]9596trigger-child:97 stage: trigger98 needs: [generate-config]99 trigger:100 include:101 - artifact: child.yml102 strategy: depend103```104105## Caching Strategy106107| Platform | Cache Key Pattern | Fallback |108|----------|------------------|----------|109| **GHA** | `${{ runner.os }}-node-${{ hashFiles('**/pnpm-lock.yaml') }}` | `${{ runner.os }}-node-` |110| **GitLab** | `cache:key:files: [yarn.lock]` | Default branch cache |111| **Turborepo** | `globalDependencies` + `outputs` arrays | Remote cache (Vercel) |112| **Nx** | `namedInputs` + `inputs` per target | Nx Cloud remote cache |113| **Bazel** | `--disk_cache` + `--remote_cache` | N/A (hermetic) |114115## Container Build Decision116117```118Need privileged access?119├── YES (trusted runners) → Docker BuildKit (fast, local cache)120└── NO (shared K8s, multi-tenant)121 └── Kaniko (daemonless, userspace, --cache=true --cache-repo=...)122```123124Always use multi-stage Dockerfiles: heavy builder → minimal runtime (`scratch`/`alpine`).125126## GitOps Flow127128```129CI Pipeline K8s Cluster130┌──────────┐ commit SHA tag ┌────────────┐131│ Build + │ ──→ Update manifest │ ArgoCD / │132│ Test │ in infra repo │ Flux │133│ └──────────┘ (Kustomize edit) │ (pull) │134│ └────────────┘135```136137ArgoCD Sync Wave order: Wave -5 (secrets/PVCs) → Wave 0 (DB) → Wave 1 (backend) → Wave 2 (frontend).138139## Zero-Downtime DB Migration140141```142Phase 1: EXPAND → ALTER TABLE ADD COLUMN (additive only)143Phase 2: DUAL-WRITE → App writes both old + new columns144Phase 3: BACKFILL → Background worker migrates legacy data145Phase 4: CONTRACT → DROP old column (days later, after telemetry confirms)146```147148Execute via ArgoCD `PreSync` hooks with `backoffLimit: 3` + `activeDeadlineSeconds: 300`.149150## OIDC Federation (Zero Static Secrets)151152```153CI Job starts → Platform generates JWT (aud + sub claims)154 → Cloud provider validates JWT signature155 → Evaluates sub claim against IAM trust policy156 → Returns short-lived STS credentials157 → Credentials auto-expire post-job158```159160Lock sub claim to exact repo + branch: `repo:org/repo:ref:refs/heads/main`.161162## Reference Files163164| File | Read When |165|------|-----------|166| [platforms.md](references/platforms.md) | Comparing/selecting CI/CD platforms |167| [github-actions.md](references/github-actions.md) | Writing GHA workflows, reusable workflows, composite actions |168| [gitlab-ci.md](references/gitlab-ci.md) | GitLab DAGs, rules, dynamic pipelines, caching |169| [monorepo-frontend.md](references/monorepo-frontend.md) | Turborepo/Nx config, affected logic, cache tuning |170| [monorepo-bazel.md](references/monorepo-bazel.md) | Bazel setup, presubmit.yml, remote exec, Gazelle |171| [container-builds.md](references/container-builds.md) | Kaniko vs BuildKit, multi-stage, K8s pod specs |172| [gitops-argocd.md](references/gitops-argocd.md) | ArgoCD sync waves, hooks, Kustomize, Helm |173| [db-migrations.md](references/db-migrations.md) | Expand-Contract, Flyway/Liquibase, PreSync hooks |174| [progressive-delivery.md](references/progressive-delivery.md) | Canary, Spinnaker, Kayenta config, statistical analysis |175| [mobile-devops.md](references/mobile-devops.md) | Fastlane Match, Fastfile, iOS/Android signing |176| [android-affected-testing.md](references/android-affected-testing.md) | Android affected module detection, test-only modules, Roborazzi |177| [android-signing-release.md](references/android-signing-release.md) | Android GHA keystore signing, GPG/base64, Play Store upload |178| [android-gradle-tuning.md](references/android-gradle-tuning.md) | Android compiler tuning (KSP), configuration/remote cache |179| [android-emulator-validation.md](references/android-emulator-validation.md) | Android git hooks auto-install, act dry-run, KVM emulator permissions |180| [android-runners-memory.md](references/android-runners-memory.md) | Android runner comparison, swap space expansion (17GB RAM) |181| [security-oidc.md](references/security-oidc.md) | OIDC federation, PPE prevention, trust policies |182| [samples.md](references/samples.md) | Full working config samples for all platforms |183| [hyperscale.md](references/hyperscale.md) | Stripe STE, Uber/Airbnb Bazel, Netflix Spinnaker |184185</instructions>186187<constraints>188- Pin 3rd-party actions to full commit SHAs, never mutable tags.189- Enforce OIDC (zero static secrets) for all cloud deployments.190- Build containers using multi-stage Dockerfiles. Use Kaniko in shared/restricted Kubernetes, BuildKit in isolated runners.191- DB migrations must run in PreSync hook / wave -5 using additive-only SQL first (Expand-and-Contract).192- Enable `interruptible: true` in GitLab and `cancel-in-progress` in GHA to kill redundant runs.193- Run swap space expansion early on standard free GitHub Actions runners to reclaim space and enable safe `-Xmx6g` Gradle heap.194- Always configure Gradle cache encryption via `cache-encryption-key` and restrict cache write access (`cache-read-only`) to production/release branches.195- Run UI/screenshot checks (e.g. Roborazzi) and instrumentation tests on a nightly schedule or triggered on UI directory changes to avoid slowing down developer PR loops.196- Auto-install local Git hooks (e.g. pre-push) by registering an `installLocalGitHooks` copy task in the root Gradle build configuration.197</constraints>198199---200> Source: [JosephSanjaya/skills](https://github.com/JosephSanjaya/skills) — distributed by [TomeVault](https://tomevault.io).201<!-- tomevault:4.0:skill_md:2026-06-15 -->