chore(deps): Bump golang from 87a41d2 to 792443b
#787
Workflow file for this run
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
| name: CI | |
| on: | |
| push: | |
| branches: [main] | |
| pull_request: | |
| branches: [main] | |
| merge_group: {} | |
| workflow_dispatch: {} | |
| permissions: | |
| contents: read | |
| pull-requests: read | |
| concurrency: | |
| group: ci-${{ github.ref }} | |
| # Cancel in-progress PR runs but never cancel main (prevents cache corruption). | |
| cancel-in-progress: ${{ github.ref != 'refs/heads/main' }} | |
| env: | |
| GOLANGCI_LINT_VERSION: "v2.12.2" | |
| GOTESTSUM_VERSION: "v1.13.0" | |
| K3D_VERSION: "v5.8.3" | |
| K3S_IMAGE: "rancher/k3s:v1.35.4-k3s1" | |
| ENVTEST_K8S_VERSION: "1.35.0" | |
| HELM_UNITTEST_VERSION: "v0.7.2" | |
| GOCOVER_COBERTURA_VERSION: "v1.5.0" | |
| BENCHSTAT_VERSION: "v0.0.0-20260512194132-3cf34090a3db" | |
| CERT_MANAGER_VERSION: "v1.17.2" | |
| PROMETHEUS_IMAGE: "quay.io/prometheus/prometheus:v3.4.1" | |
| PROMETHEUS_CHART_VERSION: "27.22.0" | |
| STRESS_NG_IMAGE: "ghcr.io/alexei-led/stress-ng:0.20.01" | |
| jobs: | |
| # Detect which files changed so downstream jobs can skip irrelevant work. | |
| # Docs-only or YAML-only PRs skip all Go CI (lint, test, build). | |
| changes: | |
| name: Detect Changes | |
| runs-on: ${{ vars.RUNNER || 'ubuntu-latest' }} | |
| timeout-minutes: 5 | |
| outputs: | |
| go: ${{ steps.filter.outputs.go }} | |
| go-source: ${{ steps.filter.outputs.go-source }} | |
| helm: ${{ steps.filter.outputs.helm }} | |
| yaml: ${{ steps.filter.outputs.yaml }} | |
| docs: ${{ steps.filter.outputs.docs }} | |
| e2e: ${{ steps.filter.outputs.e2e }} | |
| steps: | |
| - uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 | |
| with: | |
| egress-policy: audit | |
| - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 | |
| - uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v4.0.1 | |
| id: filter | |
| with: | |
| filters: | | |
| go: | |
| - '**/*.go' | |
| - 'go.mod' | |
| - 'go.sum' | |
| - 'Makefile' | |
| - 'Dockerfile' | |
| - '.github/workflows/ci.yaml' | |
| go-source: | |
| - '**/*.go' | |
| - 'go.mod' | |
| - 'go.sum' | |
| - 'Makefile' | |
| helm: | |
| - 'charts/**' | |
| yaml: | |
| - 'config/**' | |
| - '.github/workflows/**' | |
| - 'charts/attune/Chart.yaml' | |
| - 'charts/attune/values.yaml' | |
| - 'charts/attune/ci/**' | |
| - 'test/e2e/**/*.yaml' | |
| docs: | |
| - 'README.md' | |
| - 'CONTRIBUTING.md' | |
| - 'docs/**' | |
| - 'examples/**' | |
| - 'mkdocs.yml' | |
| - 'Makefile' | |
| - 'hack/verify-doc-defaults.sh' | |
| - 'hack/verify-doc-tool-versions.sh' | |
| - 'charts/attune/README.md.gotmpl' | |
| e2e: | |
| - 'test/e2e/**' | |
| lint: | |
| name: Lint | |
| runs-on: ${{ vars.RUNNER || 'ubuntu-latest' }} | |
| timeout-minutes: 10 | |
| needs: changes | |
| if: needs.changes.outputs.go == 'true' | |
| steps: | |
| - uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 | |
| with: | |
| egress-policy: audit | |
| - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 | |
| - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6 | |
| with: | |
| go-version-file: go.mod | |
| cache: true | |
| - uses: ./.github/actions/install-go-tool | |
| with: | |
| name: golangci-lint | |
| version: ${{ env.GOLANGCI_LINT_VERSION }} | |
| package: github.com/golangci/golangci-lint/v2/cmd/golangci-lint | |
| - name: golangci-lint | |
| shell: bash -Eeuo pipefail {0} | |
| run: golangci-lint run --timeout 5m --allow-serial-runners | |
| - name: Check go mod tidy | |
| shell: bash -Eeuo pipefail {0} | |
| run: | | |
| go mod tidy | |
| if ! git diff --quiet --exit-code go.mod go.sum; then | |
| echo "::error::go.mod/go.sum are not tidy. Run 'go mod tidy' and commit." | |
| git diff go.mod go.sum | |
| exit 1 | |
| fi | |
| - name: Check license boilerplate | |
| shell: bash -Eeuo pipefail {0} | |
| run: | | |
| missing=$(find . -name '*.go' -not -path './vendor/*' -not -name 'zz_generated.*' \ | |
| -exec sh -c 'head -5 "$1" | grep -q "^Copyright" || echo "$1"' _ {} \;) | |
| if [ -n "$missing" ]; then | |
| echo "::error::Missing license header in: $missing" | |
| exit 1 | |
| fi | |
| - name: Verify doc defaults consistency | |
| shell: bash -Eeuo pipefail {0} | |
| run: bash hack/verify-doc-defaults.sh | |
| - name: Verify dashboard metrics sync | |
| shell: bash -Eeuo pipefail {0} | |
| run: bash hack/verify-dashboard-metrics.sh | |
| - name: Verify PrometheusRule metrics sync | |
| shell: bash -Eeuo pipefail {0} | |
| run: bash hack/verify-prometheusrule-metrics.sh | |
| - name: Verify Helm schema field names match CRD | |
| shell: bash -Eeuo pipefail {0} | |
| run: bash hack/verify-helm-schema-fields.sh | |
| docs-check: | |
| name: Docs Check | |
| runs-on: ${{ vars.RUNNER || 'ubuntu-latest' }} | |
| timeout-minutes: 10 | |
| needs: changes | |
| if: needs.changes.outputs.docs == 'true' || needs.changes.outputs.helm == 'true' | |
| steps: | |
| - uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 | |
| with: | |
| egress-policy: audit | |
| - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 | |
| - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6 | |
| with: | |
| go-version-file: go.mod | |
| cache: true | |
| - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 | |
| with: | |
| python-version: "3.x" | |
| cache: pip | |
| - name: Install MkDocs | |
| shell: bash -Eeuo pipefail {0} | |
| run: pip install mkdocs-material==9.7.6 | |
| - name: Build docs site | |
| shell: bash -Eeuo pipefail {0} | |
| run: make docs-build | |
| - name: Check Helm README freshness | |
| shell: bash -Eeuo pipefail {0} | |
| run: make helm-docs-check | |
| - name: Verify supported tool version references | |
| shell: bash -Eeuo pipefail {0} | |
| run: bash hack/verify-doc-tool-versions.sh | |
| link-check: | |
| name: Link Check | |
| runs-on: ${{ vars.RUNNER || 'ubuntu-latest' }} | |
| timeout-minutes: 10 | |
| needs: changes | |
| if: needs.changes.outputs.docs == 'true' | |
| steps: | |
| - uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 | |
| with: | |
| egress-policy: audit | |
| - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 | |
| - name: Check links | |
| uses: lycheeverse/lychee-action@8646ba30535128ac92d33dfc9133794bfdd9b411 # v2.8.0 | |
| with: | |
| args: >- | |
| --config lychee.toml | |
| --no-progress | |
| 'README.md' | |
| 'CONTRIBUTING.md' | |
| 'SECURITY.md' | |
| 'docs/**/*.md' | |
| 'charts/attune/README.md' | |
| fail: true | |
| yaml-lint: | |
| name: YAML Lint | |
| runs-on: ${{ vars.RUNNER || 'ubuntu-latest' }} | |
| timeout-minutes: 5 | |
| needs: changes | |
| if: needs.changes.outputs.yaml == 'true' | |
| steps: | |
| - uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 | |
| with: | |
| egress-policy: audit | |
| - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 | |
| - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 | |
| with: | |
| python-version: "3.x" | |
| - name: Install yamllint | |
| shell: bash -Eeuo pipefail {0} | |
| run: pip install yamllint | |
| - name: YAML lint | |
| shell: bash -Eeuo pipefail {0} | |
| run: | | |
| yamllint_config="${RUNNER_TEMP:-/tmp}/yamllint-${GITHUB_RUN_ID}-${GITHUB_JOB}.yaml" | |
| cat > "$yamllint_config" <<'EOF' | |
| extends: default | |
| rules: | |
| line-length: | |
| max: 200 | |
| truthy: | |
| check-keys: false | |
| indentation: | |
| spaces: 2 | |
| indent-sequences: whatever | |
| EOF | |
| yamllint -c "$yamllint_config" \ | |
| config/ charts/attune/Chart.yaml charts/attune/values.yaml charts/attune/ci/ test/e2e/ | |
| lint-chainsaw: | |
| name: Lint Chainsaw Tests | |
| runs-on: ${{ vars.RUNNER || 'ubuntu-latest' }} | |
| timeout-minutes: 5 | |
| needs: changes | |
| if: needs.changes.outputs.e2e == 'true' | |
| steps: | |
| - uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 | |
| with: | |
| egress-policy: audit | |
| - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 | |
| - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6 | |
| with: | |
| go-version-file: go.mod | |
| cache: true | |
| - name: Chainsaw dry-run (parse validation) | |
| shell: bash -Eeuo pipefail {0} | |
| run: make lint-chainsaw | |
| test-unit: | |
| name: Unit Tests | |
| runs-on: ${{ vars.RUNNER || 'ubuntu-latest' }} | |
| timeout-minutes: 15 | |
| needs: changes | |
| if: needs.changes.outputs.go == 'true' | |
| permissions: | |
| contents: read | |
| code-quality: write | |
| steps: | |
| - uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 | |
| with: | |
| egress-policy: audit | |
| - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 | |
| with: | |
| ref: ${{ github.event.pull_request.head.sha || github.sha }} | |
| - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6 | |
| with: | |
| go-version-file: go.mod | |
| cache: true | |
| - uses: ./.github/actions/install-go-tool | |
| with: | |
| name: gotestsum | |
| version: ${{ env.GOTESTSUM_VERSION }} | |
| package: gotest.tools/gotestsum | |
| - uses: ./.github/actions/install-go-tool | |
| with: | |
| name: gocover-cobertura | |
| version: ${{ env.GOCOVER_COBERTURA_VERSION }} | |
| package: github.com/boumenot/gocover-cobertura | |
| - name: Run unit tests | |
| shell: bash -Eeuo pipefail -x {0} | |
| run: | | |
| gotestsum --format pkgname \ | |
| --junitfile test-results/unit.xml \ | |
| --rerun-fails --rerun-fails-max-failures=5 \ | |
| --packages="./api/... ./cmd/... ./internal/..." \ | |
| -- -race -timeout=10m \ | |
| -coverpkg=./internal/... \ | |
| -coverprofile=coverage.out \ | |
| -covermode=atomic | |
| - name: Check coverage threshold | |
| shell: bash -Eeuo pipefail {0} | |
| run: | | |
| COVERAGE=$(go tool cover -func=coverage.out | grep total: | awk '{print $3}' | tr -d '%') | |
| echo "Total coverage: ${COVERAGE}%" | |
| if (( $(echo "$COVERAGE < 80" | bc -l) )); then | |
| echo "::error::Coverage ${COVERAGE}% is below 80% threshold" | |
| exit 1 | |
| fi | |
| - name: Upload test results | |
| if: always() | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 | |
| with: | |
| name: unit-test-results | |
| path: test-results/ | |
| retention-days: 7 | |
| - name: Test summary | |
| if: always() | |
| uses: test-summary/action@37b508cfee6d4d080eedd00b5bb240a6a784a6a5 # v2.6 | |
| with: | |
| paths: test-results/unit.xml | |
| - name: Convert coverage to Cobertura XML | |
| if: always() && hashFiles('coverage.out') != '' | |
| shell: bash -Eeuo pipefail {0} | |
| run: gocover-cobertura < coverage.out > coverage.xml | |
| - name: Upload coverage | |
| if: >- | |
| always() && | |
| (github.event_name != 'pull_request' || | |
| github.event.pull_request.head.repo.full_name == github.repository) | |
| uses: actions/upload-code-coverage@abb5995db9e0199b0e2bb9dbd136fce4cb1ec4d3 # v1 | |
| with: | |
| file: coverage.xml | |
| language: Go | |
| label: code-coverage/go | |
| test-bench: | |
| name: Benchmark Tests (${{ matrix.pkg }}) | |
| runs-on: ${{ vars.RUNNER || 'ubuntu-latest' }} | |
| timeout-minutes: 15 | |
| needs: changes | |
| if: needs.changes.outputs.go == 'true' | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - pkg: controller-core | |
| path: ./internal/controller/... | |
| bench: '^Benchmark(BuildPrometheusQuery|Reconcile$|ComputeRecommendations)' | |
| - pkg: controller-workloads | |
| path: ./internal/controller/... | |
| bench: '^BenchmarkReconcile_ManyWorkloads' | |
| - pkg: controller-policies | |
| path: ./internal/controller/... | |
| bench: '^BenchmarkReconcile_(ManyPolicies|Concurrent)' | |
| - pkg: metrics | |
| path: ./internal/metrics/... | |
| bench: '.' | |
| - pkg: recommendation | |
| path: ./internal/recommendation/... | |
| bench: '.' | |
| steps: | |
| - uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 | |
| with: | |
| egress-policy: audit | |
| - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 | |
| - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6 | |
| with: | |
| go-version-file: go.mod | |
| cache: true | |
| - uses: ./.github/actions/install-go-tool | |
| with: | |
| name: benchstat | |
| version: v0.0.0-20260512194132-3cf34090a3db | |
| package: golang.org/x/perf/cmd/benchstat | |
| - name: Restore baseline benchmarks | |
| uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 | |
| with: | |
| path: bench-baseline.txt | |
| key: bench-baseline-${{ matrix.pkg }}-${{ runner.os }}-${{ hashFiles('go.sum') }} | |
| restore-keys: bench-baseline-${{ matrix.pkg }}-${{ runner.os }}- | |
| - name: Run benchmarks | |
| shell: bash -Eeuo pipefail -x {0} | |
| run: | | |
| go test ${{ matrix.path }} -bench='${{ matrix.bench }}' -benchmem -run='^$' \ | |
| -count=5 -timeout=10m | tee bench-current.txt | |
| - name: Compare with baseline | |
| shell: bash -Eeuo pipefail {0} | |
| run: | | |
| if [[ -f bench-baseline.txt ]]; then | |
| benchstat bench-baseline.txt bench-current.txt | tee bench-comparison.txt | |
| { | |
| echo "## Benchmark Comparison" | |
| echo '```' | |
| cat bench-comparison.txt | |
| echo '```' | |
| } >> "$GITHUB_STEP_SUMMARY" | |
| else | |
| echo "No baseline found; comparison will be available after the first main push." | |
| echo "## Benchmark Results" >> "$GITHUB_STEP_SUMMARY" | |
| echo "No baseline yet. One will be established on the next merge to main." >> "$GITHUB_STEP_SUMMARY" | |
| fi | |
| - name: Promote current run as baseline (main only) | |
| if: github.ref == 'refs/heads/main' && github.event_name == 'push' | |
| shell: bash -Eeuo pipefail {0} | |
| run: cp bench-current.txt bench-baseline.txt | |
| - name: Save baseline (main only) | |
| if: github.ref == 'refs/heads/main' && github.event_name == 'push' | |
| uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 | |
| with: | |
| path: bench-baseline.txt | |
| key: bench-baseline-${{ matrix.pkg }}-${{ runner.os }}-${{ hashFiles('go.sum') }} | |
| test-integration: | |
| name: Integration Tests | |
| runs-on: ${{ vars.RUNNER || 'ubuntu-latest' }} | |
| timeout-minutes: 15 | |
| needs: changes | |
| if: needs.changes.outputs.go == 'true' | |
| steps: | |
| - uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 | |
| with: | |
| egress-policy: audit | |
| - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 | |
| - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6 | |
| with: | |
| go-version-file: go.mod | |
| cache: true | |
| - uses: ./.github/actions/install-go-tool | |
| with: | |
| name: gotestsum | |
| version: ${{ env.GOTESTSUM_VERSION }} | |
| package: gotest.tools/gotestsum | |
| - uses: ./.github/actions/install-go-tool | |
| with: | |
| name: setup-envtest | |
| version: v0.24.1 | |
| package: sigs.k8s.io/controller-runtime/tools/setup-envtest | |
| - name: Setup envtest assets | |
| shell: bash -Eeuo pipefail -x {0} | |
| run: echo "KUBEBUILDER_ASSETS=$(setup-envtest use ${{ env.ENVTEST_K8S_VERSION }} -p path)" >> "$GITHUB_ENV" | |
| - name: Run integration tests | |
| shell: bash -Eeuo pipefail -x {0} | |
| run: | | |
| gotestsum --format pkgname \ | |
| --junitfile test-results/integration.xml \ | |
| --rerun-fails --rerun-fails-max-failures=3 \ | |
| --packages="./test/integration/..." \ | |
| -- -race -timeout=15m \ | |
| -tags=integration | |
| - name: Upload test results | |
| if: always() | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 | |
| with: | |
| name: integration-test-results | |
| path: test-results/ | |
| retention-days: 7 | |
| - name: Test summary | |
| if: always() | |
| uses: test-summary/action@37b508cfee6d4d080eedd00b5bb240a6a784a6a5 # v2.6 | |
| with: | |
| paths: test-results/integration.xml | |
| # E2E tests run in a single job with one shared k3d cluster. | |
| # Cluster setup starts immediately (needs: changes only) and overlaps with | |
| # lint/unit. A gate step polls the GitHub API until lint+unit complete, | |
| # then both Chainsaw and Go E2E run concurrently on the shared cluster. | |
| # This saves ~3 min vs two separate jobs blocked on lint/unit. | |
| test-e2e: | |
| name: E2E | |
| runs-on: ${{ vars.RUNNER || 'ubuntu-latest' }} | |
| timeout-minutes: 30 | |
| needs: [changes, lint, test-unit] | |
| if: needs.changes.outputs.go-source == 'true' || needs.changes.outputs.e2e == 'true' | |
| env: | |
| K3D_CLUSTER_NAME: e2e-${{ github.run_id }}-${{ github.run_attempt }} | |
| steps: | |
| - uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 | |
| with: | |
| egress-policy: audit | |
| - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 | |
| - uses: ./.github/actions/setup-e2e-cluster | |
| with: | |
| cluster-name: ${{ env.K3D_CLUSTER_NAME }} | |
| kubeconfig-path: ${{ runner.temp }}/attune-e2e-${{ github.run_id }}.kubeconfig | |
| go-version-file: go.mod | |
| k3d-version: ${{ env.K3D_VERSION }} | |
| k3s-image: ${{ env.K3S_IMAGE }} | |
| cert-manager-version: ${{ env.CERT_MANAGER_VERSION }} | |
| prometheus-image: ${{ env.PROMETHEUS_IMAGE }} | |
| prometheus-chart-version: ${{ env.PROMETHEUS_CHART_VERSION }} | |
| stress-ng-image: ${{ env.STRESS_NG_IMAGE }} | |
| # Lint and unit tests are guaranteed complete via needs: [lint, test-unit]. | |
| - name: Install Chainsaw | |
| uses: ./.github/actions/install-binary-tool | |
| with: | |
| name: chainsaw | |
| version: v0.2.15 | |
| install-command: | | |
| ARCH=$(uname -m) | |
| case "$ARCH" in | |
| x86_64) ARCH=amd64 ;; | |
| aarch64) ARCH=arm64 ;; | |
| esac | |
| URL="https://github.com/kyverno/chainsaw/releases/download/v0.2.15/chainsaw_linux_${ARCH}.tar.gz" | |
| for attempt in 1 2 3; do | |
| if curl -fsSL "$URL" -o chainsaw.tar.gz; then | |
| tar -xf chainsaw.tar.gz -C "$TOOL_DIR" chainsaw | |
| rm chainsaw.tar.gz | |
| chmod +x "$TOOL_DIR/chainsaw" | |
| exit 0 | |
| fi | |
| echo "::warning::Chainsaw download attempt $attempt failed, retrying in 10s..." | |
| sleep 10 | |
| done | |
| echo "::error::Failed to download Chainsaw after 3 attempts" | |
| exit 1 | |
| - name: Run Chainsaw and Go E2E concurrently | |
| shell: bash -Eeuo pipefail -x {0} | |
| run: | | |
| mkdir -p test-results | |
| chainsaw test test/e2e/ --config .chainsaw.yaml 2>&1 | tee test-results/chainsaw.log & | |
| chainsaw_pid=$! | |
| go test -tags=e2e ./test/e2e-go/... -race -count=1 -timeout=15m -v 2>&1 | tee test-results/go-e2e.log & | |
| go_pid=$! | |
| chainsaw_rc=0 | |
| go_rc=0 | |
| wait $chainsaw_pid || chainsaw_rc=$? | |
| wait $go_pid || go_rc=$? | |
| echo "Chainsaw exit=$chainsaw_rc, Go E2E exit=$go_rc" | |
| if (( chainsaw_rc != 0 || go_rc != 0 )); then | |
| exit 1 | |
| fi | |
| - name: Collect debug info on failure | |
| if: failure() | |
| shell: bash -Eeuo pipefail {0} | |
| run: | | |
| echo "=== cert-manager pods ===" | |
| kubectl get pods -n cert-manager || true | |
| echo "=== Operator logs ===" | |
| kubectl logs -n attune-system -l app.kubernetes.io/name=attune --tail=300 || true | |
| echo "=== Pod status ===" | |
| kubectl get pods -A || true | |
| echo "=== Events ===" | |
| kubectl get events -A --sort-by='.lastTimestamp' | tail -50 | |
| - name: Upload debug logs on failure | |
| if: failure() | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 | |
| with: | |
| name: e2e-debug-logs | |
| path: test-results/ | |
| retention-days: 7 | |
| if-no-files-found: ignore | |
| - name: Cleanup k3d cluster | |
| if: always() | |
| shell: bash -Eeuo pipefail {0} | |
| run: | | |
| k3d cluster delete "$K3D_CLUSTER_NAME" 2>/dev/null || true | |
| rm -f "$KUBECONFIG" | |
| govulncheck: | |
| name: Govulncheck | |
| runs-on: ${{ vars.RUNNER || 'ubuntu-latest' }} | |
| timeout-minutes: 10 | |
| needs: changes | |
| if: needs.changes.outputs.go == 'true' | |
| steps: | |
| - uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 | |
| with: | |
| egress-policy: audit | |
| - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 | |
| - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6 | |
| with: | |
| go-version-file: go.mod | |
| cache: true | |
| - uses: ./.github/actions/install-go-tool | |
| with: | |
| name: govulncheck | |
| version: v1.3.0 | |
| package: golang.org/x/vuln/cmd/govulncheck | |
| - name: Run govulncheck | |
| shell: bash -Eeuo pipefail -x {0} | |
| run: govulncheck ./... | |
| crd-freshness: | |
| name: CRD Freshness Check | |
| runs-on: ${{ vars.RUNNER || 'ubuntu-latest' }} | |
| timeout-minutes: 10 | |
| needs: changes | |
| if: needs.changes.outputs.go == 'true' | |
| steps: | |
| - uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 | |
| with: | |
| egress-policy: audit | |
| - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 | |
| - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6 | |
| with: | |
| go-version-file: go.mod | |
| cache: true | |
| - name: Regenerate manifests and deepcopy | |
| shell: bash -Eeuo pipefail -x {0} | |
| run: make manifests generate | |
| - name: Check for drift | |
| shell: bash -Eeuo pipefail {0} | |
| run: | | |
| if ! git diff --exit-code config/crd/ charts/attune/crds/ api/v1alpha1/zz_generated.deepcopy.go config/rbac/; then | |
| echo "::error::Generated files are stale. Run 'make manifests generate' and commit." | |
| exit 1 | |
| fi | |
| helm-lint: | |
| name: Helm Lint | |
| runs-on: ${{ vars.RUNNER || 'ubuntu-latest' }} | |
| timeout-minutes: 10 | |
| needs: changes | |
| if: needs.changes.outputs.helm == 'true' || needs.changes.outputs.go == 'true' | |
| steps: | |
| - uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 | |
| with: | |
| egress-policy: audit | |
| - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 | |
| - uses: azure/setup-helm@dda3372f752e03dde6b3237bc9431cdc2f7a02a2 # v5 | |
| with: | |
| version: v4.1.4 | |
| - name: Lint chart | |
| shell: bash -Eeuo pipefail -x {0} | |
| run: helm lint charts/attune --kube-version v1.32.0 | |
| - name: Template validation | |
| shell: bash -Eeuo pipefail -x {0} | |
| run: | | |
| for f in charts/attune/ci/*.yaml; do | |
| echo "--- Testing with $f ---" | |
| helm template attune charts/attune -f "$f" \ | |
| --kube-version v1.32.0 \ | |
| --api-versions cert-manager.io/v1 > /dev/null | |
| done | |
| - uses: ./.github/actions/install-binary-tool | |
| with: | |
| name: helm-unittest | |
| version: ${{ env.HELM_UNITTEST_VERSION }} | |
| install-command: | | |
| # Remove stale helm-unittest plugins that conflict on self-hosted runners | |
| rm -rf "$(helm env HELM_PLUGINS)/helm-unittest.git" 2>/dev/null || true | |
| ASSET_VERSION="${{ env.HELM_UNITTEST_VERSION }}" | |
| ASSET_VERSION="${ASSET_VERSION#v}" | |
| case "$(uname -s)" in | |
| Linux) HU_OS=linux ;; | |
| Darwin) HU_OS=macos ;; | |
| *) echo "unsupported OS: $(uname -s)" >&2; exit 1 ;; | |
| esac | |
| case "$(uname -m)" in | |
| x86_64) HU_ARCH=amd64 ;; | |
| aarch64|arm64) HU_ARCH=arm64 ;; | |
| *) echo "unsupported arch: $(uname -m)" >&2; exit 1 ;; | |
| esac | |
| curl -fsSL "https://github.com/helm-unittest/helm-unittest/releases/download/${{ env.HELM_UNITTEST_VERSION }}/helm-unittest-${HU_OS}-${HU_ARCH}-${ASSET_VERSION}.tgz" \ | |
| | tar xz -C "$TOOL_DIR" | |
| - name: Link helm-unittest plugin | |
| shell: bash -Eeuo pipefail {0} | |
| run: | | |
| PLUGINS_DIR="$(helm env HELM_PLUGINS)/unittest" | |
| mkdir -p "$(dirname "$PLUGINS_DIR")" | |
| rm -rf "$PLUGINS_DIR" | |
| ln -s "${RUNNER_TEMP}/bin-tool-cache/helm-unittest" "$PLUGINS_DIR" | |
| - name: Run chart unit tests | |
| shell: bash -Eeuo pipefail -x {0} | |
| run: helm unittest charts/attune | |
| - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6 | |
| with: | |
| go-version-file: go.mod | |
| cache: true | |
| - uses: ./.github/actions/install-go-tool | |
| with: | |
| name: helm-docs | |
| version: v1.14.2 | |
| package: github.com/norwoodj/helm-docs/cmd/helm-docs | |
| - name: Helm docs freshness | |
| shell: bash -Eeuo pipefail {0} | |
| run: | | |
| helm-docs --chart-search-root charts/ | |
| if ! git diff --quiet --exit-code charts/attune/README.md; then | |
| echo "::error::Helm README is stale. Run 'make helm-docs-gen' and commit." | |
| git diff charts/attune/README.md | |
| exit 1 | |
| fi | |
| - name: Verify Helm RBAC matches kustomize | |
| shell: bash -Eeuo pipefail {0} | |
| run: bash hack/verify-helm-rbac.sh | |
| build: | |
| name: Build | |
| runs-on: ${{ vars.RUNNER || 'ubuntu-latest' }} | |
| timeout-minutes: 15 | |
| needs: changes | |
| if: needs.changes.outputs.go == 'true' | |
| steps: | |
| - uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 | |
| with: | |
| egress-policy: audit | |
| - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 | |
| - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6 | |
| with: | |
| go-version-file: go.mod | |
| cache: true | |
| - uses: ./.github/actions/setup-clean-docker-config | |
| - name: Verify Docker and buildx | |
| shell: bash -Eeuo pipefail {0} | |
| run: | | |
| if ! docker buildx version >/dev/null 2>&1; then | |
| echo "::error::Docker buildx is not available." | |
| exit 1 | |
| fi | |
| - name: Build binaries | |
| shell: bash -Eeuo pipefail -x {0} | |
| run: | | |
| go build -o bin/manager ./cmd/manager/ | |
| go build -o bin/kubectl-attune ./cmd/kubectl-attune/ | |
| - name: Cross-compile kubectl plugin | |
| shell: bash -Eeuo pipefail -x {0} | |
| run: | | |
| platforms=("linux/amd64" "linux/arm64" "darwin/amd64" "darwin/arm64" "windows/amd64") | |
| for platform in "${platforms[@]}"; do | |
| os="${platform%/*}" | |
| arch="${platform#*/}" | |
| ext=""; if [ "$os" = "windows" ]; then ext=".exe"; fi | |
| echo "::group::${os}/${arch}" | |
| GOOS="$os" GOARCH="$arch" go build -o "bin/kubectl-attune-${os}-${arch}${ext}" ./cmd/kubectl-attune/ | |
| echo "::endgroup::" | |
| done | |
| - name: Build container image (no push) | |
| shell: bash -Eeuo pipefail -x {0} | |
| run: | | |
| make docker-build IMG=attune:ci-${{ github.sha }} | |
| # Gate job: always runs, aggregates all conditional job results. | |
| # This is the ONLY required status check for branch protection, | |
| # so docs-only PRs (where Go jobs are skipped) can still merge. | |
| # Note: link-check is excluded from the gate because external sites | |
| # (e.g. kubernetes.io) have transient connectivity issues on CI runners. | |
| ci-gate: | |
| name: CI Gate | |
| runs-on: ${{ vars.RUNNER || 'ubuntu-latest' }} | |
| timeout-minutes: 5 | |
| if: always() | |
| needs: | |
| - lint | |
| - lint-chainsaw | |
| - docs-check | |
| - yaml-lint | |
| - test-unit | |
| - test-bench | |
| - test-integration | |
| - test-e2e | |
| - govulncheck | |
| - crd-freshness | |
| - helm-lint | |
| - build | |
| steps: | |
| - name: Check job results | |
| shell: bash -Eeuo pipefail {0} | |
| run: | | |
| results=( \ | |
| "${{ needs.lint.result }}" \ | |
| "${{ needs.lint-chainsaw.result }}" \ | |
| "${{ needs.docs-check.result }}" \ | |
| "${{ needs.yaml-lint.result }}" \ | |
| "${{ needs.test-unit.result }}" \ | |
| "${{ needs.test-bench.result }}" \ | |
| "${{ needs.test-integration.result }}" \ | |
| "${{ needs.test-e2e.result }}" \ | |
| "${{ needs.govulncheck.result }}" \ | |
| "${{ needs.crd-freshness.result }}" \ | |
| "${{ needs.helm-lint.result }}" \ | |
| "${{ needs.build.result }}" \ | |
| ) | |
| for r in "${results[@]}"; do | |
| if [[ "$r" != "success" && "$r" != "skipped" ]]; then | |
| echo "::error::CI gate failed: at least one job reported '$r'" | |
| exit 1 | |
| fi | |
| done | |
| echo "All jobs passed or were skipped." |