build(deps): bump go_modules minor-and-patch group (6 updates) #1747
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
| # CI pipeline for OpenDecree server, SDKs, and CLI. | |
| # | |
| # Jobs: changes ──┬── tools, server-image → integration (e2e|examples|stress) ─→ check (alls-green gate) | |
| # ├── → upgrade ───────────────────────────┤ | |
| # ├── lint, docs ──────────────────────────────────────────────┤ | |
| # └── test, sdk-compat, govulncheck, deps-review ──────────────┘ | |
| # | |
| # The changes job runs dorny/paths-filter; code jobs skip on docs-only PRs; | |
| # the docs job additionally skips unless proto/CLI/mkdocs files changed. | |
| # | |
| # The tools job builds/caches the decree-tools Docker image (goose/migrate), | |
| # and the server-image job builds the decree server image ONCE and pushes it to | |
| # ghcr by digest. Both use a registry-backed buildx cache (:buildcache tag) so | |
| # unchanged layers are pulled from ghcr and only modified layers rebuild. The | |
| # integration legs (e2e/examples/stress) and the upgrade job then just `docker | |
| # pull` the prebuilt server image instead of rebuilding it once per leg. | |
| # | |
| # Security posture (issue #8): | |
| # - Workflow-level permissions default to `contents: read` — jobs opt into more. | |
| # - Only `tools` and `server-image` have `packages: write` (they push the | |
| # tools and server images); consumers get `packages: read` to pull. | |
| # - All actions/checkout steps use `persist-credentials: false` so the | |
| # GITHUB_TOKEN isn't left in .git/config for later steps. | |
| # - `deps-review` blocks PRs that add dependencies with known high-severity | |
| # vulnerabilities. | |
| # - `govulncheck` scans Go code for known vulnerabilities in imported packages. | |
| # | |
| # The check job aggregates all results for branch protection (skipped jobs allowed). | |
| # | |
| # Cache budget: Docker layer caches live on ghcr.io as `:buildcache` tags on | |
| # their respective image repos (no 10GB GHA cache limit). Writers: | |
| # - main.yml → ghcr.io/<owner>/decree:buildcache, decree-cli:buildcache | |
| # - release.yml → same refs (shared with main.yml, mode=max) | |
| # - tools job → ghcr.io/<owner>/decree-tools:buildcache | |
| # - server-image job → reads ghcr.io/<owner>/decree:buildcache (main.yml is | |
| # the canonical writer) and writes the shared `server-build` GHA scope. | |
| # ci.yml integration (e2e/examples/stress) and upgrade are read-only consumers: | |
| # they `docker pull` the prebuilt server image and no longer build it. | |
| # E2E benchmarks moved to release.yml — too slow for per-PR CI. | |
| # The unit/infra `bench` job lives in bench.yml — it ran push-to-main only and | |
| # was purely informational, but sat in check.needs and so delayed the alls-green | |
| # gate 7-10 min on every push. Relocated to a nightly schedule (issue #970). | |
| name: CI | |
| on: | |
| pull_request: | |
| branches: [main] | |
| push: | |
| branches: [main] | |
| workflow_call: | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }} | |
| cancel-in-progress: true | |
| permissions: | |
| contents: read | |
| env: | |
| TOOLS_IMAGE: ghcr.io/${{ github.repository_owner }}/decree-tools | |
| SERVER_IMAGE: ghcr.io/${{ github.repository_owner }}/decree | |
| jobs: | |
| # Detects which paths changed so downstream jobs can skip on docs-only PRs. | |
| changes: | |
| name: Detect changes | |
| runs-on: ubuntu-latest | |
| permissions: | |
| pull-requests: read | |
| timeout-minutes: 5 | |
| outputs: | |
| code: ${{ steps.filter.outputs.code }} | |
| helm: ${{ steps.filter.outputs.helm }} | |
| migrations: ${{ steps.filter.outputs.migrations }} | |
| docs: ${{ steps.filter.outputs.docs }} | |
| non_grpctransport_code: ${{ steps.filter.outputs.non_grpctransport_code }} | |
| go_code: ${{ steps.filter.outputs.go_code }} | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v7 | |
| with: | |
| persist-credentials: false | |
| - name: Run path filter | |
| # dorny/paths-filter@v4 | |
| uses: dorny/paths-filter@7b450fff21473bca461d4b92ce414b9d0420d706 | |
| id: filter | |
| with: | |
| filters: | | |
| code: | |
| - '**/*.go' | |
| - '**/go.mod' | |
| - '**/go.sum' | |
| - 'proto/**' | |
| - 'db/**' | |
| - 'build/**' | |
| - '.dockerignore' | |
| - 'Makefile' | |
| - 'docker-compose*.yml' | |
| - '.github/workflows/**' | |
| helm: | |
| - 'deploy/helm/**' | |
| - '.github/workflows/ci.yml' | |
| migrations: | |
| - 'db/migrations/**' | |
| docs: | |
| - 'proto/**' | |
| - 'cmd/decree/**' | |
| - 'internal/version/**' | |
| - 'Makefile' | |
| - 'mkdocs.yml' | |
| non_grpctransport_code: | |
| - '**/*.go' | |
| - '!sdk/grpctransport/**' | |
| - '**/go.mod' | |
| - '**/go.sum' | |
| - 'proto/**' | |
| - 'db/**' | |
| - 'build/**' | |
| - 'Makefile' | |
| - 'docker-compose*.yml' | |
| - '.github/workflows/**' | |
| go_code: | |
| - '**/*.go' | |
| - '**/go.mod' | |
| - '**/go.sum' | |
| - 'proto/**' | |
| - 'db/**' | |
| # Builds/pushes the shared decree-tools Docker image used by downstream jobs. | |
| # Skips the build if an image tagged with the Dockerfile hash already exists | |
| # on ghcr; registry-backed buildx cache reuses unchanged layers on miss. | |
| tools: | |
| name: Tools image | |
| runs-on: ubuntu-latest | |
| needs: changes | |
| if: needs.changes.outputs.code == 'true' | |
| permissions: | |
| contents: read | |
| packages: write | |
| timeout-minutes: 10 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v7 | |
| with: | |
| persist-credentials: false | |
| - name: Compute Dockerfile hash | |
| id: hash | |
| run: echo "tag=sha-$(sha256sum build/Dockerfile.tools | head -c 12)" >> $GITHUB_OUTPUT | |
| - name: Check if image exists | |
| id: check | |
| run: | | |
| if docker manifest inspect "${{ env.TOOLS_IMAGE }}:${{ steps.hash.outputs.tag }}" > /dev/null 2>&1; then | |
| echo "exists=true" >> $GITHUB_OUTPUT | |
| else | |
| echo "exists=false" >> $GITHUB_OUTPUT | |
| fi | |
| env: | |
| DOCKER_CLI_EXPERIMENTAL: enabled | |
| - name: Set up Docker Buildx | |
| if: steps.check.outputs.exists == 'false' | |
| uses: docker/setup-buildx-action@v4 | |
| - name: Log in to ghcr.io | |
| if: steps.check.outputs.exists == 'false' | |
| uses: docker/login-action@v4 | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Build and push tools image | |
| if: steps.check.outputs.exists == 'false' | |
| uses: docker/build-push-action@v7 | |
| with: | |
| context: . | |
| file: build/Dockerfile.tools | |
| push: true | |
| tags: ${{ env.TOOLS_IMAGE }}:${{ steps.hash.outputs.tag }} | |
| cache-from: type=registry,ref=${{ env.TOOLS_IMAGE }}:buildcache | |
| cache-to: type=registry,ref=${{ env.TOOLS_IMAGE }}:buildcache,mode=max | |
| outputs: | |
| image: ${{ env.TOOLS_IMAGE }}:${{ steps.hash.outputs.tag }} | |
| # Builds the server image ONCE and pushes it to ghcr by digest, so the | |
| # integration matrix (e2e/examples/stress) and the upgrade job each pull a | |
| # single prebuilt image instead of rebuilding it 4×. Runs parallel to `tools` | |
| # (both gated only on `changes`). Uses the same two-tier cache as before: | |
| # the registry :buildcache (read-only; main.yml is the canonical writer) plus | |
| # the shared GHA scope. Pushing by digest avoids tag churn on ghcr. | |
| server-image: | |
| name: Server image | |
| runs-on: ubuntu-latest | |
| needs: changes | |
| if: needs.changes.outputs.code == 'true' | |
| permissions: | |
| contents: read | |
| packages: write | |
| timeout-minutes: 15 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v7 | |
| with: | |
| persist-credentials: false | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v4 | |
| - name: Log in to ghcr.io | |
| uses: docker/login-action@v4 | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Build and push server image by digest | |
| id: build | |
| uses: docker/build-push-action@v7 | |
| with: | |
| context: . | |
| file: build/Dockerfile | |
| cache-from: | | |
| type=registry,ref=${{ env.SERVER_IMAGE }}:buildcache | |
| type=gha,scope=server-build | |
| cache-to: type=gha,scope=server-build,mode=max | |
| outputs: type=image,name=${{ env.SERVER_IMAGE }},push-by-digest=true,name-canonical=true,push=true | |
| outputs: | |
| image: ${{ env.SERVER_IMAGE }}@${{ steps.build.outputs.digest }} | |
| # Runs golangci-lint against Go code and buf lint/breaking against protos. | |
| # buf is installed via go install (no Docker image needed), so this job runs | |
| # parallel to tools instead of waiting for it. | |
| # buf breaking needs the tip of main, fetched explicitly in the next step. | |
| lint: | |
| name: Lint | |
| runs-on: ubuntu-latest | |
| needs: [changes] | |
| if: needs.changes.outputs.code == 'true' | |
| permissions: | |
| contents: read | |
| timeout-minutes: 10 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v7 | |
| with: | |
| persist-credentials: false | |
| - name: Fetch main branch for buf breaking | |
| if: github.ref != 'refs/heads/main' | |
| run: git fetch origin main:main --depth=1 | |
| - name: Check supply-chain pins | |
| run: ./scripts/check-supply-chain-pins.sh | |
| - name: Lint workflow files | |
| # rhysd/actionlint:1.7.12 — catches invalid workflow syntax (e.g. a | |
| # context used somewhere it isn't available) before it reaches GitHub, | |
| # where such errors silently produce zero jobs instead of a clear | |
| # failure. See #931. shellcheck/pyflakes are disabled here: they lint | |
| # the shell/Python embedded in `run:` steps, a different and much | |
| # noisier concern than workflow-file validity — out of scope for this | |
| # gate. | |
| uses: docker://rhysd/actionlint@sha256:b1934ee5f1c509618f2508e6eb47ee0d3520686341fec936f3b79331f9315667 | |
| with: | |
| args: -color -shellcheck= -pyflakes= | |
| - name: Set up Go | |
| uses: actions/setup-go@v7 | |
| with: | |
| go-version-file: go.mod | |
| cache-dependency-path: "**/go.sum" | |
| - name: Go lint | |
| # golangci/golangci-lint-action@v9 | |
| uses: golangci/golangci-lint-action@ba0d7d2ec06a0ea1cb5fa41b2e4a3ab91d21278a | |
| with: | |
| version: v2.11.4 | |
| - name: Go format check | |
| run: golangci-lint fmt --diff | |
| - name: Go lint (contrib + SDK modules + CLI) | |
| # golangci-lint-action only lints the root module's working directory. | |
| # Loop over the same module list used by `make test`/`build`/`vet` so | |
| # contrib/decree-docs, the SDK modules, and cmd/decree are covered too. | |
| run: | | |
| for mod in sdk/retry sdk/configclient sdk/adminclient sdk/configwatcher sdk/grpctransport sdk/tools sdk/contrib/viper sdk/contrib/envconfig sdk/contrib/koanf contrib/decree-docs cmd/decree; do | |
| echo "::group::golangci-lint: $mod" | |
| (cd "$mod" && golangci-lint run ./... && golangci-lint fmt --diff) | |
| echo "::endgroup::" | |
| done | |
| - name: Install buf | |
| env: | |
| BUF_VERSION: v1.66.1 | |
| # sha256 of buf-Linux-x86_64 from the official v1.66.1 release (sha256.txt). | |
| BUF_SHA256: ef835cb38ed973849f68e0e6a88153cb2168507e09eecbec43a2ada2f6a698be | |
| run: | | |
| mkdir -p "$HOME/.local/bin" | |
| curl -fsSL "https://github.com/bufbuild/buf/releases/download/${BUF_VERSION}/buf-Linux-x86_64" -o "$HOME/.local/bin/buf" | |
| echo "${BUF_SHA256} $HOME/.local/bin/buf" | sha256sum -c - | |
| chmod +x "$HOME/.local/bin/buf" | |
| echo "$HOME/.local/bin" >> "$GITHUB_PATH" | |
| "$HOME/.local/bin/buf" --version | |
| - name: Proto lint + breaking | |
| run: buf lint && buf breaking --against ".git#branch=main" | |
| # Runs the unit tests under the race detector with coverage, then enforces | |
| # the internal-module coverage ratchet. Split from the SDK/CLI/tools tests | |
| # (test-modules) so the two heavy sequential steps run on separate runners | |
| # concurrently. -race coverage of internal/ is preserved here unchanged. | |
| test-internal: | |
| name: Test (internal) | |
| runs-on: ubuntu-latest | |
| needs: changes | |
| if: needs.changes.outputs.code == 'true' | |
| timeout-minutes: 15 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v7 | |
| with: | |
| persist-credentials: false | |
| - name: Set up Go | |
| uses: actions/setup-go@v7 | |
| with: | |
| go-version-file: go.mod | |
| cache-dependency-path: "**/go.sum" | |
| - name: Unit tests | |
| run: go test ./internal/... -race -count=1 -coverprofile=coverage-internal.out -covermode=atomic | |
| - name: Coverage ratchet | |
| run: ./scripts/check-coverage.sh --profiles-only --profile internal=coverage-internal.out | |
| - name: Upload coverage to Codecov | |
| # codecov/codecov-action@v6.0.0 | |
| uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f | |
| with: | |
| files: coverage-internal.out | |
| token: ${{ secrets.CODECOV_TOKEN }} | |
| # Runs the SDK, CLI, and tools module tests in parallel, then enforces the | |
| # coverage ratchet for those modules. Split from test-internal so it runs on | |
| # a separate runner concurrently with the race-enabled unit tests. | |
| test-modules: | |
| name: Test (modules) | |
| runs-on: ubuntu-latest | |
| needs: changes | |
| if: needs.changes.outputs.code == 'true' | |
| timeout-minutes: 15 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v7 | |
| with: | |
| persist-credentials: false | |
| - name: Set up Go | |
| uses: actions/setup-go@v7 | |
| with: | |
| go-version-file: go.mod | |
| cache-dependency-path: "**/go.sum" | |
| - name: SDK + CLI + tools tests | |
| run: | | |
| pids=() | |
| cd sdk/configclient && go test ./... -count=1 -coverprofile=../../cov-configclient.out -covermode=atomic & pids+=($!) | |
| cd sdk/adminclient && go test ./... -count=1 -coverprofile=../../cov-adminclient.out -covermode=atomic & pids+=($!) | |
| cd sdk/configwatcher && go test ./... -count=1 -coverprofile=../../cov-configwatcher.out -covermode=atomic & pids+=($!) | |
| cd sdk/grpctransport && go test ./... -count=1 -coverprofile=../../cov-grpctransport.out -covermode=atomic & pids+=($!) | |
| cd sdk/tools && go test ./... -count=1 -coverprofile=../../cov-tools.out -covermode=atomic & pids+=($!) | |
| cd sdk/contrib/viper && go test ./... -count=1 -coverprofile=../../cov-contrib-viper.out -covermode=atomic & pids+=($!) | |
| cd sdk/contrib/envconfig && go test ./... -count=1 -coverprofile=../../cov-contrib-envconfig.out -covermode=atomic & pids+=($!) | |
| cd sdk/contrib/koanf && go test ./... -count=1 -coverprofile=../../cov-contrib-koanf.out -covermode=atomic & pids+=($!) | |
| cd contrib/decree-docs && go test ./... -count=1 -coverprofile=../../cov-decree-docs.out -covermode=atomic & pids+=($!) | |
| cd cmd/decree && go test ./... -count=1 -coverprofile=../../cov-decree.out -covermode=atomic & pids+=($!) | |
| fail=0 | |
| for pid in "${pids[@]}"; do wait "$pid" || fail=1; done | |
| exit $fail | |
| - name: Coverage ratchet | |
| run: | | |
| ./scripts/check-coverage.sh --profiles-only \ | |
| --profile sdk/configclient=cov-configclient.out \ | |
| --profile sdk/adminclient=cov-adminclient.out \ | |
| --profile sdk/configwatcher=cov-configwatcher.out \ | |
| --profile sdk/grpctransport=cov-grpctransport.out \ | |
| --profile sdk/tools=cov-tools.out \ | |
| --profile contrib/decree-docs=cov-decree-docs.out \ | |
| --profile cmd/decree=cov-decree.out | |
| - name: Merge coverage profiles | |
| run: | | |
| echo "mode: atomic" > coverage.out | |
| for f in cov-configclient.out cov-adminclient.out cov-configwatcher.out cov-grpctransport.out cov-tools.out cov-contrib-viper.out cov-contrib-envconfig.out cov-contrib-koanf.out cov-decree-docs.out cov-decree.out; do | |
| [ -f "$f" ] && grep -v "^mode:" "$f" >> coverage.out || true | |
| done | |
| - name: Upload coverage to Codecov | |
| # codecov/codecov-action@v6.0.0 | |
| uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f | |
| with: | |
| files: coverage.out | |
| token: ${{ secrets.CODECOV_TOKEN }} | |
| # Proves the SDK core modules still build and test against the floor Go | |
| # version we advertise support for (1.22). Testing 1.23+ is redundant: | |
| # if 1.22 passes, later versions pass too. Only the floor matters. | |
| sdk-compat: | |
| name: SDK compat (Go ${{ matrix.go }}) | |
| runs-on: ubuntu-latest | |
| needs: changes | |
| if: needs.changes.outputs.code == 'true' | |
| timeout-minutes: 10 | |
| strategy: | |
| matrix: | |
| go: ["1.22"] | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v7 | |
| with: | |
| persist-credentials: false | |
| - name: Set up Go ${{ matrix.go }} | |
| uses: actions/setup-go@v7 | |
| with: | |
| go-version: ${{ matrix.go }} | |
| cache-dependency-path: "**/go.sum" | |
| - name: Build SDK core modules | |
| run: | | |
| cd sdk/configclient && go build ./... | |
| cd ../adminclient && go build ./... | |
| cd ../configwatcher && go build ./... | |
| - name: Test SDK core modules | |
| run: | | |
| cd sdk/configclient && go test ./... -count=1 & | |
| cd sdk/adminclient && go test ./... -count=1 & | |
| cd sdk/configwatcher && go test ./... -count=1 & | |
| wait | |
| # Regenerates docs and fails if the output differs from what's committed. | |
| # Catches forgotten `make generate docs` runs. Gated on docs-relevant paths | |
| # (proto, cmd/decree, internal/version, Makefile, mkdocs.yml) so pure Go | |
| # or config-only PRs skip this job entirely. | |
| docs: | |
| name: Docs check | |
| runs-on: ubuntu-latest | |
| needs: [changes] | |
| if: needs.changes.outputs.docs == 'true' | |
| permissions: | |
| contents: read | |
| timeout-minutes: 10 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v7 | |
| with: | |
| persist-credentials: false | |
| - name: Set up Go | |
| uses: actions/setup-go@v7 | |
| with: | |
| go-version-file: go.mod | |
| cache-dependency-path: "**/go.sum" | |
| - name: Install buf | |
| env: | |
| BUF_VERSION: v1.66.1 | |
| # sha256 of buf-Linux-x86_64 from the official v1.66.1 release (sha256.txt). | |
| BUF_SHA256: ef835cb38ed973849f68e0e6a88153cb2168507e09eecbec43a2ada2f6a698be | |
| run: | | |
| mkdir -p "$HOME/.local/bin" | |
| curl -fsSL "https://github.com/bufbuild/buf/releases/download/${BUF_VERSION}/buf-Linux-x86_64" -o "$HOME/.local/bin/buf" | |
| echo "${BUF_SHA256} $HOME/.local/bin/buf" | sha256sum -c - | |
| chmod +x "$HOME/.local/bin/buf" | |
| echo "$HOME/.local/bin" >> "$GITHUB_PATH" | |
| "$HOME/.local/bin/buf" --version | |
| - name: Install protoc-gen-doc | |
| run: go install github.com/pseudomuto/protoc-gen-doc/cmd/protoc-gen-doc@v1.5.1 | |
| - name: Regenerate docs | |
| run: | | |
| # Remove generated CLI + man pages (all prefixed "decree") before | |
| # regenerating, so a removed command's orphaned page surfaces in the | |
| # freshness diff below — gen-docs only writes, never deletes. Matching | |
| # the "decree" prefix preserves hand-written pages such as guide.md. | |
| rm -f docs/cli/decree*.md docs/man/decree-*.1 | |
| buf generate --template buf.gen.doc.yaml | |
| make docs-cli docs-man | |
| - name: Check docs are up-to-date | |
| run: | | |
| if ! git diff --exit-code docs/; then | |
| echo "::error::Generated docs are out of date. Run 'make generate docs' and commit." | |
| exit 1 | |
| fi | |
| # E2E, examples, and stress share identical setup (checkout, Go, Docker, | |
| # pull tools image, pull prebuilt server image). Collapsed into one matrix job | |
| # to eliminate the 3× YAML duplication. Legs run in parallel by default. | |
| integration: | |
| name: ${{ matrix.name }} | |
| runs-on: ubuntu-latest | |
| needs: [tools, server-image, changes] | |
| if: needs.changes.outputs.code == 'true' | |
| permissions: | |
| contents: read | |
| packages: read | |
| timeout-minutes: 20 | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - suite: e2e | |
| name: E2E | |
| - suite: examples | |
| name: Examples | |
| - suite: stress | |
| name: Stress tests | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v7 | |
| with: | |
| persist-credentials: false | |
| - name: Set up Go | |
| uses: actions/setup-go@v7 | |
| with: | |
| go-version-file: go.mod | |
| cache-dependency-path: "**/go.sum" | |
| - name: Log in to ghcr.io | |
| uses: docker/login-action@v4 | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Pull tools image | |
| run: docker pull "${{ needs.tools.outputs.image }}" && docker tag "${{ needs.tools.outputs.image }}" decree-tools | |
| - name: Pull server image | |
| run: docker pull "${{ needs.server-image.outputs.image }}" | |
| - name: Run ${{ matrix.name }} | |
| run: | | |
| case "${{ matrix.suite }}" in | |
| e2e) | |
| docker compose up -d --wait service | |
| cd e2e && go test -tags=e2e -v -race -count=1 ./... || (cd .. && docker compose down -v && exit 1) | |
| docker compose down -v | |
| ;; | |
| examples) | |
| make examples | |
| ;; | |
| stress) | |
| CI=true make stress | |
| ;; | |
| esac | |
| env: | |
| TOOLS_IMAGE: ${{ needs.tools.outputs.image }} | |
| SERVICE_IMAGE: ${{ needs.server-image.outputs.image }} | |
| # Runs Postgres integration tests using testcontainers-go. Docker is | |
| # available on ubuntu-latest runners; no server image or tools image required. | |
| pg-integration: | |
| name: PG integration tests | |
| runs-on: ubuntu-latest | |
| needs: changes | |
| if: needs.changes.outputs.code == 'true' | |
| permissions: | |
| contents: read | |
| timeout-minutes: 15 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v7 | |
| with: | |
| persist-credentials: false | |
| - name: Set up Go | |
| uses: actions/setup-go@v7 | |
| with: | |
| go-version-file: go.mod | |
| cache-dependency-path: "**/go.sum" | |
| - name: PG integration tests | |
| run: make integration-test | |
| # Blocks PRs that introduce dependencies with known high-severity | |
| # vulnerabilities (GitHub Advisory Database). Fails closed on severity >= high. | |
| deps-review: | |
| name: Dependency review | |
| runs-on: ubuntu-latest | |
| if: github.event_name == 'pull_request' | |
| permissions: | |
| contents: read | |
| pull-requests: write | |
| timeout-minutes: 5 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v7 | |
| with: | |
| persist-credentials: false | |
| - name: Review dependencies | |
| uses: actions/dependency-review-action@v5 | |
| with: | |
| fail-on-severity: high | |
| # Scans imported Go packages (and reachable call sites) for known CVEs | |
| # using the Go vulnerability database. Complements deps-review by also | |
| # catching vulnerabilities in transitive deps already on main. | |
| govulncheck: | |
| name: Go vuln scan | |
| runs-on: ubuntu-latest | |
| needs: changes | |
| if: needs.changes.outputs.code == 'true' | |
| permissions: | |
| contents: read | |
| timeout-minutes: 10 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v7 | |
| with: | |
| persist-credentials: false | |
| # No separate "Set up Go" step here: govulncheck-action sets up its own | |
| # Go toolchain (with its own module cache restore) via go-version-input. | |
| # A second explicit actions/setup-go step used to restore the same GHA | |
| # cache key into the same GOMODCACHE dir, and the resulting tar extract | |
| # collided with files the first restore had already written | |
| # ("Cannot open: File exists"), failing this job on every PR. | |
| - name: Run govulncheck | |
| # golang/govulncheck-action@v1 | |
| uses: golang/govulncheck-action@032d45514ae346b1db93c04b0c90b841c370344f | |
| with: | |
| go-version-input: "1.25.13" | |
| work-dir: . | |
| # Validates every checked-in *.decree.schema.yaml and *.decree.config.yaml | |
| # against the v0.1.0 meta-schemas, and asserts every fixture under | |
| # schemas/v0.1.0/testdata/invalid/ FAILS validation. Closes #124. | |
| # | |
| # Runs unconditionally — no needs.changes gate — because the script also | |
| # cross-checks the meta-schema sources themselves and is fast enough that | |
| # path-filtering would add more complexity than it saves. | |
| meta-schemas: | |
| name: Meta-schemas check | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| timeout-minutes: 5 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v7 | |
| with: | |
| persist-credentials: false | |
| - name: Set up Python | |
| uses: actions/setup-python@v7 | |
| with: | |
| python-version: "3.x" | |
| - name: Install jsonschema | |
| run: pip install --no-cache-dir 'jsonschema>=4.21' 'PyYAML>=6' | |
| - name: Validate meta-schemas | |
| run: make validate-meta-schemas | |
| # Renders the Helm chart with default + NetworkPolicy-enabled values and | |
| # asserts the documented requests/limits, NetworkPolicy egress rules, and | |
| # imagePullPolicy. Also runs `helm lint`. | |
| helm: | |
| name: Helm chart | |
| runs-on: ubuntu-latest | |
| needs: changes | |
| if: needs.changes.outputs.helm == 'true' | |
| permissions: | |
| contents: read | |
| timeout-minutes: 5 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v7 | |
| with: | |
| persist-credentials: false | |
| - name: Set up Helm | |
| # azure/setup-helm@v4.3.1 | |
| uses: azure/setup-helm@9bc31f4ebc9c6b171d7bfbaa5d006ae7abdb4310 | |
| with: | |
| version: v3.18.4 | |
| - name: Run chart render tests | |
| run: ./deploy/helm/decree/tests/template_test.sh | |
| # Upgrade safety: runs the previous-release binary against a fresh DB, | |
| # populates fixture data, applies new migrations, then asserts the new binary | |
| # can read the old data and accept new writes. Only runs on PRs that touch | |
| # db/migrations/, so it's zero-cost for non-migration changes. | |
| upgrade: | |
| name: Upgrade test | |
| runs-on: ubuntu-latest | |
| needs: [tools, server-image, changes] | |
| if: needs.changes.outputs.migrations == 'true' | |
| permissions: | |
| contents: read | |
| packages: read | |
| timeout-minutes: 20 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v7 | |
| with: | |
| persist-credentials: false | |
| fetch-depth: 0 | |
| - name: Set up Go | |
| uses: actions/setup-go@v7 | |
| with: | |
| go-version-file: go.mod | |
| cache-dependency-path: "**/go.sum" | |
| - name: Log in to ghcr.io | |
| uses: docker/login-action@v4 | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Pull tools image | |
| run: docker pull "${{ needs.tools.outputs.image }}" && docker tag "${{ needs.tools.outputs.image }}" decree-tools | |
| - name: Pull server image | |
| run: docker pull "${{ needs.server-image.outputs.image }}" | |
| - name: Run upgrade test | |
| run: ./scripts/run-upgrade-test.sh | |
| env: | |
| TOOLS_IMAGE: decree-tools | |
| SERVICE_IMAGE: ${{ needs.server-image.outputs.image }} | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| # Posts a synthetic codecov/patch:success status when no Go code changed so | |
| # branch protection is not blocked waiting for a Codecov upload that will | |
| # never arrive (the test job skips on docs-only PRs). Fires only on | |
| # pull_request events — push-to-main always runs tests and gets a real status. | |
| codecov-skip: | |
| name: Codecov skip (docs-only) | |
| runs-on: ubuntu-latest | |
| needs: changes | |
| if: needs.changes.outputs.code != 'true' && github.event_name == 'pull_request' | |
| permissions: | |
| statuses: write | |
| timeout-minutes: 5 | |
| steps: | |
| - name: Post codecov/patch success | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| gh api repos/${{ github.repository }}/statuses/${{ github.event.pull_request.head.sha }} \ | |
| --method POST \ | |
| -f state=success \ | |
| -f context=codecov/patch \ | |
| -f description="No coverage change (docs-only PR)" \ | |
| -f target_url="https://codecov.io/gh/${{ github.repository }}" | |
| # Posts a synthetic codecov/patch:success when all code changes are within | |
| # sdk/grpctransport/** (fully excluded from Codecov). The test job still runs | |
| # for these PRs, but Codecov silently skips the patch webhook — leaving a | |
| # required check permanently pending. Mirrors codecov-skip for docs-only PRs. | |
| codecov-skip-grpctransport: | |
| name: Codecov skip (grpctransport-only) | |
| runs-on: ubuntu-latest | |
| needs: changes | |
| if: needs.changes.outputs.code == 'true' && needs.changes.outputs.non_grpctransport_code != 'true' && github.event_name == 'pull_request' | |
| permissions: | |
| statuses: write | |
| timeout-minutes: 5 | |
| steps: | |
| - name: Post codecov/patch success | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| gh api repos/${{ github.repository }}/statuses/${{ github.event.pull_request.head.sha }} \ | |
| --method POST \ | |
| -f state=success \ | |
| -f context=codecov/patch \ | |
| -f description="No coverage change (grpctransport-only PR)" \ | |
| -f target_url="https://codecov.io/gh/${{ github.repository }}" | |
| # Posts a synthetic codecov/patch:success when no Go/proto/db source changed | |
| # (e.g. .github/workflows/**, build/** Dockerfiles, Makefile, docker-compose). | |
| # Those paths count as "code" (so the test job runs) but produce no coverage | |
| # diff, so Codecov never uploads — leaving codecov/patch pending forever. | |
| codecov-skip-ci-only: | |
| name: Codecov skip (ci-only) | |
| runs-on: ubuntu-latest | |
| needs: changes | |
| if: needs.changes.outputs.code == 'true' && needs.changes.outputs.go_code != 'true' && github.event_name == 'pull_request' | |
| permissions: | |
| statuses: write | |
| timeout-minutes: 5 | |
| steps: | |
| - name: Post codecov/patch success | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| gh api repos/${{ github.repository }}/statuses/${{ github.event.pull_request.head.sha }} \ | |
| --method POST \ | |
| -f state=success \ | |
| -f context=codecov/patch \ | |
| -f description="No coverage change (ci-only PR)" \ | |
| -f target_url="https://codecov.io/gh/${{ github.repository }}" | |
| # Aggregates all job results for branch protection. A single required check | |
| # that passes iff every listed job passed or was legitimately skipped. | |
| check: | |
| name: CI check | |
| if: always() | |
| needs: [lint, test-internal, test-modules, sdk-compat, docs, integration, pg-integration, govulncheck, deps-review, meta-schemas, helm, upgrade, codecov-skip, codecov-skip-grpctransport, codecov-skip-ci-only] | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 5 | |
| steps: | |
| - name: All green gate | |
| # re-actors/alls-green@release/v1 | |
| uses: re-actors/alls-green@05ac9388f0aebcb5727afa17fcccfecd6f8ec5fe | |
| with: | |
| jobs: ${{ toJSON(needs) }} | |
| allowed-skips: lint, test-internal, test-modules, sdk-compat, docs, integration, pg-integration, govulncheck, deps-review, helm, upgrade, codecov-skip, codecov-skip-grpctransport, codecov-skip-ci-only |