-
Notifications
You must be signed in to change notification settings - Fork 144
240 lines (230 loc) · 12.4 KB
/
Copy pathsemver-checks.yml
File metadata and controls
240 lines (230 loc) · 12.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
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
name: SemverChecks
# Run on pull requests or merge groups
on:
pull_request:
types:
- opened
- synchronize
- reopened
- edited
issue_comment:
types:
- created
- edited
- deleted
merge_group:
# If a new commit is pushed to the branch before ongoing runs finish, cancel the ongoing runs
concurrency:
group: ${{ github.workflow }}-${{ github.ref || github.run_id }}
cancel-in-progress: true
permissions:
contents: read
pull-requests: write
issues: write
env:
CARGO_TERM_COLOR: always
jobs:
rerun_fork_check:
name: Re-run fork semver check
if: >-
github.event_name == 'issue_comment' &&
github.event.issue.pull_request &&
(github.event.action == 'deleted' ||
contains(github.event.comment.body, ':robot: SemverChecks :robot:'))
permissions:
actions: write
contents: read
pull-requests: read
issues: read
runs-on: ubuntu-latest
steps:
- name: Re-run the pull request check
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
API_URL: ${{ github.api_url }}
REPOSITORY: ${{ github.repository }}
PR_NUMBER: ${{ github.event.issue.number }}
run: |
set -euo pipefail
PR=$(curl --fail-with-body -sS \
-H "Authorization: Bearer $GH_TOKEN" \
-H 'Accept: application/vnd.github+json' \
"$API_URL/repos/$REPOSITORY/pulls/$PR_NUMBER")
IS_OPEN=$(jq -r '.state == "open"' <<<"$PR")
IS_FORK=$(jq -r '.head.repo.fork == true' <<<"$PR")
COMMENTER_IS_AUTHOR=$(jq -r --arg login "${{ github.event.comment.user.login }}" '.user.login == $login' <<<"$PR")
COMMENTER_IS_MAINTAINER=$(case '${{ github.event.comment.author_association }}' in OWNER|MEMBER|COLLABORATOR) echo true;; *) echo false;; esac)
if [ "$IS_OPEN" != true ] || [ "$IS_FORK" != true ] || \
{ [ "$COMMENTER_IS_AUTHOR" != true ] && [ "$COMMENTER_IS_MAINTAINER" != true ]; }; then
echo 'Ignoring comment from an unauthorized user or on a non-open fork PR.'
exit 0
fi
HEAD_SHA=$(jq -r '.head.sha' <<<"$PR")
RUN_ID=$(curl --fail-with-body -sS \
-H "Authorization: Bearer $GH_TOKEN" \
-H 'Accept: application/vnd.github+json' \
"$API_URL/repos/$REPOSITORY/actions/workflows/semver-checks.yml/runs?event=pull_request&head_sha=$HEAD_SHA&per_page=10" | \
jq -r --argjson pr_number "$PR_NUMBER" '[.workflow_runs[] | select(any(.pull_requests[]?; .number == $pr_number))] | first | .id // empty')
if [ -z "$RUN_ID" ]; then
echo 'No pull_request semver-checks run exists for this commit yet.' >&2
exit 1
fi
curl --fail-with-body -sS -X POST \
-H "Authorization: Bearer $GH_TOKEN" \
-H 'Accept: application/vnd.github+json' \
"$API_URL/repos/$REPOSITORY/actions/runs/$RUN_ID/rerun"
semver_checks:
name: Check SemVer Correctness
if: ${{ github.event_name != 'issue_comment' }}
runs-on: ubuntu-latest
steps:
- name: Check out repo
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Determine baseline ref
id: baseline
run: |
BASE_REF="${{ github.event.pull_request.base.ref }}"
MERGE_GROUP_BASE_REF="${{ github.event.merge_group.base_ref }}"
MERGE_GROUP_BASE_REF="${MERGE_GROUP_BASE_REF#refs/heads/}"
BASE_REF="${BASE_REF:-$MERGE_GROUP_BASE_REF}"
echo "ref=$BASE_REF" >> "$GITHUB_OUTPUT"
- name: Fetch baseline branch
run: git fetch origin "${{ steps.baseline.outputs.ref }}:${{ steps.baseline.outputs.ref }}"
- name: Set up Rust
run: |
rustup toolchain install $(awk -F'"' '/channel/{print $2}' rust-toolchain.toml) --profile minimal --no-self-update
- name: Determine packages to check
id: packages
run: |
git diff --name-only "${{ steps.baseline.outputs.ref }}" HEAD > /tmp/semver-checks-changed-files
python3 .github/tools/determine_semver_packages.py --changed-files /tmp/semver-checks-changed-files > /tmp/semver-checks-packages
if [ -s /tmp/semver-checks-packages ]; then
echo "has_packages=true" >> "$GITHUB_OUTPUT"
echo "Packages selected for semver checks:"
sed 's/^/ - /' /tmp/semver-checks-packages
else
echo "has_packages=false" >> "$GITHUB_OUTPUT"
echo "No semver-relevant crate changes detected."
fi
- name: Set up cargo-semver-checks
if: steps.packages.outputs.has_packages == 'true'
run: |
curl -L --proto '=https' --tlsv1.2 -sSf https://github.com/obi1kenobi/cargo-semver-checks/releases/latest/download/cargo-semver-checks-x86_64-unknown-linux-gnu.tar.gz | tar xzvf -
mv cargo-semver-checks ~/.cargo/bin
- name: Check semver match against the baseline branch
id: semver_check
run: |
# TODO(jayb): we are temporarily preventing a failure in semver-checks
# from showing up as a `X`, but instead triggering a comment on the
# PR. Once things go public, we will likely switch this out to make it
# actually complain as usual, possibly still keeping in the comment bot?
if [ "${{ steps.packages.outputs.has_packages }}" != "true" ]; then
: > /tmp/semver-checks-stdout
echo "Semver check skipped because no semver-relevant crate changes were detected."
echo "reaction=hooray" >> "$GITHUB_OUTPUT"
exit 0
fi
PACKAGE_ARGS=()
while IFS= read -r PACKAGE; do
PACKAGE_ARGS+=("-p" "$PACKAGE")
done < /tmp/semver-checks-packages
if cargo semver-checks "${PACKAGE_ARGS[@]}" --baseline-rev "${{ steps.baseline.outputs.ref }}" --color=never >/tmp/semver-checks-stdout; then
cat /tmp/semver-checks-stdout
echo "Semver check succeeded."
echo "reaction=hooray" >> "$GITHUB_OUTPUT"
else
cat /tmp/semver-checks-stdout
echo "Semver check failed."
echo "reaction=eyes" >> "$GITHUB_OUTPUT"
fi
# - name: React to the PR based on semver-checks
# if: ${{ github.event.pull_request }}
# run: |
# curl -L \
# -X POST \
# -H "Accept: application/vnd.github+json" \
# -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" \
# -H "X-GitHub-Api-Version: 2022-11-28" \
# https://api.github.com/repos/${{ github.repository_owner }}/${{ github.event.repository.name }}/issues/${{ github.event.number }}/reactions \
# -d '{"content":"${{ steps.semver_check.outputs.reaction }}"}'
- name: Delete old semver checks comments, if any
if: ${{ github.event_name == 'pull_request' && !github.event.pull_request.head.repo.fork }}
run: |
# Get the old comments
COMMENT_IDS=$(curl -L \
-H "Accept: application/vnd.github+json" \
-H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" \
-H "X-GitHub-Api-Version: 2022-11-28" \
https://api.github.com/repos/${{ github.repository_owner }}/${{ github.event.repository.name }}/issues/${{ github.event.number }}/comments | \
jq '.[] | select(.body | contains(":robot: SemverChecks :robot:")) | .id')
# Delete them all
for ID in $COMMENT_IDS; do
curl -L \
-X DELETE \
-H "Accept: application/vnd.github+json" \
-H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" \
-H "X-GitHub-Api-Version: 2022-11-28" \
https://api.github.com/repos/${{ github.repository_owner }}/${{ github.event.repository.name }}/issues/comments/$ID
done
- name: Add a new issue comment if needed
if: ${{ github.event_name == 'pull_request' && !github.event.pull_request.head.repo.fork }}
run: |
BASE_NOTE=""
if [ "${{ steps.baseline.outputs.ref }}" != "main" ] && [ "${{ steps.baseline.outputs.ref }}" != "ulitebox" ]; then
BASE_NOTE=":information_source: **Note:** This semver check was run against the \`${{ steps.baseline.outputs.ref }}\` branch, not \`main\` or \`ulitebox\`.\n\n"
fi
if [ "${{ steps.packages.outputs.has_packages }}" != "true" ]; then
BODY="$(echo -e "${BASE_NOTE}"':robot: SemverChecks :robot: No semver-relevant crate changes detected; skipped cargo-semver-checks.')"
elif [ -s /tmp/semver-checks-stdout ]; then
BODY="$(echo -e "${BASE_NOTE}"':robot: SemverChecks :robot: :warning: Potential breaking API changes detected :warning:\n\n<details><summary>Click for details</summary>\n\n```'"$(cat /tmp/semver-checks-stdout)"'\n```\n</details>')"
else
BODY="$(echo -e "${BASE_NOTE}"':robot: SemverChecks :robot: No breaking API changes detected\n\nNote: this does not mean API is unchanged, or even that there are no breaking changes; simply, none of the detections triggered.')"
fi
curl -L \
-X POST \
-H "Accept: application/vnd.github+json" \
-H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" \
-H "X-GitHub-Api-Version: 2022-11-28" \
https://api.github.com/repos/${{ github.repository_owner }}/${{ github.event.repository.name }}/issues/${{ github.event.number }}/comments \
-d "$(printf '%s' "$BODY" | jq -sR '{body: .}')"
- name: Require documentation from fork
if: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork }}
env:
API_URL: ${{ github.api_url }}
REPOSITORY: ${{ github.repository }}
PR_NUMBER: ${{ github.event.pull_request.number }}
run: |
set -euo pipefail
BASE_NOTE=""
if [ "${{ steps.baseline.outputs.ref }}" != "main" ] && [ "${{ steps.baseline.outputs.ref }}" != "ulitebox" ]; then
BASE_NOTE=":information_source: **Note:** This semver check was run against the \`${{ steps.baseline.outputs.ref }}\` branch, not \`main\` or \`ulitebox\`.\n\n"
fi
if [ "${{ steps.packages.outputs.has_packages }}" != "true" ]; then
BODY="$(echo -e "${BASE_NOTE}"':robot: SemverChecks :robot: No semver-relevant crate changes detected; skipped cargo-semver-checks.')"
elif [ -s /tmp/semver-checks-stdout ]; then
BODY="$(echo -e "${BASE_NOTE}"':robot: SemverChecks :robot: :warning: Potential breaking API changes detected :warning:\n\n<details><summary>Click for details</summary>\n\n```'"$(cat /tmp/semver-checks-stdout)"'\n```\n</details>')"
else
BODY="$(echo -e "${BASE_NOTE}"':robot: SemverChecks :robot: No breaking API changes detected\n\nNote: this does not mean API is unchanged, or even that there are no breaking changes; simply, none of the detections triggered.')"
fi
PREFIX='Documenting semver-checks CI text here:'
COMMENTS=$(curl --fail-with-body -sS \
-H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" \
-H 'Accept: application/vnd.github+json' \
"$API_URL/repos/$REPOSITORY/issues/$PR_NUMBER/comments?per_page=100&sort=created&direction=desc")
HAS_SEMVER_COMMENT=$(jq -r '[.[] | select(.body | contains(":robot: SemverChecks :robot:"))] | length > 0' <<<"$COMMENTS")
if [ "$HAS_SEMVER_COMMENT" = "false" ] && [ ! -s /tmp/semver-checks-stdout ]; then
echo 'No breaking changes and no prior semver-checks comment; documentation is unnecessary.'
exit 0
fi
LAST=$(jq -r --arg prefix "$PREFIX" '[.[] | select(.body | startswith($prefix))] | last | if . then .body else "" end' <<<"$COMMENTS")
EXPECTED=$(printf '%s\n\n%s' "$PREFIX" "$BODY")
if [ "$LAST" != "$EXPECTED" ]; then
echo 'The latest semver documentation comment is missing or out of date.' >&2
{
printf '## Semver-checks documentation required\n\n'
printf 'Copy and paste the following as a comment on the PR (not in the PR description):\n\n'
printf '````markdown\n%s\n````\n' "$EXPECTED"
} >> "$GITHUB_STEP_SUMMARY"
printf '\nCopy and paste this as a comment on the PR (not in the PR description):\n\n```markdown\n%s\n```\n' "$EXPECTED"
exit 1
fi