Skip to content

PR Preview feature for CapRover #1

PR Preview feature for CapRover

PR Preview feature for CapRover #1

Workflow file for this run

name: Preview
on:
pull_request:
branches: ["main"]
types: [opened, reopened, synchronize, closed]
concurrency:
group: "preview-${{ github.event.number }}"
cancel-in-progress: true
permissions:
pull-requests: write
jobs:
deploy-preview:
if: github.event.action != 'closed'
runs-on: ubuntu-latest
env:
APP_NAME: devx-pr-${{ github.event.number }}
CAPROVER_URL: ${{ secrets.CAPROVER_URL }}
CAPROVER_PASSWORD: ${{ secrets.CAPROVER_PASSWORD }}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Bun
uses: oven-sh/setup-bun@v1
with:
bun-version: latest
- name: Install dependencies
run: bun install
- name: Build
run: bun run build
env:
NEXT_PUBLIC_SUPABASE_URL: ${{ secrets.NEXT_PUBLIC_SUPABASE_URL }}
NEXT_PUBLIC_SUPABASE_ANON_KEY: ${{ secrets.NEXT_PUBLIC_SUPABASE_ANON_KEY }}
- name: Create CapRover app (idempotent)
run: |
TOKEN=$(curl -sf -X POST "$CAPROVER_URL/api/v2/login" \
-H "Content-Type: application/json" \
-d "{\"password\": \"$CAPROVER_PASSWORD\"}" \
| jq -r '.data.token')
curl -s -X POST "$CAPROVER_URL/api/v2/user/apps/appDefinitions/register" \
-H "Content-Type: application/json" \
-H "x-captain-auth: $TOKEN" \
-d "{\"appName\": \"$APP_NAME\", \"hasPersistentData\": false}" || true
- name: Package and deploy to CapRover
run: |
tar -czf deploy.tar.gz captain-definition Dockerfile out/
npm install -g caprover
caprover deploy \
--caproverUrl "$CAPROVER_URL" \
--caproverPassword "$CAPROVER_PASSWORD" \
--appName "$APP_NAME" \
--tarFile ./deploy.tar.gz
- name: Comment preview URL on PR
uses: actions/github-script@v7
env:
APP_NAME: devx-pr-${{ github.event.number }}
CAPROVER_APP_DOMAIN: ${{ secrets.CAPROVER_APP_DOMAIN }}
with:
script: |
const url = `https://${process.env.APP_NAME}.${process.env.CAPROVER_APP_DOMAIN}`;
const marker = '<!-- caprover-preview -->';
const body = `${marker}\n## Preview deployment\n\n${url}\n\n_Updated: ${new Date().toUTCString()}_`;
const { data: comments } = await github.rest.issues.listComments({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
});
const existing = comments.find(c => c.body.includes(marker));
if (existing) {
await github.rest.issues.updateComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: existing.id,
body,
});
} else {
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body,
});
}
cleanup-preview:
if: github.event.action == 'closed'
runs-on: ubuntu-latest
env:
APP_NAME: devx-pr-${{ github.event.number }}
CAPROVER_URL: ${{ secrets.CAPROVER_URL }}
CAPROVER_PASSWORD: ${{ secrets.CAPROVER_PASSWORD }}
steps:
- name: Delete CapRover app
run: |
TOKEN=$(curl -sf -X POST "$CAPROVER_URL/api/v2/login" \
-H "Content-Type: application/json" \
-d "{\"password\": \"$CAPROVER_PASSWORD\"}" \
| jq -r '.data.token')
curl -s -X POST "$CAPROVER_URL/api/v2/user/apps/appDefinitions/delete" \
-H "Content-Type: application/json" \
-H "x-captain-auth: $TOKEN" \
-d "{\"appName\": \"$APP_NAME\"}" || true