forked from ipdxco/unified-github-workflows
-
Notifications
You must be signed in to change notification settings - Fork 0
129 lines (124 loc) · 5.08 KB
/
merge-prs.yml
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
name: Merge PRs
on:
workflow_dispatch:
inputs:
branch:
description: "Branch from which to merge PRs"
required: false
dry-run:
description: "Whether to run in dry run mode"
required: false
default: 'false'
# schedule:
# - cron: "0 0 * * *" # https://crontab.guru/every-day
jobs:
merge-prs:
name: Merge PRs
runs-on: ubuntu-latest
steps:
- name: Merge PRs
env:
QUERY: is:pr author:web3-bot state:open archived:false
BRANCH: ${{ github.event.inputs.branch }}
DRY_RUN: ${{ github.event.inputs.dry-run }}
uses: actions/github-script@v6
with:
github-token: ${{ secrets.UCI_GITHUB_TOKEN }}
retries: 0
script: |
const request = async function(req, opts) {
try {
return await req(opts)
} catch(err) {
opts._attempt = (opts._attempt || 0) + 1
if (err.status === 403) {
if (err.response.headers['x-ratelimit-remaining'] === '0') {
const retryAfter = err.response.headers['x-ratelimit-reset'] - Math.floor(Date.now() / 1000) || 1
core.info(`Rate limit exceeded, retrying in ${retryAfter} seconds`)
await new Promise(resolve => setTimeout(resolve, retryAfter * 1000))
return request(req, opts)
}
if (err.message.toLowerCase().includes('secondary rate limit')) {
const retryAfter = Math.pow(2, opts._attempt)
core.info(`Secondary rate limit exceeded, retrying in ${retryAfter} seconds`)
await new Promise(resolve => setTimeout(resolve, retryAfter * 1000))
return request(req, opts)
}
}
throw err
}
}
github.hook.wrap('request', request)
let q = process.env.QUERY
if (process.env.BRANCH) {
q += ` head:${process.env.BRANCH}`
}
core.info(`Looking for PRs matching the query: ${q}`)
const items = await github.paginate(github.rest.search.issuesAndPullRequests, {
q
})
core.info(`Filtering out the PRs that cannot be merged`)
const prs = []
for (const item of items) {
core.info(`Retrieving ${item.html_url}`)
const [_, owner, repo] = item.url.match(/repos\/(.+?)\/(.+?)\/issues/)
const {data: pr} = await github.rest.pulls.get({
owner,
repo,
pull_number: item.number
})
if (pr.mergeable_state == 'clean') {
const commits = await github.paginate(github.rest.pulls.listCommits, {
owner,
repo,
pull_number: item.number
})
if (commits.filter(c => c.commit.author.name == 'web3-bot').length != commits.length) {
core.info(`${pr.html_url} cannot be merged because it contains commits from other authors`)
} else {
core.info(`${pr.html_url} can be merged`)
prs.push(pr)
}
} else {
core.info(`${pr.html_url} cannot be merged because it is in ${pr.mergeable_state} state`)
}
}
core.info(`Attempting to merge the PRs`)
const failed = []
for (const pr of prs) {
core.info(`Merging ${pr.html_url}`)
if (process.env.DRY_RUN == 'true') {
core.info(`Skipping PR merge because this is a dry run`)
continue
}
try {
let mergeMethods = ['squash', 'merge', 'rebase']
let merge = undefined
while (merge == undefined && mergeMethods.length != 0) {
const mergeMethod = mergeMethods.shift()
try {
merge = await github.rest.pulls.merge({
owner: pr.base.repo.owner.login,
repo: pr.base.repo.name,
pull_number: pr.number,
merge_method: mergeMethod
})
} catch(error) {
const message = error.message.toLowerCase()
if (!(message.includes(mergeMethod) && message.includes('not allowed on this repository'))) {
throw error
}
}
}
if (!merge) {
throw new Error('No merge methods are allowed on this repository.')
}
core.info(`${pr.html_url} merged successfully`)
} catch(error) {
core.error(`Couldn't merge ${pr.html_url}, got: ${error}`)
failed.push(pr)
}
}
if (failed.length != 0) {
throw new Error(`Failed to merge ${failed.length} PRs`)
}