name: Deploy to Preview
on:
pull_request:
types: [labeled, synchronize, closed]
permissions:
contents: write
pull-requests: write
deployments: write
jobs:
deploy:
# Deploy when: (1) the deploy-preview label is added, OR (2) a new commit is pushed to a PR that already has the label
if: |
(github.event.action == 'labeled' && github.event.label.name == 'deploy-preview') ||
(github.event.action == 'synchronize' && contains(github.event.pull_request.labels.*.name, 'deploy-preview'))
runs-on:
group: neondatabase-protected-runner-group
labels: linux-ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 1
ref: ${{ github.event.pull_request.head.sha }}
# Only remove labels from other PRs when a new label is added (not on every commit)
- name: Remove deploy-preview label from other PRs
if: github.event.action == 'labeled'
uses: actions/github-script@v7
with:
script: |
const currentPR = context.payload.pull_request.number;
// Find all open PRs with the 'deploy-preview' label
const { data: prs } = await github.rest.issues.listForRepo({
owner: context.repo.owner,
repo: context.repo.repo,
labels: 'deploy-preview',
state: 'open'
});
// Remove label from other PRs
for (const pr of prs) {
if (pr.number !== currentPR) {
console.log(`Removing 'deploy-preview' label from PR #${pr.number}`);
await github.rest.issues.removeLabel({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: pr.number,
name: 'deploy-preview'
});
// Notify the other PR
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: pr.number,
body: `⚠️ The \`deploy-preview\` label was removed because PR #${currentPR} now owns the preview environment.`
});
}
}
- name: Push to preview branch
run: |
git push origin HEAD:refs/heads/preview --force
# Creates a GitHub Deployment record to track this deployment in the GitHub UI.
# Note: This doesn't perform the actual deployment - that happens when Vercel
# detects the push to the preview branch above. This step just creates a visible
# record in GitHub's "Environments" tab and adds the deployment link to the PR.
- name: Create GitHub Deployment
uses: actions/github-script@v7
with:
script: |
const deployment = await github.rest.repos.createDeployment({
owner: context.repo.owner,
repo: context.repo.repo,
ref: context.payload.pull_request.head.sha,
environment: 'preview',
auto_merge: false,
required_contexts: [],
description: `PR #${context.payload.pull_request.number}: ${context.payload.pull_request.title}`
});
await github.rest.repos.createDeploymentStatus({
owner: context.repo.owner,
repo: context.repo.repo,
deployment_id: deployment.data.id,
state: 'success',
environment_url: 'https://preview-mcp.neon.tech',
description: 'Deployment successful'
});
# Only comment on initial label, not on every commit push
- name: Comment on PR
if: github.event.action == 'labeled'
uses: actions/github-script@v7
with:
script: |
github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body: '🚀 Deployed to https://preview-mcp.neon.tech\n\nThis PR now owns the preview environment. OAuth flow is available for testing.'
})
# Automatically remove the deploy-preview label when a PR is merged or closed
cleanup:
if: github.event.action == 'closed' && contains(github.event.pull_request.labels.*.name, 'deploy-preview')
runs-on:
group: neondatabase-protected-runner-group
labels: linux-ubuntu-latest
steps:
- name: Remove deploy-preview label
uses: actions/github-script@v7
with:
script: |
const prNumber = context.payload.pull_request.number;
const merged = context.payload.pull_request.merged;
console.log(`PR #${prNumber} was ${merged ? 'merged' : 'closed'}. Removing 'deploy-preview' label.`);
try {
await github.rest.issues.removeLabel({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: prNumber,
name: 'deploy-preview'
});
} catch (error) {
// Label might already be removed
console.log('Label already removed or not found:', error.message);
}