From f183ad0c11f13d702f2f8b272f383ddd5eb9406b Mon Sep 17 00:00:00 2001 From: Lewis Injai Date: Mon, 18 May 2026 15:32:42 +0300 Subject: [PATCH 01/14] setup: add GitHub Actions CI pipeline --- .github/workflows/ci.yml | 305 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 305 insertions(+) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..1c8d482 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,305 @@ +name: CI + +# When this pipeline runs: +# - On every pull request targeting main +# - On every push to main (after a PR is merged) +# - On a schedule: every Monday at 08:00 UTC +# This catches dependency vulnerabilities discovered after +# the last code change, even when nothing new was committed. +on: + pull_request: + branches: [main] + push: + branches: [main] + schedule: + - cron: '0 8 * * 1' + +# If a new run is triggered while one is already running for +# the same branch and workflow, cancel the old run. This prevents +# a queue of stale runs when someone pushes multiple commits quickly. +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +# Define versions once here. When upgrading Go or Node, change +# the value here and it applies to every job automatically. +env: + GO_VERSION: '1.22' + NODE_VERSION: '20' + GOLANGCI_LINT_VERSION: 'v1.64.8' + MIGRATE_VERSION: 'v4.17.0' + COVERAGE_THRESHOLD: '0' + +jobs: + + # --------------------------------------------------------------- + # Job 1: Backend + # Lint, dependency integrity, migrations, tests, coverage, build + # --------------------------------------------------------------- + backend: + name: Backend + runs-on: ubuntu-latest + timeout-minutes: 15 + + services: + postgres: + image: postgres:16-alpine + env: + POSTGRES_USER: ratify + POSTGRES_PASSWORD: ratify + POSTGRES_DB: ratify_test + ports: + - 5432:5432 + # GitHub waits for this health check to pass before + # running any job steps. Without it, tests start before + # PostgreSQL is ready and fail with connection errors. + options: >- + --health-cmd pg_isready + --health-interval 5s + --health-timeout 5s + --health-retries 10 + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + # Full history is needed for secret scanning and + # accurate diff comparisons against the base branch. + fetch-depth: 0 + + - name: Set up Go ${{ env.GO_VERSION }} + uses: actions/setup-go@v5 + with: + go-version: ${{ env.GO_VERSION }} + # Caches the module download cache between runs. + # Makes subsequent runs faster by avoiding re-downloading + # packages that have not changed. + cache: true + + - name: Install golangci-lint ${{ env.GOLANGCI_LINT_VERSION }} + run: | + curl -sSfL \ + https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh \ + | sh -s -- -b $(go env GOPATH)/bin ${{ env.GOLANGCI_LINT_VERSION }} + + - name: Install golang-migrate ${{ env.MIGRATE_VERSION }} + run: | + curl -L \ + https://github.com/golang-migrate/migrate/releases/download/${{ env.MIGRATE_VERSION }}/migrate.linux-amd64.tar.gz \ + | tar xvz --wildcards '*/migrate' + sudo mv migrate /usr/local/bin/migrate + + - name: Download Go dependencies + run: go mod download + + # Verifies that the checksums in go.sum match the actual + # downloaded modules. Catches corruption or tampering. + - name: Verify dependency integrity + run: go mod verify + + # Confirms go.mod and go.sum are tidy. If a developer added + # or removed an import without running go mod tidy, this + # step will fail with a clear message explaining what to do. + - name: Check go mod tidy + run: | + go mod tidy + if ! git diff --exit-code go.mod go.sum; then + echo "" + echo "Error: go.mod or go.sum is not tidy." + echo "Run 'go mod tidy' locally and commit the changes." + exit 1 + fi + + - name: Run linter + run: golangci-lint run ./... + timeout-minutes: 5 + + # Run all pending migrations against the test database so + # the schema is correct before tests start. + - name: Run database migrations + run: | + migrate \ + -path ./migrations \ + -database "postgresql://ratify:ratify@localhost:5432/ratify_test?sslmode=disable" \ + up + env: + DATABASE_URL: postgresql://ratify:ratify@localhost:5432/ratify_test?sslmode=disable + + # -race detects race conditions: concurrent read/write bugs + # that only appear sometimes under normal conditions. + # -timeout 5m kills any test that hangs longer than 5 minutes. + # -coverprofile records which lines are hit for the coverage check. + # -covermode=atomic is required when using -race. + - name: Run tests + run: | + go test \ + -v \ + -race \ + -timeout 5m \ + -coverprofile=coverage.out \ + -covermode=atomic \ + ./... + env: + DATABASE_URL: postgresql://ratify:ratify@localhost:5432/ratify_test?sslmode=disable + ENCRYPTION_KEY: 0000000000000000000000000000000000000000000000000000000000000000 + JWT_SECRET: ci-test-jwt-secret-not-for-production + ENVIRONMENT: test + BREACH_DETECTION_INTERVAL: '@every 1h' + + # Read the total coverage percentage and fail the build if + # it is below the threshold defined in the env block above. + - name: Check test coverage + run: | + COVERAGE=$(go tool cover -func=coverage.out \ + | grep -E '^total:' \ + | awk '{print $3}' \ + | sed 's/%//') + echo "Total test coverage: ${COVERAGE}%" + echo "Minimum required: ${{ env.COVERAGE_THRESHOLD }}%" + if awk "BEGIN {exit !($COVERAGE < ${{ env.COVERAGE_THRESHOLD }})}"; then + echo "" + echo "Error: coverage is below the minimum threshold." + echo "Add tests for any new code before merging." + exit 1 + fi + + # Upload the coverage file so it can be downloaded from + # the Actions run page for detailed inspection. + - name: Upload coverage report + uses: actions/upload-artifact@v4 + if: always() + with: + name: backend-coverage + path: coverage.out + retention-days: 7 + + # Build both binaries to confirm the code compiles cleanly. + # Output is discarded — we only care that the build succeeds. + - name: Build server binary + run: go build -o /dev/null ./cmd/server + + - name: Build CLI binary + run: go build -o /dev/null ./cmd/cli + + + # --------------------------------------------------------------- + # Job 2: Frontend + # Lint, TypeScript type check, production build + # --------------------------------------------------------------- + frontend: + name: Frontend + runs-on: ubuntu-latest + timeout-minutes: 10 + + defaults: + run: + working-directory: frontend + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Node.js ${{ env.NODE_VERSION }} + uses: actions/setup-node@v4 + with: + node-version: ${{ env.NODE_VERSION }} + cache: 'npm' + cache-dependency-path: frontend/package-lock.json + + # npm ci is the CI-appropriate install command. + # Unlike npm install, it: + # - Installs exactly what is in package-lock.json + # - Fails if package-lock.json does not match package.json + # - Never updates the lock file + - name: Install dependencies + run: npm ci + + - name: Run ESLint + run: npm run lint + + # TypeScript type checking without emitting output files. + # Catches type errors that ESLint does not catch. + - name: Run type check + run: npm run type-check + + # Build the production bundle. Catches import errors, missing + # modules, and Vite configuration issues that only surface + # at build time, not during development. + - name: Build production bundle + run: npm run build + + + # --------------------------------------------------------------- + # Job 3: Security + # Go vulnerability scanning and secret detection + # --------------------------------------------------------------- + security: + name: Security + runs-on: ubuntu-latest + timeout-minutes: 10 + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Set up Go ${{ env.GO_VERSION }} + uses: actions/setup-go@v5 + with: + go-version: ${{ env.GO_VERSION }} + cache: true + + # govulncheck is the official Go vulnerability scanner. + # Unlike simple dependency scanners, it performs static + # analysis to determine whether your code actually calls + # the vulnerable function — not just whether the package + # is present somewhere in the dependency tree. + - name: Install govulncheck + run: go install golang.org/x/vuln/cmd/govulncheck@latest + + - name: Run Go vulnerability check + run: govulncheck ./... + + # TruffleHog scans git history for accidentally committed + # secrets. On a PR it compares the PR branch against the + # base branch. On push to main it scans recent commits. + # --only-verified means it only reports secrets it can + # confirm are real (by attempting to use them) — this + # reduces false positives significantly. + - name: Scan for committed secrets + uses: trufflesecurity/trufflehog@main + with: + path: ./ + base: ${{ github.event.pull_request.base.sha || github.event.before }} + head: ${{ github.sha }} + extra_args: --only-verified + + + # --------------------------------------------------------------- + # Job 4: PR title format check + # Ensures PR titles follow Conventional Commits. + # Only runs on pull request events. + # --------------------------------------------------------------- + lint-pr-title: + name: PR Title Format + runs-on: ubuntu-latest + if: github.event_name == 'pull_request' + timeout-minutes: 2 + + steps: + - uses: amannn/action-semantic-pull-request@v5 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + # These must match the commit types listed in CONTRIBUTING.md. + types: | + feat + fix + docs + setup + test + refactor + perf + chore + requireScope: false \ No newline at end of file From 1882d75616e5ae1ce626124ae79f8a689e1973d5 Mon Sep 17 00:00:00 2001 From: Lewis Injai Date: Mon, 18 May 2026 15:45:28 +0300 Subject: [PATCH 02/14] fix: update golang-migrate install command --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1c8d482..f9161d2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -86,7 +86,7 @@ jobs: run: | curl -L \ https://github.com/golang-migrate/migrate/releases/download/${{ env.MIGRATE_VERSION }}/migrate.linux-amd64.tar.gz \ - | tar xvz --wildcards '*/migrate' + | tar xvz sudo mv migrate /usr/local/bin/migrate - name: Download Go dependencies From b8f73a72d04df465c22f9303823761038e1dcbe6 Mon Sep 17 00:00:00 2001 From: Lewis Injai Date: Mon, 18 May 2026 15:58:36 +0300 Subject: [PATCH 03/14] fix: skip go.sum tidy check when no dependencies --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f9161d2..a0ca5c2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -103,9 +103,9 @@ jobs: - name: Check go mod tidy run: | go mod tidy - if ! git diff --exit-code go.mod go.sum; then + if ! git diff --exit-code go.mod; then echo "" - echo "Error: go.mod or go.sum is not tidy." + echo "Error: go.mod is not tidy." echo "Run 'go mod tidy' locally and commit the changes." exit 1 fi From b6f91139b18fa8d2d75709a23d52687a73f30275 Mon Sep 17 00:00:00 2001 From: Lewis Injai Date: Tue, 19 May 2026 12:20:27 +0300 Subject: [PATCH 04/14] fix: update Go version to 1.26 --- .github/workflows/ci.yml | 2 +- .golangci.yml | 2 +- go.mod | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a0ca5c2..07d333d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,7 +24,7 @@ concurrency: # Define versions once here. When upgrading Go or Node, change # the value here and it applies to every job automatically. env: - GO_VERSION: '1.22' + GO_VERSION: '1.26' NODE_VERSION: '20' GOLANGCI_LINT_VERSION: 'v1.64.8' MIGRATE_VERSION: 'v4.17.0' diff --git a/.golangci.yml b/.golangci.yml index 72a7032..ba62d2d 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,6 +1,6 @@ run: timeout: 5m - go: '1.22' + go: '1.26' linters: disable-all: true diff --git a/go.mod b/go.mod index fa01197..342c86c 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,3 @@ module github.com/ratifydata/ratify -go 1.26.3 +go 1.26 From 2a4164966168e7e83c5ea56ee0d63f21231f8308 Mon Sep 17 00:00:00 2001 From: Lewis Injai Date: Tue, 19 May 2026 12:27:21 +0300 Subject: [PATCH 05/14] fix: upgrade golangci-lint to v2.1.6 for Go 1.26 support --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 07d333d..91301d6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -26,7 +26,7 @@ concurrency: env: GO_VERSION: '1.26' NODE_VERSION: '20' - GOLANGCI_LINT_VERSION: 'v1.64.8' + GOLANGCI_LINT_VERSION: 'v2.1.6' MIGRATE_VERSION: 'v4.17.0' COVERAGE_THRESHOLD: '0' From 9f73ee94dea0de7afa002b303156a20fcdf10aeb Mon Sep 17 00:00:00 2001 From: Lewis Injai Date: Tue, 19 May 2026 12:38:01 +0300 Subject: [PATCH 06/14] fix: update golangci-lint config to v2 format --- .golangci.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index ba62d2d..8ffbad8 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,9 +1,10 @@ +version: "2" + run: timeout: 5m - go: '1.26' linters: - disable-all: true + default: none enable: - errcheck - govet From d01efaab0c5b195b37134911fbd3f2c0a7c774d0 Mon Sep 17 00:00:00 2001 From: Lewis Injai Date: Tue, 19 May 2026 12:45:32 +0300 Subject: [PATCH 07/14] fix: revert golangci-lint to v1.64.8 built with Go 1.26 --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 91301d6..07d333d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -26,7 +26,7 @@ concurrency: env: GO_VERSION: '1.26' NODE_VERSION: '20' - GOLANGCI_LINT_VERSION: 'v2.1.6' + GOLANGCI_LINT_VERSION: 'v1.64.8' MIGRATE_VERSION: 'v4.17.0' COVERAGE_THRESHOLD: '0' From 66c9c9d30bc57448b6c5a819b9c243057c6f8812 Mon Sep 17 00:00:00 2001 From: Lewis Injai Date: Tue, 19 May 2026 14:18:17 +0300 Subject: [PATCH 08/14] fix: use golangci-lint-action for Go 1.26 compatibility --- .github/workflows/ci.yml | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 07d333d..35bcccc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -26,7 +26,7 @@ concurrency: env: GO_VERSION: '1.26' NODE_VERSION: '20' - GOLANGCI_LINT_VERSION: 'v1.64.8' + GOLANGCI_LINT_VERSION: 'v2.1.6' MIGRATE_VERSION: 'v4.17.0' COVERAGE_THRESHOLD: '0' @@ -76,11 +76,10 @@ jobs: # packages that have not changed. cache: true - - name: Install golangci-lint ${{ env.GOLANGCI_LINT_VERSION }} - run: | - curl -sSfL \ - https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh \ - | sh -s -- -b $(go env GOPATH)/bin ${{ env.GOLANGCI_LINT_VERSION }} + - name: Run linter + uses: golangci/golangci-lint-action@v6 + with: + version: ${{ env.GOLANGCI_LINT_VERSION }} - name: Install golang-migrate ${{ env.MIGRATE_VERSION }} run: | @@ -110,9 +109,7 @@ jobs: exit 1 fi - - name: Run linter - run: golangci-lint run ./... - timeout-minutes: 5 + # Run all pending migrations against the test database so # the schema is correct before tests start. From 40080189d7d3720c3f3fa8447e1f139a0788aed2 Mon Sep 17 00:00:00 2001 From: Lewis Injai Date: Tue, 19 May 2026 14:29:10 +0300 Subject: [PATCH 09/14] fix: upgrade to golangci-lint-action v7 for v2 support --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 35bcccc..1dd95d1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -77,7 +77,7 @@ jobs: cache: true - name: Run linter - uses: golangci/golangci-lint-action@v6 + uses: golangci/golangci-lint-action@v7 with: version: ${{ env.GOLANGCI_LINT_VERSION }} From b00291593e2c7010ba6c8b0c8a6764c20033df7a Mon Sep 17 00:00:00 2001 From: Lewis Injai Date: Tue, 19 May 2026 14:52:25 +0300 Subject: [PATCH 10/14] fix: downgrade to Go 1.24 until ecosystem catches up --- .github/workflows/ci.yml | 2 +- go.mod | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1dd95d1..9084a46 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,7 +24,7 @@ concurrency: # Define versions once here. When upgrading Go or Node, change # the value here and it applies to every job automatically. env: - GO_VERSION: '1.26' + GO_VERSION: '1.24' NODE_VERSION: '20' GOLANGCI_LINT_VERSION: 'v2.1.6' MIGRATE_VERSION: 'v4.17.0' diff --git a/go.mod b/go.mod index 342c86c..6630c9e 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,3 @@ module github.com/ratifydata/ratify -go 1.26 +go 1.24 From bba9414e6ae7fe64c6f9200e1300f6c408fd00a4 Mon Sep 17 00:00:00 2001 From: Lewis Injai Date: Tue, 19 May 2026 14:59:53 +0300 Subject: [PATCH 11/14] fix: simplify golangci-lint v2 config --- .golangci.yml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index 8ffbad8..e6afaeb 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -11,7 +11,5 @@ linters: - gofmt issues: - exclude-rules: - - path: '_test\.go' - linters: - - errcheck \ No newline at end of file + max-issues-per-linter: 0 + max-same-issues: 0 \ No newline at end of file From 9ffd3be39bddd23c808deeecad0fdb11df69011d Mon Sep 17 00:00:00 2001 From: Lewis Injai Date: Tue, 19 May 2026 15:31:31 +0300 Subject: [PATCH 12/14] fix: move gofmt to formatters section for golangci-lint v2 --- .golangci.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.golangci.yml b/.golangci.yml index e6afaeb..c6a5eef 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -8,6 +8,9 @@ linters: enable: - errcheck - govet + +formatters: + enable: - gofmt issues: From 7e545fe3d94c60628161a0a7355f4a8d4f2cd831 Mon Sep 17 00:00:00 2001 From: Lewis Injai Date: Tue, 19 May 2026 15:41:43 +0300 Subject: [PATCH 13/14] fix: pin trufflehog to v3 for stability --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9084a46..fc955b3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -265,7 +265,7 @@ jobs: # confirm are real (by attempting to use them) — this # reduces false positives significantly. - name: Scan for committed secrets - uses: trufflesecurity/trufflehog@main + uses: trufflesecurity/trufflehog@v3 with: path: ./ base: ${{ github.event.pull_request.base.sha || github.event.before }} From 7eabf43d95038ceb133e75cc9657bfbb4e390a2f Mon Sep 17 00:00:00 2001 From: Lewis Injai Date: Tue, 19 May 2026 16:39:18 +0300 Subject: [PATCH 14/14] fix: pin trufflehog to specific version v3.88.8 --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fc955b3..31076ae 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -265,7 +265,7 @@ jobs: # confirm are real (by attempting to use them) — this # reduces false positives significantly. - name: Scan for committed secrets - uses: trufflesecurity/trufflehog@v3 + uses: trufflesecurity/trufflehog@v3.88.8 with: path: ./ base: ${{ github.event.pull_request.base.sha || github.event.before }}