Repository navigation
188 lines (171 loc) · 7.21 KB
/
Copy pathpull-request-conflicts.yml
File metadata and controls
188 lines (171 loc) · 7.21 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
name: Rebase conflicted pull requests
on:
push:
branches: [main]
workflow_dispatch:
inputs:
pull_request:
description: "The pull request number to rebase"
required: false
type: string
head_sha:
description: "The expected pull request head SHA"
required: false
type: string
base_previous_ref:
description: "The previous base branch of a retargeted pull request"
required: false
type: string
base_previous_sha:
description: "The previous base SHA from the signed base-change event"
required: false
type: string
permissions: {}
concurrency:
group: pull-request-conflicts-${{ inputs.pull_request || 'main' }}
# Let an in-progress rebase finish; newer pushes replace the single pending run.
cancel-in-progress: false
env:
UV_LOCKED: 1
jobs:
identify:
name: Identify conflicted pull requests
if: >-
github.repository == 'astral-sh/uv' ||
(github.repository == 'astral-sh/uv-dev' && github.event_name == 'workflow_dispatch')
runs-on: ${{ github.repository_owner == 'astral-sh' && 'github-ubuntu-24.04-x86_64-2' || 'ubuntu-slim' }}
timeout-minutes: 5
permissions:
contents: read
pull-requests: read
outputs:
matrix: ${{ steps.identify.outputs.matrix }}
rebasable: ${{ steps.identify.outputs.rebasable }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Identify conflicted pull requests
id: identify
env:
GH_TOKEN: ${{ github.token }}
GH_REPO: ${{ github.repository }}
EXPECTED_HEAD_SHA: ${{ inputs.head_sha }}
BASE_PREVIOUS_REF: ${{ inputs.base_previous_ref }}
BASE_PREVIOUS_SHA: ${{ inputs.base_previous_sha }}
PULL_REQUEST_NUMBER: ${{ inputs.pull_request }}
run: |
if [ -n "$PULL_REQUEST_NUMBER" ]; then
if ! [[ "$PULL_REQUEST_NUMBER" =~ ^[1-9][0-9]*$ ]] || ! [[ "$EXPECTED_HEAD_SHA" =~ ^[0-9a-f]{40}$ ]]; then
echo "The dispatched pull request context is invalid." >&2
exit 1
fi
pull_request=$(gh api "repos/$GH_REPO/pulls/$PULL_REQUEST_NUMBER")
if ! jq --exit-status \
--arg repository "$GH_REPO" \
--argjson repository_id "$GITHUB_REPOSITORY_ID" \
--arg head_sha "$EXPECTED_HEAD_SHA" \
'.state == "open" and .base.repo.full_name == $repository and .base.repo.id == $repository_id and .head.repo.full_name == $repository and .head.repo.id == $repository_id and .head.sha == $head_sha' \
<<< "$pull_request" > /dev/null; then
echo "The pull request is no longer open at the expected head; refusing to rebase it." >&2
exit 1
fi
base_previous_sha=''
if [ -n "$BASE_PREVIOUS_REF" ] || [ -n "$BASE_PREVIOUS_SHA" ]; then
if [[ "$GH_REPO" != "astral-sh/uv-dev" ]] || ! git check-ref-format "refs/heads/$BASE_PREVIOUS_REF" || ! [[ "$BASE_PREVIOUS_SHA" =~ ^[0-9a-f]{40}$ ]]; then
echo "The previous pull request base is invalid." >&2
exit 1
fi
if [ "$BASE_PREVIOUS_REF" != main ]; then
base_previous_sha="$BASE_PREVIOUS_SHA"
fi
fi
pull_requests=$(
jq --compact-output \
--arg base_previous_sha "$base_previous_sha" \
'[{number, url: .html_url, base_ref: .base.ref, base_previous_sha: $base_previous_sha, head_ref: .head.ref, head_sha: .head.sha, head_repository: .head.repo.full_name}]' \
<<< "$pull_request"
)
elif [ -n "$EXPECTED_HEAD_SHA" ] || [ -n "$BASE_PREVIOUS_REF" ] || [ -n "$BASE_PREVIOUS_SHA" ]; then
echo "The dispatched rebase context is incomplete." >&2
exit 1
elif [[ "$GH_REPO" == "astral-sh/uv" ]]; then
pull_requests=$(./scripts/find-conflicted-pull-requests.sh --author app/astral-automations-bot)
else
pull_requests=$(./scripts/find-conflicted-pull-requests.sh)
fi
pull_request_count=$(jq 'length' <<< "$pull_requests")
rebasable_pull_requests=$(
jq --compact-output \
--arg repository "$GH_REPO" \
'[.[] | select(.head_repository == $repository or .head_repository == "astral-sh/uv-dev")]' \
<<< "$pull_requests"
)
rebasable=$(jq 'length' <<< "$rebasable_pull_requests")
unavailable=$(( pull_request_count - rebasable ))
if (( rebasable > 256 )); then
echo "Found $rebasable rebasable pull requests, which exceeds the GitHub Actions matrix limit of 256." >&2
exit 1
fi
matrix=$(jq --compact-output '{include: [.[] | {number, base_ref, base_previous_sha: (.base_previous_sha // ""), head_ref, head_sha, head_repository}]}' <<< "$rebasable_pull_requests")
{
echo "matrix=$matrix"
echo "rebasable=$rebasable"
} >> "$GITHUB_OUTPUT"
{
echo "## Pull requests to rebase"
echo
echo "Found $pull_request_count pull requests to rebase."
if (( unavailable > 0 )); then
echo
echo "Skipping $unavailable pull requests whose heads cannot be updated by the rebase app token."
fi
if (( pull_request_count > 0 )); then
echo
jq --raw-output '.[] | "- \(.url)"' <<< "$pull_requests"
fi
} >> "$GITHUB_STEP_SUMMARY"
rebase-and-push:
name: "Rebase pull request #${{ matrix.number }}"
needs: identify
if: needs.identify.outputs.rebasable != '0'
permissions:
contents: read
id-token: write
strategy:
fail-fast: false
max-parallel: 4
matrix: ${{ fromJSON(needs.identify.outputs.matrix) }}
uses: $/.github/workflows/rebase-conflicted-pull-request.yml
secrets: inherit
with:
number: ${{ matrix.number }}
base-ref: ${{ matrix.base_ref }}
base-previous-sha: ${{ matrix.base_previous_sha }}
head-ref: ${{ matrix.head_ref }}
head-sha: ${{ matrix.head_sha }}
head-repository: ${{ matrix.head_repository }}
remove-rebase-label:
name: "Remove rebase label from pull request #${{ inputs.pull_request }}"
needs: rebase-and-push
if: inputs.pull_request != ''
runs-on: ${{ github.repository_owner == 'astral-sh' && 'github-ubuntu-24.04-x86_64-2' || 'ubuntu-slim' }}
timeout-minutes: 5
permissions:
pull-requests: write
steps:
- name: Remove rebase label
env:
GH_TOKEN: ${{ github.token }}
PULL_REQUEST_NUMBER: ${{ inputs.pull_request }}
run: |
rebase_requested=$(
gh api "repos/$GITHUB_REPOSITORY/issues/$PULL_REQUEST_NUMBER" \
--jq '.labels | any(.name == "bot:rebase")'
)
if [[ "$rebase_requested" == "true" ]]; then
gh api \
--method DELETE \
"repos/$GITHUB_REPOSITORY/issues/$PULL_REQUEST_NUMBER/labels/bot%3Arebase" \
--silent
fi