build: Bump Verify.XunitV3 from 31.27.0 to 31.28.0 #548
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: dependabot-automerge | |
| # The low-friction lane of the Dependabot automation policy: for Dependabot PRs, enable | |
| # GitHub auto-merge on patch and minor updates so they merge on their own once | |
| # the required checks pass. Major updates are deliberately left untouched for | |
| # human review. | |
| # | |
| # IMPORTANT — this workflow only *enables* auto-merge; it does not decide when to | |
| # merge. GitHub merges the PR only once the branch's REQUIRED status checks pass. | |
| # Without a branch-protection rule on `main` that marks the CI checks as required, | |
| # auto-merge would merge immediately. The required checks are therefore the safety | |
| # gate, not this workflow. | |
| # | |
| # Why the job keys on the pull request's AUTHOR and not on `github.actor`. Auto-merge, | |
| # once enabled, SURVIVES later pushes to the head branch. `github.actor` names whoever | |
| # triggered the run, so a push by someone else made the job skip — leaving the | |
| # auto-merge armed on a tip that nobody re-checked. The author of a pull request never | |
| # changes, so keying on it means this job runs on EVERY event of a Dependabot pull | |
| # request, including a push by someone else, which is precisely when it needs to act | |
| # (Sonar githubactions:S8232, zizmor `bot-conditions`). | |
| # | |
| # Identity is then settled twice, and the two answers do different work: | |
| # - `dependabot/fetch-metadata` re-checks the pull request's author and the FIRST | |
| # commit's signature (verified by reading the action's source; both its | |
| # skip-*-verification inputs default to false). It fails closed — no outputs, so | |
| # the steps below are skipped. | |
| # - The TIP check here is the one the action does not do. `fetch-metadata` validates | |
| # the first commit; auto-merge acts on the tip. Somebody with push access can append | |
| # to a Dependabot branch without changing either the author or the first commit. | |
| # | |
| # The two guards are deliberately asymmetric: | |
| # - ENABLING requires the strong proof — the tip is Dependabot's own GitHub-signed | |
| # commit. Commit author NAMES forge freely; GitHub's signature does not. | |
| # - DISABLING triggers on the weaker signal — the tip's author is not Dependabot — | |
| # because disabling is the fail-safe direction: at worst a human merges by hand. | |
| # Keying the disable on the SIGNATURE instead would fight `dependabot-autofix`, | |
| # which deliberately keeps auto-merge on after a trivial fix; its `--amend` and | |
| # `rebase` preserve Dependabot as the author but drop the signature. | |
| on: | |
| pull_request: | |
| branches: | |
| - main | |
| # Least privilege by default; the job widens only what it needs. | |
| permissions: | |
| contents: read | |
| jobs: | |
| automerge: | |
| name: Auto-merge Dependabot patch/minor | |
| runs-on: ubuntu-latest | |
| # Fetch metadata + enable auto-merge is seconds; cap a stuck runner. | |
| timeout-minutes: 10 | |
| # Only act on pull requests Dependabot OPENED — a fact no later push can change. | |
| if: github.event.pull_request.user.login == 'dependabot[bot]' | |
| permissions: | |
| # Enable auto-merge (contents) on the pull request (pull-requests). | |
| contents: write | |
| pull-requests: write | |
| steps: | |
| - name: Fetch Dependabot metadata | |
| id: meta | |
| uses: dependabot/fetch-metadata@25dd0e34f4fe68f24cc83900b1fe3fe149efef98 # v3 | |
| - name: Inspect the head commit this event is about | |
| id: tip | |
| # Reads the tip the checks actually ran against, not whatever the branch points at | |
| # by the time this job runs. A newer push raises its own event and re-enters here. | |
| # Fail closed: anything unreadable answers "not Dependabot's", which only ever | |
| # withholds or withdraws auto-merge. | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| GH_REPO: ${{ github.repository }} | |
| HEAD_SHA: ${{ github.event.pull_request.head.sha }} | |
| run: | | |
| set -euo pipefail | |
| tip="$(gh api "repos/${GH_REPO}/commits/${HEAD_SHA}" \ | |
| --jq '"\(.author.login // "")|\(.commit.verification.verified)"' 2>/dev/null || true)" | |
| author="${tip%%|*}" | |
| echo "head ${HEAD_SHA}: author='${author:-unknown}' signed='${tip##*|}'" | |
| # Signed by Dependabot — the strong proof, required to ARM auto-merge. | |
| if [ "$tip" = "dependabot[bot]|true" ]; then | |
| echo "state=dependabot-signed" >> "$GITHUB_OUTPUT" | |
| # Authored by Dependabot but unsigned: what dependabot-autofix leaves behind | |
| # after rewording or rebasing. Not proof enough to arm, not foreign enough to | |
| # disarm — leave whatever is already set alone. | |
| elif [ "$author" = "dependabot[bot]" ]; then | |
| echo "state=dependabot-unsigned" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "state=foreign" >> "$GITHUB_OUTPUT" | |
| fi | |
| - name: Enable auto-merge for patch and minor updates | |
| # Major updates fall through this condition, so they never get auto-merge | |
| # and stay open for a human to review. | |
| if: >- | |
| steps.tip.outputs.state == 'dependabot-signed' && | |
| (steps.meta.outputs.update-type == 'version-update:semver-patch' || | |
| steps.meta.outputs.update-type == 'version-update:semver-minor') | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| PR_URL: ${{ github.event.pull_request.html_url }} | |
| run: gh pr merge --auto --merge "$PR_URL" | |
| - name: Withdraw auto-merge from a foreign head | |
| # The step that closes the gap: auto-merge armed on Dependabot's own commit stays | |
| # armed across later pushes, so a commit appended by someone with push access would | |
| # ride an approval it never got. Withdrawing is idempotent — `|| true` because the | |
| # call errors when auto-merge was not enabled in the first place, which is the | |
| # ordinary case here. | |
| if: steps.tip.outputs.state == 'foreign' | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| PR_URL: ${{ github.event.pull_request.html_url }} | |
| run: | | |
| echo "Head is not Dependabot's commit; withdrawing auto-merge so a human decides." | |
| gh pr merge --disable-auto "$PR_URL" || true |