forked from DataDog/dd-trace-java
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmirror-community-pull-request.sh
More file actions
executable file
·202 lines (186 loc) · 7.4 KB
/
mirror-community-pull-request.sh
File metadata and controls
executable file
·202 lines (186 loc) · 7.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
#!/bin/bash
set -e
#
# Handling community pull requests.
#
# The main way to handle community contributions is to contribute directly on the contributor's pull request.
# If not enabled, please ask the contributor to enable the option "Allow edits by maintainers".
# This allows maintainers to push commits to the contributor's branch.
# Then, use this script to mirror the PR to a new branch in the main repository.
# This allows running CI with the maintainer's permissions.
#
# Few hints:
# - Avoid merge commits with master and rebase the contributor's branch instead.
# - Avoid pushing changes to the mirror branch as it will be overwritten when rerunning the script.
#
# Usage: mirror-community-pull-request.sh <pr-number> [<target-branch>]
REPO="DataDog/dd-trace-java"
PR_NUMBER=$1
TARGET_BRANCH=${2:-master}
MIRROR_BRANCH="community-pr-$PR_NUMBER"
#
# Check arguments.
#
# Check if no arguments are provided
if [ $# -eq 0 ]; then
echo "Usage: $0 <pr-number> [<target-branch>]"
echo "<pr-number>: PR number to mirror"
echo "<target-branch>: Target branch for the mirror (default: master)"
echo ""
echo "This script mirrors a community PR to allow running CI with your own account."
echo "It creates a new branch 'community-pr-<number>' and pushes the PR changes."
exit 1
fi
# Check PR number is provided
if [ -z "$PR_NUMBER" ]; then
echo "❌ PR number is not provided"
exit 1
fi
# Validate PR number is numeric
if ! [[ "$PR_NUMBER" =~ ^[0-9]+$ ]]; then
echo "❌ PR number must be numeric"
exit 1
fi
#
# Check requirements.
#
echo "- Checking requirements"
# Check gh is installed
gh --version 1>/dev/null 2>&1 || { echo "❌ gh is not installed. Please install GitHub CLI."; exit 1; }
# Check that user is logged into gh cli
gh auth status 1>/dev/null 2>&1 || { echo "❌ Not logged into Github CLI. Please login with \`gh auth login\`."; exit 1; }
# Check jq is installed
jq --version 1>/dev/null 2>&1 || { echo "❌ jq is not installed. Please install jq."; exit 1; }
# Check there are no local changes
git diff --quiet --exit-code || { echo "❌ There are local changes. Please commit or stash them."; exit 1; }
#
# Fetch PR information.
#
echo "- Fetching PR #$PR_NUMBER details"
# Check if PR exists and get details
PR_DATA=$(gh pr view "$PR_NUMBER" --json headRepository,headRepositoryOwner,headRefName,title,number,state,author 2>/dev/null || echo "")
if [ -z "$PR_DATA" ]; then
echo "❌ PR #$PR_NUMBER not found"
exit 1
fi
# Parse PR details
FORK_REPO=$(echo "$PR_DATA" | jq -r '(.headRepository.nameWithOwner | select(. != "" and . != null)) // (.headRepositoryOwner.login + "/" + .headRepository.name) // empty')
FORK_BRANCH=$(echo "$PR_DATA" | jq -r '.headRefName // empty')
PR_TITLE=$(echo "$PR_DATA" | jq -r '.title // empty')
PR_AUTHOR=$(echo "$PR_DATA" | jq -r '.author.login // empty')
PR_LABELS=$(gh pr view "$PR_NUMBER" --json labels --jq '[.labels[].name] | join(",")')
if [ -z "$FORK_REPO" ] || [ -z "$FORK_BRANCH" ]; then
echo "❌ Could not determine fork repository or branch for PR #$PR_NUMBER"
exit 1
fi
#
# Create mirror branch.
#
echo "- Mirroring PR #$PR_NUMBER from $FORK_REPO:$FORK_BRANCH to $REPO:$MIRROR_BRANCH"
# Check if mirror branch already exists
echo "- Checking if mirror branch $MIRROR_BRANCH already exists locally"
if git show-ref --verify --quiet "refs/heads/$MIRROR_BRANCH" 2>/dev/null; then
echo -n "Branch $MIRROR_BRANCH already exists locally. Delete it to recreate? (y/n)"
read -r ANSWER
if [ "$ANSWER" = "y" ]; then
git branch -D "$MIRROR_BRANCH"
else
echo "Aborting."
exit 1
fi
fi
# Check if mirror branch exists on remote
echo "- Checking if mirror branch $MIRROR_BRANCH already exists on remote"
if git show-ref --verify --quiet "refs/remotes/origin/$MIRROR_BRANCH" 2>/dev/null; then
echo -n "Branch $MIRROR_BRANCH already exists on remote. Force push over it? (y/n)"
read -r ANSWER
if [ "$ANSWER" != "y" ]; then
echo "Aborting."
exit 1
fi
fi
# Fetch the PR branch from the fork directly (no remote needed)
git fetch --quiet "https://github.com/$FORK_REPO.git" "$FORK_BRANCH"
# Get list of commits from the PR to re-sign them
echo "- Getting list of commits from PR"
PR_COMMITS=$(gh pr view "$PR_NUMBER" --json commits --jq '.commits[].oid')
if [ -z "$PR_COMMITS" ]; then
echo "❌ No commits found in PR #$PR_NUMBER"
exit 1
fi
# Get current git branch for cleanup
CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD)
# Create the mirror branch from target branch (we'll cherry-pick commits)
echo "- Creating mirror branch $MIRROR_BRANCH from $TARGET_BRANCH"
git fetch --quiet origin "$TARGET_BRANCH"
git checkout -b "$MIRROR_BRANCH" "origin/$TARGET_BRANCH"
#
# Mirror commits.
#
# Cherry-pick each commit with signature to ensure all commits are signed
echo "- Cherry-picking and signing commits from PR"
for COMMIT in $PR_COMMITS; do
echo " - Cherry-picking $COMMIT"
# Check if this is a merge commit
CHERRY_PICK_ARGS=("-S")
PARENT_COUNT=$(git rev-list --parents -n 1 "$COMMIT" 2>/dev/null | wc -w)
if [ "$PARENT_COUNT" -gt 2 ]; then
CHERRY_PICK_ARGS+=("-m" "1")
fi
if ! git cherry-pick "${CHERRY_PICK_ARGS[@]}" "$COMMIT"; then
# Check if it's an empty commit
if ! git diff --cached --quiet || ! git diff --quiet; then
echo "❌ Failed to cherry-pick merge commit $COMMIT"
echo "You may need to resolve conflicts manually"
exit 1
else
echo " (empty commit, skipping)"
git cherry-pick --skip
fi
fi
done
# Push the mirror branch to origin
echo "- Pushing $MIRROR_BRANCH to origin"
git push -u origin "$MIRROR_BRANCH" --no-verify --force-with-lease
#
# Create PR if it doesn't exist.
#
echo "- Checking if PR already exists for branch $MIRROR_BRANCH"
EXISTING_PR=$(gh pr list --head "$MIRROR_BRANCH" --json number --jq '.[0].number // empty' 2>/dev/null)
if [ -n "$EXISTING_PR" ]; then
MIRROR_PR_URL="https://github.com/$REPO/pull/$EXISTING_PR"
else
echo "- Creating new PR for mirror branch $MIRROR_BRANCH"
# Create PR body with reference to original
MIRROR_PR_BODY="This PR mirrors the changes from the original community contribution to enable CI testing with maintainer privileges.
**Original PR:** https://github.com/$REPO/pull/$PR_NUMBER
**Original Author:** @$PR_AUTHOR
**Original Branch:** $FORK_REPO:$FORK_BRANCH
Closes #$PR_NUMBER
---
*This is an automated mirror created to run CI checks. See tooling/mirror-community-pull-request.sh for details.*"
# Create the PR
CREATE_ARGS=(--base "$TARGET_BRANCH" --head "$MIRROR_BRANCH" --title "🪞 $PR_NUMBER - $PR_TITLE" --body "$MIRROR_PR_BODY")
if [ -n "$PR_LABELS" ]; then
CREATE_ARGS+=(--label "$PR_LABELS")
fi
MIRROR_PR_NUMBER=$(gh pr create "${CREATE_ARGS[@]}" 2>/dev/null | grep -o '[0-9]*$')
if [ -n "$MIRROR_PR_NUMBER" ]; then
echo "- Created mirror PR: #$MIRROR_PR_NUMBER"
MIRROR_PR_URL="https://github.com/$REPO/pull/$MIRROR_PR_NUMBER"
else
echo "- Failed to create PR automatically"
MIRROR_PR_URL="https://github.com/$REPO/compare/$TARGET_BRANCH...$MIRROR_BRANCH"
fi
fi
echo ""
echo "✅ Successfully mirrored PR #$PR_NUMBER"
echo " Original: https://github.com/$REPO/pull/$PR_NUMBER (@$PR_AUTHOR)"
echo " Mirror: $MIRROR_PR_URL"
echo " Branch: $REPO:$MIRROR_BRANCH"
#
# Clean up.
#
echo "- Restoring original branch"
git checkout "$CURRENT_BRANCH"
echo "Done."