# certctl v2.X.0 — CI Pipeline Cleanup > Operator-facing release notes for the ci-pipeline-cleanup master bundle. > Operator picks the exact `v2.X.0` from the increment-from-the-last-tag rule. ## TL;DR Restructured the on-push CI pipeline. Status checks per push drop from **19 → 7**. `ci.yml` shrinks **1488 → ~430 lines** (-71%). Three lying fields closed (staticcheck soft-gate; Bundle II's fabricated digest regex-only check; Windows matrix that validated nothing). Five new gates added (digest validity, `go mod tidy` drift, gofmt parity, OpenAPI ↔ handler parity, Docker build smoke). **Zero product behavior changes.** No migrations, no API changes, no connector behavior changes. CI-only refactor. ## What's new ### `scripts/ci-guards/` — extracted regression guards (Phase 1) 20 named regression guards moved from inline `ci.yml` bash to sibling scripts: - `G-1-jwt-auth-literal.sh`, `L-001-insecure-skip-verify.sh`, `H-001-bare-from.sh`, `M-012-no-root-user.sh`, `H-009-readme-jwt.sh`, `G-2-api-key-hash-json.sh`, `U-2-plaintext-healthcheck.sh`, `U-3-migration-mount.sh`, `D-1-D-2-statusbadge-phantom.sh`, `L-1-bulk-action-loop.sh`, `B-1-orphan-crud.sh`, `S-2-strings-contains-err.sh`, `G-3-env-docs-drift.sh`, `test-naming-convention.sh`, `S-1-hardcoded-source-counts.sh`, `P-1-documented-orphan-fns.sh`, `T-1-frontend-page-coverage.sh`, `bundle-8-L-015-target-blank-rel-noopener.sh`, `bundle-8-L-019-dangerously-set-inner-html.sh`, `bundle-8-M-009-bare-usemutation.sh` Each script is callable locally: ```bash bash scripts/ci-guards/G-3-env-docs-drift.sh ``` CI step is a single loop that auto-picks up new scripts. Adding a new guard: drop a new `.sh`; no `ci.yml` change required. The 2 QA-doc guards (Part-count + seed-count) moved to `make verify-docs` instead — they protect docs-the-operator-reads, not anything the product depends on. ### `.github/coverage-thresholds.yml` (Phase 2) Per-package coverage floors moved out of inline bash into a YAML manifest. Each entry has `floor:` (integer percentage) + `why:` (load-bearing context — Bundle reference, HEAD measurement, gap rationale). Adding a new gated package: one YAML entry instead of ~30 lines of bash. Floors unchanged from HEAD. ### `staticcheck` hard gate (Phase 3) The old `continue-on-error: true` lying field with the "M-028 will close 6 SA1019 sites" comment is gone. Verified at HEAD: all live SA1019 sites either migrated (`middleware.NewAuth` → `NewAuthWithNamedKeys`) or suppressed inline with load-bearing rationale (`csr.Attributes` for RFC 2985 challengePassword; `elliptic.Marshal` only in byte-equivalence test). Gate now hard. ### `make verify` parity + `go mod tidy` drift (Phase 4) Two new steps in `go-build-and-test`: - **gofmt drift** — closes the parity gap with `Makefile::verify` (CI was running vet + lint + test but not gofmt) - **go mod tidy drift** — `go mod tidy && git diff --exit-code go.mod go.sum` ### `deploy-vendor-e2e` collapsed: 12 jobs → 1 job (Phase 5) Per-vendor matrix granularity was fake signal — verified that 115/116 vendor-edge tests are `t.Log` placeholders. Single job brings up all 11 sidecars at once + runs the full `VendorEdge_` suite + enforces skip-count (no sidecar may silently fail to come up). NEW `scripts/ci-guards/vendor-e2e-skip-check.sh` + allowlist file at `scripts/ci-guards/vendor-e2e-skip-allowlist.txt` (15 windows-iis- requiring tests legitimately skip on Linux per Phase 6). **Revises Bundle II frozen decision 0.9.** Documented in `cowork/ci-pipeline-cleanup/decisions-revised.md`. ### `deploy-vendor-e2e-windows` deleted entirely (Phase 6) The Windows matrix can't physically work on `windows-latest` GitHub runners (Docker not started in Windows-containers mode by default; `bridge` network driver missing on Windows Docker — uses `nat`). Even if fixed, all 16 IIS + WinCertStore tests are `t.Log` placeholders. NEW `docs/connector-iis.md::Operator validation playbook` documents the manual-on-Windows-host procedure operators run pre-release. The `windows-iis-test` sidecar stays in `deploy/docker-compose.test.yml` under `profiles: [deploy-e2e-windows]` for operator local use. `docs/deployment-vendor-matrix.md` IIS + WinCertStore rows status updated `pending` → `operator-playbook`. **Revises Bundle II frozen decision 0.4.** Documented in `cowork/ci-pipeline-cleanup/decisions-revised.md`. ### NEW `image-and-supply-chain` job (Phases 7-9) Top-level Ubuntu job (~3 min, parallel to `go-build-and-test`). Three steps: 1. **Digest validity** — every `@sha256:` ref in `deploy/**/*.{yml,Dockerfile*}` must resolve on its registry. Closes the H-001 lying-field gap (H-001 verifies digest *presence* only — Bundle II shipped 11 fabricated digests that passed H-001 and failed `docker pull` in CI). 2. **Docker build smoke** — all 4 Dockerfiles in the repo must build (`Dockerfile`, `Dockerfile.agent`, `deploy/test/f5-mock-icontrol/Dockerfile`, `deploy/test/libest/Dockerfile`). 3. **OpenAPI ↔ handler operationId parity** — every router route has a matching `operationId` in `api/openapi.yaml` or is documented in the new `api/openapi-handler-exceptions.yaml` (8 documented exceptions at HEAD: SCEP + SCEP-mTLS wire-protocol endpoints). ### Coverage PR-comment action (Phase 10) Self-hosted alternative to Codecov / Coveralls. Posts per-package coverage table as a PR comment; updates in place on subsequent pushes. No paid SaaS dependency. ### `make verify-docs` + `make verify-deploy` (Phase 11) Three-tier convention now: - `make verify` — required pre-commit (gofmt + vet + lint + test) - `make verify-deploy` — optional pre-push (digest validity + OpenAPI parity + Docker build smoke for server + agent) - `make verify-docs` — required pre-tag (QA-doc Part-count + seed-count) ### NEW `docs/ci-pipeline.md` (Phase 12) Operator-facing guide to the on-push pipeline. Per-job deep-dive, guard inventory, threshold management, troubleshooting matrix, branch protection list to update. ## Operator action required After merge: 1. **Update GitHub branch protection rule** for `master` branch. Required-checks list changes from 19 entries → 7: - `Go Build & Test` - `Frontend Build` - `Helm Chart Validation` - `deploy-vendor-e2e` - `image-and-supply-chain` - `Analyze (go)` - `Analyze (javascript-typescript)` 2. **(Optional)** RAM-headroom verification on a test branch with the collapsed `deploy-vendor-e2e` job. If peak RSS > 12 GB on ubuntu-latest, fall back to bucketed matrix per `cowork/ci-pipeline-cleanup/decisions-revised.md`. ## Rollback If RAM headroom proves insufficient or a guard misbehaves: - Vendor matrix collapse (Phase 5): revert that one commit; fall back to the bucketed-matrix design (3 jobs × ~4 sidecars). - staticcheck hard gate (Phase 3): revert that one commit; flip `continue-on-error: true` back temporarily until the new SA1019 site is closed. - All other phases are pure-additive or pure-extraction; reverting any single Phase commit restores the prior behavior. ## Verification ``` make verify # pre-commit gate (existing) make verify-deploy # optional pre-push (new) make verify-docs # pre-tag (new) bash scripts/ci-guards/*.sh # all 20 guards locally bash scripts/check-coverage-thresholds.sh # only after coverage.out exists ``` All passing on HEAD. ## Tag Operator picks the exact `v2.X.0` value. Bundle ships ~13 commits on master after the prior bundle's closing commit (HEAD `1de61e91`).