Skip to content

Commit bbd41c6

Browse files
committed
ci: match dispatched release checks exactly and allow reruns
Release Please passes a per-attempt request id to each trusted check and waits only on the run carrying it, never with a baseline override. The checks run in their own job so re-running it re-dispatches all three and repaints Verify on the current head, and the delivery token is minted just before enabling auto-merge.
1 parent 3d187c0 commit bbd41c6

5 files changed

Lines changed: 112 additions & 54 deletions

File tree

‎.github/workflows/pull-request-metadata.yml‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
name: Pull Request Metadata
2+
run-name: >-
3+
${{ inputs.request_id && format('{0} [{1}]', github.workflow, inputs.request_id)
4+
|| github.event.pull_request.title || github.event.head_commit.message || github.workflow }}
25
36
on:
47
pull_request:
@@ -9,6 +12,10 @@ on:
912
description: Pull request number to validate
1013
required: true
1114
type: number
15+
request_id:
16+
description: "Release Please attempt that dispatched this run; leave empty for manual runs"
17+
required: false
18+
type: string
1219

1320
permissions:
1421
contents: read

‎.github/workflows/release-performance.yml‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
name: Release Performance
2+
run-name: >-
3+
${{ inputs.request_id && format('{0} [{1}]', github.workflow, inputs.request_id)
4+
|| github.event.pull_request.title || github.event.head_commit.message || github.workflow }}
25
36
# Release Please dispatches this workflow for each release pull request and reports its result
47
# through the required Verify status. Pull requests that change the comparison itself run it too.
@@ -9,6 +12,10 @@ on:
912
description: "Published sqlbuild version to compare against (default: the release before the candidate)"
1013
required: false
1114
type: string
15+
request_id:
16+
description: "Release Please attempt that dispatched this run; leave empty for manual runs"
17+
required: false
18+
type: string
1219
pull_request:
1320
paths:
1421
- .github/workflows/release-performance.yml
@@ -51,7 +58,12 @@ jobs:
5158
- name: Compare candidate and previous release on this runner
5259
env:
5360
BASELINE: ${{ inputs.baseline }}
61+
REQUEST_ID: ${{ inputs.request_id }}
5462
run: |
63+
if [[ -n "${REQUEST_ID}" && -n "${BASELINE}" ]]; then
64+
echo "Release Please runs always compare with the previous release; drop baseline" >&2
65+
exit 1
66+
fi
5567
wheels=("${RUNNER_TEMP}"/candidate/sqlbuild-*.whl)
5668
args=(
5769
--candidate "${wheels[0]}"

‎.github/workflows/release-please.yml‎

Lines changed: 78 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ concurrency:
1919
jobs:
2020
release-please:
2121
runs-on: ubuntu-24.04
22+
outputs:
23+
prs_created: ${{ steps.release.outputs.prs_created }}
24+
release_branch: ${{ steps.release_branch.outputs.name }}
25+
release_pr: ${{ steps.release_branch.outputs.pr }}
2226
steps:
2327
- name: Create delivery token
2428
id: delivery-token
@@ -56,11 +60,13 @@ jobs:
5660
RELEASE_PRS: ${{ steps.release.outputs.prs }}
5761
run: |
5862
release_branch="$(jq -r '.[0].headBranchName // empty' <<< "${RELEASE_PRS}")"
59-
if [[ -z "${release_branch}" ]]; then
60-
echo "Release Please reported a created PR without a head branch" >&2
63+
release_pr="$(jq -r '.[0].number // empty' <<< "${RELEASE_PRS}")"
64+
if [[ -z "${release_branch}" || -z "${release_pr}" ]]; then
65+
echo "Release Please reported a created PR without a head branch or number" >&2
6166
exit 1
6267
fi
6368
echo "name=${release_branch}" >> "${GITHUB_OUTPUT}"
69+
echo "pr=${release_pr}" >> "${GITHUB_OUTPUT}"
6470
6571
- uses: actions/checkout@v4
6672
if: steps.release.outputs.prs_created == 'true'
@@ -99,93 +105,114 @@ jobs:
99105
git commit -m "chore: refresh release lockfiles"
100106
git push origin "HEAD:${RELEASE_BRANCH}"
101107
108+
# A separate job so that "Re-run failed jobs" re-dispatches and re-evaluates every trusted check
109+
# and repaints their statuses and Verify on the current head of the release branch.
110+
release-checks:
111+
name: Release pull request checks
112+
needs: release-please
113+
if: needs.release-please.outputs.prs_created == 'true'
114+
runs-on: ubuntu-24.04
115+
steps:
116+
- uses: actions/checkout@v4
117+
with:
118+
ref: ${{ needs.release-please.outputs.release_branch }}
119+
102120
- name: Run release pull request checks
103-
if: steps.release.outputs.prs_created == 'true'
104121
env:
105122
GH_TOKEN: ${{ github.token }}
106-
RELEASE_BRANCH: ${{ steps.release_branch.outputs.name }}
107-
RELEASE_PRS: ${{ steps.release.outputs.prs }}
123+
RELEASE_BRANCH: ${{ needs.release-please.outputs.release_branch }}
124+
RELEASE_PR: ${{ needs.release-please.outputs.release_pr }}
125+
REQUEST_ID: ${{ github.run_id }}-${{ github.run_attempt }}
108126
run: |
109-
release_pr="$(jq -r '.[0].number // empty' <<< "${RELEASE_PRS}")"
110-
if [[ -z "${release_pr}" ]]; then
111-
echo "Release Please reported a created PR without a number" >&2
112-
exit 1
113-
fi
114127
head_sha="$(git rev-parse HEAD)"
128+
recovery="rerun the failed Release Please job to re-check"
129+
post_status() {
130+
gh api --method POST "repos/${GITHUB_REPOSITORY}/statuses/${head_sha}" \
131+
-f "state=$1" -f "context=$2" -f "description=$3" ${4:+-f "target_url=$4"} >/dev/null
132+
}
133+
fail_verify() {
134+
post_status failure Verify "$1; ${recovery}" "${2:-}"
135+
{
136+
echo "### Release pull request checks failed"
137+
echo
138+
echo "$1. After fixing or confirming a transient cause, use **Re-run failed jobs**"
139+
echo "on this workflow run: it re-dispatches every trusted check and repaints Verify."
140+
} >> "${GITHUB_STEP_SUMMARY}"
141+
exit 1
142+
}
115143
116144
# Verify is the required status, so it only succeeds after every trusted check passes,
117-
# including the same-runner comparison with the previous release.
145+
# including the same-runner comparison with the previous release. Each dispatched run
146+
# carries this attempt's request id in its run name and is matched on it exactly.
118147
workflows=(pull-request-metadata.yml version-guard.yml release-performance.yml)
119148
contexts=("Pull Request Metadata" "Block automatic 1.0 releases" "Release performance")
120149
for context in "${contexts[@]}"; do
121-
gh api --method POST "repos/${GITHUB_REPOSITORY}/statuses/${head_sha}" \
122-
-f state=pending -f "context=${context}" \
123-
-f description="Trusted release check is running" >/dev/null
150+
post_status pending "${context}" "Trusted release check is running"
124151
done
125-
gh api --method POST "repos/${GITHUB_REPOSITORY}/statuses/${head_sha}" \
126-
-f state=pending -f context=Verify \
127-
-f description="Validating trusted release metadata and performance" >/dev/null
152+
post_status pending Verify "Validating trusted release metadata and performance"
128153
154+
dispatched_after="$(date -u -d '-2 minutes' +%Y-%m-%dT%H:%M:%SZ)"
129155
gh workflow run pull-request-metadata.yml --ref "${RELEASE_BRANCH}" \
130-
-f "pr_number=${release_pr}"
131-
gh workflow run version-guard.yml --ref "${RELEASE_BRANCH}"
132-
gh workflow run release-performance.yml --ref "${RELEASE_BRANCH}"
156+
-f "pr_number=${RELEASE_PR}" -f "request_id=${REQUEST_ID}"
157+
gh workflow run version-guard.yml --ref "${RELEASE_BRANCH}" \
158+
-f "request_id=${REQUEST_ID}"
159+
gh workflow run release-performance.yml --ref "${RELEASE_BRANCH}" \
160+
-f "request_id=${REQUEST_ID}"
133161
for index in "${!workflows[@]}"; do
134162
workflow="${workflows[${index}]}"
135163
context="${contexts[${index}]}"
136164
run_id=""
137165
for _ in {1..30}; do
138166
run_id="$(gh run list --workflow "${workflow}" --branch "${RELEASE_BRANCH}" \
139-
--event workflow_dispatch --limit 20 --json databaseId,headSha \
140-
--jq ".[] | select(.headSha == \"${head_sha}\") | .databaseId" | head -n 1)"
167+
--event workflow_dispatch --limit 50 \
168+
--json databaseId,headSha,displayTitle,createdAt \
169+
--jq ".[] | select(.headSha == \"${head_sha}\"
170+
and (.displayTitle | endswith(\" [${REQUEST_ID}]\"))
171+
and .createdAt >= \"${dispatched_after}\") | .databaseId" | head -n 1)"
141172
[[ -n "${run_id}" ]] && break
142173
sleep 2
143174
done
144175
if [[ -z "${run_id}" ]]; then
145-
gh api --method POST "repos/${GITHUB_REPOSITORY}/statuses/${head_sha}" \
146-
-f state=failure -f "context=${context}" \
147-
-f description="Trusted release check did not start" >/dev/null
148-
gh api --method POST "repos/${GITHUB_REPOSITORY}/statuses/${head_sha}" \
149-
-f state=failure -f context=Verify \
150-
-f description="${context} did not start" >/dev/null
151-
exit 1
176+
post_status failure "${context}" "Trusted release check did not start"
177+
fail_verify "${context} did not start"
152178
fi
153179
run_url="$(gh run view "${run_id}" --json url --jq .url)"
154180
if gh run watch "${run_id}" --exit-status --interval 30; then
155-
gh api --method POST "repos/${GITHUB_REPOSITORY}/statuses/${head_sha}" \
156-
-f state=success -f "context=${context}" -f target_url="${run_url}" \
157-
-f description="Trusted release check passed" >/dev/null
181+
post_status success "${context}" "Trusted release check passed" "${run_url}"
158182
else
159-
gh api --method POST "repos/${GITHUB_REPOSITORY}/statuses/${head_sha}" \
160-
-f state=failure -f "context=${context}" -f target_url="${run_url}" \
161-
-f description="Trusted release check failed" >/dev/null
162-
gh api --method POST "repos/${GITHUB_REPOSITORY}/statuses/${head_sha}" \
163-
-f state=failure -f context=Verify -f target_url="${run_url}" \
164-
-f description="${context} failed" >/dev/null
165-
exit 1
183+
post_status failure "${context}" "Trusted release check failed" "${run_url}"
184+
fail_verify "${context} failed" "${run_url}"
166185
fi
167186
done
168187
169-
unexpected_files="$(gh pr view "${release_pr}" --repo "${GITHUB_REPOSITORY}" \
188+
unexpected_files="$(gh pr view "${RELEASE_PR}" --repo "${GITHUB_REPOSITORY}" \
170189
--json files --jq '[.files[].path | select(. != ".release-please-manifest.json" and . != "CHANGELOG.md" and . != "pyproject.toml" and . != "uv.lock" and . != "Cargo.lock" and . != "crates/sqlbuild-rules-native/Cargo.toml")] | join("\n")')"
171190
if [[ -n "${unexpected_files}" ]]; then
172191
echo "Release pull request contains non-metadata changes:" >&2
173192
echo "${unexpected_files}" >&2
174-
gh api --method POST "repos/${GITHUB_REPOSITORY}/statuses/${head_sha}" \
175-
-f state=failure -f context=Verify \
176-
-f description="Release pull request contains non-metadata changes" >/dev/null
193+
post_status failure Verify "Release pull request contains non-metadata changes"
177194
exit 1
178195
fi
179-
gh api --method POST "repos/${GITHUB_REPOSITORY}/statuses/${head_sha}" \
180-
-f state=success -f context=Verify \
181-
-f description="Product changes passed verification before release metadata update" >/dev/null
196+
post_status success Verify \
197+
"Product changes passed verification before release metadata update"
198+
199+
# The checks can wait longer than an installation token lives, so mint one just before use.
200+
- name: Create delivery token
201+
id: delivery-token
202+
uses: actions/create-github-app-token@v2
203+
with:
204+
app-id: ${{ vars.CHIO_DELIVERY_APP_ID }}
205+
private-key: ${{ secrets.CHIO_DELIVERY_PRIVATE_KEY }}
206+
owner: ${{ github.repository_owner }}
207+
repositories: ${{ github.event.repository.name }}
208+
permission-actions: write
209+
permission-contents: write
210+
permission-issues: write
211+
permission-pull-requests: write
182212

183213
- name: Enable release pull request auto-merge
184-
if: steps.release.outputs.prs_created == 'true'
185214
env:
186215
# The delivery app merges, so the resulting push to main starts the tag and publish runs.
187216
GH_TOKEN: ${{ steps.delivery-token.outputs.token }}
188-
RELEASE_PRS: ${{ steps.release.outputs.prs }}
189-
run: |
190-
release_pr="$(jq -r '.[0].number // empty' <<< "${RELEASE_PRS}")"
191-
gh pr merge "${release_pr}" --repo "${GITHUB_REPOSITORY}" --auto --squash
217+
RELEASE_PR: ${{ needs.release-please.outputs.release_pr }}
218+
run: gh pr merge "${RELEASE_PR}" --repo "${GITHUB_REPOSITORY}" --auto --squash

‎.github/workflows/version-guard.yml‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
name: Version Guard
2+
run-name: >-
3+
${{ inputs.request_id && format('{0} [{1}]', github.workflow, inputs.request_id)
4+
|| github.event.pull_request.title || github.event.head_commit.message || github.workflow }}
25
36
on:
47
pull_request:
@@ -8,6 +11,11 @@ on:
811
branches:
912
- main
1013
workflow_dispatch:
14+
inputs:
15+
request_id:
16+
description: "Release Please attempt that dispatched this run; leave empty for manual runs"
17+
required: false
18+
type: string
1119

1220
permissions:
1321
contents: read

‎tests/e2e/src/sqlbuild/cli/commands/main/compile/COMPILE_BENCHMARKS.md‎

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -103,16 +103,20 @@ the medians of wall time, CPU time (user+sys) and peak RSS:
103103

104104
A command fails when its candidate median exceeds the baseline by more than 25% and by more than
105105
0.5 s (wall and CPU) or 32 MiB (peak RSS); the limits live in
106-
`scripts/release_performance/constants.py`. The baseline defaults to the highest published or
107-
release-tagged version below the candidate. A freshly tagged baseline is awaited until its wheels
106+
`scripts/release_performance/constants.py`. The baseline defaults to the highest version below
107+
the candidate that is installable from PyPI, or release-tagged but not yet on PyPI; yanked releases
108+
never count. A freshly tagged baseline is awaited until its wheels
108109
are on PyPI, installing from the uncompressed simple index's wheel URL when the CDN still lags. A
109110
command that needs a feature newer than a compared version declares `minimum_version` and appears
110111
as skipped, with the reason, in the job summary; any other failure of either version fails the
111112
check.
112113

113114
Release Please dispatches `.github/workflows/release-performance.yml` with the metadata and version
114115
checks and posts the required `Verify` status only after it passes, so a regressed release pull
115-
request cannot auto-merge. Run it manually with an optional `baseline` input, or locally:
116+
request cannot auto-merge. Each dispatch carries a request id in its run name, and Release Please
117+
waits only on the runs it started. After a transient failure, use **Re-run failed jobs** on the
118+
Release Please run: it re-dispatches all three checks and repaints `Verify` on the current head.
119+
Run the comparison manually with an optional `baseline` input, or locally:
116120

117121
```bash
118122
uv run python -m scripts.compare_release_performance --candidate 0.121.0 --baseline 0.119.1

0 commit comments

Comments
 (0)