Skip to content

Set up temporary Fly deployments for PRs #7

Set up temporary Fly deployments for PRs

Set up temporary Fly deployments for PRs #7

Workflow file for this run

name: πŸ” PR Preview Deploy
on:
pull_request:
types: [opened, synchronize, reopened, closed]
env:
FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}
jobs:
deploy-pr:
name: πŸš€ Deploy PR Preview
runs-on: ubuntu-latest
if: github.event.action != 'closed'
steps:
- name: ⬇️ Checkout repo
uses: actions/checkout@v4
- name: 🎈 Setup Fly
uses: superfly/flyctl-actions/[email protected]
- name: 🏷️ Generate app name
id: app-name
run: |
# Create a unique app name for this PR
APP_NAME="kcd-pr-${{ github.event.number }}"
echo "app_name=$APP_NAME" >> $GITHUB_OUTPUT
echo "url=https://$APP_NAME.fly.dev" >> $GITHUB_OUTPUT
- name: πŸ“¦ Install dependencies for setup script
run: |
npm install js-yaml
- name: οΏ½ Setup environment variables
run: |
# Copy environment variables for PR preview
cp .env.example .env
echo "βœ“ Environment variables copied from .env.example"
- name: οΏ½πŸ“ Setup PR-specific configurations
run: |
# Make script executable and run it
chmod +x other/setup-pr-preview.js
node other/setup-pr-preview.js "${{ steps.app-name.outputs.app_name }}"
echo "=== Modified fly.toml ==="
cat fly.toml
echo "=== Modified litefs.yml ==="
cat other/litefs.yml
- name: πŸ—οΈ Create PR app
run: |
flyctl apps create ${{ steps.app-name.outputs.app_name }} --org personal || echo "App already exists"
- name: πŸš€ Deploy PR app
run: |
flyctl deploy --depot --remote-only --build-arg COMMIT_SHA=${{ github.sha }} --app ${{ steps.app-name.outputs.app_name }}
- name: πŸ’¬ Comment on PR
uses: actions/github-script@v7
with:
script: |
const { data: comments } = await github.rest.issues.listComments({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
});
const botComment = comments.find(comment =>
comment.user.type === 'Bot' && comment.body.includes('PR Preview')
);
const commentBody = `πŸš€ **PR Preview Deployed**
Your pull request has been deployed to a temporary Fly machine:
πŸ”— **Preview URL**: ${{ steps.app-name.outputs.url }}
πŸ“± **App Name**: \`${{ steps.app-name.outputs.app_name }}\`
This preview will automatically scale to zero when not in use to save costs.
The app will be automatically deleted when this PR is closed or merged.
---
<sub>Updated: ${new Date().toISOString()}</sub>`;
if (botComment) {
github.rest.issues.updateComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: botComment.id,
body: commentBody
});
} else {
github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body: commentBody
});
}
cleanup-pr:
name: 🧹 Cleanup PR Preview
runs-on: ubuntu-latest
if: github.event.action == 'closed'
steps:
- name: 🎈 Setup Fly
uses: superfly/flyctl-actions/[email protected]
- name: 🏷️ Generate app name
id: app-name
run: |
APP_NAME="kcd-pr-${{ github.event.number }}"
echo "app_name=$APP_NAME" >> $GITHUB_OUTPUT
- name: 🧹 Cleanup volumes
run: |
# List and delete all volumes for the PR app
echo "Checking for volumes to cleanup..."
VOLUMES=$(flyctl volumes list --app ${{ steps.app-name.outputs.app_name }} --json 2>/dev/null || echo "[]")
if [ "$VOLUMES" != "[]" ] && [ -n "$VOLUMES" ]; then
echo "Found volumes to cleanup"
echo "$VOLUMES" | jq -r '.[].id' | while read -r VOLUME_ID; do
if [ -n "$VOLUME_ID" ]; then
echo "Destroying volume: $VOLUME_ID"
flyctl volumes destroy "$VOLUME_ID" --yes || echo "Failed to destroy volume $VOLUME_ID"
fi
done
else
echo "No volumes found to cleanup"
fi
- name: πŸ—‘οΈ Delete PR app
run: |
flyctl apps destroy ${{ steps.app-name.outputs.app_name }} --yes || echo "App doesn't exist or already deleted"
- name: πŸ’¬ Comment on PR
uses: actions/github-script@v7
with:
script: |
const commentBody = `🧹 **PR Preview Cleaned Up**
The temporary Fly machine for this PR has been deleted.
---
<sub>Cleaned up: ${new Date().toISOString()}</sub>`;
github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body: commentBody
});