From dbaf20b863e885d103dc8660fbc8eb44707822e3 Mon Sep 17 00:00:00 2001 From: Doglightning <170844007+Doglightning@users.noreply.github.com> Date: Wed, 15 Oct 2025 20:17:36 +0000 Subject: [PATCH] feat(monorepo): public workflow for auto deploys (#250) # Auto Deploy Preview Workflow Closes: WORLD-XXX ## Overview This PR adds a new GitHub Actions workflow for automatically deploying preview environments. The workflow is designed to be called from other workflows and handles the coordination with the World Forge deployment system. ## Brief Changelog - Added new reusable workflow `auto_deploy.yml` that can be triggered via `workflow_call` - Implemented a repository dispatch event to trigger auto-deployments - Created a status monitoring system that waits for deployment completions - Added timeout handling with appropriate error messages - Implemented status aggregation to track multiple parallel deployments ## Testing and Verifying This change can be verified by triggering the workflow from another workflow and confirming that: - The repository dispatch event is properly sent to the World Forge system - The workflow correctly waits for and reports on deployment statuses - Appropriate error handling occurs for timeouts and deployment failures --- workflows/auto_deploy.yml | 67 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 workflows/auto_deploy.yml diff --git a/workflows/auto_deploy.yml b/workflows/auto_deploy.yml new file mode 100644 index 00000000..a8dafe05 --- /dev/null +++ b/workflows/auto_deploy.yml @@ -0,0 +1,67 @@ +name: Auto Deploy Preview + +on: + workflow_call: + inputs: + base: + type: string + required: true + head: + type: string + required: true + +permissions: + contents: write + statuses: read + +jobs: + signal: + runs-on: ubuntu-latest + steps: + - name: Emit repository_dispatch to App + uses: actions/github-script@v7 + with: + script: | + await github.rest.repos.createDispatchEvent({ + owner: context.repo.owner, + repo: context.repo.repo, + event_type: "auto-deploy", + client_payload: { base: inputs.base, head: inputs.head } + }) + - name: Wait for World Forge status + uses: actions/github-script@v7 + with: + script: | + const owner = context.repo.owner, repo = context.repo.repo, ref = inputs.head; + const prefix = "world-forge/auto-deploy"; + const initDeadline = Date.now() + 2*60*1000; + const hardDeadline = Date.now() + 45*60*1000; + const sleep = (ms) => new Promise(r => setTimeout(r, ms)); + async function allStatuses() { + const { data } = await github.rest.repos.getCombinedStatusForRef({ owner, repo, ref }); + return data.statuses || []; + } + let expected = null; + while (Date.now() < initDeadline) { + const statuses = await allStatuses(); + const aggFail = statuses.find(s => s.context === prefix && (s.state === "failure" || s.state === "error")); + if (aggFail) { core.setFailed(aggFail.description || "Auto-deploy aggregate failed"); return; } + const aggs = statuses.filter(s => s.context === prefix && s.state === "pending"); + for (const a of aggs) { + const n = parseInt(a.description || "0", 10); + if (!Number.isNaN(n) && n > 0) { expected = n; break; } + } + if (expected) break; + await sleep(5000); + } + if (!expected) { core.setFailed("Auto-deploy did not publish expected deployment count."); return; } + while (Date.now() < hardDeadline) { + const statuses = await allStatuses(); + const perProject = statuses.filter(s => s.context.startsWith(prefix + "/")); + const success = perProject.filter(s => s.state === "success").length; + const failed = perProject.filter(s => s.state === "failure" || s.state === "error").length; + if (success + failed >= expected) { if (failed > 0) core.setFailed(`Deploy failures: ${failed}/${expected}`); return; } + await sleep(10000); + } + core.setFailed("Timed out waiting for auto-deploy results."); + \ No newline at end of file