Skip to content
Open
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
165 changes: 165 additions & 0 deletions .github/workflows/pr-preview.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
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: 📝 Setup PR-specific configurations
run: |
# Copy and modify fly.toml for PR preview
cp fly.toml fly.toml.backup

# Update app name for PR
sed -i 's/^app = .*/app = "${{ steps.app-name.outputs.app_name }}"/' fly.toml

# Remove consul from experimental section (if present)
sed -i '/enable_consul = true/d' fly.toml

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

sed replacement risks false matches and TOML breakage

sed -i 's/^app = .*/app = "..."/' fly.toml relies on a very specific formatting of fly.toml.
• If the app key is indented or appears twice (common with multiple processes) the wrong line may be replaced.
• A safer, semantics-aware option is to use tomlq/yq or flyctl apps list --json to update the field, e.g.:

pip install tomlkit
python - <<'PY'
import tomlkit, sys, pathlib, os
d = tomlkit.parse(pathlib.Path("fly.toml").read_text())
d["app"] = os.environ["APP_NAME"]
pathlib.Path("fly.toml").write_text(tomlkit.dumps(d))
PY
🧰 Tools
🪛 YAMLlint (1.37.1)

[error] 38-38: trailing spaces

(trailing-spaces)


[error] 41-41: trailing spaces

(trailing-spaces)

🤖 Prompt for AI Agents
In .github/workflows/pr-preview.yml around lines 37 to 41, the current sed
command to replace the app name in fly.toml risks incorrect replacements due to
formatting variations and multiple app entries. Replace the sed command with a
Python script using tomlkit to parse fly.toml, update the "app" key safely with
the environment variable APP_NAME, and write back the file. This ensures a
semantics-aware, reliable update without breaking the TOML structure.

# Add auto-scaling configuration to services section
# Find the [[services]] section and add auto-scaling after internal_port
awk '
/^\[\[services\]\]/ { in_services = 1 }
/^ internal_port = / && in_services {
print $0
print " auto_stop_machines = true"
print " auto_start_machines = true"
print " min_machines_running = 0"
print " processes = [\"app\"]"
in_services = 0
next
}
{ print }
' fly.toml > fly.toml.tmp && mv fly.toml.tmp fly.toml

# Copy and modify litefs.yml for PR preview (standalone, no consul)
cp other/litefs.yml other/litefs.yml.backup

# Replace consul lease with static lease for PR preview isolation
# This makes each PR preview a standalone instance
sed -i '/^lease:/,/^exec:/c\
lease:\
type: static\
\
exec:' other/litefs.yml
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

YAML parse error: multi-line sed replacement breaks the workflow
actionlint reports could not find expected ':' at line 64 because the raw lease: block exits the run: scalar. Escape the newline or, better, switch to yq:

-sed -i '/^lease:/,/^exec:/c\
-lease:\
-  type: static\
-\
-exec:' other/litefs.yml
+yq -i 'del(.lease) | .lease.type="static"' other/litefs.yml

This eliminates the syntax error and avoids brittle text manipulation.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
sed -i '/^lease:/,/^exec:/c\
lease:\
type: static\
\
exec:' other/litefs.yml
yq -i 'del(.lease) | .lease.type="static"' other/litefs.yml
🧰 Tools
🪛 actionlint (1.7.7)

64-64: could not parse as YAML: yaml: line 64: could not find expected ':'

(syntax-check)

🪛 YAMLlint (1.37.1)

[error] 65-65: syntax error: could not find expected ':'

(syntax)

🤖 Prompt for AI Agents
In .github/workflows/pr-preview.yml around lines 63 to 67, the multi-line sed
replacement breaks the YAML syntax causing a parse error. To fix this, replace
the sed command with a yq command to perform the YAML modification safely. This
avoids breaking the run scalar block and prevents brittle text manipulation by
using proper YAML-aware editing.


# Add a comment explaining the isolation
sed -i '/^lease:/i\
# PR Preview: Using static lease type for standalone instance\
# This prevents syncing with production data' other/litefs.yml

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: 🗑️ 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
});
Loading