Skip to content

Commit df14f5e

Browse files
committed
ci(cli): publish pkg.pr.new previews on demand
1 parent 8680362 commit df14f5e

6 files changed

Lines changed: 430 additions & 28 deletions

File tree

.github/workflows/pkg-pr-new.yml

Lines changed: 308 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,308 @@
1+
name: pkg.pr.new
2+
3+
on:
4+
workflow_run:
5+
workflows:
6+
- Test
7+
- Smoke Test (PR)
8+
types:
9+
- completed
10+
pull_request_review:
11+
types:
12+
- submitted
13+
14+
permissions:
15+
actions: read
16+
checks: read
17+
contents: read
18+
issues: write
19+
pull-requests: read
20+
21+
concurrency:
22+
group: pkg-pr-new-${{ github.event.workflow_run.head_sha || github.event.pull_request.head.sha || github.run_id }}
23+
cancel-in-progress: false
24+
25+
jobs:
26+
publish:
27+
name: Publish preview package
28+
runs-on: ubuntu-latest
29+
if: >-
30+
(
31+
github.event_name == 'workflow_run' &&
32+
github.event.workflow_run.event == 'pull_request' &&
33+
github.event.workflow_run.conclusion == 'success'
34+
) ||
35+
(
36+
github.event_name == 'pull_request_review' &&
37+
github.event.review.state == 'approved' &&
38+
github.event.pull_request.draft == false &&
39+
github.event.pull_request.head.repo.full_name == github.event.pull_request.base.repo.full_name
40+
)
41+
env:
42+
GH_TOKEN: ${{ github.token }}
43+
steps:
44+
- name: Resolve publish context
45+
id: context
46+
env:
47+
EVENT_NAME: ${{ github.event_name }}
48+
REPOSITORY: ${{ github.repository }}
49+
run: |
50+
set -euo pipefail
51+
52+
should_publish=false
53+
pr_number=""
54+
pr_head_sha=""
55+
smoke_run_id=""
56+
test_run_id=""
57+
58+
if [[ "${EVENT_NAME}" == "workflow_run" ]]; then
59+
pr_number="$(jq -r '.workflow_run.pull_requests[0].number // ""' "$GITHUB_EVENT_PATH")"
60+
pr_head_sha="$(jq -r '.workflow_run.head_sha // ""' "$GITHUB_EVENT_PATH")"
61+
pr_head_branch="$(jq -r '.workflow_run.head_branch // ""' "$GITHUB_EVENT_PATH")"
62+
triggering_workflow="$(jq -r '.workflow_run.name // ""' "$GITHUB_EVENT_PATH")"
63+
64+
if [[ -z "${pr_head_sha}" ]]; then
65+
echo "Workflow run has no head SHA; skipping."
66+
echo "should_publish=false" >> "$GITHUB_OUTPUT"
67+
exit 0
68+
fi
69+
70+
if [[ -z "${pr_number}" && -n "${pr_head_branch}" ]]; then
71+
pr_number="$(
72+
gh pr list \
73+
--repo "${REPOSITORY}" \
74+
--head "${pr_head_branch}" \
75+
--state open \
76+
--json number,headRefOid \
77+
--jq 'map(select(.headRefOid == "'"${pr_head_sha}"'")) | .[0].number // ""'
78+
)"
79+
fi
80+
81+
if [[ -z "${pr_number}" ]]; then
82+
echo "Workflow run is not associated with an open pull request; skipping."
83+
echo "should_publish=false" >> "$GITHUB_OUTPUT"
84+
exit 0
85+
fi
86+
87+
if [[ "${triggering_workflow}" == "Smoke Test (PR)" ]]; then
88+
smoke_run_id="$(jq -r '.workflow_run.id' "$GITHUB_EVENT_PATH")"
89+
elif [[ "${triggering_workflow}" == "Test" ]]; then
90+
test_run_id="$(jq -r '.workflow_run.id' "$GITHUB_EVENT_PATH")"
91+
else
92+
echo "Unexpected triggering workflow: ${triggering_workflow}; skipping."
93+
echo "should_publish=false" >> "$GITHUB_OUTPUT"
94+
exit 0
95+
fi
96+
else
97+
pr_number="$(jq -r '.pull_request.number' "$GITHUB_EVENT_PATH")"
98+
pr_head_sha="$(jq -r '.pull_request.head.sha' "$GITHUB_EVENT_PATH")"
99+
fi
100+
101+
pr_json="$(gh api "repos/${REPOSITORY}/pulls/${pr_number}")"
102+
current_head_sha="$(jq -r '.head.sha' <<< "${pr_json}")"
103+
state="$(jq -r '.state' <<< "${pr_json}")"
104+
draft="$(jq -r '.draft' <<< "${pr_json}")"
105+
head_repo="$(jq -r '.head.repo.full_name' <<< "${pr_json}")"
106+
base_repo="$(jq -r '.base.repo.full_name' <<< "${pr_json}")"
107+
108+
if [[ "${state}" != "open" ]]; then
109+
echo "PR #${pr_number} is ${state}; skipping."
110+
echo "should_publish=false" >> "$GITHUB_OUTPUT"
111+
exit 0
112+
fi
113+
114+
if [[ "${draft}" == "true" ]]; then
115+
echo "PR #${pr_number} is draft; skipping."
116+
echo "should_publish=false" >> "$GITHUB_OUTPUT"
117+
exit 0
118+
fi
119+
120+
if [[ "${head_repo}" != "${base_repo}" ]]; then
121+
echo "PR #${pr_number} comes from fork ${head_repo}; skipping."
122+
echo "should_publish=false" >> "$GITHUB_OUTPUT"
123+
exit 0
124+
fi
125+
126+
if [[ "${pr_head_sha}" != "${current_head_sha}" ]]; then
127+
echo "Workflow SHA ${pr_head_sha} is stale; current PR head is ${current_head_sha}. Skipping."
128+
echo "should_publish=false" >> "$GITHUB_OUTPUT"
129+
exit 0
130+
fi
131+
132+
review_decision="$(
133+
gh api graphql \
134+
-f owner="${REPOSITORY%/*}" \
135+
-f repo="${REPOSITORY#*/}" \
136+
-F number="${pr_number}" \
137+
-f query='
138+
query($owner: String!, $repo: String!, $number: Int!) {
139+
repository(owner: $owner, name: $repo) {
140+
pullRequest(number: $number) {
141+
reviewDecision
142+
}
143+
}
144+
}
145+
' \
146+
--jq '.data.repository.pullRequest.reviewDecision // ""'
147+
)"
148+
149+
if [[ "${review_decision}" != "APPROVED" ]]; then
150+
echo "PR #${pr_number} review decision is ${review_decision:-<none>}; skipping."
151+
echo "should_publish=false" >> "$GITHUB_OUTPUT"
152+
exit 0
153+
fi
154+
155+
if [[ -z "${smoke_run_id}" ]]; then
156+
smoke_run_id="$(
157+
gh run list \
158+
--workflow smoke-test-pr.yml \
159+
--event pull_request \
160+
--commit "${pr_head_sha}" \
161+
--status success \
162+
--json databaseId,conclusion,headSha \
163+
--jq 'map(select(.headSha == "'"${pr_head_sha}"'" and .conclusion == "success")) | .[0].databaseId // ""'
164+
)"
165+
fi
166+
167+
if [[ -z "${test_run_id}" ]]; then
168+
test_run_id="$(
169+
gh run list \
170+
--workflow test.yml \
171+
--event pull_request \
172+
--commit "${pr_head_sha}" \
173+
--status success \
174+
--json databaseId,conclusion,headSha \
175+
--jq 'map(select(.headSha == "'"${pr_head_sha}"'" and .conclusion == "success")) | .[0].databaseId // ""'
176+
)"
177+
fi
178+
179+
if [[ -z "${smoke_run_id}" ]]; then
180+
echo "No successful Smoke Test (PR) run found for ${pr_head_sha}; skipping."
181+
echo "should_publish=false" >> "$GITHUB_OUTPUT"
182+
exit 0
183+
fi
184+
185+
if [[ -z "${test_run_id}" ]]; then
186+
echo "No successful Test run found for ${pr_head_sha}; skipping."
187+
echo "should_publish=false" >> "$GITHUB_OUTPUT"
188+
exit 0
189+
fi
190+
191+
artifact_name="$(
192+
gh api "repos/${REPOSITORY}/actions/runs/${smoke_run_id}/artifacts" \
193+
--paginate \
194+
--jq '.artifacts[] | select(.name | startswith("cli-build-legacy-")) | .name' \
195+
| head -n 1
196+
)"
197+
198+
if [[ -z "${artifact_name}" ]]; then
199+
echo "No legacy CLI build artifact found on Smoke Test (PR) run ${smoke_run_id}; skipping."
200+
echo "should_publish=false" >> "$GITHUB_OUTPUT"
201+
exit 0
202+
fi
203+
204+
preview_version="${artifact_name#cli-build-legacy-}"
205+
should_publish=true
206+
207+
{
208+
echo "should_publish=${should_publish}"
209+
echo "pr_number=${pr_number}"
210+
echo "pr_head_sha=${pr_head_sha}"
211+
echo "preview_version=${preview_version}"
212+
echo "artifact_name=${artifact_name}"
213+
echo "smoke_run_id=${smoke_run_id}"
214+
} >> "$GITHUB_OUTPUT"
215+
216+
- name: Checkout
217+
if: steps.context.outputs.should_publish == 'true'
218+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
219+
with:
220+
ref: ${{ steps.context.outputs.pr_head_sha }}
221+
persist-credentials: false
222+
223+
- name: Setup
224+
if: steps.context.outputs.should_publish == 'true'
225+
uses: ./.github/actions/setup
226+
227+
- name: Download smoke artifacts
228+
if: steps.context.outputs.should_publish == 'true'
229+
run: gh run download "${SMOKE_RUN_ID}" --name "${ARTIFACT_NAME}"
230+
env:
231+
ARTIFACT_NAME: ${{ steps.context.outputs.artifact_name }}
232+
SMOKE_RUN_ID: ${{ steps.context.outputs.smoke_run_id }}
233+
234+
- name: Prepare package files
235+
if: steps.context.outputs.should_publish == 'true'
236+
run: |
237+
set -euo pipefail
238+
pnpm exec bun apps/cli/scripts/sync-versions.ts --version "${PREVIEW_VERSION}"
239+
pnpm --dir apps/cli build:shim
240+
find packages -path '*/bin/supabase*' -type f -exec chmod +x {} +
241+
env:
242+
PREVIEW_VERSION: ${{ steps.context.outputs.preview_version }}
243+
244+
- name: Publish preview package
245+
id: publish
246+
if: steps.context.outputs.should_publish == 'true'
247+
run: |
248+
pnpm exec pkg-pr-new publish \
249+
--pnpm \
250+
--bin \
251+
--comment=off \
252+
--json pkg-pr-new.json \
253+
--no-template \
254+
'./packages/cli-darwin-arm64' \
255+
'./packages/cli-darwin-x64' \
256+
'./packages/cli-linux-arm64' \
257+
'./packages/cli-linux-arm64-musl' \
258+
'./packages/cli-linux-x64' \
259+
'./packages/cli-linux-x64-musl' \
260+
'./packages/cli-windows-arm64' \
261+
'./packages/cli-windows-x64' \
262+
'./apps/cli'
263+
264+
- name: Smoke test preview command
265+
if: steps.context.outputs.should_publish == 'true'
266+
run: |
267+
set -euo pipefail
268+
preview_url="https://pkg.pr.new/supabase@${PR_NUMBER}"
269+
npx --yes "${preview_url}" --version
270+
env:
271+
PR_NUMBER: ${{ steps.context.outputs.pr_number }}
272+
273+
- name: Update PR comment
274+
if: steps.context.outputs.should_publish == 'true'
275+
env:
276+
PR_HEAD_SHA: ${{ steps.context.outputs.pr_head_sha }}
277+
PR_NUMBER: ${{ steps.context.outputs.pr_number }}
278+
PREVIEW_VERSION: ${{ steps.context.outputs.preview_version }}
279+
REPOSITORY: ${{ github.repository }}
280+
run: |
281+
set -euo pipefail
282+
preview_url="https://pkg.pr.new/supabase@${PR_NUMBER}"
283+
short_sha="${PR_HEAD_SHA:0:7}"
284+
marker="<!-- pkg-pr-new-preview -->"
285+
cat > comment.md <<EOF
286+
${marker}
287+
## pkg.pr.new preview
288+
289+
Published version \`${PREVIEW_VERSION}\` from commit [\`${short_sha}\`](https://github.com/${REPOSITORY}/commit/${PR_HEAD_SHA}) after the PR test and release smoke workflows passed.
290+
291+
\`\`\`sh
292+
npx ${preview_url}
293+
\`\`\`
294+
EOF
295+
296+
jq -n --rawfile body comment.md '{ body: $body }' > comment.json
297+
comment_id="$(
298+
gh api "repos/${REPOSITORY}/issues/${PR_NUMBER}/comments" \
299+
--paginate \
300+
--jq '.[] | select(.body | contains("'"${marker}"'")) | .id' \
301+
| head -n1
302+
)"
303+
304+
if [[ -n "${comment_id}" ]]; then
305+
gh api --method PATCH "repos/${REPOSITORY}/issues/comments/${comment_id}" --input comment.json >/dev/null
306+
else
307+
gh api --method POST "repos/${REPOSITORY}/issues/${PR_NUMBER}/comments" --input comment.json >/dev/null
308+
fi

.github/workflows/release-shared.yml

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -113,15 +113,13 @@ jobs:
113113
needs: build
114114
strategy:
115115
fail-fast: false
116-
# macos-15-intel is the slowest smoke leg and the only one not on
117-
# Blacksmith (Blacksmith macOS is ARM-only). Drop it from the matrix
118-
# on prereleases (PR smoke + develop -> beta) so beta wall-clock isn't
119-
# gated by it; stable releases on main still run the full matrix.
120-
# The matrix list is built via fromJSON because GitHub Actions does
121-
# not allow the `matrix` context in a job-level `if:` (matrix
122-
# expansion happens after job conditions are evaluated).
123116
matrix:
124-
runner: ${{ fromJSON(inputs.prerelease && '["blacksmith-8vcpu-ubuntu-2404","blacksmith-6vcpu-macos-latest","blacksmith-8vcpu-windows-2025"]' || '["blacksmith-8vcpu-ubuntu-2404","blacksmith-6vcpu-macos-latest","macos-15-intel","blacksmith-8vcpu-windows-2025"]') }}
117+
runner:
118+
- blacksmith-8vcpu-ubuntu-2404
119+
- blacksmith-6vcpu-macos-latest
120+
- macos-15-intel
121+
- blacksmith-8vcpu-windows-2025
122+
- windows-11-arm
125123
runs-on: ${{ matrix.runner }}
126124
env:
127125
NPM_TAG: ${{ inputs.npm_tag }}

0 commit comments

Comments
 (0)