PABCD Initiative Documentation Hub pabcd_initiative codexclaw cli-jaw

CI/CD & Deploy — Pipeline & Delivery Patterns

Source: dev-devops/references/ci-cd-deploy.md

CI/CD & Deploy — Pipeline & Delivery Patterns

Last reviewed: 2026-06-16

Applies to: GitHub Actions, ArgoCD 2.14+, Argo Rollouts 1.8+

When to read: Deploy pipeline setup or modification

Canonical owner: dev-devops §2

---

§0 Release Routing

For package publishing and release-auth decisions, read

references/package-release.md before writing workflow YAML.

For platform engineering, DORA capability framing, or provider-routing breadth,

read references/platform-engineering.md.

This file owns deployment pipelines, GitOps promotion, rollback, environments,

and progressive delivery. package-release.md owns package registry defaults

such as npm/PyPI trusted publishing, Bun-to-npm decisions, Homebrew as a

downstream channel, and token fallback boundaries.

§1 GHA Reusable Workflow Templates

Called Workflow (Template)

# .github/workflows/templates/build-test.yml
name: Build & Test
on:
  workflow_call:
    inputs:
      service_name:
        required: true
        type: string
      dockerfile:
        required: false
        type: string
        default: Dockerfile
    outputs:
      image_digest:
        value: ${{ jobs.build.outputs.digest }}
    secrets:
      REGISTRY_TOKEN:
        required: true

jobs:
  build:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write
    outputs:
      digest: ${{ steps.push.outputs.digest }}
    steps:
      - uses: actions/checkout@v4
      - uses: docker/setup-buildx-action@v3
      - uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.REGISTRY_TOKEN }}
      - uses: docker/build-push-action@v6
        id: push
        with:
          push: true
          file: ${{ inputs.dockerfile }}
          tags: ghcr.io/${{ github.repository }}/${{ inputs.service_name }}:${{ github.sha }}
          cache-from: type=gha
          cache-to: type=gha,mode=max

Caller Workflow

# .github/workflows/ci.yml
name: CI
on:
  push:
    branches: [main]

jobs:
  build:
    uses: ./.github/workflows/templates/build-test.yml
    with:
      service: payments
    secrets: inherit

  deploy-staging:
    needs: build
    uses: ./.github/workflows/templates/deploy-gitops.yml
    with:
      environment: staging
      image_digest: ${{ needs.build.outputs.image_digest }}
    secrets: inherit

Rules

RuleDetail
NestingMax 10 levels (GitHub limit)
PermissionsCaller cannot escalate called workflow permissions
Secretssecrets: inherit or explicit pass; never hardcode
PinningPin reusable workflows to SHA or tag: @v2 or @sha256:...

---

§2 GitOps Architecture

Actions = CI, ArgoCD = CD

Developer → PR → CI (lint/test/build/scan/push) → merge
  → CI updates deploy repo (image digest) → ArgoCD detects → sync to cluster
PatternWhen
Single repoSmall team, 1-3 services
App + Deploy repoLarger teams, separation of CI and deploy config

Digest-Based Promotion

# CI updates deploy repo kustomization.yaml
yq -i '.images[0].digest = "sha256:abc123..."' overlays/prod/kustomization.yaml
git commit -m "promote payments to sha256:abc123"
git push
# ArgoCD auto-syncs
BannedFix
image: app:v2.1 (mutable tag)image: app@sha256:abc123... (immutable digest)
CI runs kubectl applyArgoCD reconciles from Git

Environment Protection

# GitHub Environment settings (UI or API)
environment:
  name: production
  protection_rules:
    required_reviewers: 2
    prevent_self_review: true
    wait_timer: 5  # minutes

---

§3 Progressive Delivery

Argo Rollouts — Canary

apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
  name: payments
spec:
  replicas: 10
  strategy:
    canary:
      steps:
        - setWeight: 10
        - pause: { duration: 5m }
        - setWeight: 30
        - pause: { duration: 5m }
        - setWeight: 60
        - pause: { duration: 5m }
      canaryService: payments-canary
      stableService: payments-stable
      analysis:
        templates:
          - templateName: success-rate
        startingStep: 2

Argo Rollouts — Blue-Green

spec:
  strategy:
    blueGreen:
      activeService: payments-active
      previewService: payments-preview
      autoPromotionEnabled: false
      prePromotionAnalysis:
        templates:
          - templateName: smoke-test

Flagger

apiVersion: flagger.app/v1beta1
kind: Canary
metadata:
  name: payments
spec:
  targetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: payments
  service:
    port: 8080
  analysis:
    interval: 1m
    threshold: 5
    maxWeight: 50
    stepWeight: 10
    metrics:
      - name: request-success-rate
        thresholdRange: { min: 99 }
      - name: request-duration
        thresholdRange: { max: 500 }

Selection Guide

ToolBest ForGranularity
Argo RolloutsK8s-native canary/blue-green, manual or metric-drivenTraffic weight steps
FlaggerFully automated analysis + promote/rollbackMetric-driven auto
Feature flagsCode-level, user-segment targetingPer-user/segment

---

§4 Rollback & DB Migration

Expand-Contract Pattern

Phase 1: Add new column (nullable) → deploy code that writes both
Phase 2: Backfill old data → verify
Phase 3: Deploy code that reads new column only
Phase 4: Drop old column
RuleDetail
Forward-onlyNever write down() migrations; use expand-contract
Backward-compatibleNew code must work with old schema during rollout
Rollback planDocument per-service: what to rollback, how, who
Time budgetEvery deploy rollback-capable within 5 minutes

---

§5 Anti-Patterns

BannedWhyFix
Mutable tag deployCan't reproduce; ArgoCD driftDigest-based promotion
kubectl apply from CINo audit trail, manual driftGitOps (ArgoCD)
Manual deploymentHuman error, no rollback pathAutomated pipeline
No prod approvalUnreviewed changes hit usersEnvironment protection
down() migrationsRollback breaks forward-deployed codeExpand-contract
Deploy without smoke testSilent failuresPost-deploy smoke in pipeline

---

§6 Verification Evidence Rules

Owner: this file. The GO/NO-GO decision rules that consume these live in

dev-devops SKILL.md §2.8. Each was learned by getting it wrong first, and the

incident is kept with the rule because the rule alone reads like bureaucracy.

§6.1 Suite partitioning (DEVOPS-SUITE-PARTITION-01, STRICT)

A local one-process full-suite run is not the CI suite gate. Replay the

partition CI actually applies — the general shards plus each segregated job's exact

command — and record both forms.

Why: a real pipeline excluded three load-sensitive path patterns from its general

batches and covered them with two dedicated jobs, one running six files together and

one running a single file. Running everything in one process therefore failed tests

CI never runs together, and the resulting red was not the gate's verdict.

Decomposed, the same tree gave a fully green general suite plus a green segregated

job.

Corollary: if you cannot state which CI job a local command corresponds to, that

command is not a gate.

§6.2 Baseline versus defect (DEVOPS-BASELINE-DEFECT-01, STRICT)

A local red test is a candidate defect until all three hold:

  1. the identical failure reproduces on the untouched pre-change baseline SHA,
  2. no merged unit in the change set touches that code, and
  3. CI's matching job is green at the freeze SHA.

Two out of three is not evidence. "It's flaky" and "it's environmental" are

conclusions, not observations, and they need the same proof as any other claim.

Remediation of a genuine flake belongs to dev-testing

references/ci-pipeline.md §5; this rule only decides whether you are allowed to

call it one.

§6.3 Instrument stability (DEVOPS-VERIFY-INSTRUMENT-01, STRICT)

Do not change the verification instrument — runner flags, parallelism, shard layout,

timeouts, retry policy — while using it to certify a freeze. Change it against a

known-good baseline, or defer it.

Stated at the source as: **a verification instrument gets changed against a

known-good baseline; it does not get used to establish one.** The train that

produced this deferred its parallel-runner change for exactly that reason — across

five recorded runs four different tests flaked, and the freeze gate was itself a

full-suite run, so landing the new runner would have made red indistinguishable from

noise.

§6.4 Exact-head evidence (DEVOPS-EXACT-HEAD-01, STRICT)

Re-read the PR or branch head immediately before claiming exact-head evidence. A

push mid-verification makes a recorded SHA stale. Keep stale rows labeled stale

and never merge on them; a remembered pass is not evidence.

The case: a MERGE recommendation was formed against a head the author had already

replaced. The fix was not more care but a run table that marks superseded rows

explicitly, so staleness is visible rather than remembered.

§6.5 Stability counting (DEVOPS-FLAKE-STABILITY-01, DEFAULT)

A flaky-capable suite is stable only after N consecutive greens at one head plus

the required CI matrix. Declare N in the GO report. The train behind this rule set

N=3 plus two OS matrices, never collected it, and deferred the change rather than

landing it — at the head under test the recorded runs were green, green, then a

failure. One green run is not a land signal.

This rule counts greens; it does not tell you how to fix a flake. That is

dev-testing references/ci-pipeline.md §5, which owns TEST-FLAKE-*.