From f94820e6dd82f96acd348b52d8defb28ce89b55c Mon Sep 17 00:00:00 2001 From: Pierre-Louis Mercereau <24897252+plmercereau@users.noreply.github.com> Date: Fri, 18 Sep 2026 11:22:00 +0200 Subject: [PATCH] Deploy a preview again when a pull request opens without one Vercel deploys on push, not when a pull request opens. A branch whose pull request was closed lost its previews to our cleanup, and opening a new pull request on the same commit did not bring one back (#34, #35). Claude-Session: https://claude.ai/code/session_01BLKZu4E8T123xzXASL2erH --- .../workflows/delete-preview-deployments.yml | 34 --------- .github/workflows/preview-deployments.yml | 69 +++++++++++++++++++ docs/adr/0004-neon-and-vercel.md | 4 +- 3 files changed, 72 insertions(+), 35 deletions(-) delete mode 100644 .github/workflows/delete-preview-deployments.yml create mode 100644 .github/workflows/preview-deployments.yml diff --git a/.github/workflows/delete-preview-deployments.yml b/.github/workflows/delete-preview-deployments.yml deleted file mode 100644 index 72d0134..0000000 --- a/.github/workflows/delete-preview-deployments.yml +++ /dev/null @@ -1,34 +0,0 @@ -# Neon's Free plan allows 10 database branches per project, and the Vercel integration creates one -# per pull request. Neon drops a branch as soon as the last Vercel deployment of that git branch is -# deleted, but Vercel keeps previews for months, so without this the eleventh pull request fails -# with "Resource provisioning failed". -name: Delete the preview deployments - -on: - pull_request: - types: [closed] - -jobs: - delete-previews: - runs-on: ubuntu-latest - env: - VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }} - TEAM_ID: team_LGEdjw58WAVHk0VFbQJSrGFQ - PROJECT_ID: prj_1F0rehSGxd1yq30yGOgacvayBaTe - BRANCH: ${{ github.head_ref }} - steps: - - name: Delete every preview deployment of the closed branch - run: | - ids=$(curl -sf -G "https://api.vercel.com/v6/deployments" \ - -H "Authorization: Bearer $VERCEL_TOKEN" \ - --data-urlencode "teamId=$TEAM_ID" \ - --data-urlencode "projectId=$PROJECT_ID" \ - --data-urlencode "branch=$BRANCH" \ - --data-urlencode "limit=100" | - jq -r --arg branch "$BRANCH" \ - '.deployments[] | select(.target != "production" and .meta.githubCommitRef == $branch) | .uid') - for id in $ids; do - echo "Deleting $id" - curl -sf -X DELETE "https://api.vercel.com/v13/deployments/$id?teamId=$TEAM_ID" \ - -H "Authorization: Bearer $VERCEL_TOKEN" >/dev/null - done diff --git a/.github/workflows/preview-deployments.yml b/.github/workflows/preview-deployments.yml new file mode 100644 index 0000000..fca59f8 --- /dev/null +++ b/.github/workflows/preview-deployments.yml @@ -0,0 +1,69 @@ +# Neon's Free plan allows 10 database branches per project, and the Vercel integration creates one +# per pull request. Neon drops a branch as soon as the last Vercel deployment of that git branch is +# deleted, but Vercel keeps previews for months, so without this the eleventh pull request fails +# with "Resource provisioning failed". +# +# Vercel deploys on push, not when a pull request opens. So a branch whose pull request was closed +# and then opened again (pull requests #34 and #35) had no preview left. The second job puts one +# back. +name: Preview deployments + +on: + pull_request: + types: [closed, opened, reopened] + +env: + VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }} + TEAM_ID: team_LGEdjw58WAVHk0VFbQJSrGFQ + PROJECT_ID: prj_1F0rehSGxd1yq30yGOgacvayBaTe + BRANCH: ${{ github.head_ref }} + +jobs: + delete-previews: + if: github.event.action == 'closed' + runs-on: ubuntu-latest + steps: + - name: Delete every preview deployment of the closed branch + run: | + ids=$(curl -sf -G "https://api.vercel.com/v6/deployments" \ + -H "Authorization: Bearer $VERCEL_TOKEN" \ + --data-urlencode "teamId=$TEAM_ID" \ + --data-urlencode "projectId=$PROJECT_ID" \ + --data-urlencode "branch=$BRANCH" \ + --data-urlencode "limit=100" | + jq -r --arg branch "$BRANCH" \ + '.deployments[] | select(.target != "production" and .meta.githubCommitRef == $branch) | .uid') + for id in $ids; do + echo "Deleting $id" + curl -sf -X DELETE "https://api.vercel.com/v13/deployments/$id?teamId=$TEAM_ID" \ + -H "Authorization: Bearer $VERCEL_TOKEN" >/dev/null + done + + ensure-preview: + if: github.event.action != 'closed' + runs-on: ubuntu-latest + env: + SHA: ${{ github.event.pull_request.head.sha }} + REPO_ID: ${{ github.event.repository.id }} + steps: + - name: Deploy the head commit if it has no preview deployment + run: | + existing=$(curl -sf -G "https://api.vercel.com/v6/deployments" \ + -H "Authorization: Bearer $VERCEL_TOKEN" \ + --data-urlencode "teamId=$TEAM_ID" \ + --data-urlencode "projectId=$PROJECT_ID" \ + --data-urlencode "branch=$BRANCH" \ + --data-urlencode "limit=100" | + jq -r --arg sha "$SHA" \ + '[.deployments[] | select(.meta.githubCommitSha == $sha and .state != "ERROR" and .state != "CANCELED")] | length') + if [ "$existing" != "0" ]; then + echo "A preview of $SHA exists: nothing to do" + exit 0 + fi + echo "No preview of $SHA: deploying $BRANCH" + curl -sf -X POST "https://api.vercel.com/v13/deployments?teamId=$TEAM_ID" \ + -H "Authorization: Bearer $VERCEL_TOKEN" \ + -H "Content-Type: application/json" \ + -d "$(jq -n --arg project "$PROJECT_ID" --arg ref "$BRANCH" --arg sha "$SHA" --argjson repoId "$REPO_ID" \ + '{name: "wee-app", project: $project, gitSource: {type: "github", repoId: $repoId, ref: $ref, sha: $sha}}')" | + jq -r '"Deployment " + .id + " " + .url' diff --git a/docs/adr/0004-neon-and-vercel.md b/docs/adr/0004-neon-and-vercel.md index 5c15a73..8616d74 100644 --- a/docs/adr/0004-neon-and-vercel.md +++ b/docs/adr/0004-neon-and-vercel.md @@ -28,7 +28,9 @@ disqualifying for a repository whose purpose is test-driven development. Neon's Free plan allows ten branches per project. Neon drops a preview branch when the last Vercel deployment of its git branch is deleted, and Vercel keeps previews for months. A GitHub workflow -therefore deletes the preview deployments of a pull request when it closes. It goes through Vercel +therefore deletes the preview deployments of a pull request when it closes. Vercel deploys on push +and not when a pull request opens, so the same workflow deploys the head commit again when a pull +request opens without a preview, as happens after a close and a reopen. It goes through Vercel rather than Neon because it then needs a single secret, `VERCEL_TOKEN`, and no access to the Neon console.