ci: harden PR workflow with docker build, codeql, security scans, and pr-lint#53
Merged
Conversation
- Remove .github/workflows/codeql.yml: repo uses CodeQL default setup which already covers actions/go/javascript-typescript/python. - Switch osv-scanner to the subdirectory action path (google/osv-scanner-action/osv-scanner-action) and pin v2.3.5. The previous root-path SHA pointed to v1.6.2-beta1 which has no top-level runs: section. - Update maintainer doc required-checks list to match default-setup CodeQL check names.
The web Dockerfile expects to be built from the repo root (it COPYs pnpm-workspace.yaml + pnpm-lock.yaml from /), but the compose file still pointed at ./web. This made docker compose up --build fail on the web service. The deploy workflow already does this; align compose with that.
osv-scanner flagged 16 vulnerabilities in Go stdlib 1.26.2 affecting both api/go.mod and goopy/go.mod. All fixed in 1.26.3. Refs: GO-2026-4918, 4971, 4976, 4977, 4980, 4981, 4982, 4986
Aligns with the recent pnpm 11 migration in deploy and the open Renovate PR #45. Lockfile is v9 which both pnpm 10 and 11 support, so this is a transparent bump.
The TestPackageHandler_Get_DifferentPackagesIndependent test threshold was tuned to Go 1.26.2 scheduler timing. Under 1.26.3 on Ubuntu CI, goroutines occasionally interleave such that 4/5 concurrent requests share a singleflight call instead of all 5. The test still catches the no-dedup regression (>4 = bug).
The previous version raced: it relied on goroutines arriving at singleflight before the first upstream call returned. Under Go 1.26.3 on Ubuntu CI, scheduler timing changed and one goroutine would slip past, producing 4 upstream calls instead of the expected 1. Replace the brittle threshold with a deterministic gate: pin every upstream request open on a release channel, poll until the counters settle at the expected state (exactly 1 per package), then release. While the gate is held, any singleflight regression would surface as upstream count >1 long before the deadline. Reverts the threshold loosening (>3 -> >4) and asserts == 1 instead. Verified locally with -race -count=10.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds Tier 1+2 of the CI/CD hardening plan. Five new/extended workflows plus documentation to enable a defensible OSS PR workflow.
New workflows
docker.yml— buildsapiandwebimages on every PR (no push), publishes:mainand:sha-<short>tags on main push, runs a docker-compose smoke test against the prod profile (Caddy → web/api), and Trivy-scans published images. Image names matchdeploy.yml(ghcr.io/mattstrayer/pypx/{api,web});:latestand:vX.Y.Zremain owned bydeploy.yml(workflow_dispatch).codeql.yml— weekly + per-PR CodeQL for Go (manual build of bothapi/andgoopy/) and JavaScript/TypeScript.security.yml— gitleaks (full history), govulncheck (matrix overapi/goopy), osv-scanner with SARIF upload to Code scanning, andactions/dependency-review-action(PR-only, fails onhighseverity, license allow-list).pr-lint.yml— enforces Conventional Commit PR titles viaamannn/action-semantic-pull-request. Closes the gap where pre-commit's hook only catches local commits, not squash-merge titles.CI extension
web-buildadded toci.yml. Runspnpm run buildto catch SSR-time regressions that lint/unit-tests miss.Documentation
docs/CONTRIBUTING-MAINTAINERS.md— branch protection ruleset checklist, repo settings checklist (Copilot review, Dependabot, secret scanning, push protection, squash-only merges), GHCR package visibility, and a process for adding new required checks over time.CONTRIBUTING.md— documents PR-only workflow, squash-merge requirement, and points to the maintainer doc.Deferred (not in this PR)
web-typecheckjob — tracked in Enable nuxi typecheck in CI #52. Addingvue-tsc+typescripttriggers a pnpm peer-dep regression that breaksnuxt-site-configfor@nuxtjs/sitemap. Combined with a pile of pre-existing TS errors, this needs its own focused PR.Action SHA pinning
All third-party actions pinned by full commit SHA, matching the project's existing scorecard-friendly convention.
Test plan
These checks should appear on this PR and run green before any required-status configuration:
After all green, follow the maintainer-doc checklist to enable Copilot code review, branch protection, secret scanning, and the rest. Then mark the GHCR packages public after the first main push.
Likely friction (anticipated)
dependency-reviewmay flag a license outside the allow-list — extend list or replace dep.osv-scannermay surface pre-existing CVEs in transitive deps; doesn't fail the workflow but inspect the Code Scanning tab.compose-smokecould time out on slow runners; bump iterations if so.