diff --git a/.github/workflows/changelog.yml b/.github/workflows/changelog.yml index dde678d9645..a19ec4b7f12 100644 --- a/.github/workflows/changelog.yml +++ b/.github/workflows/changelog.yml @@ -2,6 +2,7 @@ name: changelog permissions: contents: read + pull-requests: read concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.id }} @@ -10,24 +11,36 @@ concurrency: on: pull_request: branches: ["main"] + # labeled/unlabeled: skip-changelog and changelog-version-edit waive parts + # of this check, so the verdict is only valid for the label set it was + # computed under. Without these types, removing a label leaves the green + # run that the label produced standing, and the gate silently stays open. + # + # The job cannot cheaply no-op on an unrelated label: a job skipped by `if` + # reports its check as skipped, which branch protection counts as passing, + # so it would overwrite a real failure with a green. Every run therefore + # computes the real verdict. + types: [opened, synchronize, reopened, labeled, unlabeled] jobs: changelog: runs-on: ubuntu-latest timeout-minutes: 10 steps: - - name: Check for skip-changelog label - id: skip + # Read labels from the API so re-runs do not use a stale event payload. + - name: Read PR labels + id: labels shell: bash env: - SKIP_CHANGELOG: ${{ contains(github.event.pull_request.labels.*.name, 'skip-changelog') }} + GH_TOKEN: ${{ github.token }} + PR_URL: ${{ github.event.pull_request.url }} run: | - if [ "$SKIP_CHANGELOG" = "true" ]; then - echo "skip=true" >> "$GITHUB_OUTPUT" - echo "PR has 'skip-changelog' label; bypassing changelog check." - else - echo "skip=false" >> "$GITHUB_OUTPUT" - fi + set -euo pipefail + gh api "$PR_URL" --jq ' + [.labels[].name] | + "skip=\(index("skip-changelog") != null)", + "version_edit=\(index("changelog-version-edit") != null)" + ' >> "$GITHUB_OUTPUT" # The heading guard runs even for skip-changelog PRs: that label only # waives the news-fragment requirement, not the publish-trigger guard. - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 @@ -40,19 +53,19 @@ jobs: # from, so the guard and the publisher cannot disagree. Escape hatch # for deliberate restructuring: the 'changelog-version-edit' label. - uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0 - if: ${{ !contains(github.event.pull_request.labels.*.name, 'changelog-version-edit') && !startsWith(github.head_ref, 'release/') }} + if: steps.labels.outputs.version_edit != 'true' && !startsWith(github.head_ref, 'release/') - name: Reject manual changelog version headings - if: ${{ !contains(github.event.pull_request.labels.*.name, 'changelog-version-edit') && !startsWith(github.head_ref, 'release/') }} + if: steps.labels.outputs.version_edit != 'true' && !startsWith(github.head_ref, 'release/') env: BASE_REF: origin/${{ github.base_ref }} run: uv run --no-config --locked --script scripts/release.py check-headings - uses: ./.github/actions/setup_build_env - if: steps.skip.outputs.skip != 'true' + if: steps.labels.outputs.skip != 'true' with: python-version: "3.14" run-uv-sync: true - name: Determine affected packages - if: steps.skip.outputs.skip != 'true' + if: steps.labels.outputs.skip != 'true' id: affected shell: bash run: | @@ -75,7 +88,7 @@ jobs: echo "Affected packages:" cat affected.txt - name: Check for news fragments - if: steps.skip.outputs.skip != 'true' + if: steps.labels.outputs.skip != 'true' shell: bash run: | set -euo pipefail