-
-
Notifications
You must be signed in to change notification settings - Fork 3.2k
242 lines (206 loc) · 9.78 KB
/
Copy pathauto_merge_approved_prs.yml
File metadata and controls
242 lines (206 loc) · 9.78 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
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
name: Auto Merge Approved PRs
on:
schedule:
- cron: '0 */1 * * *' # Every 1 hour
workflow_dispatch: # Allow manual triggering
permissions:
contents: write
pull-requests: write
actions: read
jobs:
auto-merge-prs:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 1 # Only need latest commit for PR operations
token: ${{ secrets.PAT_TOKEN }}
- name: Configure git
run: |
git config --global user.email "action@github.com"
git config --global user.name "GitHub Action"
- name: Install GitHub CLI
run: |
curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg | sudo dd of=/usr/share/keyrings/githubcli-archive-keyring.gpg \
&& sudo chmod go+r /usr/share/keyrings/githubcli-archive-keyring.gpg \
&& echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" | sudo tee /etc/apt/sources.list.d/github-cli.list > /dev/null \
&& sudo apt update \
&& sudo apt install gh -y
- name: Check for running workflows
id: check_workflows
run: |
gh_with_retry() {
local max_attempts=5
local base_sleep_seconds=2
local attempt=1
local stderr_file
stderr_file=$(mktemp)
while true; do
if gh "$@" 2>"$stderr_file"; then
rm -f "$stderr_file"
return 0
fi
local exit_code=$?
if [ "$attempt" -ge "$max_attempts" ]; then
echo "gh command failed after $max_attempts attempts: gh $*" >&2
cat "$stderr_file" >&2
rm -f "$stderr_file"
return "$exit_code"
fi
local sleep_for=$((base_sleep_seconds * attempt))
echo "gh command failed (attempt $attempt/$max_attempts): gh $*" >&2
cat "$stderr_file" >&2
echo "Retrying in ${sleep_for}s..." >&2
sleep "$sleep_for"
attempt=$((attempt + 1))
done
}
# Get all running workflows except this one
running_workflows=$(gh_with_retry run list --status in_progress --json workflowName,name --repo "$GITHUB_REPOSITORY" --jq '.[].name' | grep -v "Auto Merge Approved PRs" | wc -l)
echo "running_workflows=$running_workflows" >> $GITHUB_OUTPUT
if [ "$running_workflows" -gt 0 ]; then
echo "Found $running_workflows running workflows. Exiting to avoid conflicts."
echo "should_continue=false" >> $GITHUB_OUTPUT
else
echo "No other workflows running. Proceeding with auto-merge."
echo "should_continue=true" >> $GITHUB_OUTPUT
fi
env:
GH_TOKEN: ${{ secrets.PAT_TOKEN }}
- name: Find and merge approved PRs
if: steps.check_workflows.outputs.should_continue == 'true'
run: |
gh_with_retry() {
local max_attempts=5
local base_sleep_seconds=2
local attempt=1
local stderr_file
stderr_file=$(mktemp)
while true; do
if gh "$@" 2>"$stderr_file"; then
rm -f "$stderr_file"
return 0
fi
local exit_code=$?
if [ "$attempt" -ge "$max_attempts" ]; then
echo "gh command failed after $max_attempts attempts: gh $*" >&2
cat "$stderr_file" >&2
rm -f "$stderr_file"
return "$exit_code"
fi
local sleep_for=$((base_sleep_seconds * attempt))
echo "gh command failed (attempt $attempt/$max_attempts): gh $*" >&2
cat "$stderr_file" >&2
echo "Retrying in ${sleep_for}s..." >&2
sleep "$sleep_for"
attempt=$((attempt + 1))
done
}
authorized_user="carlospolop"
max_merges=2
echo "Authorized user: $authorized_user"
echo "Looking for PRs with exact comment 'merge' from $authorized_user..."
# Get all open PRs, paginating through the full result set instead of
# relying on `gh pr list`'s default page size.
prs=$(gh_with_retry api --paginate "repos/$GITHUB_REPOSITORY/pulls?state=open&per_page=100" | jq -s 'add | map({number, title, url: .html_url, author: {login: .user.login}})')
if [ "$prs" = "[]" ]; then
echo "No open PRs found."
exit 0
fi
# Create a temp file to track merge count
echo "0" > /tmp/merged_count
# Process each PR
echo "$prs" | jq -r '.[] | @base64' | while IFS= read -r pr_data; do
current_count=$(cat /tmp/merged_count)
if [ "$current_count" -ge "$max_merges" ]; then
echo "Reached maximum merge limit ($max_merges). Stopping."
break
fi
pr_info=$(echo "$pr_data" | base64 --decode)
pr_number=$(echo "$pr_info" | jq -r '.number')
pr_title=$(echo "$pr_info" | jq -r '.title')
pr_url=$(echo "$pr_info" | jq -r '.url')
pr_author=$(echo "$pr_info" | jq -r '.author.login')
echo "Checking PR #$pr_number: $pr_title (author: $pr_author)"
# Special case: allow merge without "merge" comment for Research Update Enhanced PRs by the authorized user
eligible_by_title=false
if [[ "$pr_title" == "Research Update Enhanced"* && "$pr_author" == "$authorized_user" ]]; then
echo "PR #$pr_number qualifies for direct merge based on title and author."
eligible_by_title=true
fi
# Check if any comment from carlospolop contains exactly "merge"
has_merge_comment=false
if [ "$eligible_by_title" != true ]; then
# Get all comments for this PR
if ! comments=$(gh_with_retry pr view "$pr_number" --json comments --jq '.comments[]' --repo "$GITHUB_REPOSITORY"); then
echo "Failed to fetch comments for PR #$pr_number after retries. Skipping PR."
continue
fi
# Print all comment authors for debugging
echo "Comments in PR #$pr_number:"
echo "$comments" | jq -r '" - Author: " + .author.login + " | Comment: " + (.body | split("\n")[0] | .[0:100])'
echo "$comments" | jq -r '.author.login + "|" + .body' | while IFS='|' read -r comment_author comment_body; do
if [ "$comment_author" = "$authorized_user" ]; then
if echo "$comment_body" | grep -iExq "merge"; then
echo "Found exact 'merge' comment from $authorized_user in PR #$pr_number"
echo "true" > /tmp/has_merge_comment_$pr_number
break
fi
fi
done
fi
if [ -f "/tmp/has_merge_comment_$pr_number" ]; then
has_merge_comment=true
fi
if [ "$has_merge_comment" = true ] || [ "$eligible_by_title" = true ]; then
echo "Attempting to merge PR #$pr_number..."
# Get PR details including head branch
if ! pr_details=$(gh_with_retry pr view "$pr_number" --json headRefName,baseRefName --repo "$GITHUB_REPOSITORY"); then
echo "Failed to fetch details for PR #$pr_number after retries. Skipping PR."
continue
fi
head_branch=$(echo "$pr_details" | jq -r '.headRefName')
base_branch=$(echo "$pr_details" | jq -r '.baseRefName')
# --- Polling for non-UNKNOWN mergeable status ---
max_retries=10
retry=0
while true; do
if ! pr_mergeable=$(gh_with_retry pr view "$pr_number" --json mergeable --jq '.mergeable' --repo "$GITHUB_REPOSITORY"); then
echo "Failed to fetch mergeable status for PR #$pr_number after retries."
pr_mergeable="UNKNOWN"
fi
if [ "$pr_mergeable" != "UNKNOWN" ]; then
break
fi
if [ $retry -ge $max_retries ]; then
echo "Timeout: mergeable status is still UNKNOWN after $max_retries retries"
break
fi
echo "mergeable status UNKNOWN, retrying in 2s..."
sleep 2
retry=$((retry + 1))
done
if [ "$pr_mergeable" = "MERGEABLE" ]; then
if gh pr merge "$pr_number" --merge --delete-branch --repo "$GITHUB_REPOSITORY"; then
echo "Successfully merged PR #$pr_number: $pr_title"
current_count=$(cat /tmp/merged_count)
echo $((current_count + 1)) > /tmp/merged_count
else
echo "Failed to merge PR #$pr_number: $pr_title"
fi
elif [ "$pr_mergeable" = "CONFLICTED" ] || [ "$pr_mergeable" = "CONFLICTING" ]; then
echo "PR #$pr_number has conflicts. Skipping auto-merge so it can be resolved manually."
else
echo "PR #$pr_number is not mergeable (status: $pr_mergeable)"
fi
else
echo "No exact 'merge' comment found from $authorized_user in PR #$pr_number"
fi
rm -f "/tmp/has_merge_comment_$pr_number"
done
final_count=$(cat /tmp/merged_count)
echo "Auto-merge process completed. Merged $final_count PRs."
rm -f /tmp/merged_count
env:
GH_TOKEN: ${{ secrets.PAT_TOKEN }}