chore(main): release 1.189.0 #1548
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: Quality Gate | |
| # The Quality Gate is the single required check on the PR. Every floor lives | |
| # as a separate job (so failures show up granularly in the Checks UI) and the | |
| # aggregator job at the bottom is what branch protection should require. | |
| # | |
| # Configuration is centralised in .github/quality-gate.yml — thresholds and | |
| # enforcement modes are not hardcoded here. | |
| on: | |
| pull_request: | |
| branches: [main, develop] | |
| types: [opened, synchronize, reopened, labeled, unlabeled, ready_for_review] | |
| concurrency: | |
| group: quality-gate-${{ github.event.pull_request.number }} | |
| cancel-in-progress: true | |
| permissions: | |
| contents: read | |
| pull-requests: write | |
| statuses: read | |
| checks: read | |
| env: | |
| GO_VERSION_FILE: go.mod | |
| jobs: | |
| # --------------------------------------------------------------------- | |
| # meta — short job that decides whether to run the gate at all and | |
| # exposes shared values (PR labels, docs-only flag) to downstream jobs. | |
| # --------------------------------------------------------------------- | |
| meta: | |
| name: Meta | |
| runs-on: ubuntu-latest | |
| outputs: | |
| skip: ${{ steps.decide.outputs.skip }} | |
| docs_only: ${{ steps.decide.outputs.docs_only }} | |
| release_please: ${{ steps.decide.outputs.release_please }} | |
| labels: ${{ steps.decide.outputs.labels }} | |
| base_ref: ${{ steps.decide.outputs.base_ref }} | |
| steps: | |
| - uses: actions/checkout@v7 | |
| with: | |
| fetch-depth: 0 | |
| - name: Install yq + jq | |
| run: | | |
| sudo wget -qO /usr/local/bin/yq https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64 | |
| sudo chmod +x /usr/local/bin/yq | |
| - id: decide | |
| shell: bash | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| GITHUB_EVENT_PATH: ${{ github.event_path }} | |
| run: | | |
| set -euo pipefail | |
| source scripts/qg/lib.sh | |
| # Skip release-please / bot PRs | |
| actor='${{ github.actor }}' | |
| mapfile -t skip_actors < <(qg_yq '.skip_actors[]?') | |
| skip="false" | |
| for a in "${skip_actors[@]}"; do | |
| if [[ "$actor" == "$a" ]]; then | |
| skip="true"; break | |
| fi | |
| done | |
| # quality-gate-skip nuclear bypass | |
| if qg_has_label quality-gate-skip; then | |
| skip="true" | |
| echo "::warning::Quality Gate bypassed via 'quality-gate-skip' label" | |
| fi | |
| base_ref="origin/${{ github.base_ref }}" | |
| git fetch origin "${{ github.base_ref }}" --quiet || true | |
| # docs-only detection | |
| export QG_BASE_REF="$base_ref" | |
| docs_only="false" | |
| if qg_is_docs_only; then docs_only="true"; fi | |
| # release-please PR detection: branch is deterministic | |
| # ("release-please--branches--<base>"). The PR only bumps versions | |
| # and CHANGELOG, so the heavy floors are noise — we keep | |
| # commit-lint running as a sanity check and skip the rest. | |
| release_please="false" | |
| head_ref='${{ github.head_ref }}' | |
| if [[ "$head_ref" == release-please--* ]]; then | |
| release_please="true" | |
| fi | |
| labels=$(qg_pr_labels | xargs) | |
| { | |
| echo "skip=$skip" | |
| echo "docs_only=$docs_only" | |
| echo "release_please=$release_please" | |
| echo "labels=$labels" | |
| echo "base_ref=$base_ref" | |
| } >> "$GITHUB_OUTPUT" | |
| # --------------------------------------------------------------------- | |
| # Floor 1 — build, vet, gofmt, golangci-lint | |
| # | |
| # Runs the same checks twice: once at the repo root (chatcli main module) | |
| # and once inside operator/ (separate Go module). gofmt is run repo-wide | |
| # because the formatter doesn't care about module boundaries. | |
| # --------------------------------------------------------------------- | |
| build-static: | |
| name: Floor 1 · Build & Static | |
| needs: meta | |
| if: needs.meta.outputs.skip != 'true' && needs.meta.outputs.release_please != 'true' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - uses: actions/setup-go@v7 | |
| with: | |
| go-version-file: ${{ env.GO_VERSION_FILE }} | |
| cache: true | |
| - name: go build (root) | |
| run: go build ./... | |
| - name: go build (operator) | |
| working-directory: operator | |
| run: go build ./... | |
| - name: go vet (root) | |
| run: go vet ./... | |
| - name: go vet (operator) | |
| working-directory: operator | |
| run: go vet ./... | |
| - name: gofmt (repo-wide) | |
| run: | | |
| out=$(gofmt -l .) | |
| if [[ -n "$out" ]]; then | |
| echo "::error::gofmt: files not formatted:" | |
| echo "$out" | |
| exit 1 | |
| fi | |
| - name: golangci-lint (root, full module) | |
| # Both module baselines are at zero (misspell in PR #1055, the five | |
| # heavy linters in #1056, the remainder — including the operator | |
| # module — in the change that flipped this), so enforcement covers | |
| # the whole tree: pre-existing debt can no longer accumulate | |
| # silently outside PR diffs. | |
| uses: golangci/golangci-lint-action@v6 | |
| with: | |
| version: v1.64.8 | |
| install-mode: goinstall | |
| args: --timeout=8m | |
| - name: golangci-lint (operator, full module) | |
| uses: golangci/golangci-lint-action@v6 | |
| with: | |
| version: v1.64.8 | |
| install-mode: goinstall | |
| working-directory: operator | |
| args: --timeout=8m | |
| # --------------------------------------------------------------------- | |
| # Floor 2 — total coverage ratchet (unit tests with -race) | |
| # Also produces coverage.out for floor 3 (patch-coverage). | |
| # --------------------------------------------------------------------- | |
| test-coverage: | |
| name: Floor 2 · Coverage ratchet | |
| needs: meta | |
| if: needs.meta.outputs.skip != 'true' && needs.meta.outputs.release_please != 'true' | |
| runs-on: ubuntu-latest | |
| outputs: | |
| passed: ${{ steps.ratchet.outputs.passed }} | |
| current: ${{ steps.ratchet.outputs.current }} | |
| baseline: ${{ steps.ratchet.outputs.baseline }} | |
| delta: ${{ steps.ratchet.outputs.delta }} | |
| steps: | |
| - uses: actions/checkout@v7 | |
| with: | |
| fetch-depth: 0 | |
| - uses: actions/setup-go@v7 | |
| with: | |
| go-version-file: ${{ env.GO_VERSION_FILE }} | |
| cache: true | |
| - name: Install yq | |
| run: | | |
| sudo wget -qO /usr/local/bin/yq https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64 | |
| sudo chmod +x /usr/local/bin/yq | |
| - name: go test root + operator (merged profile) | |
| # -coverpkg=./... is REQUIRED for patch-coverage correctness: it | |
| # forces the profile to include every package in the module, even | |
| # those without _test.go files. Without it, brand-new packages | |
| # added in a PR have zero profile entries and look like "100 percent | |
| # covered" to diff-cover. See tools/qg/diffcover/compute.go. | |
| # | |
| # Both modules contribute to a single coverage.out so Floors 2 and | |
| # 3 see the unified picture. Merging by concatenating records | |
| # (with one mode header) is safe: -race forces atomic mode in both | |
| # modules, and tools/qg/diffcover dedupes overlapping ranges. | |
| run: | | |
| go test -race -coverpkg=./... -coverprofile=coverage-root.out ./... | |
| ( cd operator && go test -race -coverpkg=./... -coverprofile=../coverage-operator.out ./... ) | |
| head -1 coverage-root.out > coverage.out | |
| tail -n +2 coverage-root.out >> coverage.out | |
| tail -n +2 coverage-operator.out >> coverage.out | |
| go tool cover -func=coverage.out | tail -n 1 | |
| - name: Upload coverage profile | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: coverage-out | |
| path: coverage.out | |
| retention-days: 14 | |
| - id: ratchet | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| GITHUB_BASE_REF: ${{ github.base_ref }} | |
| QG_BASE_REF: ${{ needs.meta.outputs.base_ref }} | |
| run: bash scripts/qg/coverage-ratchet.sh coverage.out | |
| # --------------------------------------------------------------------- | |
| # Floor 3 — patch coverage on the diff | |
| # --------------------------------------------------------------------- | |
| patch-coverage: | |
| name: Floor 3 · Patch coverage | |
| needs: [meta, test-coverage] | |
| if: needs.meta.outputs.skip != 'true' && needs.meta.outputs.release_please != 'true' | |
| runs-on: ubuntu-latest | |
| outputs: | |
| passed: ${{ steps.patch.outputs.passed }} | |
| percent: ${{ steps.patch.outputs.percent }} | |
| threshold: ${{ steps.patch.outputs.threshold }} | |
| steps: | |
| - uses: actions/checkout@v7 | |
| with: | |
| fetch-depth: 0 | |
| - uses: actions/setup-go@v7 | |
| with: | |
| go-version-file: ${{ env.GO_VERSION_FILE }} | |
| cache: true | |
| - name: Install yq | |
| run: | | |
| sudo wget -qO /usr/local/bin/yq https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64 | |
| sudo chmod +x /usr/local/bin/yq | |
| - uses: actions/download-artifact@v6 | |
| with: | |
| name: coverage-out | |
| - id: patch | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| PR_LABELS: ${{ needs.meta.outputs.labels }} | |
| QG_BASE_REF: ${{ needs.meta.outputs.base_ref }} | |
| # qg-diffcover is built on demand by scripts/qg/patch-coverage.sh; | |
| # the Go toolchain layer above is the only system dep it needs. | |
| run: bash scripts/qg/patch-coverage.sh coverage.out | |
| # --------------------------------------------------------------------- | |
| # Floor 4 — AI smells scanner | |
| # --------------------------------------------------------------------- | |
| ai-smells: | |
| name: Floor 4 · AI smells | |
| needs: meta | |
| if: needs.meta.outputs.skip != 'true' && needs.meta.outputs.docs_only != 'true' && needs.meta.outputs.release_please != 'true' | |
| runs-on: ubuntu-latest | |
| outputs: | |
| passed: ${{ steps.scan.outputs.passed }} | |
| steps: | |
| - uses: actions/checkout@v7 | |
| with: | |
| fetch-depth: 0 | |
| - name: Install yq | |
| run: | | |
| sudo wget -qO /usr/local/bin/yq https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64 | |
| sudo chmod +x /usr/local/bin/yq | |
| - id: scan | |
| env: | |
| PR_LABELS: ${{ needs.meta.outputs.labels }} | |
| QG_BASE_REF: ${{ needs.meta.outputs.base_ref }} | |
| run: bash scripts/qg/ai-smells.sh | |
| # --------------------------------------------------------------------- | |
| # Floor 5 — scope budget | |
| # --------------------------------------------------------------------- | |
| scope-budget: | |
| name: Floor 5 · Scope budget | |
| needs: meta | |
| if: needs.meta.outputs.skip != 'true' && needs.meta.outputs.release_please != 'true' | |
| runs-on: ubuntu-latest | |
| outputs: | |
| passed: ${{ steps.scope.outputs.passed }} | |
| files: ${{ steps.scope.outputs.files }} | |
| loc: ${{ steps.scope.outputs.loc }} | |
| code_loc: ${{ steps.scope.outputs.code_loc }} | |
| tooling_loc: ${{ steps.scope.outputs.tooling_loc }} | |
| steps: | |
| - uses: actions/checkout@v7 | |
| with: | |
| fetch-depth: 0 | |
| - name: Install yq | |
| run: | | |
| sudo wget -qO /usr/local/bin/yq https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64 | |
| sudo chmod +x /usr/local/bin/yq | |
| - id: scope | |
| env: | |
| PR_LABELS: ${{ needs.meta.outputs.labels }} | |
| QG_BASE_REF: ${{ needs.meta.outputs.base_ref }} | |
| run: bash scripts/qg/scope-budget.sh | |
| # --------------------------------------------------------------------- | |
| # Floor 6 — E2E + race (the big behavioural test) | |
| # --------------------------------------------------------------------- | |
| e2e: | |
| name: Floor 6 · E2E | |
| needs: meta | |
| if: needs.meta.outputs.skip != 'true' && needs.meta.outputs.docs_only != 'true' && needs.meta.outputs.release_please != 'true' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 15 | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - uses: actions/setup-go@v7 | |
| with: | |
| go-version-file: ${{ env.GO_VERSION_FILE }} | |
| cache: true | |
| - name: Run e2e suite | |
| run: go test -race -count=1 -timeout 10m ./e2e/... | |
| # --------------------------------------------------------------------- | |
| # Floor 7 — commit hygiene | |
| # --------------------------------------------------------------------- | |
| commit-lint: | |
| name: Floor 7 · Commit lint | |
| needs: meta | |
| if: needs.meta.outputs.skip != 'true' | |
| runs-on: ubuntu-latest | |
| outputs: | |
| passed: ${{ steps.lint.outputs.passed }} | |
| steps: | |
| - uses: actions/checkout@v7 | |
| with: | |
| fetch-depth: 0 | |
| - name: Install yq | |
| run: | | |
| sudo wget -qO /usr/local/bin/yq https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64 | |
| sudo chmod +x /usr/local/bin/yq | |
| - id: lint | |
| env: | |
| QG_BASE_REF: ${{ needs.meta.outputs.base_ref }} | |
| run: bash scripts/qg/commit-lint.sh | |
| # --------------------------------------------------------------------- | |
| # Floor 8 — cyclo on changed files | |
| # --------------------------------------------------------------------- | |
| cyclo-new: | |
| name: Floor 8 · Cyclo (new code) | |
| needs: meta | |
| if: needs.meta.outputs.skip != 'true' && needs.meta.outputs.docs_only != 'true' && needs.meta.outputs.release_please != 'true' | |
| runs-on: ubuntu-latest | |
| outputs: | |
| passed: ${{ steps.cyclo.outputs.passed }} | |
| checked: ${{ steps.cyclo.outputs.checked }} | |
| steps: | |
| - uses: actions/checkout@v7 | |
| with: | |
| fetch-depth: 0 | |
| - uses: actions/setup-go@v7 | |
| with: | |
| go-version-file: ${{ env.GO_VERSION_FILE }} | |
| cache: true | |
| - name: Install yq | |
| run: | | |
| sudo wget -qO /usr/local/bin/yq https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64 | |
| sudo chmod +x /usr/local/bin/yq | |
| - id: cyclo | |
| env: | |
| QG_BASE_REF: ${{ needs.meta.outputs.base_ref }} | |
| run: bash scripts/qg/cyclo-new.sh | |
| # --------------------------------------------------------------------- | |
| # Floor 9 — secrets scan (gitleaks on the diff) | |
| # --------------------------------------------------------------------- | |
| secrets-scan: | |
| name: Floor 9 · Secrets scan | |
| needs: meta | |
| if: needs.meta.outputs.skip != 'true' && needs.meta.outputs.release_please != 'true' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v7 | |
| with: | |
| fetch-depth: 0 | |
| - name: gitleaks (PR scope) | |
| uses: gitleaks/gitleaks-action@v3 | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| GITLEAKS_ENABLE_COMMENTS: "false" | |
| GITLEAKS_ENABLE_UPLOAD_ARTIFACT: "true" | |
| # --------------------------------------------------------------------- | |
| # Floor 10 — i18n parity (cross-locale keys + Go usage coverage) | |
| # --------------------------------------------------------------------- | |
| i18n-parity: | |
| name: Floor 10 · i18n parity | |
| needs: meta | |
| if: needs.meta.outputs.skip != 'true' && needs.meta.outputs.release_please != 'true' | |
| runs-on: ubuntu-latest | |
| outputs: | |
| passed: ${{ steps.parity.outputs.passed }} | |
| missing_keys: ${{ steps.parity.outputs.missing_keys }} | |
| unknown_usages: ${{ steps.parity.outputs.unknown_usages }} | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - uses: actions/setup-go@v7 | |
| with: | |
| go-version-file: ${{ env.GO_VERSION_FILE }} | |
| cache: true | |
| - name: Install yq | |
| run: | | |
| sudo wget -qO /usr/local/bin/yq https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64 | |
| sudo chmod +x /usr/local/bin/yq | |
| - id: parity | |
| env: | |
| QG_BASE_REF: ${{ needs.meta.outputs.base_ref }} | |
| run: bash scripts/qg/i18n-parity.sh | |
| # --------------------------------------------------------------------- | |
| # Floor 11 — CRD drift (controller-gen vs checked-in YAML) | |
| # --------------------------------------------------------------------- | |
| crd-drift: | |
| name: Floor 11 · CRD drift | |
| needs: meta | |
| if: needs.meta.outputs.skip != 'true' && needs.meta.outputs.release_please != 'true' | |
| runs-on: ubuntu-latest | |
| outputs: | |
| passed: ${{ steps.drift.outputs.passed }} | |
| drifted_files: ${{ steps.drift.outputs.drifted_files }} | |
| steps: | |
| - uses: actions/checkout@v7 | |
| with: | |
| fetch-depth: 0 | |
| - uses: actions/setup-go@v7 | |
| with: | |
| go-version-file: ${{ env.GO_VERSION_FILE }} | |
| cache: true | |
| - name: Install yq | |
| run: | | |
| sudo wget -qO /usr/local/bin/yq https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64 | |
| sudo chmod +x /usr/local/bin/yq | |
| - id: drift | |
| env: | |
| QG_BASE_REF: ${{ needs.meta.outputs.base_ref }} | |
| run: bash scripts/qg/crd-drift.sh | |
| # --------------------------------------------------------------------- | |
| # Floor 12 — License headers on new Go files | |
| # --------------------------------------------------------------------- | |
| license-headers: | |
| name: Floor 12 · License headers | |
| needs: meta | |
| if: needs.meta.outputs.skip != 'true' && needs.meta.outputs.docs_only != 'true' && needs.meta.outputs.release_please != 'true' | |
| runs-on: ubuntu-latest | |
| outputs: | |
| passed: ${{ steps.headers.outputs.passed }} | |
| missing_count: ${{ steps.headers.outputs.missing_count }} | |
| steps: | |
| - uses: actions/checkout@v7 | |
| with: | |
| fetch-depth: 0 | |
| - name: Install yq | |
| run: | | |
| sudo wget -qO /usr/local/bin/yq https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64 | |
| sudo chmod +x /usr/local/bin/yq | |
| - id: headers | |
| env: | |
| QG_BASE_REF: ${{ needs.meta.outputs.base_ref }} | |
| run: bash scripts/qg/license-headers.sh | |
| # --------------------------------------------------------------------- | |
| # Floor 13 — apidiff against base ref | |
| # --------------------------------------------------------------------- | |
| api-breaking: | |
| name: Floor 13 · API breaking changes | |
| needs: meta | |
| if: needs.meta.outputs.skip != 'true' && needs.meta.outputs.docs_only != 'true' && needs.meta.outputs.release_please != 'true' | |
| runs-on: ubuntu-latest | |
| outputs: | |
| passed: ${{ steps.apidiff.outputs.passed }} | |
| incompatible_count: ${{ steps.apidiff.outputs.incompatible_count }} | |
| steps: | |
| - uses: actions/checkout@v7 | |
| with: | |
| fetch-depth: 0 | |
| - uses: actions/setup-go@v7 | |
| with: | |
| go-version-file: ${{ env.GO_VERSION_FILE }} | |
| cache: true | |
| - name: Install yq | |
| run: | | |
| sudo wget -qO /usr/local/bin/yq https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64 | |
| sudo chmod +x /usr/local/bin/yq | |
| - id: apidiff | |
| env: | |
| QG_BASE_REF: ${{ needs.meta.outputs.base_ref }} | |
| PR_LABELS: ${{ needs.meta.outputs.labels }} | |
| run: bash scripts/qg/api-breaking.sh | |
| # --------------------------------------------------------------------- | |
| # Floor 14 — binary size budget | |
| # --------------------------------------------------------------------- | |
| binary-size: | |
| name: Floor 14 · Binary size | |
| needs: meta | |
| if: needs.meta.outputs.skip != 'true' && needs.meta.outputs.docs_only != 'true' && needs.meta.outputs.release_please != 'true' | |
| runs-on: ubuntu-latest | |
| outputs: | |
| passed: ${{ steps.size.outputs.passed }} | |
| chatcli_bytes: ${{ steps.size.outputs.chatcli_bytes }} | |
| operator_bytes: ${{ steps.size.outputs.operator_bytes }} | |
| steps: | |
| - uses: actions/checkout@v7 | |
| with: | |
| fetch-depth: 0 | |
| - uses: actions/setup-go@v7 | |
| with: | |
| go-version-file: ${{ env.GO_VERSION_FILE }} | |
| cache: true | |
| - name: Install yq | |
| run: | | |
| sudo wget -qO /usr/local/bin/yq https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64 | |
| sudo chmod +x /usr/local/bin/yq | |
| - id: size | |
| env: | |
| QG_BASE_REF: ${{ needs.meta.outputs.base_ref }} | |
| run: bash scripts/qg/binary-size.sh | |
| # --------------------------------------------------------------------- | |
| # Floor 15 — LLM provider parity matrix | |
| # --------------------------------------------------------------------- | |
| provider-parity: | |
| name: Floor 15 · Provider parity | |
| needs: meta | |
| if: needs.meta.outputs.skip != 'true' && needs.meta.outputs.release_please != 'true' | |
| runs-on: ubuntu-latest | |
| outputs: | |
| passed: ${{ steps.parity.outputs.passed }} | |
| violations: ${{ steps.parity.outputs.violations }} | |
| providers: ${{ steps.parity.outputs.providers }} | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - uses: actions/setup-go@v7 | |
| with: | |
| go-version-file: ${{ env.GO_VERSION_FILE }} | |
| cache: true | |
| - name: Install yq | |
| run: | | |
| sudo wget -qO /usr/local/bin/yq https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64 | |
| sudo chmod +x /usr/local/bin/yq | |
| - id: parity | |
| run: bash scripts/qg/provider-parity.sh | |
| # --------------------------------------------------------------------- | |
| # Aggregator — the single status check that branch protection requires. | |
| # Posts a sticky comment with the per-floor verdict. | |
| # --------------------------------------------------------------------- | |
| quality-gate: | |
| name: Quality Gate | |
| needs: | |
| - meta | |
| - build-static | |
| - test-coverage | |
| - patch-coverage | |
| - ai-smells | |
| - scope-budget | |
| - e2e | |
| - commit-lint | |
| - cyclo-new | |
| - secrets-scan | |
| - i18n-parity | |
| - crd-drift | |
| - license-headers | |
| - api-breaking | |
| - binary-size | |
| - provider-parity | |
| if: always() | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - uses: actions/setup-go@v7 | |
| with: | |
| go-version-file: ${{ env.GO_VERSION_FILE }} | |
| cache: true | |
| - uses: ./.github/actions/setup-qg-tools | |
| with: | |
| with-qg-binaries: 'true' | |
| - name: Render verdict (qg-verdict) | |
| id: verdict | |
| shell: bash | |
| env: | |
| # Floor result statuses. | |
| R_BUILD: ${{ needs.build-static.result }} | |
| R_COV: ${{ needs.test-coverage.result }} | |
| R_PATCH: ${{ needs.patch-coverage.result }} | |
| R_SMELLS: ${{ needs.ai-smells.result }} | |
| R_SCOPE: ${{ needs.scope-budget.result }} | |
| R_E2E: ${{ needs.e2e.result }} | |
| R_LINT: ${{ needs.commit-lint.result }} | |
| R_CYCLO: ${{ needs.cyclo-new.result }} | |
| R_SEC: ${{ needs.secrets-scan.result }} | |
| R_I18N: ${{ needs.i18n-parity.result }} | |
| R_CRD: ${{ needs.crd-drift.result }} | |
| R_LICENSE: ${{ needs.license-headers.result }} | |
| R_APIDIFF: ${{ needs.api-breaking.result }} | |
| R_BINSIZE: ${{ needs.binary-size.result }} | |
| R_PROVPARITY: ${{ needs.provider-parity.result }} | |
| # Floor outputs. | |
| COV_CURRENT: ${{ needs.test-coverage.outputs.current }} | |
| COV_BASELINE: ${{ needs.test-coverage.outputs.baseline }} | |
| COV_DELTA: ${{ needs.test-coverage.outputs.delta }} | |
| PATCH_PCT: ${{ needs.patch-coverage.outputs.percent }} | |
| PATCH_THR: ${{ needs.patch-coverage.outputs.threshold }} | |
| SCOPE_FILES: ${{ needs.scope-budget.outputs.files }} | |
| SCOPE_LOC: ${{ needs.scope-budget.outputs.loc }} | |
| SCOPE_CODE_LOC: ${{ needs.scope-budget.outputs.code_loc }} | |
| SCOPE_TOOL_LOC: ${{ needs.scope-budget.outputs.tooling_loc }} | |
| CYCLO_CHECKED: ${{ needs.cyclo-new.outputs.checked }} | |
| I18N_MISSING: ${{ needs.i18n-parity.outputs.missing_keys }} | |
| I18N_UNKNOWN: ${{ needs.i18n-parity.outputs.unknown_usages }} | |
| CRD_DRIFTED: ${{ needs.crd-drift.outputs.drifted_files }} | |
| LICENSE_MISSING: ${{ needs.license-headers.outputs.missing_count }} | |
| API_INCOMPAT: ${{ needs.api-breaking.outputs.incompatible_count }} | |
| BIN_CHATCLI: ${{ needs.binary-size.outputs.chatcli_bytes }} | |
| BIN_OPERATOR: ${{ needs.binary-size.outputs.operator_bytes }} | |
| PROV_COUNT: ${{ needs.provider-parity.outputs.providers }} | |
| PROV_VIOLATIONS: ${{ needs.provider-parity.outputs.violations }} | |
| # Meta flags. | |
| DOCS_ONLY: ${{ needs.meta.outputs.docs_only }} | |
| RELEASE_PLEASE: ${{ needs.meta.outputs.release_please }} | |
| SKIP: ${{ needs.meta.outputs.skip }} | |
| PR_LABELS: ${{ needs.meta.outputs.labels }} | |
| run: | | |
| set -euo pipefail | |
| # qg-verdict was pre-built by setup-qg-tools; render the body and | |
| # forward the verdict line into $GITHUB_OUTPUT. Body is loaded as | |
| # a heredoc-style multi-line output so the sticky-comment action | |
| # can interpolate it verbatim. | |
| qg-verdict -body body.md >> "$GITHUB_OUTPUT" | |
| { | |
| echo 'body<<__QG_EOF__' | |
| cat body.md | |
| echo '__QG_EOF__' | |
| } >> "$GITHUB_OUTPUT" | |
| - name: Sticky comment on PR | |
| uses: marocchino/sticky-pull-request-comment@v3 | |
| with: | |
| header: quality-gate | |
| message: ${{ steps.verdict.outputs.body }} | |
| - name: Final verdict | |
| shell: bash | |
| env: | |
| V: ${{ steps.verdict.outputs.verdict }} | |
| run: | | |
| case "$V" in | |
| pass|bypassed) echo "Quality Gate: $V"; exit 0;; | |
| *) echo "Quality Gate: $V"; exit 1;; | |
| esac |