mirror of
https://github.com/shankar0123/certctl.git
synced 2026-06-07 13:41:30 +00:00
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:
@@ -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`).
|
||||
@@ -0,0 +1,219 @@
|
||||
# CI Pipeline — Operator Guide
|
||||
|
||||
> Authoritative guide to certctl's CI pipeline shape.
|
||||
> Per `cowork/ci-pipeline-cleanup-prompt.md` Phase 12.
|
||||
|
||||
## Trigger model
|
||||
|
||||
Three triggers, each with its own scope. Don't mix.
|
||||
|
||||
| Trigger | Workflow | Scope | Wall-clock target |
|
||||
|---|---|---|---|
|
||||
| Push to master, PR to master | `.github/workflows/ci.yml` + `.github/workflows/codeql.yml` | Blocking — every check earns its keep | <10 min |
|
||||
| Daily 06:00 UTC + `workflow_dispatch` | `.github/workflows/security-deep-scan.yml` | Slow scans (gosec, osv, trivy, ZAP, schemathesis, nuclei, testssl, semgrep, mutation, `-race -count=10`); best-effort, never blocks | 60 min budget |
|
||||
| Tag push (`v*`) | `.github/workflows/release.yml` | Cross-platform binaries, ghcr.io push, SLSA provenance, GitHub release | n/a |
|
||||
|
||||
This guide covers the **on-push pipeline** only.
|
||||
|
||||
## On-push pipeline (7 status checks)
|
||||
|
||||
```
|
||||
push to master
|
||||
├── CI workflow (5 jobs)
|
||||
│ ├── go-build-and-test (~6-7 min)
|
||||
│ ├── frontend-build (~1 min)
|
||||
│ ├── helm-lint (~10 sec)
|
||||
│ ├── deploy-vendor-e2e (~5 min, depends on go-build-and-test)
|
||||
│ └── image-and-supply-chain (~3 min, parallel)
|
||||
└── CodeQL workflow (2 jobs)
|
||||
├── Analyze (go) (~5 min, parallel)
|
||||
└── Analyze (javascript-typescript) (~5 min, parallel)
|
||||
```
|
||||
|
||||
End-to-end wall-clock: dominated by `go-build-and-test` + `deploy-vendor-e2e` chain (~12 min) running in parallel with CodeQL (~5 min). Target ~10 min.
|
||||
|
||||
## Per-job deep-dive
|
||||
|
||||
### `go-build-and-test` (Ubuntu, ~6-7 min)
|
||||
|
||||
Runs the Go build/test suite + 18 of 20 regression guards.
|
||||
|
||||
Steps:
|
||||
1. `actions/checkout@v4`
|
||||
2. `actions/setup-go@v5` (Go 1.25.9)
|
||||
3. `go build ./cmd/...` (server, agent, mcp-server, cli)
|
||||
4. **gofmt drift** — `gofmt -l .` must be empty (Makefile::verify parity)
|
||||
5. **go mod tidy drift** — `go mod tidy && git diff --exit-code go.mod go.sum`
|
||||
6. `go vet ./...`
|
||||
7. Install + run **golangci-lint** v2.11.4 (`--timeout 5m`)
|
||||
8. Install + run **govulncheck** (hard gate)
|
||||
9. Install + run **staticcheck** (hard gate; `continue-on-error: false`)
|
||||
10. **Race Detection** — `go test -race -count=1 ./internal/...` (9-package list, 5min timeout)
|
||||
11. **Go Test with Coverage** — full coverage profile to `coverage.out`
|
||||
12. **Check Coverage Thresholds** — `bash scripts/check-coverage-thresholds.sh` (reads `.github/coverage-thresholds.yml`)
|
||||
13. **Upload Coverage Report** — artifact (`go-coverage`, 30-day retention)
|
||||
14. **Coverage PR comment** — posts/updates per-PR coverage table (PR builds only)
|
||||
15. **Regression guards** — loop runs all `scripts/ci-guards/*.sh` (18 of 20 guards)
|
||||
|
||||
Local equivalent: `make verify` covers steps 4, 6, 7, 11 (with `-short`).
|
||||
|
||||
### `frontend-build` (Ubuntu, ~1 min)
|
||||
|
||||
Vitest tests + tsc check + vite build + 2 of 20 regression guards (already covered by the ci-guards loop in `go-build-and-test`).
|
||||
|
||||
Steps:
|
||||
1. `actions/checkout@v4`
|
||||
2. `actions/setup-node@v4` (Node 22)
|
||||
3. `npm ci`
|
||||
4. `npx tsc --noEmit`
|
||||
5. `npx vitest run`
|
||||
6. `npx vite build`
|
||||
7. **Regression guards** — same `scripts/ci-guards/*.sh` loop as `go-build-and-test` (catches frontend-side guards: S-1, P-1, T-1, L-015, L-019, M-009, G-3)
|
||||
|
||||
### `helm-lint` (Ubuntu, ~10 sec)
|
||||
|
||||
Helm chart validation in 3 modes + inverse fail-loud test:
|
||||
1. `helm lint` with existingSecret
|
||||
2. `helm template` (existingSecret mode)
|
||||
3. `helm template` (cert-manager mode)
|
||||
4. `helm template` (no TLS source — MUST fail per fail-loud guard)
|
||||
|
||||
### `deploy-vendor-e2e` (Ubuntu, ~5 min, depends on `go-build-and-test`)
|
||||
|
||||
Single-job collapse of the prior 12-job matrix (per ci-pipeline-cleanup Phase 5 / frozen decision 0.4 — revises Bundle II decision 0.9).
|
||||
|
||||
Steps:
|
||||
1. `actions/checkout@v5`
|
||||
2. `actions/setup-go@v5` (Go 1.25.9, cache: true)
|
||||
3. **Build f5-mock-icontrol sidecar** — only sidecar without published image
|
||||
4. **Bring up all vendor sidecars** — `docker compose --profile deploy-e2e up -d` (11 sidecars)
|
||||
5. **Run all vendor-edge e2e** — `go test -tags integration -race -count=1 -run 'VendorEdge_'`; output captured to `test-output.log`
|
||||
6. **Skip-count enforcement** — `bash scripts/ci-guards/vendor-e2e-skip-check.sh test-output.log` (catches sidecar boot failures via skip-count vs allowlist)
|
||||
7. **Tear down sidecars** — `docker compose down -v` (always runs)
|
||||
|
||||
The `deploy-vendor-e2e-windows` matrix was deleted entirely (per ci-pipeline-cleanup Phase 6 / frozen decision 0.5 — revises Bundle II decision 0.4). IIS + WinCertStore validation moved to [`docs/connector-iis.md::Operator validation playbook`](connector-iis.md#operator-validation-playbook-windows-host).
|
||||
|
||||
### `image-and-supply-chain` (Ubuntu, ~3 min, parallel)
|
||||
|
||||
Three checks bundled (per ci-pipeline-cleanup Phases 7-9 / frozen decision 0.8):
|
||||
1. **Digest validity** — `bash scripts/ci-guards/digest-validity.sh`. Resolves every `@sha256:<digest>` ref in `deploy/**/*.{yml,Dockerfile*}` against its registry. Closes the H-001 lying-field gap.
|
||||
2. **Docker build smoke** — builds all 4 Dockerfiles (`Dockerfile`, `Dockerfile.agent`, `deploy/test/f5-mock-icontrol/Dockerfile`, `deploy/test/libest/Dockerfile`).
|
||||
3. **OpenAPI ↔ handler operationId parity** — `bash scripts/ci-guards/openapi-handler-parity.sh`. Every router route must have a matching `operationId` in `api/openapi.yaml` or be documented in `api/openapi-handler-exceptions.yaml`.
|
||||
|
||||
### CodeQL (Ubuntu × 2 languages, ~5 min)
|
||||
|
||||
`.github/workflows/codeql.yml` — interprocedural taint tracking. Two matrix jobs: `go` and `javascript-typescript`. Triggers on push, PR, and weekly Sunday cron.
|
||||
|
||||
## The 20 regression guards
|
||||
|
||||
Located at `scripts/ci-guards/<id>.sh`. Each script is callable locally:
|
||||
|
||||
```bash
|
||||
bash scripts/ci-guards/G-3-env-docs-drift.sh
|
||||
```
|
||||
|
||||
Or run all of them:
|
||||
|
||||
```bash
|
||||
for g in scripts/ci-guards/*.sh; do
|
||||
echo "=== $(basename "$g") ==="
|
||||
bash "$g" || echo " FAILED"
|
||||
done
|
||||
```
|
||||
|
||||
| ID | Catches |
|
||||
|---|---|
|
||||
| `G-1-jwt-auth-literal` | JWT silent auth downgrade reappearing |
|
||||
| `L-001-insecure-skip-verify` | Bare `InsecureSkipVerify: true` without `//nolint:gosec` |
|
||||
| `H-001-bare-from` | Bare Dockerfile `FROM` without `@sha256:` digest pin |
|
||||
| `M-012-no-root-user` | Dockerfile missing terminal `USER <non-root>` |
|
||||
| `H-009-readme-jwt` | README re-introducing JWT-as-supported claim |
|
||||
| `G-2-api-key-hash-json` | `api_key_hash` in JSON-emitting surface |
|
||||
| `U-2-plaintext-healthcheck` | Plaintext `http://` in HEALTHCHECK |
|
||||
| `U-3-migration-mount` | Migration file mounted into postgres initdb |
|
||||
| `D-1-D-2-statusbadge-phantom` | Dead StatusBadge keys + 8 TS phantom fields across 4 interfaces |
|
||||
| `L-1-bulk-action-loop` | Client-side `for ... await` bulk action loops |
|
||||
| `B-1-orphan-crud` | 8 update/create/delete fns lose page consumers |
|
||||
| `S-2-strings-contains-err` | `strings.Contains(err.Error(), ...)` brittle dispatch |
|
||||
| `G-3-env-docs-drift` | `CERTCTL_*` env var defined OR documented but not both |
|
||||
| `test-naming-convention` | `func TestXxx` lowercase first letter (Go silently skips) |
|
||||
| `S-1-hardcoded-source-counts` | Hardcoded "N issuer connectors" prose |
|
||||
| `P-1-documented-orphan-fns` | 16 read-fn names removed from client.ts exports |
|
||||
| `T-1-frontend-page-coverage` | New page in `web/src/pages/` without sibling `.test.tsx` |
|
||||
| `bundle-8-L-015-target-blank-rel-noopener` | `target="_blank"` without `rel="noopener noreferrer"` |
|
||||
| `bundle-8-L-019-dangerously-set-inner-html` | `dangerouslySetInnerHTML` outside `safeHtml.ts` |
|
||||
| `bundle-8-M-009-bare-usemutation` | Bare `useMutation()` outside the `useTrackedMutation` wrapper |
|
||||
|
||||
Plus three additional scripts for non-guard operator workflows:
|
||||
- `scripts/ci-guards/vendor-e2e-skip-check.sh` — vendor-e2e skip-count enforcement (used by `deploy-vendor-e2e` job)
|
||||
- `scripts/ci-guards/digest-validity.sh` — used by `image-and-supply-chain` job
|
||||
- `scripts/ci-guards/openapi-handler-parity.sh` — used by `image-and-supply-chain` job
|
||||
- `scripts/ci-guards/coverage-pr-comment.sh` — used by `go-build-and-test` job
|
||||
- `scripts/check-coverage-thresholds.sh` — used by `go-build-and-test` job
|
||||
|
||||
## Coverage thresholds
|
||||
|
||||
Manifest at `.github/coverage-thresholds.yml`. Each entry has `floor:` (integer percentage) + `why:` (load-bearing context). Lowering a floor REQUIRES corresponding code-side test work — never lower the gate to make CI green.
|
||||
|
||||
To add a new gated package: add an entry to the YAML; no script changes needed.
|
||||
|
||||
## Make targets — three-tier convention
|
||||
|
||||
| Target | When | What |
|
||||
|---|---|---|
|
||||
| `make verify` | **Required pre-commit** | gofmt + vet + golangci-lint + go test -short |
|
||||
| `make verify-deploy` | Optional pre-push | digest-validity + OpenAPI parity + Docker build smoke (server + agent only — fast subset) |
|
||||
| `make verify-docs` | **Required pre-tag** | QA-doc Part-count + seed-count drift checks |
|
||||
|
||||
## Adding a new check
|
||||
|
||||
| Check type | Where it goes | Auto-picked-up by CI? |
|
||||
|---|---|---|
|
||||
| Regression guard (grep / shape pattern) | New `scripts/ci-guards/<id>.sh` script | Yes — loop step iterates `*.sh` |
|
||||
| Coverage threshold (per-package) | New entry in `.github/coverage-thresholds.yml` | Yes — bash loop reads YAML |
|
||||
| OpenAPI route exception | New entry in `api/openapi-handler-exceptions.yaml` | Yes — parity script reads YAML |
|
||||
| Vendor-e2e expected skip | New line in `scripts/ci-guards/vendor-e2e-skip-allowlist.txt` | Yes — skip-check script reads file |
|
||||
| New CI job | Edit `.github/workflows/ci.yml` directly | n/a (job definition is the source) |
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
| CI step fails | Likely cause | Fix |
|
||||
|---|---|---|
|
||||
| `gofmt drift` | source needs `gofmt -w` | `make fmt` locally + commit |
|
||||
| `go mod tidy drift` | imported a package without committing go.mod | `go mod tidy` + commit |
|
||||
| `Run staticcheck` | new SA1019 deprecated-API site | migrate the API OR add `//lint:ignore SA1019 <reason>` |
|
||||
| `Check Coverage Thresholds` | per-package coverage dropped below floor | add tests; do NOT lower the floor |
|
||||
| `Regression guards` (any `<id>.sh`) | the audit-finding the guard pinned reappeared | read the guard's head-comment block for the closure rationale + fix the regression |
|
||||
| `Skip-count enforcement` | a vendor sidecar failed to start | check docker logs; fix sidecar; OR if a new Windows-only test was added, add to `scripts/ci-guards/vendor-e2e-skip-allowlist.txt` |
|
||||
| `Digest validity` | a `@sha256` digest doesn't resolve | re-resolve from registry, replace in compose / Dockerfile |
|
||||
| `OpenAPI ↔ handler parity` | new router route without operationId | add to `api/openapi.yaml` (preferred) OR `api/openapi-handler-exceptions.yaml` |
|
||||
| `Docker build smoke` | Dockerfile syntax error or COPY path drift | fix the Dockerfile |
|
||||
| `CodeQL Analyze` | interprocedural dataflow finding | review the SARIF in Security → Code scanning tab |
|
||||
|
||||
## Status check accounting
|
||||
|
||||
**Current (post-cleanup):** 7 status checks per push.
|
||||
- 1 × `Go Build & Test`
|
||||
- 1 × `Frontend Build`
|
||||
- 1 × `Helm Chart Validation`
|
||||
- 1 × `deploy-vendor-e2e`
|
||||
- 1 × `image-and-supply-chain`
|
||||
- 2 × `CodeQL Analyze (<lang>)` (go + javascript-typescript)
|
||||
|
||||
**Pre-cleanup (HEAD `1de61e91`):** 19 status checks. The 12-vendor matrix + 2-vendor Windows matrix collapsed to 1 + 0 respectively; the 3 Go/Frontend/Helm jobs unchanged; 2 CodeQL unchanged; 1 new `image-and-supply-chain` added.
|
||||
|
||||
## Required GitHub branch protection list
|
||||
|
||||
When updating the `master` branch protection rule (Settings → Branches), the "Require status checks to pass" list should be exactly:
|
||||
|
||||
```
|
||||
Go Build & Test
|
||||
Frontend Build
|
||||
Helm Chart Validation
|
||||
deploy-vendor-e2e
|
||||
image-and-supply-chain
|
||||
Analyze (go)
|
||||
Analyze (javascript-typescript)
|
||||
```
|
||||
|
||||
Old-name checks (`deploy-vendor-e2e (<vendor>)` × 12, `deploy-vendor-e2e-windows (<vendor>)` × 2) won't appear on new PRs after the workflow change. Operator removes them from the required list.
|
||||
Reference in New Issue
Block a user