chore(deps): bump github/codeql-action/analyze from 4.37.0 to 4.37.2 #820
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
| # Integration test workflow (act + gitea testcontainers). | |
| # Triggers: | |
| # push: branches main standalone run on trunk - populates the status badge. | |
| # pull_request gate every PR before merge. | |
| # merge_group runs as a merge-queue gate before merging to main. | |
| # workflow_dispatch manual run against any ref (existing). | |
| # | |
| # This is a ~27min testcontainers run. Path filtering now happens at the job | |
| # level (via the `changes` job using dorny/paths-filter) so the required gate | |
| # context always reports, preventing the required-but-skipped deadlock. The | |
| # concurrency group cancels a superseded run on the same ref so a fast | |
| # follow-up push does not stack two 27min runs. Run locally | |
| # (`go test -v ./e2e/...`) before pushing too. | |
| # | |
| # NOTE: the `name:` below is referenced by fleet-e2e.yaml's workflow_run trigger | |
| # ("Integration (act + gitea)"). Keep the two in sync if this is ever renamed. | |
| name: Integration (act + gitea) | |
| on: | |
| push: | |
| branches: [main] | |
| pull_request: | |
| merge_group: | |
| workflow_dispatch: | |
| inputs: | |
| ref: | |
| description: 'Branch or commit SHA to test (default: main)' | |
| required: false | |
| default: main | |
| timeout: | |
| description: 'Per-package go test timeout (e.g. 60m)' | |
| required: false | |
| default: '60m' | |
| parallel: | |
| description: 'Subtest parallelism (lower = slower but more reliable)' | |
| required: false | |
| default: '1' | |
| shard: | |
| description: 'Run only this single shard index 0-4 (blank = full matrix)' | |
| required: false | |
| default: '' | |
| scenario: | |
| description: 'Run only scenarios whose subtest name matches this -run pattern (blank = all)' | |
| required: false | |
| default: '' | |
| # A superseded run on the same ref is cancelled rather than left to burn a full | |
| # ~27min testcontainers slot. Keyed on github.ref so each branch/PR/tag is its | |
| # own lane; merge_group runs key on their own ref and never collide with main. | |
| concurrency: | |
| group: integration-${{ github.ref }} | |
| cancel-in-progress: true | |
| permissions: | |
| contents: read | |
| jobs: | |
| changes: | |
| name: Detect Code Changes | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| outputs: | |
| code: ${{ steps.compute.outputs.code }} | |
| steps: | |
| - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 | |
| - id: filter | |
| if: github.event_name == 'pull_request' | |
| uses: dorny/paths-filter@7b450fff21473bca461d4b92ce414b9d0420d706 # v4.0.2 | |
| with: | |
| filters: | | |
| code: | |
| - 'cmd/**' | |
| - 'e2e/**' | |
| - 'go.mod' | |
| - 'go.sum' | |
| - 'internal/**' | |
| - id: compute | |
| run: | | |
| if [ "${{ github.event_name }}" != "pull_request" ]; then | |
| echo "code=true" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "code=${{ steps.filter.outputs.code }}" >> "$GITHUB_OUTPUT" | |
| fi | |
| plan: | |
| name: Plan Shards | |
| needs: changes | |
| if: needs.changes.outputs.code == 'true' | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| outputs: | |
| shards: ${{ steps.compute.outputs.shards }} | |
| steps: | |
| - id: compute | |
| # Build the matrix shard list. A manual `shard` input collapses the | |
| # matrix to that single leg for targeted reruns. A `scenario` input | |
| # without a shard also collapses to a single leg, because the test code | |
| # bypasses sharding when a scenario filter is set and would otherwise run | |
| # the same scenarios on every leg. Otherwise the full set of five shards | |
| # runs. The shard input is validated to one digit 0-4 so it cannot | |
| # inject arbitrary JSON into the matrix expression. | |
| env: | |
| SHARD_INPUT: ${{ github.event.inputs.shard }} | |
| SCENARIO_INPUT: ${{ github.event.inputs.scenario }} | |
| run: | | |
| if [ -n "$SHARD_INPUT" ]; then | |
| case "$SHARD_INPUT" in | |
| [0-4]) | |
| echo "shards=[$SHARD_INPUT]" >> "$GITHUB_OUTPUT" | |
| exit 0 | |
| ;; | |
| *) | |
| echo "Invalid shard input '$SHARD_INPUT'; expected a single index 0-4." >&2 | |
| exit 1 | |
| ;; | |
| esac | |
| fi | |
| if [ -n "$SCENARIO_INPUT" ]; then | |
| echo 'shards=[0]' >> "$GITHUB_OUTPUT" | |
| exit 0 | |
| fi | |
| echo 'shards=[0,1,2,3,4]' >> "$GITHUB_OUTPUT" | |
| e2e: | |
| # The scenario suite is split across N runners (one matrix leg per shard). | |
| # Each leg runs its slice serially (E2E_PARALLEL=1) to keep the per-box | |
| # reliability floor, so a single environmental flake fails one short leg | |
| # rather than the whole multi-hour suite. E2E_SHARD_TOTAL must match the | |
| # length of the shard list below; the Go side reads both values from the | |
| # environment and falls back to running everything when they are unset. | |
| name: E2E Tests (shard ${{ matrix.shard }}) | |
| needs: [changes, plan] | |
| if: needs.changes.outputs.code == 'true' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 35 | |
| strategy: | |
| # One shard's flake must not cancel the others; each is independently | |
| # rerunnable from the failed matrix leg. | |
| fail-fast: false | |
| matrix: | |
| # Computed by the plan job: the full five-shard set, or a single shard | |
| # when the workflow_dispatch `shard` input collapses the matrix. | |
| shard: ${{ fromJSON(needs.plan.outputs.shards) }} | |
| steps: | |
| - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 | |
| with: | |
| # workflow_dispatch: honour the explicit ref input. | |
| # All other triggers (push:tags, merge_group): use the exact SHA | |
| # that triggered the run so we test what GitHub resolved. | |
| ref: ${{ github.event.inputs.ref || github.sha }} | |
| - uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0 | |
| with: | |
| # The e2e module declares its own toolchain (go 1.24). Drive the | |
| # installed Go straight from e2e/go.mod so the runner always matches | |
| # the module requirement. Pinning a literal version here drifts: when | |
| # it lags the module's `go` directive, `go test` fails immediately | |
| # under GOTOOLCHAIN=local with "go.mod requires go >= 1.24.0". | |
| go-version-file: e2e/go.mod | |
| cache: true | |
| cache-dependency-path: e2e/go.sum | |
| - name: Show runner resources | |
| run: | | |
| echo "=== CPU ===" | |
| nproc | |
| echo "=== Memory ===" | |
| free -h | |
| echo "=== Disk ===" | |
| df -h / | |
| - name: Install gotestsum | |
| # gotestsum drives the rerun-on-failure self-heal: on a failed run it | |
| # reruns only the individual failed scenarios (not the whole shard), | |
| # giving three total attempts per leg before the shard fails. | |
| run: go install gotest.tools/gotestsum@v1.13.0 | |
| - name: Run e2e tests | |
| working-directory: e2e | |
| env: | |
| # Each shard runs roughly a fifth of the suite, so 30m of per-package | |
| # headroom is ample. Override per-dispatch as needed. | |
| E2E_TIMEOUT: ${{ github.event.inputs.timeout || '30m' }} | |
| # Cap subtest parallelism. The GitHub runner has 4 cores / ~7.9GB | |
| # RAM. Each scenario spins up gitea + act + N job containers; at | |
| # the default GOMAXPROCS=4, four scenarios concurrently exhaust | |
| # memory and the test process is OOM-killed (silent FAIL with no | |
| # per-test output, see #104). Even at 2 the concurrent container | |
| # load throttles gitea and destabilises act runs, so the default is | |
| # serial (1); raise it per-dispatch only when chasing wall-clock. | |
| E2E_PARALLEL: ${{ github.event.inputs.parallel || '1' }} | |
| # Scenario sharding. The Go side sorts scenarios by name and selects | |
| # those whose position modulo the total equals this index, so the | |
| # union of all legs is the whole suite with no overlap. Keep | |
| # E2E_SHARD_TOTAL equal to the matrix shard-list length. | |
| E2E_SHARD_INDEX: ${{ matrix.shard }} | |
| E2E_SHARD_TOTAL: 5 | |
| # Optional single-scenario filter for manual debugging. The test code | |
| # reads it as a regular expression over scenario names and, when set, | |
| # bypasses sharding so one leg runs only the matching scenarios. It is | |
| # consumed only by the Go test process, never by the shell. | |
| E2E_SCENARIO: ${{ github.event.inputs.scenario }} | |
| run: | | |
| # --rerun-fails=2 gives three total attempts and reruns only the | |
| # scenarios that failed; the leg passes if every scenario passes | |
| # within those attempts. | |
| gotestsum \ | |
| --format standard-verbose \ | |
| --rerun-fails=2 \ | |
| --rerun-fails-report rerun-report.txt \ | |
| --packages=./... \ | |
| -- \ | |
| -timeout "$E2E_TIMEOUT" \ | |
| -parallel "$E2E_PARALLEL" | |
| # On a retry-exhausted scenario the harness writes the last attempt's raw | |
| # act stdout/stderr to e2e/_artifacts/<scenario>-attempt<N>.log so the | |
| # stack-trace origin survives the CI log retention window. Upload it on | |
| # every run (always()) so the evidence is recoverable whether the job | |
| # failed or a flake was absorbed. The directory may not exist when no | |
| # scenario exhausted its retries; if-no-files-found: ignore keeps that a | |
| # clean no-op rather than a warning. | |
| - name: Upload e2e crash evidence | |
| if: always() | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| # Per-shard name: upload-artifact rejects duplicate names across | |
| # matrix legs, so each shard writes its own evidence artifact. | |
| name: e2e-crash-evidence-shard-${{ matrix.shard }} | |
| path: e2e/_artifacts/ | |
| if-no-files-found: ignore | |
| retention-days: 14 | |
| gate: | |
| # This always-run context is the branch-protection required check. It mirrors | |
| # the heavy E2E result when E2E runs and passes cleanly when E2E is correctly | |
| # skipped on non-code changes, avoiding the required-but-skipped deadlock. | |
| # needs.e2e aggregates every shard in the matrix: its result is success only | |
| # when all legs pass and failure if any single shard fails, so this gate | |
| # stays the one required-check identity over the whole sharded suite. | |
| name: Integration Gate | |
| needs: [changes, plan, e2e] | |
| if: always() | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| steps: | |
| - name: Check Integration Status | |
| env: | |
| CHANGES_RESULT: ${{ needs.changes.result }} | |
| PLAN_RESULT: ${{ needs.plan.result }} | |
| E2E_RESULT: ${{ needs.e2e.result }} | |
| run: | | |
| # The detection job must succeed for its `code` output to be trusted. | |
| # If it failed or was cancelled, fail the gate rather than wave the | |
| # change through on a stale/empty signal. | |
| if [ "$CHANGES_RESULT" != "success" ]; then | |
| echo "Integration gate: change-detection result=$CHANGES_RESULT, failing the gate." | |
| exit 1 | |
| fi | |
| # The plan job computes the shard matrix. When no code changed it is | |
| # correctly skipped (alongside e2e); any other non-success is a real | |
| # failure that must not be waved through. | |
| if [ "$PLAN_RESULT" != "success" ] && [ "$PLAN_RESULT" != "skipped" ]; then | |
| echo "Integration gate: shard-plan result=$PLAN_RESULT, failing the gate." | |
| exit 1 | |
| fi | |
| # E2E success means a code change passed the heavy suite. E2E skipped | |
| # means no code path changed, which is a legitimate pass. | |
| if [ "$E2E_RESULT" = "success" ] || [ "$E2E_RESULT" = "skipped" ]; then | |
| echo "Integration gate: E2E result=$E2E_RESULT, gate passes." | |
| exit 0 | |
| fi | |
| echo "Integration gate: E2E result=$E2E_RESULT, failing the gate." | |
| exit 1 |