Replace PR #201666 #271
This file contains 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: Create replacement pull request | |
run-name: "Replace PR #${{ inputs.pull_request }}" | |
defaults: | |
run: | |
shell: bash -xeuo pipefail {0} | |
concurrency: | |
group: ${{ github.workflow }}-${{ github.event.inputs.pull_request }}-${{ github.event.inputs.upload }} | |
cancel-in-progress: ${{ !fromJson(github.event.inputs.upload) }} | |
on: | |
workflow_dispatch: | |
inputs: | |
pull_request: | |
description: Pull request number | |
type: number | |
required: true | |
autosquash: | |
description: "Squash pull request commits according to Homebrew style? (default: true)" | |
type: boolean | |
required: false | |
default: true | |
upload: | |
description: > | |
Upload bottles built from original pull request? (default: false) | |
Warning: This destroys status check information when used with autosquash! | |
type: boolean | |
required: false | |
default: false | |
warn_on_upload_failure: | |
description: "Pass `--warn-on-upload-failure` to `brew pr-pull`? (default: false)" | |
type: boolean | |
required: false | |
default: false | |
message: | |
description: "Message to include when autosquashing revision bumps, deletions, and rebuilds (requires autosquash)" | |
required: false | |
env: | |
PR: ${{ inputs.pull_request }} | |
GNUPGHOME: /tmp/gnupghome | |
HOMEBREW_DEVELOPER: 1 | |
HOMEBREW_NO_AUTO_UPDATE: 1 | |
HOMEBREW_NO_INSTALL_FROM_API: 1 | |
GH_REPO: ${{ github.repository }} | |
GH_NO_UPDATE_NOTIFIER: 1 | |
GH_PROMPT_DISABLED: 1 | |
RUN_URL: ${{ github.event.repository.html_url }}/actions/runs/${{ github.run_id }} | |
REPLACEMENT_BRANCH: PR/${{ inputs.pull_request }} | |
jobs: | |
create: | |
runs-on: ubuntu-latest | |
container: | |
image: ghcr.io/homebrew/ubuntu22.04:master | |
permissions: | |
contents: read | |
pull-requests: write # for `post-comment`, `gh api`, `gh pr edit` | |
repository-projects: write # for `gh pr edit` | |
attestations: write # for actions/attest-build-provenance | |
id-token: write # for actions/attest-build-provenance | |
steps: | |
- name: Post comment once started | |
uses: Homebrew/actions/post-comment@master | |
with: | |
token: ${{ secrets.GITHUB_TOKEN }} | |
issue: ${{ env.PR }} | |
body: ":shipit: @${{ github.actor }} has [requested creation of a replacement PR](${{ env.RUN_URL }})." | |
bot_body: ":robot: An automated task has [requested creation of a replacement PR](${{ env.RUN_URL }})." | |
bot: github-actions[bot] | |
- name: Set up Homebrew | |
id: set-up-homebrew | |
uses: Homebrew/actions/setup-homebrew@master | |
with: | |
core: true | |
cask: false | |
test-bot: false | |
- name: Get reviewers | |
id: reviewers | |
env: | |
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
run: | | |
review_data="$( | |
gh api \ | |
--header 'Accept: application/vnd.github+json' \ | |
--header 'X-GitHub-Api-Version: 2022-11-28' \ | |
"repos/$GITHUB_REPOSITORY/pulls/$PR/reviews" | |
)" | |
reviewers="$(jq --compact-output '[.[].user.login]' <<< "$review_data")" | |
approved="$(jq --raw-output 'any(.[].state; .== "APPROVED")' <<< "$review_data")" | |
# See https://github.com/octokit/octokit.net/issues/1763 for possible `mergeable_state` values. | |
mergeable="$( | |
gh api \ | |
--header 'Accept: application/vnd.github+json' \ | |
--header 'X-GitHub-Api-Version: 2022-11-28' \ | |
"repos/$GITHUB_REPOSITORY/pulls/$PR" \ | |
--jq '(.mergeable_state == "clean") and (.draft | not)' | |
)" | |
{ | |
echo "reviewers=$reviewers" | |
echo "approved=$approved" | |
echo "mergeable=$mergeable" | |
} >> "$GITHUB_OUTPUT" | |
- name: Check approval if needed | |
if: > | |
inputs.upload && | |
!(fromJson(steps.reviewers.outputs.approved) && fromJson(steps.reviewers.outputs.mergeable)) | |
run: | | |
echo "::error ::Refusing to upload bottles because PR #$PR is not mergeable!" | |
exit 1 | |
- name: Configure Git user | |
id: git-user-config | |
uses: Homebrew/actions/git-user-config@master | |
with: | |
username: BrewTestBot | |
- name: Checkout PR branch | |
if: ${{ !inputs.autosquash }} | |
working-directory: ${{steps.set-up-homebrew.outputs.repository-path}} | |
env: | |
GH_TOKEN: ${{secrets.GITHUB_TOKEN}} | |
run: gh pr checkout "$PR" --repo "$GITHUB_REPOSITORY" | |
- name: Checkout replacement PR branch | |
working-directory: ${{ steps.set-up-homebrew.outputs.repository-path }} | |
env: | |
START_POINT: ${{ inputs.autosquash && 'origin/master' || 'HEAD' }} | |
run: | | |
if git ls-remote --exit-code --heads origin "$REPLACEMENT_BRANCH" | |
then | |
echo "::error ::Branch $REPLACEMENT_BRANCH already exists!" | |
exit 1 | |
fi | |
git checkout -b "$REPLACEMENT_BRANCH" "$START_POINT" | |
- name: Dismiss approvals | |
if: fromJson(steps.reviewers.outputs.approved) | |
uses: Homebrew/actions/dismiss-approvals@master | |
with: | |
token: ${{ secrets.GITHUB_TOKEN }} | |
pr: ${{ env.PR }} | |
message: Replacement PR dispatched | |
- name: Set up commit signing | |
uses: Homebrew/actions/setup-commit-signing@master | |
with: | |
signing_key: ${{ secrets.BREWTESTBOT_GPG_SIGNING_SUBKEY }} | |
- name: Pull PR | |
id: pr-pull | |
working-directory: ${{ steps.set-up-homebrew.outputs.repository-path }} | |
env: | |
BREWTESTBOT_NAME_EMAIL: "BrewTestBot <[email protected]>" | |
HOMEBREW_GPG_PASSPHRASE: ${{ inputs.autosquash && secrets.BREWTESTBOT_GPG_SIGNING_SUBKEY_PASSPHRASE }} | |
HOMEBREW_GITHUB_API_TOKEN: ${{ secrets.HOMEBREW_CORE_PUBLIC_REPO_EMAIL_TOKEN }} | |
MESSAGE: ${{ inputs.message }} | |
AUTOSQUASH_FLAG: ${{ inputs.autosquash && '--autosquash' || '' }} | |
CLEAN_FLAG: ${{ inputs.autosquash && '' || '--clean' }} | |
NO_CHERRY_PICK_FLAG: ${{ inputs.autosquash && '' || '--no-cherry-pick' }} | |
run: | | |
# Don't quote arguments that might be empty; this causes errors. | |
brew pr-pull \ | |
--no-upload \ | |
--debug \ | |
--branch-okay \ | |
--workflows=tests.yml \ | |
--committer="$BREWTESTBOT_NAME_EMAIL" \ | |
--root-url="https://ghcr.io/v2/homebrew/core" \ | |
--retain-bottle-dir \ | |
${AUTOSQUASH_FLAG:+"${AUTOSQUASH_FLAG}"} \ | |
${CLEAN_FLAG:+"--clean"} \ | |
${NO_CHERRY_PICK_FLAG:+"--no-cherry-pick"} \ | |
${MESSAGE:+"--message=${MESSAGE}"} \ | |
"$PR" | |
- name: Generate build provenance | |
uses: actions/attest-build-provenance@7668571508540a607bdfd90a87a560489fe372eb # v2.1.0 | |
with: | |
subject-path: '${{steps.pr-pull.outputs.bottle_path}}/*.tar.gz' | |
if: inputs.upload | |
- name: Upload bottles to GitHub Packages | |
id: pr-upload | |
if: inputs.upload | |
working-directory: ${{steps.pr-pull.outputs.bottle_path}} | |
env: | |
BREWTESTBOT_NAME_EMAIL: "BrewTestBot <[email protected]>" | |
HOMEBREW_GPG_PASSPHRASE: ${{ secrets.BREWTESTBOT_GPG_SIGNING_SUBKEY_PASSPHRASE }} | |
HOMEBREW_GITHUB_PACKAGES_USER: brewtestbot | |
HOMEBREW_GITHUB_PACKAGES_TOKEN: ${{secrets.HOMEBREW_CORE_GITHUB_PACKAGES_TOKEN}} | |
WARN_ON_UPLOAD_FAILURE_FLAG: ${{inputs.warn_on_upload_failure && '--warn-on-upload-failure' || ''}} | |
run: | | |
# Don't quote arguments that might be empty; this causes errors when `brew` | |
# interprets them as empty arguments when we want `brew` to ignore them instead. | |
brew pr-upload \ | |
--debug \ | |
--committer="$BREWTESTBOT_NAME_EMAIL" \ | |
--root-url="https://ghcr.io/v2/homebrew/core" \ | |
${WARN_ON_UPLOAD_FAILURE_FLAG:+"${WARN_ON_UPLOAD_FAILURE_FLAG}"} | |
- name: Push commits | |
uses: Homebrew/actions/git-try-push@master | |
with: | |
token: ${{ secrets.HOMEBREW_GITHUB_PUBLIC_REPO_TOKEN }} | |
directory: ${{ steps.set-up-homebrew.outputs.repository-path }} | |
branch: ${{ env.REPLACEMENT_BRANCH }} | |
env: | |
GIT_COMMITTER_NAME: ${{ steps.git-user-config.outputs.name }} | |
GIT_COMMITTER_EMAIL: ${{ steps.git-user-config.outputs.email }} | |
HOMEBREW_GPG_PASSPHRASE: ${{ secrets.BREWTESTBOT_GPG_SIGNING_SUBKEY_PASSPHRASE }} | |
- name: Open replacement pull request | |
id: create-pr | |
working-directory: ${{ steps.set-up-homebrew.outputs.repository-path }} | |
env: | |
GH_TOKEN: ${{ secrets.HOMEBREW_GITHUB_PUBLIC_REPO_TOKEN }} | |
REVIEWERS: ${{ join(fromJson(steps.reviewers.outputs.reviewers)) }} | |
LABELS: ${{ inputs.upload && 'CI-published-bottle-commits' || '' }} | |
run: | | |
cat <<MESSAGE > body.txt | |
Created by [\`create-replacement-pr.yml\`]($RUN_URL) | |
----- | |
Closes #$PR | |
MESSAGE | |
original_title="$(gh pr view "$PR" --json title --jq '.title' --repo "$GITHUB_REPOSITORY")" | |
gh pr create \ | |
--base "$GITHUB_REF" \ | |
--title "$original_title (replacement for #$PR)" \ | |
--body-file body.txt \ | |
--head "$REPLACEMENT_BRANCH" \ | |
--reviewer "$REVIEWERS" \ | |
--label "$LABELS" \ | |
--repo "$GITHUB_REPOSITORY" | |
pull_number="$(gh pr list --head "$REPLACEMENT_BRANCH" --limit 1 --json number --jq '.[].number' --repo "$GITHUB_REPOSITORY")" | |
echo "pull_number=$pull_number" >> "$GITHUB_OUTPUT" | |
- name: Approve replacement PR if applicable | |
if: > | |
fromJson(steps.reviewers.outputs.approved) && | |
fromJson(steps.reviewers.outputs.mergeable) | |
working-directory: ${{ steps.set-up-homebrew.outputs.repository-path }} | |
env: | |
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
REPLACEMENT_PR: ${{ steps.create-pr.outputs.pull_number }} | |
run: gh pr review --approve "$REPLACEMENT_PR" --repo "$GITHUB_REPOSITORY" | |
- name: Enable automerge for replacement PR if applicable | |
if: inputs.upload | |
working-directory: ${{ steps.set-up-homebrew.outputs.repository-path }} | |
env: | |
GH_TOKEN: ${{ secrets.HOMEBREW_GITHUB_PUBLIC_REPO_TOKEN }} | |
REPLACEMENT_PR: ${{ steps.create-pr.outputs.pull_number }} | |
run: | | |
gh pr merge "$REPLACEMENT_PR" \ | |
--auto \ | |
--merge \ | |
--delete-branch \ | |
--match-head-commit "$(git rev-parse HEAD)" \ | |
--repo "$GITHUB_REPOSITORY" | |
- name: Label replaced PR | |
env: | |
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
run: gh pr edit --add-label automerge-skip --add-label superseded "$PR" --repo "$GITHUB_REPOSITORY" | |
- name: Mark replaced PR as draft | |
env: # We need a PAT here. https://github.com/github/docs/issues/8925#issuecomment-970255180 | |
GH_TOKEN: ${{ secrets.HOMEBREW_GITHUB_PUBLIC_REPO_TOKEN }} | |
run: gh pr ready --undo "$PR" --repo "$GITHUB_REPOSITORY" | |
- name: Post comment on success | |
uses: Homebrew/actions/post-comment@master | |
with: | |
token: ${{ secrets.GITHUB_TOKEN }} | |
issue: ${{ env.PR }} | |
body: ":white_check_mark: @${{ github.actor }} replacement PR created at #${{ steps.create-pr.outputs.pull_number }}." | |
bot_body: ":white_check_mark: Replacement PR created at #${{ steps.create-pr.outputs.pull_number }}." | |
bot: github-actions[bot] | |
- name: Post comment on failure | |
if: ${{ !success() }} | |
uses: Homebrew/actions/post-comment@master | |
with: | |
token: ${{ secrets.GITHUB_TOKEN }} | |
issue: ${{ env.PR }} | |
body: ":warning: @${{ github.actor }} replacement PR creation [failed](${{ env.RUN_URL }})." | |
bot_body: ":warning: Replacement PR creation [failed](${{ env.RUN_URL }})." | |
bot: github-actions[bot] |