CI/CD Baseline
Project: MCPg Baseline created: 2026-09-01 Last audited: 2026-09-01 ci-cd-plumber skill version: 0.1.0 (from agent-skills repo at audit time) Maturity target: high-assurance (production, long-lived, multi-channel release)
Platform & triggers
- Primary platform: GitHub Actions
- CI triggers:
pushtomainandclaude/**;pull_request(all) - Release trigger:
pushof tags matchingv*.*.* - Scheduled: Scorecard (weekly Thu), CodeQL (weekly Wed), Actions-security / zizmor (weekly Mon)
- Concurrency:
ci-$withcancel-in-progress: trueon the main CI workflow
Pipeline layers (observed)
- PR / main validation (
ci.yml)lint— ruff check + format –check, mypy onsrc/mcpgsecurity— pip-audit –strict (runtime deps), bandit SAST, non-blocking pip-licensestest— matrix PG 14–18 (required) + experimental PG 19 and WarehousePG; coverage gate fail_under=90
- Static / supply-chain security
codeql.yml— CodeQL python + actions languages on PR + main + scheduleactions-security.yml— zizmor (SARIF) + actionlint (reporting mode)scorecard.yml— OpenSSF Scorecard, publish_results + code-scanning upload- ClusterFuzzLite (
cflite_pr.yml,cflite_batch.yml)
- Release / promotion (
publish.ymlonv*.*.*tags)- build sdist+wheel → version-tag sanity check → CycloneDX SBOM → build-provenance attestation
- TestPyPI (OIDC) → smoke install (hash-pinned, simple-index poll) → human-gated PyPI (OIDC environment protection)
- GitHub Release (notes from CHANGELOG.md section) + attach dist + SBOM +
.mcpb - MCP Registry (OIDC), GHCR image (
:version+:latest), optional Smithery / HF Space refresh
- Docs site —
pages.yml - Dependency automation — Dependabot (pip + github-actions + docker), weekly, grouped minor/patch; Conventional Commit prefixes
Security posture (recorded decisions)
- Workflow default
permissions: contents: read(orread-allwhere appropriate); elevated scopes only on jobs that need them (id-token,attestations,security-events,packages,contents: writefor release) - Third-party actions pinned to full commit SHAs with version comments (including
github/codeql-actioninit/analyze/upload-sarif) persist-credentials: falseon checkouts- step-security/harden-runner on every job (egress-policy: audit — progressive rollout toward block)
- OIDC Trusted Publishing for PyPI / TestPyPI; OIDC for MCP Registry login and attestations
- CycloneDX SBOM +
actions/attest-build-provenanceon distributions - CODEOWNERS routes
/.github/workflows/, SQL kernel, policy, packaging, SECURITY.md to @devopam
Release documentation
- Keep a Changelog
CHANGELOG.md(SemVer) - Tag-driven releases; GitHub Release body extracted from matching CHANGELOG section
- Conventional Commits used by Dependabot and project practice
- Detailed operator playbook:
docs/release-process.md - Per-version release notes under
docs/release-notes-*.mdfor major milestones - No release-please / semantic-release automation (manual tag + process by design)
Deliberate exceptions (do not treat as regressions unless intent changes)
- zizmor / actionlint in reporting mode — see TODO below before promoting to blocking.
- Harden-Runner egress-policy: audit — see TODO below before block mode on publish.
- PG 19 and WarehousePG matrix lanes —
continue-on-error: true/ experimental; non-gating by design until GA / image stability. - Parameterized CI Postgres Dockerfile (
.github/ci-postgres.Dockerfile) — intentionally not digest-pinned (driven byPG_MAJORmatrix); other Dockerfiles use digests where fixed. - Docker build-push
provenance: falseon GHCR job — intentional to keep package page as a single clean manifest. - Manual tag-based release rather than release-please — matches documented release-process.md; not a gap unless automation is later desired.
TODO (deferred from 2026-09-01 ci-cd-plumber audit)
Do not treat these as open defects until you choose to schedule them. Order matches increasing operational risk.
- [M2] Promote zizmor / actionlint to blocking
- Precondition: confirm Security → Code scanning (zizmor category) is clean, or triage remaining findings / waivers.
- Then in
.github/workflows/actions-security.yml: removecontinue-on-error: trueon the zizmor step; set actionlintfail-on-error: true. - Avoid promoting while known open findings remain (otherwise every PR fails for no new value).
- [L1] Harden-Runner block mode on
publish.ymlfirst- Precondition: review egress audit logs from real publish runs; build an allow-list covering PyPI, TestPyPI, GHCR, MCP registry, uv/npx, attestation endpoints, etc.
- Only then switch
egress-policy: blockon publish jobs (not on guesswork — a bad list breaks tagged releases).
- Optional (leave unless priorities change)
- [L2] Path filters /
paths-ignoreonci.ymlfor pure-docs changes (trade-off: some doc PRs skip the test matrix). - [L3] Re-enable Docker build-push provenance on GHCR if multi-manifest package UI is acceptable.
- [L4] release-please / similar only if release cadence or contributor count grows enough to justify automation.
- [L2] Path filters /
Drift log
- 2026-09-01: Baseline created retroactively by ci-cd-plumber. First full audit run in the same invocation. Findings reported in conversation; no bulk auto-fixes applied.
- 2026-09-01: M1 remediated —
github/codeql-action/initandanalyzein.github/workflows/codeql.ymlpinned from mutable@v4tags to full SHAcdf488f595d80d6e07e03d4674febd5ab45fa938(# v4), matching existingupload-sarifpins elsewhere. - 2026-09-01: Remaining audit items (M2, L1, optional L2–L4) recorded under TODO (deferred…); no further changes in this pass.