diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml index f4b9dc5..6e0df06 100644 --- a/.github/workflows/docker-build.yml +++ b/.github/workflows/docker-build.yml @@ -154,6 +154,26 @@ jobs: type=sha,format=short type=raw,value=latest,enable={{is_default_branch}} + # OCI references must be lowercase, but `image-name` is normally + # `github.repository`, which preserves the org's casing (e.g. + # HordiaLabs/store-postgres). docker/metadata-action lowercases + # internally, so the push tags are fine — but the digest-based refs + # handed to Trivy and the SBOM generator below are built by hand from + # the raw input, and those tools reject a mixed-case reference with + # "could not parse reference". Resolve the lowercase form once here. + - name: Resolve image ref + id: imageref + shell: bash + env: + REGISTRY: ${{ inputs.registry }} + IMAGE_NAME: ${{ inputs.image-name }} + run: | + set -euo pipefail + # `tr` rather than bash 4's ${VAR,,}: this reusable also runs on + # self-hosted runners, and macOS still ships bash 3.2. + lower="$(printf '%s/%s' "$REGISTRY" "$IMAGE_NAME" | tr '[:upper:]' '[:lower:]')" + echo "repo=${lower}" >> "$GITHUB_OUTPUT" + - name: Build & push id: build uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0 @@ -200,7 +220,7 @@ jobs: if: inputs.run-trivy-scan && inputs.push uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0 with: - image-ref: ${{ inputs.registry }}/${{ inputs.image-name }}@${{ steps.build.outputs.digest }} + image-ref: ${{ steps.imageref.outputs.repo }}@${{ steps.build.outputs.digest }} format: table exit-code: '1' severity: ${{ inputs.trivy-severity }} @@ -210,7 +230,7 @@ jobs: if: inputs.run-trivy-scan && inputs.push && github.event_name != 'pull_request' uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0 with: - image-ref: ${{ inputs.registry }}/${{ inputs.image-name }}@${{ steps.build.outputs.digest }} + image-ref: ${{ steps.imageref.outputs.repo }}@${{ steps.build.outputs.digest }} format: sarif output: trivy.sarif severity: ${{ inputs.trivy-severity }} @@ -226,7 +246,7 @@ jobs: if: inputs.generate-sbom && inputs.push uses: anchore/sbom-action@3ad7283483fc7af8ff2b4ea19663c2d5ca935e26 # v0.24.2 with: - image: ${{ inputs.registry }}/${{ inputs.image-name }}@${{ steps.build.outputs.digest }} + image: ${{ steps.imageref.outputs.repo }}@${{ steps.build.outputs.digest }} format: spdx-json output-file: sbom.spdx.json upload-artifact: ${{ inputs.upload-artifacts }} diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 0934ec0..ada9b0e 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -127,6 +127,16 @@ on: description: "Package pattern to fuzz" type: string default: "./..." + go-cache: + description: | + Cache the Go module and build directories via actions/setup-go. + Set false on runners where the post-job cache save is unreliable: + on containerized self-hosted runners the `tar` that packs a cold + cgo-heavy build cache has been observed to wedge, burning the job's + whole timeout in the "Post Set up Go" step. Only applies to the + setup-go path (use-mise: false). + type: boolean + default: true postgres-enabled: description: | Start a throwaway Postgres container before the test step and export @@ -149,6 +159,13 @@ on: postgres-db: type: string default: "postgres" + postgres-ready-timeout: + description: | + Seconds to wait for Postgres to accept TCP connections before + failing. The image is pulled before the clock starts, so this + budgets container init only. + type: number + default: 60 secrets: DEPS_READER_PRIVATE_KEY: description: "Private key for the deps-reader GitHub App (required when deps-reader-client-id is set)" @@ -198,6 +215,7 @@ jobs: uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 with: go-version: ${{ inputs.go-version }} + cache: ${{ inputs.go-cache }} cache-dependency-path: ${{ inputs.working-directory }}/go.sum # setup-go caches the module/build dirs itself; the mise path has no @@ -279,6 +297,7 @@ jobs: PG_USER: ${{ inputs.postgres-user }} PG_PASSWORD: ${{ inputs.postgres-password }} PG_DB: ${{ inputs.postgres-db }} + PG_READY_TIMEOUT: ${{ inputs.postgres-ready-timeout }} run: | set -euo pipefail # On self-hosted runners the workspace isn't ephemeral, so a @@ -286,6 +305,12 @@ jobs: # skipped teardown) collides on the `ci-postgres` name. Remove any # stale one first so the step is idempotent and self-healing. docker rm -f ci-postgres >/dev/null 2>&1 || true + # Pull before starting the readiness clock. A cold pull of the + # postgres image on a slow runner used to eat most of the budget, + # so the wait below timed out on container init that had barely + # begun — the failure looked like a hung database rather than a + # slow download. + docker pull -q "$PG_IMAGE" docker run -d --name ci-postgres \ -e POSTGRES_USER="$PG_USER" \ -e POSTGRES_PASSWORD="$PG_PASSWORD" \ @@ -293,7 +318,7 @@ jobs: -p 5432:5432 \ "$PG_IMAGE" # Poll pg_isready inside the container until it accepts connections - # (image pull + init can take a few seconds), capped at ~60s. + # (init can take a few seconds), capped at postgres-ready-timeout. # # -h 127.0.0.1 IS THE POINT, not decoration. On first init the # postgres image's entrypoint runs a TEMPORARY server so it can @@ -307,14 +332,14 @@ jobs: # and gets `FATAL 57P03 (cannot_connect_now) the database system is # starting up`. Probing TCP cannot pass early: pg_isready exits 1 # ("rejecting connections") for the whole of that window. - for i in $(seq 1 60); do + for i in $(seq 1 "$PG_READY_TIMEOUT"); do if docker exec ci-postgres \ pg_isready -h 127.0.0.1 -p 5432 -U "$PG_USER" -d "$PG_DB" >/dev/null 2>&1; then echo "postgres accepting TCP connections after ${i}s" break fi - if [ "$i" = "60" ]; then - echo "::error::postgres did not become ready in 60s" + if [ "$i" = "$PG_READY_TIMEOUT" ]; then + echo "::error::postgres did not become ready in ${PG_READY_TIMEOUT}s" docker logs ci-postgres || true exit 1 fi diff --git a/CHANGELOG.md b/CHANGELOG.md index 8788e16..ea3f727 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,32 @@ project uses [SemVer](https://semver.org/) for the `vMAJOR.MINOR.PATCH` tags. ### Fixed +- `docker-build.yml` no longer fails the Trivy image scan and SBOM step for + orgs whose name contains uppercase letters. The digest-based refs were + assembled by hand from the raw `image-name` input — normally + `github.repository`, which preserves casing — while `docker/metadata-action` + lowercases internally for the push tags. Trivy rejected the result with + `could not parse reference: ghcr.io/HordiaLabs/store-postgres@sha256:...`, + so every push to the default branch failed at the scan step even though the + image had built and pushed cleanly. A new `Resolve image ref` step + lowercases registry + name once, and the three digest refs use it. + Lowercasing uses `tr` rather than bash 4's `${VAR,,}`, since this reusable + also runs on self-hosted macOS runners, where bash is still 3.2. + +- `go.yml` gained `go-cache` (default `true`, unchanged behaviour). Setting it + `false` skips `actions/setup-go`'s module/build cache on runners where the + post-job save is unreliable: on a containerized self-hosted runner the `tar` + that packs a cold cgo-heavy build cache has been seen to wedge, still + running 28 minutes later and consuming the job's entire 30-minute timeout in + the `Post Set up Go` step, after every real step had passed. + +- `go.yml` now pulls the Postgres image before starting the readiness clock, + and the wait is configurable via `postgres-ready-timeout` (default `60`, + unchanged). A cold pull on a slow runner used to consume most of the 60s + budget, so the wait expired on container init that had barely started — + reported as `postgres did not become ready in 60s` while the container log + showed the server coming up right at the boundary. + - Renovate's `customManager` now matches every `# renovate:` annotation in the repo (8 of 8, previously 5). The regex anchored `default:` to the line *immediately* after the comment, so three inputs that carried a `type:`