Repository navigation
π fix(crux): cmdlog next_seq never skips an unfinished command's ended line #20357
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: Go Security Analysis | |
| # Static analysis and vulnerability scanning for the Go module under src/. | |
| # | |
| # This complements, and does not replace, the `go vet` step already in | |
| # v2-ci.yml. Three tools, three different questions: | |
| # | |
| # govulncheck β does this code CALL a known-vulnerable symbol? Unlike a | |
| # dependency-version scanner (dependabot), it is reachability | |
| # -based, so it does not fire on a vulnerable package the | |
| # code never reaches. That makes its findings actionable | |
| # rather than noise. | |
| # gosec β insecure patterns in OUR code: command injection, weak | |
| # randomness, unhandled errors on security-relevant calls, | |
| # file permissions. | |
| # golangci-lint β correctness and maintainability lints beyond `go vet`. | |
| # | |
| # A fourth job, notice-drift, asks a different question entirely β not "is | |
| # this code safe" but "is the repo's third-party attribution (NOTICE, at the | |
| # repo root) still accurate" β and a fifth, action-pins, asks "does every | |
| # SHA-pinned action actually resolve". Both are static, both are Go/repo- | |
| # structure checks, and both fit this workflow's existing shape better than a | |
| # new file. | |
| # | |
| # Each runs as its OWN job rather than sequential steps in one job: a gosec | |
| # finding must not hide a govulncheck finding behind an early exit. Five | |
| # independent verdicts also means a reviewer sees which class of problem | |
| # failed without opening the log. | |
| # | |
| # WHY THIS EXISTS: the OpenSSF Best Practices badge asks whether the project | |
| # applies static analysis and checks for known vulnerabilities. Before this | |
| # workflow the honest answer was "go vet only", which is thin for a project | |
| # that runs model output with credentials able to write to source repos. | |
| # notice-drift answers a related but separate ask from a CNCF General | |
| # Technical Review: how does the project ensure third-party code carries | |
| # correct and complete attribution. See that job's own comment for the full | |
| # reasoning. | |
| # NOTICE is in the path filter because the notice-drift job below compares the | |
| # COMMITTED repo-root NOTICE byte-for-byte against a fresh regeneration | |
| # (`check-notice-drift.sh NOTICE /tmp/NOTICE.generated`). NOTICE does not live | |
| # under src/, so before #5388 a PR editing NOTICE alone β hand-correcting an | |
| # attribution, or reverting the autofix commit β changed the exact file this | |
| # gate polices while running neither that gate nor anything else. The drift | |
| # would surface only on the next unrelated src/ change, attributed to that PR. | |
| # Same shape as dashboard/openapi.json in v2-tests.yml: guarded by a job whose | |
| # filter excluded it. | |
| on: | |
| workflow_call: | |
| inputs: | |
| integration_candidate: | |
| description: Run an installed immutable Integration-aware candidate with isolated read-only technical jobs. | |
| type: boolean | |
| default: false | |
| merge_group: | |
| types: [checks_requested] | |
| push: | |
| branches: [v4, v5, v6] | |
| # release PRs touch only these; the required contexts must report β #10795 | |
| paths: ['CHANGELOG.md', 'changelog.d/**', 'src/**', 'NOTICE', '.github/workflows/**', '.github/scripts/**', '.github/release-lines.yml'] | |
| pull_request: | |
| branches: [v4, v5, v6] | |
| # Required contexts from this workflow must appear on every PR head. Keep | |
| # the trigger unfiltered and let required-context-scope below skip Go scans | |
| # for out-of-scope diffs. | |
| schedule: | |
| # Weekly, so a newly-PUBLISHED advisory against unchanged code is still | |
| # found. A push-only trigger cannot catch that: the vulnerability appears | |
| # in the database, not in a diff. | |
| - cron: '0 6 * * 1' | |
| # Nightly full run: harmless today, and preserves a safety net if the | |
| # project later decides to skip duplicate post-merge push runs. | |
| - cron: '17 6 * * *' | |
| # Also weekly for notice-drift: an upstream dependency's LICENSE file can | |
| # change (a relicense, a typo fix) with go.mod/go.sum untouched, which a | |
| # push-only trigger would never catch either. | |
| permissions: | |
| contents: read | |
| # Same discipline as v2-ci.yml: cancel superseded PR runs, never cancel a | |
| # push-to-branch run (keyed on run_id so each gets its own group). | |
| concurrency: | |
| group: ${{ inputs.integration_candidate && 'integration-security' || github.workflow }}-${{ github.event_name == 'pull_request' && github.event.pull_request.number || github.run_id }} | |
| cancel-in-progress: ${{ !inputs.integration_candidate && github.event_name == 'pull_request' }} | |
| # Build-cache isolation for the self-hosted pool (see | |
| # .github/scripts/go-cache-guard.sh). `job` gives each job its own GOCACHE | |
| # under RUNNER_TEMP instead of the cephfs volume every runner pod shares; | |
| # GOMODCACHE stays shared. Set the repo variable HIVE_GO_BUILD_CACHE=shared | |
| # to go back to the shared build cache without a code change. | |
| env: | |
| GO_CACHE_GUARD_BUILD_CACHE: ${{ vars.HIVE_GO_BUILD_CACHE || 'job' }} | |
| jobs: | |
| required-context-scope: | |
| name: required context scope | |
| runs-on: ${{ !inputs.integration_candidate && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) && fromJSON(vars.HIVE_RUNNER_LABELS || '["ubuntu-latest"]') || 'ubuntu-latest' }} | |
| timeout-minutes: 10 | |
| outputs: | |
| run-go-security: ${{ steps.scope.outputs.run-go-security }} | |
| steps: | |
| - name: Decide whether Go security checks need real scans | |
| id: scope | |
| env: | |
| GITHUB_TOKEN: ${{ github.token }} | |
| PR_NUMBER: ${{ github.event.pull_request.number }} | |
| run: | | |
| set -euo pipefail | |
| if [ "${{ inputs.integration_candidate }}" = true ] && | |
| { [ "$GITHUB_EVENT_NAME" != workflow_dispatch ] || [[ "$GITHUB_REF" != refs/tags/hive-integration/* ]]; }; then | |
| echo "::error::Integration candidates require an explicitly dispatched immutable candidate tag" | |
| exit 1 | |
| fi | |
| if [ "${{ github.event_name }}" != "pull_request" ]; then | |
| echo "run-go-security=true" >> "$GITHUB_OUTPUT" | |
| exit 0 | |
| fi | |
| : > changed-files.txt | |
| page=1 | |
| api_root="${GITHUB_API_URL:-https://api.github.com}" | |
| while :; do | |
| curl -fsSL -H "Authorization: Bearer ${GITHUB_TOKEN}" -H "Accept: application/vnd.github+json" "${api_root}/repos/${GITHUB_REPOSITORY}/pulls/${PR_NUMBER}/files?per_page=100&page=${page}" -o changed-files-page.json | |
| python3 - <<'PYFILES' >> changed-files.txt | |
| import json | |
| with open('changed-files-page.json', encoding='utf-8') as f: | |
| for item in json.load(f): | |
| print(item['filename']) | |
| PYFILES | |
| count=$(python3 - <<'PYCOUNT' | |
| import json | |
| with open('changed-files-page.json', encoding='utf-8') as f: | |
| print(len(json.load(f))) | |
| PYCOUNT | |
| ) | |
| [ "$count" -lt 100 ] && break | |
| page=$((page + 1)) | |
| done | |
| rm -f changed-files-page.json | |
| run=false | |
| while IFS= read -r path; do | |
| case "$path" in | |
| CHANGELOG.md|changelog.d/*|src/*|NOTICE|.github/workflows/*|.github/scripts/*|.github/release-lines.yml) | |
| run=true | |
| ;; | |
| esac | |
| done < changed-files.txt | |
| echo "run-go-security=${run}" >> "$GITHUB_OUTPUT" | |
| if [ "$run" = true ]; then | |
| echo "Go security checks will run real scans." | |
| else | |
| echo "No Go security paths changed; required scan contexts will be skipped/success." | |
| fi | |
| govulncheck: | |
| needs: required-context-scope | |
| # release-gate/* is created only by tagged-release.yml and carries only CHANGELOG.md/changelog.d changes; report the required context immediately (#10863). | |
| if: ${{ always() && (needs.required-context-scope.result != 'success' || needs.required-context-scope.outputs.run-go-security == 'true') && !(github.event_name == 'pull_request' && startsWith(github.head_ref, 'release-gate/')) }} | |
| name: govulncheck (known vulnerabilities) | |
| # Required on v5: keep on the self-hosted pool so a GitHub-hosted runner | |
| # outage does not make otherwise-mergeable PRs impossible to merge. | |
| runs-on: ${{ !inputs.integration_candidate && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) && fromJSON(vars.HIVE_RUNNER_LABELS || '["ubuntu-latest"]') || 'ubuntu-latest' }} | |
| timeout-minutes: 25 | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| persist-credentials: ${{ !inputs.integration_candidate }} | |
| # Populate the shared node-local Go tool cache under the runner pods' | |
| # node-wide lock before setup-go, so parallel jobs on a cold cache (a | |
| # src/go.mod bump) stop clobbering each other's rm -rf + copy (#11175, | |
| # #10234). Never fails the job. | |
| - name: Warm shared Go tool cache (serialize the cold-cache population) | |
| run: bash "$GITHUB_WORKSPACE/.github/scripts/go-toolcache-guard.sh" | |
| - uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 | |
| with: | |
| cache: false | |
| go-version-file: src/go.mod | |
| - name: Guard shared Go cache (fall back when the runner volume is full) | |
| run: bash "$GITHUB_WORKSPACE/.github/scripts/go-cache-guard.sh" | |
| # Fill GOMODCACHE up front with bounded retries so a transient | |
| # proxy.golang.org stream reset (INTERNAL_ERROR, #14976) cannot fail the | |
| # job's first module-fetching step; see .github/actions/go-mod-download-retry. | |
| - name: Download Go modules (retry transient proxy resets) | |
| uses: ./.github/actions/go-mod-download-retry | |
| # Installed by version rather than via a marketplace action so the tool | |
| # itself is pinned and the supply chain is one hop shorter. | |
| - name: Install govulncheck | |
| run: go install golang.org/x/vuln/cmd/govulncheck@v1.1.4 | |
| - name: Scan | |
| working-directory: src | |
| run: govulncheck ./... ./crux/... | |
| gosec: | |
| needs: required-context-scope | |
| # release-gate/* is created only by tagged-release.yml and carries only CHANGELOG.md/changelog.d changes; report the required context immediately (#10863). | |
| if: ${{ always() && (needs.required-context-scope.result != 'success' || needs.required-context-scope.outputs.run-go-security == 'true') && !(github.event_name == 'pull_request' && startsWith(github.head_ref, 'release-gate/')) }} | |
| name: gosec (insecure patterns) | |
| # Required on v5: keep on the self-hosted pool so a GitHub-hosted runner | |
| # outage does not make otherwise-mergeable PRs impossible to merge. | |
| runs-on: ${{ !inputs.integration_candidate && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) && fromJSON(vars.HIVE_RUNNER_LABELS || '["ubuntu-latest"]') || 'ubuntu-latest' }} | |
| timeout-minutes: 25 | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| persist-credentials: ${{ !inputs.integration_candidate }} | |
| # cache: false β the hive runners mount a persistent GOCACHE/GOMODCACHE; | |
| # setup-go's post-step would otherwise tar that multi-GB shared cache | |
| # into the Actions cache on every run (#8762). | |
| # Populate the shared node-local Go tool cache under the runner pods' | |
| # node-wide lock before setup-go, so parallel jobs on a cold cache (a | |
| # src/go.mod bump) stop clobbering each other's rm -rf + copy (#11175, | |
| # #10234). Never fails the job. | |
| - name: Warm shared Go tool cache (serialize the cold-cache population) | |
| run: bash "$GITHUB_WORKSPACE/.github/scripts/go-toolcache-guard.sh" | |
| - uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 | |
| with: | |
| cache: false | |
| go-version-file: src/go.mod | |
| - name: Guard shared Go cache (fall back when the runner volume is full) | |
| run: bash "$GITHUB_WORKSPACE/.github/scripts/go-cache-guard.sh" | |
| # Fill GOMODCACHE up front with bounded retries so a transient | |
| # proxy.golang.org stream reset (INTERNAL_ERROR, #14976) cannot fail the | |
| # job's first module-fetching step; see .github/actions/go-mod-download-retry. | |
| - name: Download Go modules (retry transient proxy resets) | |
| uses: ./.github/actions/go-mod-download-retry | |
| # Version floor is load-bearing: gosec v2.21.4 pins golang.org/x/tools | |
| # v0.25.0, which does not COMPILE under Go 1.25 ("invalid array length"). | |
| # The failure looks like a scan failure but is a build failure in the | |
| # tool itself β do not downgrade to "fix" a finding. | |
| - name: Restore pinned gosec binary | |
| id: gosec-cache | |
| uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 | |
| with: | |
| path: ${{ runner.temp }}/hive-ci-tools/gosec | |
| key: hive-gosec-${{ runner.os }}-${{ runner.arch }}-v2.29.0-${{ hashFiles('src/go.mod') }} | |
| - name: Install gosec | |
| if: steps.gosec-cache.outputs.cache-hit != 'true' | |
| env: | |
| GOBIN: ${{ runner.temp }}/hive-ci-tools | |
| run: go install github.com/securego/gosec/v2/cmd/gosec@v2.29.0 | |
| # -exclude-generated: generated files are not hand-maintained, so a | |
| # finding there is not actionable by a reviewer. | |
| # Test files are IN scope deliberately: a test that shells out on | |
| # attacker-influenced input is a real hole, not a test-only concern. | |
| # NON-BLOCKING FOR NOW, deliberately and temporarily. | |
| # | |
| # The first run surfaced a real backlog, including G115 integer-overflow | |
| # conversions in pkg/knowledge and pkg/dashboard. Those deserve triage on | |
| # their own merits, not a rushed batch fix under a red gate β and a gate | |
| # that blocks every PR from day one on a pre-existing backlog gets | |
| # disabled rather than drained. | |
| # | |
| # So gosec RUNS and its findings are visible in the log on every PR, but | |
| # does not fail the build yet. Flipping this to blocking is the last step | |
| # of the triage issue #4903; do not flip it before the | |
| # HIGH-severity findings are resolved, and do not delete the step to make | |
| # the summary look clean. | |
| # | |
| # -concurrency 4: gosec defaults to runtime.NumCPU(), which in a runner | |
| # pod is the NODE's 32 vCPUs, not the pod's 6-CPU limit | |
| # (src/deploy/ci-runners/lke/hive-runners-lke-values.yaml). Each worker | |
| # type-checks and builds SSA for its own package, so 32 at once can | |
| # create memory pressure. This bounds a plausible contributor to the | |
| # "runner lost communication" failures of #16040, not a proven OOM | |
| # diagnosis. Keep this at or below the runner CPU limit | |
| # (test-native-queue-workflows.py enforces it). | |
| - name: Scan | |
| working-directory: src | |
| continue-on-error: true | |
| # The nested src/crux module is not loaded from here; it has its own | |
| # "Scan crux module" step below. | |
| run: '"${{ runner.temp }}/hive-ci-tools/gosec" -concurrency 4 -exclude-generated ./...' | |
| # src/crux is a nested Go module: the src/ scan above does not load it | |
| # (0 "Checking file: .../src/crux/" lines), so scan it from its own root. | |
| - name: Scan crux module | |
| working-directory: src/crux | |
| continue-on-error: true | |
| run: '"${{ runner.temp }}/hive-ci-tools/gosec" -concurrency 4 -exclude-generated ./...' | |
| golangci-lint: | |
| needs: required-context-scope | |
| # release-gate/* is created only by tagged-release.yml and carries only CHANGELOG.md/changelog.d changes; report the required context immediately (#10863). | |
| if: ${{ always() && (needs.required-context-scope.result != 'success' || needs.required-context-scope.outputs.run-go-security == 'true') && !(github.event_name == 'pull_request' && startsWith(github.head_ref, 'release-gate/')) }} | |
| name: golangci-lint | |
| # Required on v5: keep on the self-hosted pool so a GitHub-hosted runner | |
| # outage does not make otherwise-mergeable PRs impossible to merge. | |
| runs-on: ${{ !inputs.integration_candidate && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) && fromJSON(vars.HIVE_RUNNER_LABELS || '["ubuntu-latest"]') || 'ubuntu-latest' }} | |
| timeout-minutes: 25 | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| persist-credentials: ${{ !inputs.integration_candidate }} | |
| # Populate the shared node-local Go tool cache under the runner pods' | |
| # node-wide lock before setup-go, so parallel jobs on a cold cache (a | |
| # src/go.mod bump) stop clobbering each other's rm -rf + copy (#11175, | |
| # #10234). Never fails the job. | |
| - name: Warm shared Go tool cache (serialize the cold-cache population) | |
| run: bash "$GITHUB_WORKSPACE/.github/scripts/go-toolcache-guard.sh" | |
| - uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 | |
| with: | |
| cache: false | |
| go-version-file: src/go.mod | |
| - name: Guard shared Go cache (fall back when the runner volume is full) | |
| run: bash "$GITHUB_WORKSPACE/.github/scripts/go-cache-guard.sh" | |
| # Fill GOMODCACHE up front with bounded retries so a transient | |
| # proxy.golang.org stream reset (INTERNAL_ERROR, #14976) cannot fail the | |
| # job's first module-fetching step; see .github/actions/go-mod-download-retry. | |
| - name: Download Go modules (retry transient proxy resets) | |
| uses: ./.github/actions/go-mod-download-retry | |
| # skip-cache: the action's own cache save/restore is the same multi-GB | |
| # tar problem as setup-go's; the runners already keep GOCACHE warm. | |
| - uses: golangci/golangci-lint-action@ba0d7d2ec06a0ea1cb5fa41b2e4a3ab91d21278a # v9.3.0 | |
| with: | |
| version: v2.13.1 | |
| working-directory: src | |
| # src/go.work: ./... covers the root module only; the nested Crux | |
| # module (#13536) is addressed as ./crux/... from src/. | |
| args: ./... ./crux/... | |
| skip-cache: true | |
| action-pins: | |
| name: action pins resolve | |
| needs: required-context-scope | |
| if: ${{ always() && (needs.required-context-scope.result != 'success' || needs.required-context-scope.outputs.run-go-security == 'true') }} | |
| runs-on: ${{ !inputs.integration_candidate && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) && fromJSON(vars.HIVE_RUNNER_LABELS || '["ubuntu-latest"]') || 'ubuntu-latest' }} | |
| timeout-minutes: 10 | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| persist-credentials: ${{ !inputs.integration_candidate }} | |
| # A fabricated 40-hex SHA looks exactly like a correct pin and is only | |
| # caught when the workflow RUNS. tagged-release.yml shipped one (#4908), so every | |
| # tagged release failed at "Prepare all required actions" β discovered | |
| # only when someone tried to cut a release. | |
| - name: Action-pins gate self-test | |
| run: bash src/scripts/test-check-action-pins.sh | |
| - name: Check every pinned action SHA exists | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: src/scripts/check-action-pins.sh .github/workflows | |
| notice-drift: | |
| name: NOTICE matches the module graph | |
| needs: required-context-scope | |
| if: ${{ always() && (needs.required-context-scope.result != 'success' || needs.required-context-scope.outputs.run-go-security == 'true') }} | |
| runs-on: ${{ !inputs.integration_candidate && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) && fromJSON(vars.HIVE_RUNNER_LABELS || '["ubuntu-latest"]') || 'ubuntu-latest' }} | |
| timeout-minutes: 20 | |
| # WHY THIS JOB EXISTS: a CNCF General Technical Review asks how the | |
| # project ensures third-party attribution stays correct and complete. A | |
| # generated NOTICE that can silently go stale is worse than no NOTICE at | |
| # all β it makes a false completeness claim the moment a dependency is | |
| # bumped, added, or removed without anyone regenerating the file by hand. | |
| # This job is the enforcement: it regenerates NOTICE the same way a | |
| # contributor would (src/scripts/generate-notice.sh, itself pinned β see | |
| # that script's header for why google/go-licenses was chosen) and fails | |
| # if the committed NOTICE differs from a fresh run against the current | |
| # src/go.mod / src/go.sum. | |
| # | |
| # FIRST-RUN STATE: the NOTICE committed alongside this job is a | |
| # STATICALLY-DERIVED placeholder (see NOTICE's own header) β it was | |
| # assembled from src/go.mod without running `go` tooling, because the PR | |
| # that introduced it was authored in an environment where `go` could not | |
| # be run. That placeholder is EXPECTED to differ from this job's output | |
| # the first time this job runs for real (this job needs `go`, which is | |
| # exactly what was unavailable when NOTICE was hand-assembled). This job | |
| # deliberately does NOT special-case that expected first-run diff by | |
| # skipping or soft-failing β a diff against the placeholder is a real, | |
| # actionable diff (the placeholder is explicitly not yet the resolved | |
| # module graph), and the fix is the same either way: run | |
| # src/scripts/generate-notice.sh locally, commit the result. There is | |
| # nothing for this job to silently tolerate; "the file is stale, here is | |
| # the regenerated version, commit it" is the correct message on run one | |
| # exactly as it is on every run after. | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| persist-credentials: ${{ !inputs.integration_candidate }} | |
| - name: Exercise NOTICE drift diagnostics | |
| run: src/scripts/test-check-notice-drift.sh | |
| # The autofix workflow commits a generated NOTICE unattended, so the | |
| # guard that decides whether a generated file is fit to commit needs its | |
| # own regression coverage here β notice-autofix.yml runs on | |
| # workflow_run and cannot gate a PR itself. | |
| - name: Exercise generated-NOTICE validation | |
| run: src/scripts/test-validate-generated-notice.sh | |
| # Populate the shared node-local Go tool cache under the runner pods' | |
| # node-wide lock before setup-go, so parallel jobs on a cold cache (a | |
| # src/go.mod bump) stop clobbering each other's rm -rf + copy (#11175, | |
| # #10234). Never fails the job. | |
| - name: Warm shared Go tool cache (serialize the cold-cache population) | |
| run: bash "$GITHUB_WORKSPACE/.github/scripts/go-toolcache-guard.sh" | |
| - uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 | |
| with: | |
| cache: false | |
| go-version-file: src/go.mod | |
| - name: Guard shared Go cache (fall back when the runner volume is full) | |
| run: bash "$GITHUB_WORKSPACE/.github/scripts/go-cache-guard.sh" | |
| # Fill GOMODCACHE up front with bounded retries so a transient | |
| # proxy.golang.org stream reset (INTERNAL_ERROR, #14976) cannot fail the | |
| # job's first module-fetching step; see .github/actions/go-mod-download-retry. | |
| - name: Download Go modules (retry transient proxy resets) | |
| uses: ./.github/actions/go-mod-download-retry | |
| - name: Regenerate NOTICE from the current module graph | |
| run: src/scripts/generate-notice.sh /tmp/NOTICE.generated | |
| # Publish the regenerated file BEFORE the gate runs, so it is uploaded | |
| # on the failing path too β that is the only path where anyone needs it. | |
| # notice-autofix.yml consumes this artifact via workflow_run to commit | |
| # the regeneration back onto a Dependabot branch (#5256). Uploading here | |
| # rather than regenerating in the autofix workflow is deliberate: this | |
| # job already runs the generator with a read-only token and no secrets, | |
| # so the privileged workflow never has to execute PR-head code. | |
| - name: Publish the regenerated NOTICE for the autofix workflow | |
| if: github.event_name == 'pull_request' | |
| uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2 | |
| with: | |
| name: notice-generated | |
| path: /tmp/NOTICE.generated | |
| retention-days: 1 | |
| if-no-files-found: error | |
| # The PR number travels with the artifact because workflow_run does not | |
| # expose it: the triggering run's event payload is not forwarded, and | |
| # resolving head SHA back to a PR is an extra API round trip that can | |
| # match the wrong PR when several share a head. | |
| - name: Record the PR number for the autofix workflow | |
| if: github.event_name == 'pull_request' | |
| run: echo "${{ github.event.pull_request.number }}" > /tmp/pr-number.txt | |
| - name: Publish the PR number for the autofix workflow | |
| if: github.event_name == 'pull_request' | |
| uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2 | |
| with: | |
| name: notice-pr-number | |
| path: /tmp/pr-number.txt | |
| retention-days: 1 | |
| if-no-files-found: error | |
| - name: Fail if the committed NOTICE is stale | |
| run: src/scripts/check-notice-drift.sh NOTICE /tmp/NOTICE.generated |