From 0ba2957ab9d91ab6603b6ed5817e301d81e248c6 Mon Sep 17 00:00:00 2001 From: Mirotin Artem Date: Tue, 28 Apr 2026 22:02:09 +0300 Subject: [PATCH 1/4] ci: split go-test into fast PR gate + thorough main-push gate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The single 'Go Tests' job ran 'go test -race -coverprofile ./...' on every PR and push, which took ~6min on GitHub-hosted runners. -race adds 5-10x to test runtime; coverage adds another 10-20%. Most of that cost was paid on PR builds, including the weekly stream of dependabot patch bumps that touch zero Go code. Split the job: - go-test (Go Tests): fast 'go test -count=1 ./...' on every event. No -race, no coverage. Cuts the PR feedback loop to ~45-60s. - go-test-race (Go Tests (race + coverage)): full race + coverage pass, gated to push events on main only. Ensures every merge commit is race-clean and the Codecov dashboard tracks the deployable history. Pre-push hooks (.githooks/pre-push) already mirror the race run locally, so committers still get the race-detector signal before a push reaches the remote — the CI split moves the redundant PR-side race run to a single per-merge run instead. --- .github/workflows/ci.yml | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2043c1e1..e90106a9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,9 +14,29 @@ concurrency: cancel-in-progress: true jobs: + # PR / push gate: fast Go test pass without -race or coverage. + # Drops the wall-clock from ~6min to ~45-60s so dependabot PRs (and + # every PR from a human) get quick feedback. -race + coverage still + # run on every push to main via go-test-race below; pre-push hooks + # mirror the race run locally for committers. go-test: name: Go Tests runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - uses: actions/setup-go@v6 + with: + go-version: "1.26" + - run: go test -count=1 ./... + + # Slow gate: race detector + coverage. Runs only on push to main so + # the merge commit is always race-clean, and Codecov tracks coverage + # at the same cadence as the deployable history. Skipped on PRs and + # pushes to feature branches. + go-test-race: + name: Go Tests (race + coverage) + if: github.event_name == 'push' && github.ref == 'refs/heads/main' + runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - uses: actions/setup-go@v6 From 787df57e0f4d4fc6e9cfe87c0fcd7292f486d527 Mon Sep 17 00:00:00 2001 From: Mirotin Artem Date: Tue, 28 Apr 2026 22:19:43 +0300 Subject: [PATCH 2/4] ci(buf): disable pr_comment so buf-action stops failing on PR runs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit bufbuild/buf-action defaults to posting a 'lint passed' comment on the PR. The default GITHUB_TOKEN scoped via top-level 'permissions: contents: read' does not allow issue-comment writes, so even a clean lint reports 'Resource not accessible by integration' and turns the check red. GitHub already surfaces buf lint/breaking errors as inline annotations, so the comment is redundant — drop it instead of widening token scope. --- .github/workflows/ci.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e90106a9..f6006e37 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -228,11 +228,18 @@ jobs: buf-lint: name: Protobuf Lint runs-on: ubuntu-latest + # bufbuild/buf-action posts a status comment on the PR by default. + # Without pull-requests: write the post fails with 'Resource not + # accessible by integration' even when the lint itself passed — + # which used to surface as a red CI on every PR. Disable the + # comment instead of broadening token scope; GitHub annotations + # already surface buf errors inline on the diff. steps: - uses: actions/checkout@v6 - uses: bufbuild/buf-action@v1 with: lint: true + pr_comment: false sqlc-check: name: SQLc Verify From ed70b0cc766c3ed83d1f5bc0fd01aceeeacfb923 Mon Sep 17 00:00:00 2001 From: Mirotin Artem Date: Tue, 28 Apr 2026 22:30:12 +0300 Subject: [PATCH 3/4] ci: address Copilot review on PR #57 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three valid points from Copilot's review: 1. The 'skipped on PRs and pushes to feature branches' comment was misleading — push events on feature branches do not start the workflow at all (on.push.branches: [main] in this repo). Reword so the skip set matches what actually happens. 2. On main pushes both go-test and go-test-race ran, even though race+coverage already executes the full suite. Make go-test pull_request-only so the merge commit only runs the slower job (with coverage) and we stop paying for a duplicate ~45s run. 3. go-test-race gated to push-on-main was a post-merge alert, not a true merge gate — branch protection evaluates required checks on the PR run. Add a detect-changes job that flips a Go-touched output flag, and trigger go-test-race on PRs whose changes hit **/*.go / go.mod / go.sum / the workflow file. Pushes to main keep running it for coverage upload and post-merge sanity. PRs that do not touch Go (dependabot npm bumps, doc tweaks) skip the slow job entirely. --- .github/workflows/ci.yml | 47 +++++++++++++++++++++++++++++++--------- 1 file changed, 37 insertions(+), 10 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f6006e37..82b0bbca 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,13 +14,35 @@ concurrency: cancel-in-progress: true jobs: - # PR / push gate: fast Go test pass without -race or coverage. - # Drops the wall-clock from ~6min to ~45-60s so dependabot PRs (and - # every PR from a human) get quick feedback. -race + coverage still - # run on every push to main via go-test-race below; pre-push hooks - # mirror the race run locally for committers. + # detect-changes emits per-area output flags so downstream jobs can + # decide whether they actually need to run. Only Go-touching PRs + # need the slow race+coverage gate; npm/docs PRs (incl. dependabot + # patch bumps) skip it entirely. + detect-changes: + name: Detect changes + runs-on: ubuntu-latest + outputs: + go: ${{ steps.filter.outputs.go }} + steps: + - uses: actions/checkout@v6 + - uses: dorny/paths-filter@v3 + id: filter + with: + filters: | + go: + - '**/*.go' + - 'go.mod' + - 'go.sum' + - '.github/workflows/ci.yml' + + # PR fast gate: no -race, no coverage. Runs on every pull request so + # docs/CI/web-only PRs still get a Go-build sanity check (~45-60s). + # Skipped on push events because main pushes are post-merge — the + # PR-side run already proved the same tree, and go-test-race below + # re-validates with race+coverage. go-test: name: Go Tests + if: github.event_name == 'pull_request' runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 @@ -29,13 +51,18 @@ jobs: go-version: "1.26" - run: go test -count=1 ./... - # Slow gate: race detector + coverage. Runs only on push to main so - # the merge commit is always race-clean, and Codecov tracks coverage - # at the same cadence as the deployable history. Skipped on PRs and - # pushes to feature branches. + # Slow gate: race detector + coverage. Runs on: + # - PRs that touch Go files / go.mod / go.sum (real merge gate + # under branch protection — required check evaluates here) + # - pushes to main (covers the merge commit and uploads coverage) + # Skipped on PRs that do not touch Go (dependabot npm bumps, doc + # tweaks) so the long pole moves off the dependabot critical path. go-test-race: name: Go Tests (race + coverage) - if: github.event_name == 'push' && github.ref == 'refs/heads/main' + needs: detect-changes + if: | + (github.event_name == 'pull_request' && needs.detect-changes.outputs.go == 'true') || + (github.event_name == 'push' && github.ref == 'refs/heads/main') runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 From 98183cf1aa1052955f46acc629c50e6cd904e696 Mon Sep 17 00:00:00 2001 From: Mirotin Artem Date: Tue, 28 Apr 2026 22:42:40 +0300 Subject: [PATCH 4/4] ci: pin dorny/paths-filter to a commit SHA (Sonar hotspot) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SonarCloud flagged the dorny/paths-filter@v3 reference as a security hotspot — third-party action tags are mutable and can be rewritten by the upstream maintainer to point at a malicious commit. Pin to the exact commit SHA that v3 resolves to today, with a comment recording the tag for future readers. The other actions in this workflow (actions/checkout, setup-go, upload-artifact, codecov, bufbuild/buf-action, sqlc-dev/setup-sqlc, aquasecurity/trivy-action, gitleaks/gitleaks-action, golangci/golangci-lint-action) sit below the new-code analysis baseline and were not flagged. They carry the same risk and are good follow-up work, but pinning them is out of scope for this PR. --- .github/workflows/ci.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 82b0bbca..d7766587 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,7 +25,11 @@ jobs: go: ${{ steps.filter.outputs.go }} steps: - uses: actions/checkout@v6 - - uses: dorny/paths-filter@v3 + # Pinned to a commit SHA per the SonarCloud hotspot guidance — + # third-party actions can have their tags rewritten to point at + # a malicious commit, so consume the action by immutable hash. + # Tag at the time of pinning: v3 (commit d1c1ffe). + - uses: dorny/paths-filter@d1c1ffe0248fe513906c8e24db8ea791d46f8590 # v3 id: filter with: filters: |