diff --git a/.github/workflows/bump-version.yaml b/.github/workflows/bump-version.yaml index b2190a5..693fac3 100644 --- a/.github/workflows/bump-version.yaml +++ b/.github/workflows/bump-version.yaml @@ -169,26 +169,46 @@ jobs: echo "Failed to push bumped versions; tried $TRY times." exit 1 working-directory: self + # Stamp metadata into the GitHub release body so downstream workflows (docker.yaml, deploy.yaml) + # can recover context that would otherwise be lost. Once this workflow creates the release using + # WORKFLOW_PAT, every subsequent event (release:created, release:released) is triggered + # by leeroy-travis (the PAT owner), washing out information like who actually clicked the button + # and which PR produced this commit. We encode it here so it survives the handoff. + # + # Output is a compact JSON object, e.g.: + # {"triggered_by":"leeroy-travis","latest_pr":1234} - name: Generate release text id: release-body run: | set -x - # Get the most recent commit. Hopefully it was a PR merge. + + # --- triggered_by: who actually kicked off this run --- + # Always recorded. deploy.yaml reads this to decide whether to apply the auto-promote label: + # scheduled rebuilds dispatch bump-version via rebuild.yaml's PAT (leeroy-travis), while + # humans clicking workflow_dispatch in the UI show up as themselves. + METADATA=$(jq -cn --arg triggered_by "${{ github.triggering_actor }}" \ + '{triggered_by: $triggered_by}') + + # --- latest_pr: the PR that produced the commit we're about to release --- + # Best-effort. deploy.yaml uses it to comment back on the original PR and assign reviewers. + # May be absent when: + # - Triggered by workflow_dispatch (the `push` event payload's `.after` field doesn't exist). + # - The commit landed on main without a PR (direct push, admin override of branch protection). COMMIT=$(jq -r '.after' ${{ github.event_path }}) - if [ "${COMMIT}" = "null" ] || [ -z "${COMMIT}" ] ; then - exit 0 - fi - # Get the most recent PRs; hopefully ours is one of them. - curl -fSs -H "Accept: application/vnd.github+json" \ - -H "Authorization: Bearer ${{ secrets.WORKFLOW_PAT }}" \ - https://api.github.com/repos/${{ github.repository }}/pulls?state=all\&base=${{ github.ref }}\&sort=updated\&direction=desc > /tmp/prs.json - # Find a PR that resulted in our commit. - PR=$(jq -r ".[] | select(.merge_commit_sha == \"${COMMIT}\") | .number" /tmp/prs.json) - if [ "${PR}" = "null" ] || [ -z "${PR}" ] ; then - exit 0 + if [ "${COMMIT}" != "null" ] && [ -n "${COMMIT}" ] ; then + # GitHub's PRs API has no "find by merge commit" endpoint, so we list the most recently + # updated PRs against this branch and look for one whose merge_commit_sha matches ours. + # If the merge happened long enough ago that it's fallen off the first page, we miss it. + curl -fSs -H "Accept: application/vnd.github+json" \ + -H "Authorization: Bearer ${{ secrets.WORKFLOW_PAT }}" \ + https://api.github.com/repos/${{ github.repository }}/pulls?state=all\&base=${{ github.ref }}\&sort=updated\&direction=desc > /tmp/prs.json + PR=$(jq -r ".[] | select(.merge_commit_sha == \"${COMMIT}\") | .number" /tmp/prs.json) + if [ "${PR}" != "null" ] && [ -n "${PR}" ] ; then + METADATA=$(echo "$METADATA" | jq -c --argjson pr "$PR" '. + {latest_pr: $pr}') + fi fi - # Build the string we'll use as the description of the release. - echo "body=latest_pr:${PR}" >> "$GITHUB_OUTPUT" + + echo "body=${METADATA}" >> "$GITHUB_OUTPUT" working-directory: self # This triggers the Docker workflow's `release: created` - name: Create GitHub Release diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index fddc8e0..f3a2a1f 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -61,16 +61,25 @@ jobs: with: ref: ${{ inputs.tag }} path: software - # Find the original PR to the software repo. The git tag should correspond to a GitHub release, which should have a - # description pointing to the PR number. - - name: Get original PR + # Pull metadata out of the GitHub release body. bump-version stamps a JSON object containing + # `triggered_by` and (when discoverable) `latest_pr` so downstream workflows can recover context + # that the WORKFLOW_PAT-driven release event would otherwise lose. + - name: Get release metadata id: software-pr working-directory: software run: | set -x - gh release view ${{ inputs.tag }} --json body --jq '.body' | sed 's/.*latest_pr:\([0-9]*\).*/\1/' > /tmp/pr - PR=$(cat /tmp/pr) - if [ -z "${PR}" ] || [ "${PR}" = "null" ] ; then + BODY=$(gh release view ${{ inputs.tag }} --json body --jq '.body') + # Releases predating the JSON format have plain-text bodies; treat them as having no metadata. + if ! echo "$BODY" | jq -e . >/dev/null 2>&1 ; then + exit 0 + fi + TRIGGERED_BY=$(echo "$BODY" | jq -r '.triggered_by // empty') + if [ -n "$TRIGGERED_BY" ] ; then + echo "triggered_by=$TRIGGERED_BY" >> "$GITHUB_OUTPUT" + fi + PR=$(echo "$BODY" | jq -r '.latest_pr // empty') + if [ -z "${PR}" ] ; then exit 0 fi echo "pr=$PR" >> "$GITHUB_OUTPUT" @@ -136,10 +145,13 @@ jobs: git commit -m "${COMMIT_MSG}" git push -u origin "${NEW_BRANCH}" - # If we can't find the originating PR, it must mean the change came from automation, and we should auto-promote. - if [ -z "${{ steps.software-pr.outputs.pr }}" ] ; then - LABELS="--label auto-promote" - fi + # Auto-promote only when bump-version was triggered by a known automation actor (e.g., + # leeroy-travis from rebuild.yaml). Manual workflow_dispatch by a human is intentionally excluded. + case "${{ steps.software-pr.outputs.triggered_by }}" in + leeroy-travis) + LABELS="--label auto-promote" + ;; + esac AUTHOR="${{ steps.software-pr.outputs.author }}" if [ -n "$AUTHOR" ] && [ "$AUTHOR" != "app/dependabot" ]; then ASSIGNEE="--assignee $AUTHOR --reviewer $AUTHOR"