ci-pipeline-cleanup Phase 12: docs/ci-pipeline.md + bundle artefacts

Bundle: ci-pipeline-cleanup, Phase 12.

NEW docs/ci-pipeline.md (operator-facing guide to the on-push pipeline):
- Trigger model (push, daily, tag)
- Per-job deep-dive for all 5 CI jobs + 2 CodeQL jobs
- The 20 regression guards table with what each catches
- Coverage threshold management
- Three-tier make convention (verify, verify-deploy, verify-docs)
- Adding a new check (where it goes, auto-pickup)
- Troubleshooting matrix
- Status check accounting (19 → 7)
- Required GitHub branch protection list (operator action)

NEW cowork/ci-pipeline-cleanup/v2.X.0-release-notes.md — operator-facing
release notes covering all 13 phases + the operator action items
post-merge.

NEW cowork/ci-pipeline-cleanup/reddit-beat.md — Reddit / HN announce
draft (don't auto-post; operator times manually after the tag lands).

Active Focus updated in cowork/CLAUDE.md (workspace, separate edit
since CLAUDE.md isn't in the repo) — added ci-pipeline-cleanup entry
to 'Recently shipped bundles' + new env-var summary line + two new
operator-decision items (RAM headroom + branch protection rules).
This commit is contained in:
shankar0123
2026-04-30 20:59:22 +00:00
parent 59ba163c95
commit 30970ab8a1
3 changed files with 483 additions and 0 deletions
+73
View File
@@ -0,0 +1,73 @@
# Reddit / HN announce — ci-pipeline-cleanup
> Don't auto-post. Operator times manually after the tag lands.
## r/devops / r/golang
> **certctl 2.X.0 — CI pipeline cleanup: 19 status checks → 7, ci.yml -71%**
>
> Open-source Go cert lifecycle tool. v2.X.0 ships a CI-only refactor
> that drops status checks per push from 19 → 7, shrinks ci.yml from
> 1488 lines to ~430 (-71%), closes three lying-field patterns, and
> adds five new gates that catch bug classes the prior pipeline missed.
>
> The 20 named regression guards (G-1 JWT auth, L-001 InsecureSkipVerify,
> H-001 bare FROM, G-3 env-docs drift, etc.) extracted from inline
> ci.yml bash to sibling scripts/ci-guards/<id>.sh — each callable
> locally as `bash scripts/ci-guards/<id>.sh`. Adding a new guard:
> drop a new script; CI loop auto-picks it up.
>
> Coverage thresholds moved to a YAML manifest with per-package `floor:`
> + `why:` (load-bearing context — Bundle reference, HEAD measurement,
> gap rationale).
>
> Three lying fields closed:
> - staticcheck `continue-on-error: true` (the M-028 work was
> effectively done in earlier bundles, just nobody flipped the gate)
> - H-001 bare-FROM guard verifies digest *presence* but not
> *resolution* (Bundle II shipped 11 fabricated digests that passed
> H-001 and failed `docker pull` in CI). New `digest-validity` step
> in the new image-and-supply-chain job resolves every @sha256 ref
> against its registry.
> - Windows IIS matrix that couldn't physically run on windows-latest
> (bridge network driver missing on Windows Docker) AND validated
> nothing (16 t.Log placeholders). Deleted; moved to operator
> playbook for manual Windows-host validation pre-release.
>
> Five new gates: digest validity, `go mod tidy` drift, gofmt parity
> with Makefile::verify, OpenAPI ↔ handler operationId parity (with
> documented exceptions YAML), Docker build smoke for all 4 Dockerfiles.
>
> Repo: <github>/certctl. Operator guide: docs/ci-pipeline.md.
## Hacker News
> **certctl: CI pipeline cleanup — 19 status checks → 7, ci.yml -71%**
>
> Open-source cert lifecycle tool. v2.X.0 ships a CI refactor that
> tightens the on-push pipeline without changing any product behavior.
>
> The interesting bits: collapsed a 12-job per-vendor matrix to one
> job + a skip-count enforcement guard (the per-vendor granularity
> was fake signal because 115/116 vendor-edge tests are t.Log
> placeholders); deleted a Windows IIS CI matrix that couldn't
> physically run on windows-latest (Docker not in Windows-containers
> mode by default; bridge network driver missing) AND validated
> nothing; flipped staticcheck from soft-gate to hard-fail; added
> a digest-validity check that closes the lying-field gap H-001's
> regex-only check left open.
>
> Coverage thresholds in a YAML manifest with per-package `why:`
> context. 20 regression guards as standalone scripts, each
> callable locally. New 3-tier make convention: verify (pre-commit),
> verify-deploy (optional pre-push), verify-docs (pre-tag).
## Discord (announcement channel template)
> 🚀 v2.X.0 ships ci-pipeline-cleanup — 19 status checks → 7,
> ci.yml -71%, 3 lying fields closed, 5 new gates.
>
> docs/ci-pipeline.md is the new operator guide. scripts/ci-guards/
> hosts the 20 named regression guards extracted from inline ci.yml
> bash. .github/coverage-thresholds.yml is the per-package floor
> manifest. cowork/ci-pipeline-cleanup/ has the bundle artefacts.
@@ -0,0 +1,191 @@
# 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 `<id>.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:<digest>` 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`).