Skip to content
124 changes: 34 additions & 90 deletions .github/workflows/ci-deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,154 +2,98 @@ name: CI + Deploy (prebuilt)

on:
push:
branches: [ '**' ]
branches: ['**']
pull_request:
branches: [ main ]
branches: [main]

permissions:
contents: read
issues: write
pull-requests: write
deployments: write

env:
VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }}
VERCEL_ORG: ${{ secrets.VERCEL_ORG_ID }}

jobs:
build_and_test:
build_and_deploy:
runs-on: ubuntu-latest
steps:
# Checkout repo
# Checkout
- uses: actions/checkout@v4

# Setup Node + cache
# Node + npm cache
- uses: actions/setup-node@v4
with:
node-version: 18
cache: 'npm'
cache: npm

# Install dependencies (CI preferred)
- name: Install
# Install deps
- name: Install dependencies
run: npm ci || npm install

# Lint if exists
- name: Lint
run: npm run -s | grep -qE '(^| )lint( |:)' && npm run lint || echo "No lint script"

# Unit tests if exist
- name: Unit tests
run: npm run -s | grep -qE '(^| )test( |:)' && npm test --ci --passWithNoTests=false || echo "No test script"

# E2E tests if exist
- name: E2E tests (optional)
run: npm run -s | grep -qE '(^| )e2e( |:)' && npm run e2e || echo "No e2e script"

# Project build
- name: App build
run: npm run build

# Short summary for PR checks
- name: Build summary
run: echo "Build & tests passed ✅" >> "$GITHUB_STEP_SUMMARY"

deploy:
needs: build_and_test
if: ${{ success() }}
runs-on: ubuntu-latest
steps:
# Checkout again for deploy context
- uses: actions/checkout@v4

# Setup Node for Vercel CLI
- uses: actions/setup-node@v4
with:
node-version: 18
cache: 'npm'

# Install Vercel CLI
- name: Install Vercel CLI
# Vercel CLI
- name: Setup Vercel CLI
run: npm i -g vercel@latest

# Decide environment target
- name: Decide target
id: tgt
run: |
if [ "${{ github.event_name }}" = "pull_request" ]; then
echo "target=preview" >> $GITHUB_OUTPUT
elif [ "${{ github.ref_name }}" = "main" ]; then
echo "target=production" >> $GITHUB_OUTPUT
else
echo "target=preview" >> $GITHUB_OUTPUT
fi

# Pull Vercel config + envs
- name: Pull Vercel project settings
# Pull project config + envs
- name: Pull Vercel settings
run: |
vercel pull --yes \
--environment "${{ steps.tgt.outputs.target }}" \
--token "${{ env.VERCEL_TOKEN }}" \
--scope "${{ env.VERCEL_ORG }}"
--environment="$([ '${{ github.ref_name }}' = 'main' ] && echo production || echo preview)" \
--token="${{ env.VERCEL_TOKEN }}" \
--scope="${{ env.VERCEL_ORG }}"

# Prebuild locally
- name: Vercel prebuild
# Prebuild (.vercel/output)
- name: Vercel build (prebuilt)
run: |
vercel build \
--token "${{ env.VERCEL_TOKEN }}" \
--scope "${{ env.VERCEL_ORG }}"
--token="${{ env.VERCEL_TOKEN }}" \
--scope="${{ env.VERCEL_ORG }}"

# Deploy prebuilt output (skip build on Vercel)
- name: Deploy (prebuilt)
# Deploy prebuilt
- name: Deploy prebuilt build
id: deploy
env:
VC_TOKEN: ${{ env.VERCEL_TOKEN }}
VC_TEAM: ${{ env.VERCEL_ORG }}
TARGET: ${{ steps.tgt.outputs.target }}
run: |
set -e
ARGS=(deploy --yes --token "$VC_TOKEN" --scope "$VC_TEAM" --prebuilt)
[ "$TARGET" = "production" ] && ARGS+=(--prod) || true
URL="$(vercel "${ARGS[@]}")"
if [ "${{ github.ref_name }}" = "main" ]; then
OUT="$(vercel deploy --prebuilt --prod --yes --token "$VC_TOKEN" --scope "$VC_TEAM")"
else
OUT="$(vercel deploy --prebuilt --yes --token "$VC_TOKEN" --scope "$VC_TEAM")"
fi
URL="$(printf "%s\n" "$OUT" | grep -Eo 'https?://[a-zA-Z0-9.-]+\.vercel\.app' | tail -1 || true)"
echo "url=$URL" >> "$GITHUB_OUTPUT"
echo "Deployed: $URL"

# Show summary in job checks
- name: Summary
run: |
echo "### Deployment" >> "$GITHUB_STEP_SUMMARY"
echo "- Target: **${{ steps.tgt.outputs.target }}**" >> "$GITHUB_STEP_SUMMARY"
echo "- URL: ${{ steps.deploy.outputs.url }}" >> "$GITHUB_STEP_SUMMARY"
echo "✅ Deployed to: ${URL:-(no public URL)}"

# Comment preview link for PR reviewers
- name: Post Preview URL to PR
- name: Comment Preview URL on PR
if: ${{ github.event_name == 'pull_request' && steps.deploy.outputs.url != '' }}
uses: actions/github-script@v7
with:
script: |
await github.rest.issues.createComment({
...context.repo,
issue_number: context.payload.pull_request.number,
body: `✅ Preview ready: ${{ steps.deploy.outputs.url }}`
body: `✅ **Preview ready:** ${{ steps.deploy.outputs.url }}`
})

# Link deployment to merge commit (appears in PR "merged commit ..." line)
- name: Link deployment to merge commit
if: ${{ github.event_name == 'push' && steps.deploy.outputs.url != '' }}
- name: Create Deployment Card (main only)
if: ${{ github.ref_name == 'main' }}
uses: actions/github-script@v7
with:
script: |
const envName = '${{ github.ref_name }}';
const { data: dep } = await github.rest.repos.createDeployment({
...context.repo,
ref: context.sha,
environment: envName,
environment: 'production',
auto_merge: false,
required_contexts: []
});
await github.rest.repos.createDeploymentStatus({
...context.repo,
deployment_id: dep.id,
state: 'success',
environment: envName,
environment_url: '${{ steps.deploy.outputs.url }}'
environment: 'production'
});