|
| 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 | + draft="$(jq -r '.draft' <<< "${pr_json}")" |
| 104 | + head_repo="$(jq -r '.head.repo.full_name' <<< "${pr_json}")" |
| 105 | + base_repo="$(jq -r '.base.repo.full_name' <<< "${pr_json}")" |
| 106 | +
|
| 107 | + if [[ "${draft}" == "true" ]]; then |
| 108 | + echo "PR #${pr_number} is draft; skipping." |
| 109 | + echo "should_publish=false" >> "$GITHUB_OUTPUT" |
| 110 | + exit 0 |
| 111 | + fi |
| 112 | +
|
| 113 | + if [[ "${head_repo}" != "${base_repo}" ]]; then |
| 114 | + echo "PR #${pr_number} comes from fork ${head_repo}; skipping." |
| 115 | + echo "should_publish=false" >> "$GITHUB_OUTPUT" |
| 116 | + exit 0 |
| 117 | + fi |
| 118 | +
|
| 119 | + if [[ "${pr_head_sha}" != "${current_head_sha}" ]]; then |
| 120 | + echo "Workflow SHA ${pr_head_sha} is stale; current PR head is ${current_head_sha}. Skipping." |
| 121 | + echo "should_publish=false" >> "$GITHUB_OUTPUT" |
| 122 | + exit 0 |
| 123 | + fi |
| 124 | +
|
| 125 | + review_decision="$( |
| 126 | + gh api graphql \ |
| 127 | + -f owner="${REPOSITORY%/*}" \ |
| 128 | + -f repo="${REPOSITORY#*/}" \ |
| 129 | + -F number="${pr_number}" \ |
| 130 | + -f query=' |
| 131 | + query($owner: String!, $repo: String!, $number: Int!) { |
| 132 | + repository(owner: $owner, name: $repo) { |
| 133 | + pullRequest(number: $number) { |
| 134 | + reviewDecision |
| 135 | + } |
| 136 | + } |
| 137 | + } |
| 138 | + ' \ |
| 139 | + --jq '.data.repository.pullRequest.reviewDecision // ""' |
| 140 | + )" |
| 141 | +
|
| 142 | + if [[ "${review_decision}" != "APPROVED" ]]; then |
| 143 | + echo "PR #${pr_number} review decision is ${review_decision:-<none>}; skipping." |
| 144 | + echo "should_publish=false" >> "$GITHUB_OUTPUT" |
| 145 | + exit 0 |
| 146 | + fi |
| 147 | +
|
| 148 | + if [[ -z "${smoke_run_id}" ]]; then |
| 149 | + smoke_run_id="$( |
| 150 | + gh run list \ |
| 151 | + --workflow smoke-test-pr.yml \ |
| 152 | + --event pull_request \ |
| 153 | + --commit "${pr_head_sha}" \ |
| 154 | + --status success \ |
| 155 | + --json databaseId,conclusion,headSha \ |
| 156 | + --jq 'map(select(.headSha == "'"${pr_head_sha}"'" and .conclusion == "success")) | .[0].databaseId // ""' |
| 157 | + )" |
| 158 | + fi |
| 159 | +
|
| 160 | + if [[ -z "${test_run_id}" ]]; then |
| 161 | + test_run_id="$( |
| 162 | + gh run list \ |
| 163 | + --workflow test.yml \ |
| 164 | + --event pull_request \ |
| 165 | + --commit "${pr_head_sha}" \ |
| 166 | + --status success \ |
| 167 | + --json databaseId,conclusion,headSha \ |
| 168 | + --jq 'map(select(.headSha == "'"${pr_head_sha}"'" and .conclusion == "success")) | .[0].databaseId // ""' |
| 169 | + )" |
| 170 | + fi |
| 171 | +
|
| 172 | + if [[ -z "${smoke_run_id}" ]]; then |
| 173 | + echo "No successful Smoke Test (PR) run found for ${pr_head_sha}; skipping." |
| 174 | + echo "should_publish=false" >> "$GITHUB_OUTPUT" |
| 175 | + exit 0 |
| 176 | + fi |
| 177 | +
|
| 178 | + if [[ -z "${test_run_id}" ]]; then |
| 179 | + echo "No successful Test run found for ${pr_head_sha}; skipping." |
| 180 | + echo "should_publish=false" >> "$GITHUB_OUTPUT" |
| 181 | + exit 0 |
| 182 | + fi |
| 183 | +
|
| 184 | + artifact_name="$( |
| 185 | + gh api "repos/${REPOSITORY}/actions/runs/${smoke_run_id}/artifacts" \ |
| 186 | + --paginate \ |
| 187 | + --jq '.artifacts[] | select(.name | startswith("cli-build-legacy-")) | .name' \ |
| 188 | + | head -n 1 |
| 189 | + )" |
| 190 | +
|
| 191 | + if [[ -z "${artifact_name}" ]]; then |
| 192 | + echo "No legacy CLI build artifact found on Smoke Test (PR) run ${smoke_run_id}; skipping." |
| 193 | + echo "should_publish=false" >> "$GITHUB_OUTPUT" |
| 194 | + exit 0 |
| 195 | + fi |
| 196 | +
|
| 197 | + preview_version="${artifact_name#cli-build-legacy-}" |
| 198 | + should_publish=true |
| 199 | +
|
| 200 | + { |
| 201 | + echo "should_publish=${should_publish}" |
| 202 | + echo "pr_number=${pr_number}" |
| 203 | + echo "pr_head_sha=${pr_head_sha}" |
| 204 | + echo "preview_version=${preview_version}" |
| 205 | + echo "artifact_name=${artifact_name}" |
| 206 | + echo "smoke_run_id=${smoke_run_id}" |
| 207 | + } >> "$GITHUB_OUTPUT" |
| 208 | +
|
| 209 | + - name: Checkout |
| 210 | + if: steps.context.outputs.should_publish == 'true' |
| 211 | + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 |
| 212 | + with: |
| 213 | + ref: ${{ steps.context.outputs.pr_head_sha }} |
| 214 | + persist-credentials: false |
| 215 | + |
| 216 | + - name: Setup |
| 217 | + if: steps.context.outputs.should_publish == 'true' |
| 218 | + uses: ./.github/actions/setup |
| 219 | + |
| 220 | + - name: Download smoke artifacts |
| 221 | + if: steps.context.outputs.should_publish == 'true' |
| 222 | + run: gh run download "${SMOKE_RUN_ID}" --name "${ARTIFACT_NAME}" |
| 223 | + env: |
| 224 | + ARTIFACT_NAME: ${{ steps.context.outputs.artifact_name }} |
| 225 | + SMOKE_RUN_ID: ${{ steps.context.outputs.smoke_run_id }} |
| 226 | + |
| 227 | + - name: Prepare package files |
| 228 | + if: steps.context.outputs.should_publish == 'true' |
| 229 | + run: | |
| 230 | + set -euo pipefail |
| 231 | + pnpm exec bun apps/cli/scripts/sync-versions.ts --version "${PREVIEW_VERSION}" |
| 232 | + pnpm --dir apps/cli build:shim |
| 233 | + find packages -path '*/bin/supabase*' -type f -exec chmod +x {} + |
| 234 | + env: |
| 235 | + PREVIEW_VERSION: ${{ steps.context.outputs.preview_version }} |
| 236 | + |
| 237 | + - name: Publish preview package |
| 238 | + id: publish |
| 239 | + if: steps.context.outputs.should_publish == 'true' |
| 240 | + run: | |
| 241 | + pnpm exec pkg-pr-new publish \ |
| 242 | + --pnpm \ |
| 243 | + --bin \ |
| 244 | + --comment=off \ |
| 245 | + --json pkg-pr-new.json \ |
| 246 | + --no-template \ |
| 247 | + './packages/cli-darwin-arm64' \ |
| 248 | + './packages/cli-darwin-x64' \ |
| 249 | + './packages/cli-linux-arm64' \ |
| 250 | + './packages/cli-linux-arm64-musl' \ |
| 251 | + './packages/cli-linux-x64' \ |
| 252 | + './packages/cli-linux-x64-musl' \ |
| 253 | + './packages/cli-windows-arm64' \ |
| 254 | + './packages/cli-windows-x64' \ |
| 255 | + './apps/cli' |
| 256 | +
|
| 257 | + - name: Smoke test preview command |
| 258 | + if: steps.context.outputs.should_publish == 'true' |
| 259 | + run: | |
| 260 | + set -euo pipefail |
| 261 | + preview_url="https://pkg.pr.new/supabase@${PR_NUMBER}" |
| 262 | + npx --yes "${preview_url}" --version |
| 263 | + env: |
| 264 | + PR_NUMBER: ${{ steps.context.outputs.pr_number }} |
| 265 | + |
| 266 | + - name: Update PR comment |
| 267 | + if: steps.context.outputs.should_publish == 'true' |
| 268 | + env: |
| 269 | + PR_HEAD_SHA: ${{ steps.context.outputs.pr_head_sha }} |
| 270 | + PR_NUMBER: ${{ steps.context.outputs.pr_number }} |
| 271 | + PREVIEW_VERSION: ${{ steps.context.outputs.preview_version }} |
| 272 | + REPOSITORY: ${{ github.repository }} |
| 273 | + run: | |
| 274 | + set -euo pipefail |
| 275 | + preview_url="https://pkg.pr.new/supabase@${PR_NUMBER}" |
| 276 | + short_sha="${PR_HEAD_SHA:0:7}" |
| 277 | + marker="<!-- pkg-pr-new-preview -->" |
| 278 | + cat > comment.md <<EOF |
| 279 | + ${marker} |
| 280 | + ## pkg.pr.new preview |
| 281 | +
|
| 282 | + 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. |
| 283 | +
|
| 284 | + \`\`\`sh |
| 285 | + npx ${preview_url} |
| 286 | + \`\`\` |
| 287 | + EOF |
| 288 | +
|
| 289 | + jq -n --rawfile body comment.md '{ body: $body }' > comment.json |
| 290 | + comment_id="$( |
| 291 | + gh api "repos/${REPOSITORY}/issues/${PR_NUMBER}/comments" \ |
| 292 | + --paginate \ |
| 293 | + --jq '.[] | select(.body | contains("'"${marker}"'")) | .id' \ |
| 294 | + | head -n1 |
| 295 | + )" |
| 296 | +
|
| 297 | + if [[ -n "${comment_id}" ]]; then |
| 298 | + gh api --method PATCH "repos/${REPOSITORY}/issues/comments/${comment_id}" --input comment.json >/dev/null |
| 299 | + else |
| 300 | + gh api --method POST "repos/${REPOSITORY}/issues/${PR_NUMBER}/comments" --input comment.json >/dev/null |
| 301 | + fi |
0 commit comments