Files
windmill/.github/workflows/spawn-ephemeral-backend.yml
Diego Imbert 4f2f7356c0 Fullstack CI Preview (#7665)
* update cf worker hostname

* set remote_url cookie from param

* ephemeral backends v1

* nit

* Run queue server

* ntis

* timeout

* better db process management

* commit hash and worktree

* nit use map

* nit

* err handling

* Revert "err handling"

This reverts commit 19de00c0c0.

* nits

* auto cleanup

* Ephemeral backend command action

* remove checkout

* checkout ee repo

* nits

* process.env.GIT_EE_DEPLOY_KEY_FILE

* resumeURLs logic

* nit

* use windmill flow for ephemeral backend action

* fixes

* new token

* worktree pools

* Delete GH secret on cleanup

* linux deploy

* nit

* nit

* unhandled promises

* nit

* fix docker bridge IP on linux

* pass cf_frontend_url to wmill flow

* git fetch

* release worktree when binary started

* send error

* logger

* logging

* logging 2

* delete log files periodically

* redirect to raw app with logs

* CORS

* MANAGER_AUTH_TOKEN

* Check organization membership

* nit

* bwrap

* nit

* return timeoutAt in resumeUrl

* nit

* Change password

* nit remove https
2026-02-05 17:26:51 +00:00

159 lines
5.9 KiB
YAML

name: Spawn Ephemeral Backend
on:
issue_comment:
types: [created]
workflow_dispatch:
inputs:
pr_number:
description: "PR number"
required: true
type: number
jobs:
spawn-backend:
# Only run on PR comments that contain /spawn-backend, or manual dispatch
if: |
github.event_name == 'workflow_dispatch' ||
(github.event.issue.pull_request && contains(github.event.comment.body, '/spawn-backend'))
runs-on: ubuntu-latest
permissions:
pull-requests: write
contents: read
steps:
- name: Check organization membership
if: github.event_name == 'issue_comment'
id: check-org-member
uses: actions/github-script@v7
with:
script: |
const commenter = context.payload.comment.user.login;
try {
const membership = await github.rest.orgs.checkMembershipForUser({
org: 'windmill-labs',
username: commenter
});
core.setOutput('is_member', 'true');
console.log(`✓ User ${commenter} is a member of windmill-labs`);
} catch (error) {
core.setOutput('is_member', 'false');
console.log(`✗ User ${commenter} is not a member of windmill-labs`);
}
- name: Post unauthorized comment
if: |
github.event_name == 'issue_comment' &&
steps.check-org-member.outputs.is_member == 'false'
uses: actions/github-script@v7
with:
script: |
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body: `❌ Unauthorized: Only members of the windmill-labs organization can spawn ephemeral backends.`
});
- name: Fail if not org member
if: |
github.event_name == 'issue_comment' &&
steps.check-org-member.outputs.is_member == 'false'
run: |
echo "Error: User is not a member of windmill-labs organization"
exit 1
- name: Get PR details
id: pr-details
uses: actions/github-script@v7
with:
script: |
const prNumber = context.eventName === 'workflow_dispatch'
? context.payload.inputs.pr_number
: context.issue.number;
const pr = await github.rest.pulls.get({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: prNumber
});
// Get branch name and format it for Cloudflare Pages
// Replace '/' with '-' for the URL
const branchName = pr.data.head.ref;
const formattedBranch = branchName.replace(/\//g, '-');
const cfFrontendUrl = `https://${formattedBranch}.windmill.pages.dev`;
core.setOutput('commit_hash', pr.data.head.sha);
core.setOutput('pr_number', prNumber);
core.setOutput('branch_name', branchName);
core.setOutput('cf_frontend_url', cfFrontendUrl);
- name: Check manager URL
id: check-manager-url
run: |
if [ -z "${{ secrets.EPHEMERAL_BACKEND_QUEUE_URL }}" ]; then
echo "manager_url_set=false" >> $GITHUB_OUTPUT
else
echo "manager_url_set=true" >> $GITHUB_OUTPUT
fi
- name: Post error comment if manager not running
if: steps.check-manager-url.outputs.manager_url_set == 'false'
uses: actions/github-script@v7
with:
script: |
const prNumber = context.eventName === 'workflow_dispatch'
? Number(context.payload.inputs.pr_number)
: context.issue.number;
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: prNumber,
body: `❌ Manager URL not set (did you start the ephemeral backend manager?)\n\nThe ephemeral backend manager needs to be running to spawn backends. Please start the manager first.`
});
- name: Fail if manager not running
if: steps.check-manager-url.outputs.manager_url_set == 'false'
run: |
echo "Error: EPHEMERAL_BACKEND_QUEUE_URL secret is not set"
exit 1
- name: Trigger Windmill flow
if: steps.check-manager-url.outputs.manager_url_set == 'true'
id: trigger-flow
run: |
JOB_UUID=$(curl -s -X POST "https://app.windmill.dev/api/w/windmill-labs/jobs/run/f/f/all/run_ephemeral_backend" \
-H "Authorization: Bearer ${{ secrets.WINDMILL_RUN_FLOW_TOKEN }}" \
-H "Content-Type: application/json" \
-d '{
"manager_url": "${{ secrets.EPHEMERAL_BACKEND_QUEUE_URL }}",
"commit_hash": "${{ steps.pr-details.outputs.commit_hash }}",
"pr_number": ${{ steps.pr-details.outputs.pr_number }},
"cf_frontend_url": "${{ steps.pr-details.outputs.cf_frontend_url }}"
}' | tr -d '"')
echo "Job UUID: $JOB_UUID"
echo "job_uuid=$JOB_UUID" >> $GITHUB_OUTPUT
- name: Post comment with job link
if: steps.check-manager-url.outputs.manager_url_set == 'true'
uses: actions/github-script@v7
with:
script: |
const jobUuid = '${{ steps.trigger-flow.outputs.job_uuid }}';
const appUrl = `https://app.windmill.dev/public/windmill-labs/a106bad0256c1dfa7a4f9279c42b1a4b#${jobUuid}`;
const prNumber = context.eventName === 'workflow_dispatch'
? Number(context.payload.inputs.pr_number)
: context.issue.number;
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: prNumber,
body: `🚀 Spawning new ephemeral backend!\n\n${appUrl}`
});