From a47bb9b2c27252dc0f6e744e19ac12329e5d74a5 Mon Sep 17 00:00:00 2001 From: Antoine du Hamel Date: Fri, 6 Sep 2024 09:08:24 +0200 Subject: [PATCH] tools: add util scripts to land and rebase PRs PR-URL: https://github.com/nodejs/node/pull/54656 Reviewed-By: James M Snell Reviewed-By: LiviaMedeiros --- tools/actions/merge.sh | 62 +++++++++++++++++++++++++++++++++++++++++ tools/actions/rebase.sh | 11 ++++++++ 2 files changed, 73 insertions(+) create mode 100755 tools/actions/merge.sh create mode 100755 tools/actions/rebase.sh diff --git a/tools/actions/merge.sh b/tools/actions/merge.sh new file mode 100755 index 00000000000000..898e9e59ea3ea1 --- /dev/null +++ b/tools/actions/merge.sh @@ -0,0 +1,62 @@ +#!/bin/sh + +# Requires [gh](https://cli.github.com/), [jq](https://jqlang.github.io), git, and grep. Also awk if you pass a URL. + +# This script can be used to "purple-merge" PRs that are supposed to land as a single commit, using the "Squash and Merge" feature of GitHub. +# To land a PR with this tool: +# 1. Run `git node land --fixupAll` +# 2. Copy the hash of the commit at the top of the PR branch. +# 3. Run `tools/actions/merge.sh `. + +set -xe + +pr=$1 +commit_head=$2 +shift 2 || { echo "Expected two arguments"; exit 1; } + +OWNER=nodejs +REPOSITORY=node + +if expr "X$pr" : 'Xhttps://github.com/[^/]\{1,\}/[^/]\{1,\}/pull/[0-9]\{1,\}' >/dev/null; then + OWNER="$(echo "$pr" | awk 'BEGIN { FS = "/" } ; { print $4 }')" + REPOSITORY="$(echo "$pr" | awk 'BEGIN { FS = "/" } ; { print $5 }')" + pr="$(echo "$pr" | awk 'BEGIN { FS = "/" } ; { print $7 }')" +elif ! expr "X$pr" : 'X[0-9]\{1,\}' >/dev/null; then + echo "The first argument should be the PR ID or URL" +fi + +git log -1 HEAD --pretty='format:%B' | git interpret-trailers --parse --no-divider | \ + grep -q -x "^PR-URL: https://github.com/$OWNER/$REPOSITORY/pull/$pr$" || { + echo "Invalid PR-URL trailer" + exit 1 + } +git log -1 HEAD^ --pretty='format:%B' | git interpret-trailers --parse --no-divider | \ + grep -q -x "^PR-URL: https://github.com/$OWNER/$REPOSITORY/pull/$pr$" && { + echo "Refuse to squash and merge a PR landing in more than one commit" + exit 1 + } + +commit_title=$(git log -1 --pretty='format:%s') +commit_body=$(git log -1 --pretty='format:%b') + +jq -n \ + --arg title "${commit_title}" \ + --arg body "${commit_body}" \ + --arg head "${commit_head}" \ + '{merge_method:"squash",commit_title:$title,commit_message:$body,sha:$head}' > output.json +cat output.json +if ! gh api -X PUT "repos/${OWNER}/${REPOSITORY}/pulls/${pr}/merge" --input output.json > output; then + cat output + echo "Failed to merge $pr" + rm output output.json + exit 1 +fi +cat output +if ! commits="$(jq -r 'if .merged then .sha else error("not merged") end' < output)"; then + echo "Failed to merge $pr" + rm output output.json + exit 1 +fi +rm output.json output + +gh pr comment "$pr" --repo "$OWNER/$REPOSITORY" --body "Landed in $commits" diff --git a/tools/actions/rebase.sh b/tools/actions/rebase.sh new file mode 100755 index 00000000000000..6fdf07af0b81cf --- /dev/null +++ b/tools/actions/rebase.sh @@ -0,0 +1,11 @@ +#!/bin/sh + +set -xe + +# shellcheck disable=SC2016 +gh api graphql -F "prID=$(gh pr view "$1" --json id --jq .id || true)" -f query=' +mutation RebasePR($prID: ID!) { + updatePullRequestBranch(input:{pullRequestId:$prID,updateMethod:REBASE}) { + clientMutationId + } +}'