1
1
#! /usr/bin/env bash
2
- # A script to mirror an external contributor's pull request to a maintainer's fork
3
- # for the purpose of running E2E tests.
4
2
#
5
- # Prerequisites:
6
- # 1. GitHub CLI (`gh`) must be installed and authenticated (`gh auth login`).
7
- # 2. You must have a fork of the repository.
8
- # 3. You must have a git remote configured for the upstream repository (e.g., "upstream").
9
- #
10
- # Usage:
11
- # ./mirror-pr.sh <PR_NUMBER> <FORK_REMOTE>
12
- #
13
- # Example:
14
- # ./mirror-pr.sh 1234 my-github-user
15
- #
16
- # if no PR number is provided, it will prompt you to select one using `fzf`.
3
+ no_verify=
4
+ show_help () {
5
+ cat << EOF
6
+ 🪞 Mirror an external contributor's pull request to a maintainer's fork for E2E tests.
7
+
8
+ 🛠️ Prerequisites:
9
+ 1. 🐙 GitHub CLI (gh) must be installed and authenticated (gh auth login).
10
+ 2. 🍴 You must have a fork of the repository.
11
+ 3. 🔗 You must have a git remote configured for the upstream repository (e.g., "upstream").
12
+ 4. 👾 You need fzf and jq installed for selecting PRs and parsing JSON.
13
+
14
+ ▶️ Usage:
15
+ ./mirror-pr.sh <PR_NUMBER> <FORK_REMOTE>
16
+
17
+ 💡 Example:
18
+ ./mirror-pr.sh 1234 my-github-user
19
+
20
+ If no PR number or not fork are provided, it will prompt you to select one
21
+ using fzf.
22
+
23
+ EOF
24
+ grep -E " [ ]*[a-zA-Z0-9-]\) ##" $0 |
25
+ sed -e ' s/^[ ]*/-/' \
26
+ -e ' s/-\([0-9A-Za-z]*\)[ ]*|[ ]*\([0-9A-Za-z]*\)/-\1, -\2/' \
27
+ -e ' s/##//' -e ' s/)[ ]*/ - /' |
28
+ awk -F" - " ' {printf "%-10s %s\n", $1, $2}'
29
+
30
+ cat << EOF
31
+
32
+ EOF
33
+ }
34
+ while getopts " hn" opt; do
35
+ case $opt in
36
+ n) # # do not run pre-commit checks
37
+ no_verify=yes
38
+ ;;
39
+ h)
40
+ echo " usage: $( basename $( readlink -f $0 ) ) "
41
+ show_help
42
+ exit 0
43
+ ;;
44
+ * )
45
+ echo " unknown option: -${OPTARG} " >&2
46
+ show_help
47
+ exit 1
48
+ ;;
49
+ esac
50
+ done
51
+ shift $(( OPTIND - 1 ))
17
52
18
53
set -eo pipefail
19
54
20
55
if ! command -v gh & > /dev/null; then
21
- echo " Error: GitHub CLI ('gh') is not installed. ❌ Please install it to continue."
22
- echo " See: https://cli.github.com/"
56
+ echo " 🛑 Error: GitHub CLI ('gh') is not installed. Please install it to continue."
57
+ echo " 🔗 See: https://cli.github.com/"
23
58
exit 1
24
59
fi
25
60
26
- echo " ✅ GitHub CLI is installed."
61
+ echo " ✅ GitHub CLI is installed. Ready to proceed! "
27
62
28
63
PR_NUMBER=${1:- }
29
64
FORK_REMOTE=${GH_FORK_REMOTE:- $2 }
30
65
UPSTREAM_REPO=${GH_UPSTREAM_REPO:- " openshift-pipelines/pipelines-as-code" }
31
66
32
- # Check if there is any changes in the current branch or bail out
67
+ # 🛡️ Check for uncommitted changes
33
68
if ! git diff-index --quiet HEAD --; then
34
- echo " ❌ Error: There are uncommitted changes in the current branch. Please commit or stash them before running this script."
69
+ echo " 📝 Error: There are uncommitted changes in the current branch. Please commit or stash them before running this script."
35
70
exit 1
36
71
fi
37
72
38
73
CURRENT_BRANCH=$( git rev-parse --abbrev-ref HEAD)
39
74
resetgitbranch () {
40
75
new_branch_name=$( git rev-parse --abbrev-ref HEAD)
41
- echo " 🔄 Resetting to original branch ${CURRENT_BRANCH} from ${new_branch_name} "
76
+ echo " ↩️ Resetting to original branch ${CURRENT_BRANCH} from ${new_branch_name} "
42
77
git checkout " $CURRENT_BRANCH " || true
43
78
}
44
79
trap resetgitbranch EXIT
45
80
81
+ # 🎯 Select PR number if not provided
46
82
if [[ -z ${PR_NUMBER} ]]; then
47
- PR_SELECTION=$( gh pr list --repo " $UPSTREAM_REPO " --json number,title,author --template ' {{range .}}{{.number}}: {{.title}} (by {{.author.login}})
48
- {{end}}' | grep -v " \[MIRRORED\]" | fzf --prompt=" Select PR: " )
49
- PR_NUMBER=$( echo " $PR_SELECTION " | awk -F: ' {print $1}' | xargs)
83
+ if [[ ${CURRENT_BRANCH} =~ test-pr-([0-9]+)-([a-zA-Z0-9_-]+) ]]; then
84
+ PR_NUMBER=" ${BASH_REMATCH[1]} "
85
+ else
86
+ PR_SELECTION=$( gh pr list --repo " $UPSTREAM_REPO " --json number,title,author --template ' {{range .}}{{.number}}: {{.title}} (by {{.author.login}})
87
+ {{end}}' | grep -v " \[MIRRORED\]" | fzf --prompt=" 🔎 Select PR: " )
88
+ PR_NUMBER=$( echo " $PR_SELECTION " | awk -F: ' {print $1}' | xargs)
89
+ fi
50
90
fi
51
91
92
+ # 🔍 Check if a mirrored PR already exists
93
+ already_opened_pr=$(
94
+ gh pr list --repo $UPSTREAM_REPO \
95
+ --json number,headRepositoryOwner,headRepository,headRefName |
96
+ jq -r --arg pn " $PR_NUMBER " \
97
+ ' .[] | select(.headRefName | test("^test-pr-\($pn)-.*")) | "[email protected] :\(.headRepositoryOwner.login)/\(.headRepository.name).git"'
98
+ )
99
+ [[ -n ${already_opened_pr} ]] && FORK_REMOTE=${already_opened_pr}
100
+
101
+ # 🌿 Select fork remote if not provided
52
102
if [[ -z $FORK_REMOTE ]]; then
53
- FORK_REMOTE=$( git remote | awk ' {print $1}' | grep -v origin | sort -u | fzf -1 --prompt=" Select fork remote: " )
103
+ FORK_REMOTE=$( git remote | awk ' {print $1}' | grep -v origin | sort -u | fzf -1 --prompt=" 🌿 Select fork remote: " )
54
104
fi
55
105
56
106
if [[ -z " $PR_NUMBER " || -z " $FORK_REMOTE " ]]; then
57
- echo " Usage: $0 <PR_NUMBER> <YOUR_REMOTE_FORK>"
58
- echo " Example: $0 1234 my-github-user"
59
- echo " UPSTREAM_REPO is ${UPSTREAM_REPO} unless you configure the env variable GH_UPSTREAM_REPO."
107
+ echo " ℹ️ Usage: $0 <PR_NUMBER> <YOUR_REMOTE_FORK>"
108
+ echo " 💡 Example: $0 1234 my-github-user"
109
+ echo " 🔎 UPSTREAM_REPO is ${UPSTREAM_REPO} unless you configure the env variable GH_UPSTREAM_REPO."
60
110
exit 1
61
111
fi
62
112
63
- # --- Main Logic ---
113
+ echo " 🔍 Fetching details for PR #${PR_NUMBER} from ${UPSTREAM_REPO} ..."
114
+ echo " 🌿 Fork remote: ${FORK_REMOTE} "
64
115
65
- echo " 🔄 Fetching details for PR #${PR_NUMBER} from ${UPSTREAM_REPO} ..."
66
-
67
- # Fetch PR title and author using GitHub CLI
116
+ # 📋 Fetch PR title and author
68
117
PR_TITLE=$( gh pr view " $PR_NUMBER " --repo " $UPSTREAM_REPO " --json title -q .title)
69
118
PR_AUTHOR=$( gh pr view " $PR_NUMBER " --repo " $UPSTREAM_REPO " --json author -q .author.login)
70
119
PR_URL=" https://github.com/${UPSTREAM_REPO} /pull/${PR_NUMBER} "
@@ -74,71 +123,77 @@ if [[ -z "$PR_TITLE" ]]; then
74
123
exit 1
75
124
fi
76
125
77
- echo " - Title: $PR_TITLE "
78
- echo " - Author: $PR_AUTHOR "
126
+ echo " 📝 - Title: $PR_TITLE "
127
+ echo " 👤 - Author: $PR_AUTHOR "
79
128
80
- # 1. Checkout the PR locally
81
- echo " 🔄 Checking out PR #${PR_NUMBER} locally..."
129
+ # 1️⃣ Checkout the PR locally
130
+ echo " 📥 Checking out PR #${PR_NUMBER} locally..."
82
131
gh pr checkout --force " $PR_NUMBER " --repo " $UPSTREAM_REPO "
83
132
84
- # 2. Push the branch to your fork
133
+ # 2️⃣ Push the branch to your fork
85
134
NEW_BRANCH_NAME=" test-pr-${PR_NUMBER} -${PR_AUTHOR} "
86
135
87
- # check if we didn't already have a pull request open for this branch
88
- already_opened_pr=$(
89
- gh pr list --repo " $UPSTREAM_REPO " --head \
90
- " ${NEW_BRANCH_NAME} " --json url --jq ' .[0].url'
91
- )
92
-
93
136
if [[ -n ${already_opened_pr} ]]; then
94
- echo " 🔄 A pull request already exists for this branch, pushing to the pull request target: ${already_opened_pr} "
95
-
96
- gh pr list --repo openshift-pipelines/pipelines-as-code --head ${NEW_BRANCH_NAME} --json headRepositoryOwner,headRepository --jq ' .[0].headRepositoryOwner.login + "/" + .[0].headRepository.name'
97
- ) "
98
- echo " 🔨 Pushing changes to existing pull request branch '${NEW_BRANCH_NAME} ' fork (${FORK_REMOTE} )..."
137
+ echo " 🔁 A pull request already exists for this branch, pushing to the pull request target: ${already_opened_pr} "
138
+ echo " 🚚 Pushing changes to existing pull request branch '${NEW_BRANCH_NAME} ' fork (${FORK_REMOTE} )..."
99
139
else
100
-
101
- echo " 🔨 Pushing changes to a new branch '${NEW_BRANCH_NAME} ' on your fork (${FORK_REMOTE} )..."
140
+ echo " 🚀 Pushing changes to a new branch '${NEW_BRANCH_NAME} ' on your fork (${FORK_REMOTE} )..."
102
141
fi
103
142
104
- # Force push in case the branch already exists from a previous test run
105
- git push " $FORK_REMOTE " " HEAD:${NEW_BRANCH_NAME} " --force
143
+ # 🚨 Force push in case the branch already exists from a previous test run
144
+ if [[ -n ${no_verify} ]]; then
145
+ echo " ⚠️ Skipping pre-push verification due to --no-verify flag."
146
+ else
147
+ if ! command -v " pre-commit" > /dev/null 2>&1 ; then
148
+ echo " ⚠️ You need to have the 'pre-commit' tool installed to run this script."
149
+ exit 1
150
+ fi
151
+ echo " 🚜 Running pre-commit checks before pushing..."
152
+ pre-commit run --all-files --show-diff-on-failure || {
153
+ echo " ❗ Pre-commit checks failed. Please fix the issues before pushing."
154
+ echo " You can fix user errors locally and pushing to the user branch."
155
+ echo " git commit --amend the commit (or add a new commit) and then run this command"
156
+ gh pr view " $PR_NUMBER " --repo " $UPSTREAM_REPO " --json headRefName,headRepositoryOwner,headRepository |
157
+ jq -r
' "git push --force-with-lease [email protected] :\(.headRepositoryOwner.login)/\(.headRepository.name).git HEAD:\(.headRefName)"'
158
+ echo " (or use --force if you know what you are doing)"
159
+ exit 1
160
+ }
161
+ fi
162
+ git push " $FORK_REMOTE " " HEAD:${NEW_BRANCH_NAME} " --force --no-verify
106
163
107
164
if [[ -n ${already_opened_pr} ]]; then
108
- echo " 🔗 Pull request has successfully been synched ${already_opened_pr} "
109
165
exit 0
110
166
fi
111
167
112
- # 3. Create a new Pull Request from the fork to the upstream repo
113
- MIRRORED_PR_TITLE=" [MIRRORED] ${PR_TITLE} "
114
- MIRRORED_PR_BODY=" Mirrors ${PR_URL} to run E2E tests. Original author: @${PR_AUTHOR} "
168
+ # 3️⃣ Create a new Pull Request from the fork to the upstream repo
169
+ MIRRORED_PR_TITLE=" 🪞 [MIRRORED] ${PR_TITLE} "
170
+ MIRRORED_PR_BODY=" 🔄 Mirrors ${PR_URL} to run E2E tests. Original author: @${PR_AUTHOR} "
115
171
DO_NOT_MERGE_LABEL=" do-not-merge" # You might need to create this label in your repo if it doesn't exist
116
172
117
- echo " 🔄 Creating a new mirrored pull request on ${UPSTREAM_REPO} ..."
173
+ echo " 🆕 Creating a new mirrored pull request on ${UPSTREAM_REPO} ..."
118
174
119
- # Create the PR as a draft to prevent accidental merges before tests run.
120
- # The --head flag specifies the branch in your fork.
175
+ # 📝 Create the PR as a draft to prevent accidental merges before tests run.
121
176
CREATED_PR_URL=$( gh pr create \
122
177
--repo " $UPSTREAM_REPO " \
123
178
--title " $MIRRORED_PR_TITLE " \
124
179
--body " $MIRRORED_PR_BODY " \
125
180
--head " ${FORK_REMOTE} :${NEW_BRANCH_NAME} " \
126
181
--label " $DO_NOT_MERGE_LABEL " \
127
- --draft) # Using --draft is safer
182
+ --draft)
128
183
129
- # Check if the PR was created successfully
184
+ # ✅ Check if the PR was created successfully
130
185
if [[ -z " $CREATED_PR_URL " ]]; then
131
- echo " ❌ Error: Failed to create the mirrored pull request."
186
+ echo " ❗ Error: Failed to create the mirrored pull request."
132
187
exit 1
133
188
fi
134
189
135
190
gh pr comment " $PR_NUMBER " --repo " $UPSTREAM_REPO " --body \
136
- " :rocket: **Mirrored PR Created for E2E Testing**<br><br>\
191
+ " 🚀 **Mirrored PR Created for E2E Testing**<br><br>\
137
192
A mirrored PR has been opened for end-to-end testing: [View PR](${CREATED_PR_URL} )<br><br>\
138
- :hourglass_flowing_sand: Follow progress there for E2E results.<br>\
193
+ ⏳ Follow progress there for E2E results.<br>\
139
194
If you need to update the PR with new changes, please ask a maintainer to rerun \` hack/mirror-pr.sh\` ."
140
195
141
- echo " ✅ Successfully created mirrored pull request!"
196
+ echo " 🎉 Successfully created mirrored pull request!"
142
197
echo " ${CREATED_PR_URL} "
143
198
144
- echo " 🚀 Done."
199
+ echo " 🏁 Done."
0 commit comments