@repo-os/repoos 0.5.45
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +110 -0
- package/README.md +272 -0
- package/dist/.build-info.json +4 -0
- package/dist/.build-stamp.json +3 -0
- package/dist/cli/colors.d.ts +13 -0
- package/dist/cli/colors.js +37 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.js +221 -0
- package/dist/commands/check.d.ts +170 -0
- package/dist/commands/check.js +1227 -0
- package/dist/commands/docs.d.ts +2 -0
- package/dist/commands/docs.js +70 -0
- package/dist/commands/gc.d.ts +1 -0
- package/dist/commands/gc.js +57 -0
- package/dist/commands/init.d.ts +9 -0
- package/dist/commands/init.js +699 -0
- package/dist/commands/screenshots.d.ts +7 -0
- package/dist/commands/screenshots.js +77 -0
- package/dist/commands/serve.d.ts +55 -0
- package/dist/commands/serve.js +204 -0
- package/dist/commands/status.d.ts +140 -0
- package/dist/commands/status.js +569 -0
- package/dist/commands/stop.d.ts +1 -0
- package/dist/commands/stop.js +59 -0
- package/dist/commands/tasks.d.ts +47 -0
- package/dist/commands/tasks.js +334 -0
- package/dist/commands/tunnel.d.ts +1 -0
- package/dist/commands/tunnel.js +1271 -0
- package/dist/commands/ui-harness.d.ts +53 -0
- package/dist/commands/ui-harness.js +62 -0
- package/dist/commands/ui-smoke.d.ts +6 -0
- package/dist/commands/ui-smoke.js +220 -0
- package/dist/commands/upgrade.d.ts +15 -0
- package/dist/commands/upgrade.js +188 -0
- package/dist/core/auth-store.d.ts +81 -0
- package/dist/core/auth-store.js +530 -0
- package/dist/core/auth.d.ts +75 -0
- package/dist/core/auth.js +124 -0
- package/dist/core/bootstrap.d.ts +41 -0
- package/dist/core/bootstrap.js +236 -0
- package/dist/core/build.d.ts +92 -0
- package/dist/core/build.js +249 -0
- package/dist/core/canary.d.ts +10 -0
- package/dist/core/canary.js +14 -0
- package/dist/core/check-failure-summary.d.ts +10 -0
- package/dist/core/check-failure-summary.js +34 -0
- package/dist/core/config.d.ts +160 -0
- package/dist/core/config.js +1288 -0
- package/dist/core/context-pack.d.ts +74 -0
- package/dist/core/context-pack.js +778 -0
- package/dist/core/db.d.ts +236 -0
- package/dist/core/db.js +747 -0
- package/dist/core/detect.d.ts +69 -0
- package/dist/core/detect.js +251 -0
- package/dist/core/docs.d.ts +76 -0
- package/dist/core/docs.js +225 -0
- package/dist/core/frontmatter.d.ts +29 -0
- package/dist/core/frontmatter.js +215 -0
- package/dist/core/git.d.ts +505 -0
- package/dist/core/git.js +1453 -0
- package/dist/core/indexer.d.ts +33 -0
- package/dist/core/indexer.js +282 -0
- package/dist/core/input.d.ts +66 -0
- package/dist/core/input.js +222 -0
- package/dist/core/logger.d.ts +70 -0
- package/dist/core/logger.js +138 -0
- package/dist/core/models.d.ts +45 -0
- package/dist/core/models.js +291 -0
- package/dist/core/net-probe.d.ts +4 -0
- package/dist/core/net-probe.js +20 -0
- package/dist/core/providers/deepinfra.d.ts +23 -0
- package/dist/core/providers/deepinfra.js +44 -0
- package/dist/core/providers/index.d.ts +24 -0
- package/dist/core/providers/index.js +59 -0
- package/dist/core/providers/openrouter.d.ts +28 -0
- package/dist/core/providers/openrouter.js +82 -0
- package/dist/core/providers/spend.d.ts +86 -0
- package/dist/core/providers/spend.js +286 -0
- package/dist/core/providers/types.d.ts +56 -0
- package/dist/core/providers/types.js +39 -0
- package/dist/core/repoos.d.ts +59 -0
- package/dist/core/repoos.js +221 -0
- package/dist/core/runtime.d.ts +35 -0
- package/dist/core/runtime.js +167 -0
- package/dist/core/skills-registry.d.ts +39 -0
- package/dist/core/skills-registry.js +148 -0
- package/dist/core/skills-registry.test.d.ts +1 -0
- package/dist/core/skills-registry.test.js +52 -0
- package/dist/core/sysmem.d.ts +7 -0
- package/dist/core/sysmem.js +55 -0
- package/dist/core/tailscale.d.ts +2 -0
- package/dist/core/tailscale.js +19 -0
- package/dist/core/task.d.ts +53 -0
- package/dist/core/task.js +310 -0
- package/dist/core/toml-line.d.ts +20 -0
- package/dist/core/toml-line.js +52 -0
- package/dist/core/toml-validate.d.ts +44 -0
- package/dist/core/toml-validate.js +335 -0
- package/dist/core/tunnel-assistant.d.ts +30 -0
- package/dist/core/tunnel-assistant.js +65 -0
- package/dist/core/tunnel-registry.d.ts +96 -0
- package/dist/core/tunnel-registry.js +267 -0
- package/dist/core/tunnel-registry.test.d.ts +1 -0
- package/dist/core/tunnel-registry.test.js +309 -0
- package/dist/core/tunnel.d.ts +103 -0
- package/dist/core/tunnel.js +320 -0
- package/dist/core/types.d.ts +811 -0
- package/dist/core/types.js +27 -0
- package/dist/core/worktree-gc.d.ts +57 -0
- package/dist/core/worktree-gc.js +194 -0
- package/dist/server/__tests__/integration-job.test.d.ts +1 -0
- package/dist/server/__tests__/integration-job.test.js +129 -0
- package/dist/server/agents.d.ts +1020 -0
- package/dist/server/agents.js +4070 -0
- package/dist/server/attachments.d.ts +51 -0
- package/dist/server/attachments.js +130 -0
- package/dist/server/auto-engineering.d.ts +45 -0
- package/dist/server/auto-engineering.js +275 -0
- package/dist/server/branchless-release.d.ts +22 -0
- package/dist/server/branchless-release.js +67 -0
- package/dist/server/built-in-agents.d.ts +305 -0
- package/dist/server/built-in-agents.js +1838 -0
- package/dist/server/cto-monitor.d.ts +35 -0
- package/dist/server/cto-monitor.js +281 -0
- package/dist/server/cto.d.ts +125 -0
- package/dist/server/cto.js +590 -0
- package/dist/server/deployments.d.ts +119 -0
- package/dist/server/deployments.js +325 -0
- package/dist/server/diff-snapshot.d.ts +20 -0
- package/dist/server/diff-snapshot.js +73 -0
- package/dist/server/done.d.ts +160 -0
- package/dist/server/done.js +488 -0
- package/dist/server/freeform.d.ts +55 -0
- package/dist/server/freeform.js +179 -0
- package/dist/server/handoff.d.ts +99 -0
- package/dist/server/handoff.js +575 -0
- package/dist/server/hetzner.d.ts +45 -0
- package/dist/server/hetzner.js +117 -0
- package/dist/server/icons.d.ts +8 -0
- package/dist/server/icons.js +195 -0
- package/dist/server/integration-job.d.ts +75 -0
- package/dist/server/integration-job.js +144 -0
- package/dist/server/integration-orchestrator.d.ts +211 -0
- package/dist/server/integration-orchestrator.js +1299 -0
- package/dist/server/integration-status.d.ts +48 -0
- package/dist/server/integration-status.js +71 -0
- package/dist/server/live-index.d.ts +295 -0
- package/dist/server/live-index.js +452 -0
- package/dist/server/model-test.d.ts +20 -0
- package/dist/server/model-test.js +100 -0
- package/dist/server/ntfy.d.ts +57 -0
- package/dist/server/ntfy.js +112 -0
- package/dist/server/pm-attachments.d.ts +35 -0
- package/dist/server/pm-attachments.js +181 -0
- package/dist/server/pm-runs.d.ts +58 -0
- package/dist/server/pm-runs.js +80 -0
- package/dist/server/preview.d.ts +174 -0
- package/dist/server/preview.js +641 -0
- package/dist/server/release.d.ts +88 -0
- package/dist/server/release.js +449 -0
- package/dist/server/reload.d.ts +194 -0
- package/dist/server/reload.js +586 -0
- package/dist/server/remote-validation.d.ts +132 -0
- package/dist/server/remote-validation.js +478 -0
- package/dist/server/repo-lock.d.ts +36 -0
- package/dist/server/repo-lock.js +141 -0
- package/dist/server/review-guard.d.ts +14 -0
- package/dist/server/review-guard.js +159 -0
- package/dist/server/review.d.ts +243 -0
- package/dist/server/review.js +1211 -0
- package/dist/server/routes/agents.d.ts +6 -0
- package/dist/server/routes/agents.js +28 -0
- package/dist/server/routes/auth.d.ts +60 -0
- package/dist/server/routes/auth.js +755 -0
- package/dist/server/routes/config.d.ts +20 -0
- package/dist/server/routes/config.js +347 -0
- package/dist/server/routes/debugger.d.ts +33 -0
- package/dist/server/routes/debugger.js +360 -0
- package/dist/server/routes/deployments.d.ts +3 -0
- package/dist/server/routes/deployments.js +16 -0
- package/dist/server/routes/docs.d.ts +8 -0
- package/dist/server/routes/docs.js +135 -0
- package/dist/server/routes/helpers.d.ts +21 -0
- package/dist/server/routes/helpers.js +171 -0
- package/dist/server/routes/index.d.ts +19 -0
- package/dist/server/routes/index.js +18 -0
- package/dist/server/routes/info.d.ts +17 -0
- package/dist/server/routes/info.js +177 -0
- package/dist/server/routes/inputs.d.ts +7 -0
- package/dist/server/routes/inputs.js +125 -0
- package/dist/server/routes/model-providers.d.ts +27 -0
- package/dist/server/routes/model-providers.js +94 -0
- package/dist/server/routes/models.d.ts +3 -0
- package/dist/server/routes/models.js +36 -0
- package/dist/server/routes/notify.d.ts +2 -0
- package/dist/server/routes/notify.js +17 -0
- package/dist/server/routes/playground.d.ts +29 -0
- package/dist/server/routes/playground.js +81 -0
- package/dist/server/routes/release.d.ts +20 -0
- package/dist/server/routes/release.js +86 -0
- package/dist/server/routes/router.d.ts +12 -0
- package/dist/server/routes/router.js +32 -0
- package/dist/server/routes/skill-registry.d.ts +5 -0
- package/dist/server/routes/skill-registry.js +51 -0
- package/dist/server/routes/tasks.d.ts +58 -0
- package/dist/server/routes/tasks.js +1442 -0
- package/dist/server/routes/transcribe.d.ts +2 -0
- package/dist/server/routes/transcribe.js +124 -0
- package/dist/server/routes/types.d.ts +68 -0
- package/dist/server/routes/types.js +1 -0
- package/dist/server/routes/ui.d.ts +5 -0
- package/dist/server/routes/ui.js +81 -0
- package/dist/server/routes/utils.d.ts +3 -0
- package/dist/server/routes/utils.js +23 -0
- package/dist/server/serve-reaper.d.ts +135 -0
- package/dist/server/serve-reaper.js +491 -0
- package/dist/server/server.d.ts +49 -0
- package/dist/server/server.js +2171 -0
- package/dist/server/supervisor.d.ts +89 -0
- package/dist/server/supervisor.js +231 -0
- package/dist/server/system.d.ts +198 -0
- package/dist/server/system.js +480 -0
- package/dist/server/task-check.d.ts +45 -0
- package/dist/server/task-check.js +71 -0
- package/dist/server/task-transitions.d.ts +51 -0
- package/dist/server/task-transitions.js +78 -0
- package/dist/server/task-watchdog.d.ts +158 -0
- package/dist/server/task-watchdog.js +681 -0
- package/dist/server/test-run.d.ts +28 -0
- package/dist/server/test-run.js +81 -0
- package/dist/server/watcher.d.ts +21 -0
- package/dist/server/watcher.js +194 -0
- package/dist/server/write.d.ts +94 -0
- package/dist/server/write.js +345 -0
- package/dist/ui/assets/AgentsView-CF4wRvHh.css +1 -0
- package/dist/ui/assets/AgentsView-D-myL-kA.js +1 -0
- package/dist/ui/assets/ContextView-D3TC-RSp.css +1 -0
- package/dist/ui/assets/ContextView-DsOqAdyE.js +4 -0
- package/dist/ui/assets/DashboardView-BOhsIJqO.js +1 -0
- package/dist/ui/assets/DashboardView-Ck9o0RP2.css +1 -0
- package/dist/ui/assets/DeploymentsView--iIUVfEH.css +1 -0
- package/dist/ui/assets/DeploymentsView-DXu_FYR2.js +1 -0
- package/dist/ui/assets/InputsView-24HVbVPk.js +1 -0
- package/dist/ui/assets/InputsView-BLb7GHtx.css +1 -0
- package/dist/ui/assets/LoginView-6QlGg6cx.css +1 -0
- package/dist/ui/assets/LoginView-BqupI0Lt.js +1 -0
- package/dist/ui/assets/ReleasesView-BEG1A0YH.css +1 -0
- package/dist/ui/assets/ReleasesView-CAgMuxpe.js +3 -0
- package/dist/ui/assets/SettingsView-CA-NYSMK.css +1 -0
- package/dist/ui/assets/SettingsView-O3iZSnjg.js +3 -0
- package/dist/ui/assets/WorkView-D8Tl6qwy.js +1 -0
- package/dist/ui/assets/WorkView-DMYL2s3W.css +1 -0
- package/dist/ui/assets/abnfDiagram-VCTEODGH-J5lkSnEE.js +1 -0
- package/dist/ui/assets/arc-BHZnmhhM.js +1 -0
- package/dist/ui/assets/architecture-7GRP2DOG-pLdFt-xS.js +1 -0
- package/dist/ui/assets/architectureDiagram-5GKGNRK7-C3k89uKX.js +36 -0
- package/dist/ui/assets/array-BifhSqXX.js +1 -0
- package/dist/ui/assets/blockDiagram-I7D4REHJ-AWNoBzop.js +129 -0
- package/dist/ui/assets/button-lbikqSph.js +1 -0
- package/dist/ui/assets/c4Diagram-7LVT6UL2-DKG1D5gd.js +38 -0
- package/dist/ui/assets/channel-gA4qBv3A.js +1 -0
- package/dist/ui/assets/chunk-2Q5K7J3B-C1jixKkw.js +1 -0
- package/dist/ui/assets/chunk-4HAMMTFA-D1YR8lF_.js +62 -0
- package/dist/ui/assets/chunk-5VM5RSS4-ZNzvKenW.js +15 -0
- package/dist/ui/assets/chunk-75Z2AOVW-5GuBxfsL.js +2 -0
- package/dist/ui/assets/chunk-DU6HZSFF-DfdNNqwa.js +127 -0
- package/dist/ui/assets/chunk-F27PBJKO-BPZzinQ4.js +1 -0
- package/dist/ui/assets/chunk-FOHPRMQF-l2z4K2jD.js +161 -0
- package/dist/ui/assets/chunk-GMAD6QVW-DGWvSKj5.js +72 -0
- package/dist/ui/assets/chunk-GVQU2GXP-D6Aw75wJ.js +1 -0
- package/dist/ui/assets/chunk-IMKFNOWR-B8mMBKCW.js +231 -0
- package/dist/ui/assets/chunk-JWPE2WC7-DVXcaiue.js +1 -0
- package/dist/ui/assets/chunk-L3NEJ4N5-D5OJuXYE.js +1 -0
- package/dist/ui/assets/chunk-OSK3NFVY-B7r9w2UW.js +10 -0
- package/dist/ui/assets/chunk-P2QGCYS3-C74X4AQX.js +1 -0
- package/dist/ui/assets/chunk-POPQ4Y6H-C5_u1gUS.js +1 -0
- package/dist/ui/assets/chunk-PWAF6VOD-DfoTLtxn.js +1 -0
- package/dist/ui/assets/chunk-SHT3W25Y-D40m2eqz.js +168 -0
- package/dist/ui/assets/chunk-SVP7TREG-euxi0Kap.js +88 -0
- package/dist/ui/assets/chunk-TICWLB2K-fkdxngzN.js +206 -0
- package/dist/ui/assets/chunk-XXDRQBXY-DFBUG-OT.js +1 -0
- package/dist/ui/assets/chunk-Y2CYZVJY-DsF7k-Jl.js +1 -0
- package/dist/ui/assets/classDiagram-ZZMXUADV-BzW8bq2P.js +1 -0
- package/dist/ui/assets/classDiagram-v2-VYDZK3BY-BzW8bq2P.js +1 -0
- package/dist/ui/assets/cose-bilkent-JH36ORCC-BTcgMon1.js +1 -0
- package/dist/ui/assets/cynefin-OW5HDTMX-B5m_COsS.js +1 -0
- package/dist/ui/assets/cynefinDiagram-5FMLGOSQ-C-AG6CGe.js +62 -0
- package/dist/ui/assets/cytoscape.esm-FBntH-9t.js +321 -0
- package/dist/ui/assets/dagre-GXQ25YYZ-DDfDnp8L.js +4 -0
- package/dist/ui/assets/dagre-PrKaheQc.js +1 -0
- package/dist/ui/assets/defaultLocale-BFoDCU3G.js +1 -0
- package/dist/ui/assets/diagram-S7CK7UJ4-DjQZAlEY.js +30 -0
- package/dist/ui/assets/diagram-UQ7AKVKN-DAZ9yp1B.js +41 -0
- package/dist/ui/assets/diagram-VSXAHHWV-BWepUKJD.js +3 -0
- package/dist/ui/assets/diagram-VX7I27RA-EcV1J81b.js +24 -0
- package/dist/ui/assets/diagram-Z3DM3KII-Dbz7XU_K.js +24 -0
- package/dist/ui/assets/dist-DTg6UBE_.js +1 -0
- package/dist/ui/assets/ebnfDiagram-PWID7BFC-BUyQw8Mm.js +1 -0
- package/dist/ui/assets/erDiagram-RLTQ6QDP-WVf8ukKt.js +99 -0
- package/dist/ui/assets/eventmodeling-NTZA5JFV-DHqyLOAn.js +1 -0
- package/dist/ui/assets/flowDiagram-HODETNUW-CYk_ze5K.js +1 -0
- package/dist/ui/assets/ganttDiagram-EL5Y4UJY-rB4MZuB-.js +292 -0
- package/dist/ui/assets/gitGraph-4MIJSDKK-BOTWM8dY.js +1 -0
- package/dist/ui/assets/gitGraphDiagram-WWUBYQGX-eYscuhtP.js +106 -0
- package/dist/ui/assets/graphlib-DS17s2tU.js +1 -0
- package/dist/ui/assets/index-BACBfnga.css +1 -0
- package/dist/ui/assets/index-BOQ-ust6.js +26 -0
- package/dist/ui/assets/info-A6RAGUB7-_67wOORe.js +1 -0
- package/dist/ui/assets/infoDiagram-27XIBGKW-XVXbddTA.js +2 -0
- package/dist/ui/assets/init-C-OQMol4.js +1 -0
- package/dist/ui/assets/ishikawaDiagram-5VMMS53U-ClLNiEru.js +70 -0
- package/dist/ui/assets/journeyDiagram-3NMN7TZE-NRawjR6i.js +139 -0
- package/dist/ui/assets/kanban-definition-UXKFOSKX-BgSzBfIx.js +89 -0
- package/dist/ui/assets/katex-DolUETbr.js +257 -0
- package/dist/ui/assets/line-KAyfuul4.js +1 -0
- package/dist/ui/assets/linear-cCCPOpu7.js +1 -0
- package/dist/ui/assets/mermaid-parser.core-CgXZk1iN.js +7 -0
- package/dist/ui/assets/mermaid.core-BUUkChHW.js +44 -0
- package/dist/ui/assets/mindmap-definition-YA3MSWOX-Cpn7vW4r.js +96 -0
- package/dist/ui/assets/ordinal-BDEzSJ7C.js +1 -0
- package/dist/ui/assets/packet-AYTQ26CC-0lZwM-d6.js +1 -0
- package/dist/ui/assets/path-COt_16Va.js +1 -0
- package/dist/ui/assets/pegDiagram-XKGWAZYB-impy0inn.js +1 -0
- package/dist/ui/assets/pie-WAS4IAKB-CqfdGHYg.js +1 -0
- package/dist/ui/assets/pieDiagram-E7YTZNPT-CxjL4I1h.js +39 -0
- package/dist/ui/assets/quadrantDiagram-AXDQQJYC-Cjd1QMIe.js +7 -0
- package/dist/ui/assets/radar-RG4KPBEZ-CRkQIwiv.js +1 -0
- package/dist/ui/assets/railroad-74A4TZTK-i67XSWTB.js +1 -0
- package/dist/ui/assets/railroad-abnf-HS5TGJTU-Cv9m5aS3.js +1 -0
- package/dist/ui/assets/railroad-ebnf-LZEXJU2U-C5ZIn-Jh.js +1 -0
- package/dist/ui/assets/railroad-peg-WCYAUIDC-CGra9Oq-.js +1 -0
- package/dist/ui/assets/railroadDiagram-O6MQD6OU-_pK0qyaY.js +1 -0
- package/dist/ui/assets/releases-B0WNV-SY.js +1 -0
- package/dist/ui/assets/repoos-cto-square.webp +0 -0
- package/dist/ui/assets/repoos-helpful-pm-square.webp +0 -0
- package/dist/ui/assets/repoos-orchestrator-square.webp +0 -0
- package/dist/ui/assets/repoos-performance-agent-square.webp +0 -0
- package/dist/ui/assets/repoos-ross-from-friends-square.webp +0 -0
- package/dist/ui/assets/repoos-tech-debt-agent-square.webp +0 -0
- package/dist/ui/assets/requirementDiagram-BXWQKSXE-CRrFX-yw.js +84 -0
- package/dist/ui/assets/rough.esm-By172zw-.js +1 -0
- package/dist/ui/assets/sankeyDiagram-P5KCCOFB-X_xFRj9E.js +40 -0
- package/dist/ui/assets/sequenceDiagram-WJ2MYXX4-Byb49lKf.js +162 -0
- package/dist/ui/assets/sizeCapture-INFHLROL-B0uUizjq.js +1 -0
- package/dist/ui/assets/src-BH-TyZbA.js +1 -0
- package/dist/ui/assets/stateDiagram-D77RDMKH-CzaPrqYF.js +1 -0
- package/dist/ui/assets/stateDiagram-v2-MP3YSRHH-BCXckz7K.js +1 -0
- package/dist/ui/assets/swimlanes-42K2YHIH-Cx_1lnqr.js +1 -0
- package/dist/ui/assets/swimlanesDiagram-VR7AAH4N-54qsKi6A.js +8 -0
- package/dist/ui/assets/timeline-definition-24CTP7MA-B1IbZPQ-.js +120 -0
- package/dist/ui/assets/treeView-Q6P3EWNA-DFWhVE3d.js +1 -0
- package/dist/ui/assets/treemap-WGGIJYW6-BmbYPThn.js +1 -0
- package/dist/ui/assets/vendor-ui-DPm7zLxi.js +3 -0
- package/dist/ui/assets/vendor-vue-CobgyEJp.js +1 -0
- package/dist/ui/assets/vennDiagram-4TSXK5OY-Ae2AtRpt.js +34 -0
- package/dist/ui/assets/wardley-WFR3VGLG-BtD4X4tL.js +1 -0
- package/dist/ui/assets/wardleyDiagram-VM6X3IG4-B3YN2fTd.js +78 -0
- package/dist/ui/assets/xychartDiagram-S5SC5T6Z-CIzJfMKA.js +7 -0
- package/dist/ui/favicon.svg +11 -0
- package/dist/ui/index.html +49 -0
- package/dist/ui/sw.js +48 -0
- package/package.json +91 -0
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import type { DeploymentConfig, RepoOSConfig } from "../core/types.js";
|
|
2
|
+
export interface DeploymentRow {
|
|
3
|
+
name: string;
|
|
4
|
+
/** Groups this row with others of the same service across branches (#0365). Defaults to `name`. */
|
|
5
|
+
service: string;
|
|
6
|
+
branch: string;
|
|
7
|
+
/** Plain provider label from config (e.g. "cloudflare-workers"). */
|
|
8
|
+
provider: string | null;
|
|
9
|
+
/** The live URL — the row's primary link. */
|
|
10
|
+
url: string | null;
|
|
11
|
+
/** Optional provider-dashboard URL, also rendered as a link when present. */
|
|
12
|
+
dashboardUrl: string | null;
|
|
13
|
+
/** Subdirectory the freshness lookup was scoped to, when configured. */
|
|
14
|
+
subdir: string | null;
|
|
15
|
+
/** ISO timestamp of the last commit on the branch touching the subdir. */
|
|
16
|
+
lastPushAt: string | null;
|
|
17
|
+
/** Short SHA of that commit. */
|
|
18
|
+
lastPushSha: string | null;
|
|
19
|
+
/**
|
|
20
|
+
* Subdir-scoped version of the branch's `mainSync` ahead/behind counts
|
|
21
|
+
* (#0367): of the commits separating origin/<branch> from local main, how
|
|
22
|
+
* many actually touch THIS row's subdir. Null when no `subdir` is
|
|
23
|
+
* configured (the branch-level count already is this row's scope) or when
|
|
24
|
+
* either ref couldn't be resolved.
|
|
25
|
+
*/
|
|
26
|
+
changesVsMain: {
|
|
27
|
+
aheadOfMain: number;
|
|
28
|
+
behindMain: number;
|
|
29
|
+
} | null;
|
|
30
|
+
}
|
|
31
|
+
export interface DeploymentBranch {
|
|
32
|
+
branch: string;
|
|
33
|
+
/** Commits on local <branch> missing from origin/<branch>; null when the remote ref is unknown. */
|
|
34
|
+
ahead: number | null;
|
|
35
|
+
/** Commits on origin/<branch> missing from local <branch>. */
|
|
36
|
+
behind: number | null;
|
|
37
|
+
/** True when origin/<branch> resolves at all. */
|
|
38
|
+
hasOrigin: boolean;
|
|
39
|
+
/** True when the local branch exists (a configured-but-unborn branch can't deploy). */
|
|
40
|
+
localExists: boolean;
|
|
41
|
+
/**
|
|
42
|
+
* When set, "Deploy" fast-forwards this branch to this configured branch
|
|
43
|
+
* before pushing (e.g. prod ← main). Recomputed from actual git ancestry —
|
|
44
|
+
* a branch equal to its neighbor (just deployed) drops back to a plain push.
|
|
45
|
+
*/
|
|
46
|
+
ffFrom: string | null;
|
|
47
|
+
/**
|
|
48
|
+
* Where the DEPLOYED ref (`origin/<branch>`) stands relative to the LOCAL
|
|
49
|
+
* `main` branch (#0365) — the headline sync metric, replacing the raw
|
|
50
|
+
* ahead/behind-vs-own-origin chips. "same": origin/<branch> == local main.
|
|
51
|
+
* "behind": origin/<branch> is a strict ancestor of local main (deploying
|
|
52
|
+
* would fast-forward it up to date — see the `ffFrom` badge). "ahead":
|
|
53
|
+
* local main is a strict ancestor of origin/<branch> (the deployed ref has
|
|
54
|
+
* commits local main lacks — unusual, e.g. a hotfix landed directly on this
|
|
55
|
+
* branch). "diverged": neither is an ancestor of the other. "unknown": no
|
|
56
|
+
* origin/<branch>, or no local `main` ref to compare against.
|
|
57
|
+
*/
|
|
58
|
+
mainSync: {
|
|
59
|
+
state: "same" | "behind" | "ahead" | "diverged" | "unknown";
|
|
60
|
+
/** Commits on origin/<branch> missing from local main (0 unless "ahead"/"diverged"). */
|
|
61
|
+
aheadOfMain: number;
|
|
62
|
+
/** Commits on local main missing from origin/<branch> (0 unless "behind"/"diverged"). */
|
|
63
|
+
behindMain: number;
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
export interface DeploymentsStatus {
|
|
67
|
+
enabled: boolean;
|
|
68
|
+
rows: DeploymentRow[];
|
|
69
|
+
branches: DeploymentBranch[];
|
|
70
|
+
/** Main checkout the git state was read from (a preview worktree resolves here). */
|
|
71
|
+
root: string | null;
|
|
72
|
+
/** Uncommitted changes in the main checkout — blocks every deploy. */
|
|
73
|
+
dirty: boolean;
|
|
74
|
+
/** Branch the main checkout is currently on (informational). */
|
|
75
|
+
currentBranch: string | null;
|
|
76
|
+
}
|
|
77
|
+
export interface DeployResult {
|
|
78
|
+
ok: boolean;
|
|
79
|
+
output: string;
|
|
80
|
+
}
|
|
81
|
+
interface CommandResult {
|
|
82
|
+
code: number | null;
|
|
83
|
+
stdout: string;
|
|
84
|
+
stderr: string;
|
|
85
|
+
error?: Error;
|
|
86
|
+
}
|
|
87
|
+
export type DeployCommandRunner = (command: string, args: string[], cwd: string, timeout?: number) => Promise<CommandResult>;
|
|
88
|
+
export declare function captureOutput(stdout: string, stderr: string): string;
|
|
89
|
+
export declare function configuredDeployments(config: RepoOSConfig): DeploymentConfig[];
|
|
90
|
+
/**
|
|
91
|
+
* The checkout deploys run against: the MAIN checkout, even when the server is
|
|
92
|
+
* serving a task preview from a linked worktree — branch refs are shared
|
|
93
|
+
* across worktrees, but dirty-state and branch-health belong to the checkout
|
|
94
|
+
* the user actually manages (same resolution rule as board reads, #0068).
|
|
95
|
+
*/
|
|
96
|
+
export declare function deployRoot(config: RepoOSConfig): string;
|
|
97
|
+
/** Distinct configured branches, in first-appearance order. */
|
|
98
|
+
export declare function deploymentBranches(rows: DeploymentConfig[]): string[];
|
|
99
|
+
/**
|
|
100
|
+
* Nearest configured branch that this branch can be fast-forwarded to: a
|
|
101
|
+
* strict ancestor relationship (branch behind, never diverged), closest first.
|
|
102
|
+
* Derived entirely from actual topology + config — no main/prod constants, so
|
|
103
|
+
* a repo with differently named (or more than two) branches gets the right
|
|
104
|
+
* pairing. Returns null when the branch should just be pushed as-is.
|
|
105
|
+
*/
|
|
106
|
+
export declare function fastForwardSource(exec: DeployCommandRunner, branch: string, branches: string[], cwd: string): Promise<string | null>;
|
|
107
|
+
export declare function getDeploymentsStatus(config: RepoOSConfig, exec?: DeployCommandRunner): Promise<DeploymentsStatus>;
|
|
108
|
+
/**
|
|
109
|
+
* Deploy one branch: a plain `git push origin <branch>`, or — when the branch
|
|
110
|
+
* is strictly behind another configured branch (prod behind main) — a
|
|
111
|
+
* fast-forward of the local ref first, then the push. The fast-forward uses
|
|
112
|
+
* `git merge --ff-only <src>` when the checkout is ON that branch (git refuses
|
|
113
|
+
* to fetch into a checked-out branch) and `git fetch . <src>:<branch>` — which
|
|
114
|
+
* git itself refuses on non-fast-forward — otherwise. There is no code path
|
|
115
|
+
* that pushes with --force: every non-fast-forward case fails loudly with
|
|
116
|
+
* git's own refusal in the output.
|
|
117
|
+
*/
|
|
118
|
+
export declare function deployBranch(config: RepoOSConfig, branch: string, exec?: DeployCommandRunner): Promise<DeployResult>;
|
|
119
|
+
export {};
|
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deployments status + push actions (#0340).
|
|
3
|
+
*
|
|
4
|
+
* Where `release.ts` models one versioned artifact, this module models the
|
|
5
|
+
* grid of configured [[deployments]] rows (service × branch) and the shared
|
|
6
|
+
* branch state behind them. Everything here is plain git against the already-
|
|
7
|
+
* configured `origin` remote — no provider API calls, no new credentials.
|
|
8
|
+
* Cloudflare (or any git-connected host) rebuilds from the push; whether that
|
|
9
|
+
* build succeeded is deliberately out of scope for v1.
|
|
10
|
+
*/
|
|
11
|
+
import { spawn } from "node:child_process";
|
|
12
|
+
import { mainCheckoutRoot } from "../core/config.js";
|
|
13
|
+
export function captureOutput(stdout, stderr) {
|
|
14
|
+
return [stdout.trim(), stderr.trim()].filter(Boolean).join("\n");
|
|
15
|
+
}
|
|
16
|
+
const run = (command, args, cwd, timeout = 30_000) => new Promise((resolveRun) => {
|
|
17
|
+
const child = spawn(command, args, { cwd, env: process.env });
|
|
18
|
+
let stdout = "";
|
|
19
|
+
let stderr = "";
|
|
20
|
+
let settled = false;
|
|
21
|
+
const finish = (code, error) => {
|
|
22
|
+
if (settled)
|
|
23
|
+
return;
|
|
24
|
+
settled = true;
|
|
25
|
+
clearTimeout(timer);
|
|
26
|
+
resolveRun({ code, stdout, stderr, error });
|
|
27
|
+
};
|
|
28
|
+
child.stdout.on("data", (data) => (stdout += data.toString("utf8")));
|
|
29
|
+
child.stderr.on("data", (data) => (stderr += data.toString("utf8")));
|
|
30
|
+
child.on("error", (error) => finish(null, error));
|
|
31
|
+
child.on("close", (code) => finish(code));
|
|
32
|
+
const timer = setTimeout(() => child.kill("SIGKILL"), timeout);
|
|
33
|
+
});
|
|
34
|
+
export function configuredDeployments(config) {
|
|
35
|
+
return Array.isArray(config.deployments) ? config.deployments : [];
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* The checkout deploys run against: the MAIN checkout, even when the server is
|
|
39
|
+
* serving a task preview from a linked worktree — branch refs are shared
|
|
40
|
+
* across worktrees, but dirty-state and branch-health belong to the checkout
|
|
41
|
+
* the user actually manages (same resolution rule as board reads, #0068).
|
|
42
|
+
*/
|
|
43
|
+
export function deployRoot(config) {
|
|
44
|
+
return mainCheckoutRoot(config.root) ?? config.root;
|
|
45
|
+
}
|
|
46
|
+
/** Distinct configured branches, in first-appearance order. */
|
|
47
|
+
export function deploymentBranches(rows) {
|
|
48
|
+
const seen = [];
|
|
49
|
+
for (const row of rows)
|
|
50
|
+
if (!seen.includes(row.branch))
|
|
51
|
+
seen.push(row.branch);
|
|
52
|
+
return seen;
|
|
53
|
+
}
|
|
54
|
+
function cmd(exec, args, cwd, timeout) {
|
|
55
|
+
return exec("git", args, cwd, timeout);
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* The local branch every deployment target's sync status is measured against
|
|
59
|
+
* (#0365) — this repo's (and most repos') trunk, matching the same "main"
|
|
60
|
+
* assumption `release.ts` already makes for cutting a release. Not sourced
|
|
61
|
+
* from config: making this configurable is real scope this task explicitly
|
|
62
|
+
* deferred (the user accepted the simplification of comparing every branch,
|
|
63
|
+
* including prod, against local main directly rather than a stricter
|
|
64
|
+
* prod-vs-origin/main chain).
|
|
65
|
+
*/
|
|
66
|
+
const SYNC_REFERENCE_BRANCH = "main";
|
|
67
|
+
/**
|
|
68
|
+
* Where `origin/<branch>` (the deployed ref) stands relative to the local
|
|
69
|
+
* `main` branch (#0365) — see `DeploymentBranch.mainSync`.
|
|
70
|
+
*/
|
|
71
|
+
async function branchVsMain(exec, branch, cwd) {
|
|
72
|
+
const unknown = { state: "unknown", aheadOfMain: 0, behindMain: 0 };
|
|
73
|
+
const origin = `origin/${branch}`;
|
|
74
|
+
const [originRef, mainRef] = await Promise.all([
|
|
75
|
+
cmd(exec, ["rev-parse", "--verify", "--quiet", origin], cwd),
|
|
76
|
+
cmd(exec, ["rev-parse", "--verify", "--quiet", SYNC_REFERENCE_BRANCH], cwd),
|
|
77
|
+
]);
|
|
78
|
+
if (originRef.code !== 0 || mainRef.code !== 0)
|
|
79
|
+
return unknown;
|
|
80
|
+
const [aheadRes, behindRes] = await Promise.all([
|
|
81
|
+
cmd(exec, ["rev-list", "--count", `${SYNC_REFERENCE_BRANCH}..${origin}`], cwd),
|
|
82
|
+
cmd(exec, ["rev-list", "--count", `${origin}..${SYNC_REFERENCE_BRANCH}`], cwd),
|
|
83
|
+
]);
|
|
84
|
+
if (aheadRes.code !== 0 || behindRes.code !== 0)
|
|
85
|
+
return unknown;
|
|
86
|
+
const aheadOfMain = Number(aheadRes.stdout.trim()) || 0;
|
|
87
|
+
const behindMain = Number(behindRes.stdout.trim()) || 0;
|
|
88
|
+
const state = aheadOfMain === 0 && behindMain === 0
|
|
89
|
+
? "same"
|
|
90
|
+
: aheadOfMain === 0
|
|
91
|
+
? "behind"
|
|
92
|
+
: behindMain === 0
|
|
93
|
+
? "ahead"
|
|
94
|
+
: "diverged";
|
|
95
|
+
return { state, aheadOfMain, behindMain };
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Same question as `branchVsMain`, scoped to one service's `subdir` (#0367):
|
|
99
|
+
* of the commits separating `origin/<branch>` from local `main`, how many
|
|
100
|
+
* actually touch this subtree? The branch-level count is honestly whole-
|
|
101
|
+
* branch (a repo doing unrelated work between deploys inflates it), which
|
|
102
|
+
* reads as misleading next to a specific service — this answers "does any of
|
|
103
|
+
* that distance actually apply to what I'm looking at."
|
|
104
|
+
*
|
|
105
|
+
* Returns null when there's nothing to scope (no subdir configured — the
|
|
106
|
+
* branch-level count already IS this row's scope) or when either ref can't
|
|
107
|
+
* be resolved (mirrors `branchVsMain`'s "unknown" case without a redundant
|
|
108
|
+
* ref-existence check, since a branch's own `branchVsMain` result already
|
|
109
|
+
* covers that call site).
|
|
110
|
+
*/
|
|
111
|
+
async function subdirVsMain(exec, branch, subdir, cwd) {
|
|
112
|
+
if (!subdir)
|
|
113
|
+
return null;
|
|
114
|
+
const origin = `origin/${branch}`;
|
|
115
|
+
const [aheadRes, behindRes] = await Promise.all([
|
|
116
|
+
cmd(exec, ["rev-list", "--count", `${SYNC_REFERENCE_BRANCH}..${origin}`, "--", subdir], cwd),
|
|
117
|
+
cmd(exec, ["rev-list", "--count", `${origin}..${SYNC_REFERENCE_BRANCH}`, "--", subdir], cwd),
|
|
118
|
+
]);
|
|
119
|
+
if (aheadRes.code !== 0 || behindRes.code !== 0)
|
|
120
|
+
return null;
|
|
121
|
+
return {
|
|
122
|
+
aheadOfMain: Number(aheadRes.stdout.trim()) || 0,
|
|
123
|
+
behindMain: Number(behindRes.stdout.trim()) || 0,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Nearest configured branch that this branch can be fast-forwarded to: a
|
|
128
|
+
* strict ancestor relationship (branch behind, never diverged), closest first.
|
|
129
|
+
* Derived entirely from actual topology + config — no main/prod constants, so
|
|
130
|
+
* a repo with differently named (or more than two) branches gets the right
|
|
131
|
+
* pairing. Returns null when the branch should just be pushed as-is.
|
|
132
|
+
*/
|
|
133
|
+
export async function fastForwardSource(exec, branch, branches, cwd) {
|
|
134
|
+
const others = branches.filter((b) => b !== branch);
|
|
135
|
+
if (!others.length)
|
|
136
|
+
return null;
|
|
137
|
+
const sha = await cmd(exec, ["rev-parse", branch], cwd);
|
|
138
|
+
if (sha.code !== 0)
|
|
139
|
+
return null;
|
|
140
|
+
const candidates = [];
|
|
141
|
+
for (const other of others) {
|
|
142
|
+
const otherSha = await cmd(exec, ["rev-parse", other], cwd);
|
|
143
|
+
if (otherSha.code !== 0)
|
|
144
|
+
continue;
|
|
145
|
+
if (otherSha.stdout.trim() === sha.stdout.trim())
|
|
146
|
+
continue; // equal ≠ strict ancestor
|
|
147
|
+
const isAncestor = await cmd(exec, ["merge-base", "--is-ancestor", branch, other], cwd);
|
|
148
|
+
if (isAncestor.code !== 0)
|
|
149
|
+
continue;
|
|
150
|
+
const distance = await cmd(exec, ["rev-list", "--count", `${branch}..${other}`], cwd);
|
|
151
|
+
if (distance.code !== 0)
|
|
152
|
+
continue;
|
|
153
|
+
candidates.push({ branch: other, distance: Number(distance.stdout.trim()) || 0 });
|
|
154
|
+
}
|
|
155
|
+
candidates.sort((a, b) => a.distance - b.distance);
|
|
156
|
+
return candidates[0]?.branch ?? null;
|
|
157
|
+
}
|
|
158
|
+
async function branchSummary(exec, branch, branches, cwd) {
|
|
159
|
+
const origin = `origin/${branch}`;
|
|
160
|
+
const [originRef, localRef] = await Promise.all([
|
|
161
|
+
cmd(exec, ["rev-parse", "--verify", "--quiet", origin], cwd),
|
|
162
|
+
cmd(exec, ["rev-parse", "--verify", "--quiet", branch], cwd),
|
|
163
|
+
]);
|
|
164
|
+
const hasOrigin = originRef.code === 0;
|
|
165
|
+
const localExists = localRef.code === 0;
|
|
166
|
+
let ahead = null;
|
|
167
|
+
let behind = null;
|
|
168
|
+
if (hasOrigin) {
|
|
169
|
+
const [aheadRes, behindRes] = await Promise.all([
|
|
170
|
+
cmd(exec, ["rev-list", "--count", `${origin}..${branch}`], cwd),
|
|
171
|
+
cmd(exec, ["rev-list", "--count", `${branch}..${origin}`], cwd),
|
|
172
|
+
]);
|
|
173
|
+
if (aheadRes.code === 0)
|
|
174
|
+
ahead = Number(aheadRes.stdout.trim()) || 0;
|
|
175
|
+
if (behindRes.code === 0)
|
|
176
|
+
behind = Number(behindRes.stdout.trim()) || 0;
|
|
177
|
+
}
|
|
178
|
+
const [ffFrom, mainSync] = await Promise.all([
|
|
179
|
+
fastForwardSource(exec, branch, branches, cwd),
|
|
180
|
+
branchVsMain(exec, branch, cwd),
|
|
181
|
+
]);
|
|
182
|
+
return { branch, ahead, behind, hasOrigin, localExists, ffFrom, mainSync };
|
|
183
|
+
}
|
|
184
|
+
async function rowFreshness(exec, row, cwd) {
|
|
185
|
+
// The revision goes BEFORE `--`: `git log -1 <branch> -- <subdir>`. Appending
|
|
186
|
+
// the branch after `--` would read it as a path, silently degrading to HEAD.
|
|
187
|
+
const args = ["log", "-1", "--format=%cI%n%h", row.branch];
|
|
188
|
+
if (row.subdir)
|
|
189
|
+
args.push("--", row.subdir);
|
|
190
|
+
const [res, changesVsMain] = await Promise.all([
|
|
191
|
+
cmd(exec, args, cwd),
|
|
192
|
+
subdirVsMain(exec, row.branch, row.subdir, cwd),
|
|
193
|
+
]);
|
|
194
|
+
if (res.code !== 0)
|
|
195
|
+
return { lastPushAt: null, lastPushSha: null, changesVsMain };
|
|
196
|
+
const [at, sha] = res.stdout.trim().split("\n");
|
|
197
|
+
return { lastPushAt: at?.trim() || null, lastPushSha: sha?.trim() || null, changesVsMain };
|
|
198
|
+
}
|
|
199
|
+
export async function getDeploymentsStatus(config, exec = run) {
|
|
200
|
+
const rows = configuredDeployments(config);
|
|
201
|
+
if (!rows.length) {
|
|
202
|
+
return {
|
|
203
|
+
enabled: false,
|
|
204
|
+
rows: [],
|
|
205
|
+
branches: [],
|
|
206
|
+
root: null,
|
|
207
|
+
dirty: false,
|
|
208
|
+
currentBranch: null,
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
const cwd = deployRoot(config);
|
|
212
|
+
const branches = deploymentBranches(rows);
|
|
213
|
+
const [dirtRes, currentRes, ...summaries] = await Promise.all([
|
|
214
|
+
cmd(exec, ["status", "--porcelain"], cwd),
|
|
215
|
+
cmd(exec, ["branch", "--show-current"], cwd),
|
|
216
|
+
...branches.map((b) => branchSummary(exec, b, branches, cwd)),
|
|
217
|
+
]);
|
|
218
|
+
// Freshness (+ subdir-scoped sync, #0367) is cached per (branch, subdir) —
|
|
219
|
+
// two rows sharing both run one set of git calls, not two.
|
|
220
|
+
const freshness = new Map();
|
|
221
|
+
const resolvedRows = await Promise.all(rows.map(async (row) => {
|
|
222
|
+
const key = `${row.branch}\u0000${row.subdir ?? ""}`;
|
|
223
|
+
let pending = freshness.get(key);
|
|
224
|
+
if (!pending) {
|
|
225
|
+
pending = rowFreshness(exec, row, cwd);
|
|
226
|
+
freshness.set(key, pending);
|
|
227
|
+
}
|
|
228
|
+
const f = await pending;
|
|
229
|
+
return {
|
|
230
|
+
name: row.name,
|
|
231
|
+
service: row.service ?? row.name,
|
|
232
|
+
branch: row.branch,
|
|
233
|
+
provider: row.provider ?? null,
|
|
234
|
+
url: row.url ?? null,
|
|
235
|
+
dashboardUrl: row.dashboardUrl ?? null,
|
|
236
|
+
subdir: row.subdir ?? null,
|
|
237
|
+
lastPushAt: f.lastPushAt,
|
|
238
|
+
lastPushSha: f.lastPushSha,
|
|
239
|
+
changesVsMain: f.changesVsMain,
|
|
240
|
+
};
|
|
241
|
+
}));
|
|
242
|
+
return {
|
|
243
|
+
enabled: true,
|
|
244
|
+
rows: resolvedRows,
|
|
245
|
+
branches: summaries,
|
|
246
|
+
root: cwd,
|
|
247
|
+
dirty: dirtRes.code === 0 ? dirtRes.stdout.trim() !== "" : true,
|
|
248
|
+
currentBranch: currentRes.code === 0 ? currentRes.stdout.trim() || null : null,
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* Deploy one branch: a plain `git push origin <branch>`, or — when the branch
|
|
253
|
+
* is strictly behind another configured branch (prod behind main) — a
|
|
254
|
+
* fast-forward of the local ref first, then the push. The fast-forward uses
|
|
255
|
+
* `git merge --ff-only <src>` when the checkout is ON that branch (git refuses
|
|
256
|
+
* to fetch into a checked-out branch) and `git fetch . <src>:<branch>` — which
|
|
257
|
+
* git itself refuses on non-fast-forward — otherwise. There is no code path
|
|
258
|
+
* that pushes with --force: every non-fast-forward case fails loudly with
|
|
259
|
+
* git's own refusal in the output.
|
|
260
|
+
*/
|
|
261
|
+
export async function deployBranch(config, branch, exec = run) {
|
|
262
|
+
const rows = configuredDeployments(config);
|
|
263
|
+
if (!rows.length)
|
|
264
|
+
return { ok: false, output: "Deployments are not configured for this repository." };
|
|
265
|
+
if (!deploymentBranches(rows).includes(branch))
|
|
266
|
+
return { ok: false, output: `"${branch}" is not a configured deployment branch.` };
|
|
267
|
+
const cwd = deployRoot(config);
|
|
268
|
+
const dirty = await cmd(exec, ["status", "--porcelain"], cwd);
|
|
269
|
+
if (dirty.code !== 0)
|
|
270
|
+
return { ok: false, output: "Could not read the git status of the checkout." };
|
|
271
|
+
if (dirty.stdout.trim() !== "")
|
|
272
|
+
return {
|
|
273
|
+
ok: false,
|
|
274
|
+
output: "The checkout has uncommitted changes — commit or stash them before deploying. " +
|
|
275
|
+
"This is the same dirty-tree guard `just release` uses.",
|
|
276
|
+
};
|
|
277
|
+
const local = await cmd(exec, ["rev-parse", "--verify", "--quiet", branch], cwd);
|
|
278
|
+
if (local.code !== 0)
|
|
279
|
+
return {
|
|
280
|
+
ok: false,
|
|
281
|
+
output: `No local branch "${branch}" exists in this checkout — fetch or create it before deploying ` +
|
|
282
|
+
`(e.g. git fetch origin "${branch}:" then git checkout "${branch}").`,
|
|
283
|
+
};
|
|
284
|
+
const branches = deploymentBranches(rows);
|
|
285
|
+
const src = await fastForwardSource(exec, branch, branches, cwd);
|
|
286
|
+
if (src) {
|
|
287
|
+
// On the target branch, `fetch . src:branch` refuses ("refusing to fetch
|
|
288
|
+
// into branch … checked out"); merge --ff-only is the equivalent ref move
|
|
289
|
+
// there, and equally refuses a non-fast-forward.
|
|
290
|
+
const current = await cmd(exec, ["branch", "--show-current"], cwd);
|
|
291
|
+
const onBranch = current.code === 0 && current.stdout.trim() === branch;
|
|
292
|
+
const ff = onBranch
|
|
293
|
+
? await cmd(exec, ["merge", "--ff-only", src], cwd)
|
|
294
|
+
: await cmd(exec, ["fetch", ".", `${src}:${branch}`], cwd);
|
|
295
|
+
if (ff.code !== 0) {
|
|
296
|
+
return {
|
|
297
|
+
ok: false,
|
|
298
|
+
output: `Could not fast-forward ${branch} to ${src} — the branches have diverged, ` +
|
|
299
|
+
`so the deploy was refused rather than force-pushed.\n` +
|
|
300
|
+
(captureOutput(ff.stdout, ff.stderr) || ff.error?.message || ""),
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
const pushed = await cmd(exec, ["push", "origin", branch], cwd, 120_000);
|
|
304
|
+
if (pushed.code !== 0) {
|
|
305
|
+
return {
|
|
306
|
+
ok: false,
|
|
307
|
+
output: `Fast-forwarded ${branch} to ${src}, but the push to origin failed — the remote may have moved. ` +
|
|
308
|
+
`Nothing was force-pushed; origin still has the old ${branch}. ` +
|
|
309
|
+
`The local ${branch} is now at ${src}, so the NEXT deploy will be a plain push.\n` +
|
|
310
|
+
(captureOutput(pushed.stdout, pushed.stderr) || pushed.error?.message || ""),
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
return { ok: true, output: `Fast-forwarded ${branch} to ${src} and pushed to origin.` };
|
|
314
|
+
}
|
|
315
|
+
const pushed = await cmd(exec, ["push", "origin", branch], cwd, 120_000);
|
|
316
|
+
if (pushed.code !== 0) {
|
|
317
|
+
return {
|
|
318
|
+
ok: false,
|
|
319
|
+
output: `Could not push ${branch} to origin — the remote may have commits this checkout hasn't fetched. ` +
|
|
320
|
+
`Nothing was force-pushed.\n` +
|
|
321
|
+
(captureOutput(pushed.stdout, pushed.stderr) || pushed.error?.message || ""),
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
return { ok: true, output: `Pushed ${branch} to origin.` };
|
|
325
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Durable task-diff snapshots.
|
|
3
|
+
*
|
|
4
|
+
* A task worktree and branch are intentionally removed after a successful
|
|
5
|
+
* close-out. Keep the reviewed diff in `.repoos/` first so the Changes tab
|
|
6
|
+
* remains useful for completed tasks without retaining Git resources forever.
|
|
7
|
+
*/
|
|
8
|
+
import type { DiffResult, DiffStats } from "../core/git.js";
|
|
9
|
+
declare const SNAPSHOT_VERSION = 1;
|
|
10
|
+
export interface DiffSnapshot {
|
|
11
|
+
version: typeof SNAPSHOT_VERSION;
|
|
12
|
+
capturedAt: string;
|
|
13
|
+
stats: DiffStats;
|
|
14
|
+
diff: DiffResult;
|
|
15
|
+
}
|
|
16
|
+
/** Persist a complete task diff atomically. Best-effort: close-out must not fail solely because history cannot be cached. */
|
|
17
|
+
export declare function saveDiffSnapshot(root: string, cacheDir: string, taskId: string, stats: DiffStats, diff: DiffResult): void;
|
|
18
|
+
/** Load the captured completed-task diff, or null if this task predates snapshots. */
|
|
19
|
+
export declare function loadDiffSnapshot(root: string, cacheDir: string, taskId: string): DiffSnapshot | null;
|
|
20
|
+
export {};
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Durable task-diff snapshots.
|
|
3
|
+
*
|
|
4
|
+
* A task worktree and branch are intentionally removed after a successful
|
|
5
|
+
* close-out. Keep the reviewed diff in `.repoos/` first so the Changes tab
|
|
6
|
+
* remains useful for completed tasks without retaining Git resources forever.
|
|
7
|
+
*/
|
|
8
|
+
import { existsSync, mkdirSync, readFileSync, renameSync, unlinkSync, writeFileSync, } from "node:fs";
|
|
9
|
+
import { join } from "node:path";
|
|
10
|
+
const SNAPSHOT_VERSION = 1;
|
|
11
|
+
const SNAPSHOT_DIR = "diffs";
|
|
12
|
+
function snapshotPath(root, cacheDir, taskId) {
|
|
13
|
+
// Task IDs are normally numeric, but never let an unexpected ID influence a
|
|
14
|
+
// filesystem path outside RepoOS's cache directory.
|
|
15
|
+
if (!/^[A-Za-z0-9_-]+$/.test(taskId))
|
|
16
|
+
return null;
|
|
17
|
+
return join(root, cacheDir, SNAPSHOT_DIR, `${taskId}.json`);
|
|
18
|
+
}
|
|
19
|
+
/** Persist a complete task diff atomically. Best-effort: close-out must not fail solely because history cannot be cached. */
|
|
20
|
+
export function saveDiffSnapshot(root, cacheDir, taskId, stats, diff) {
|
|
21
|
+
const file = snapshotPath(root, cacheDir, taskId);
|
|
22
|
+
if (!file)
|
|
23
|
+
return;
|
|
24
|
+
let temp = null;
|
|
25
|
+
try {
|
|
26
|
+
mkdirSync(join(root, cacheDir, SNAPSHOT_DIR), { recursive: true });
|
|
27
|
+
temp = `${file}.${process.pid}.tmp`;
|
|
28
|
+
const snapshot = {
|
|
29
|
+
version: SNAPSHOT_VERSION,
|
|
30
|
+
capturedAt: new Date().toISOString(),
|
|
31
|
+
stats,
|
|
32
|
+
diff,
|
|
33
|
+
};
|
|
34
|
+
writeFileSync(temp, JSON.stringify(snapshot), "utf8");
|
|
35
|
+
renameSync(temp, file);
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
/* History is a convenience; never block a successfully validated release. */
|
|
39
|
+
}
|
|
40
|
+
finally {
|
|
41
|
+
if (temp && existsSync(temp)) {
|
|
42
|
+
try {
|
|
43
|
+
unlinkSync(temp);
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
/* best-effort */
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
/** Load the captured completed-task diff, or null if this task predates snapshots. */
|
|
52
|
+
export function loadDiffSnapshot(root, cacheDir, taskId) {
|
|
53
|
+
const file = snapshotPath(root, cacheDir, taskId);
|
|
54
|
+
if (!file || !existsSync(file))
|
|
55
|
+
return null;
|
|
56
|
+
try {
|
|
57
|
+
const parsed = JSON.parse(readFileSync(file, "utf8"));
|
|
58
|
+
if (parsed.version !== SNAPSHOT_VERSION ||
|
|
59
|
+
typeof parsed.capturedAt !== "string" ||
|
|
60
|
+
!parsed.stats ||
|
|
61
|
+
!parsed.diff ||
|
|
62
|
+
typeof parsed.stats.filesChanged !== "number" ||
|
|
63
|
+
typeof parsed.stats.additions !== "number" ||
|
|
64
|
+
typeof parsed.stats.deletions !== "number" ||
|
|
65
|
+
typeof parsed.diff.patch !== "string" ||
|
|
66
|
+
typeof parsed.diff.truncated !== "boolean")
|
|
67
|
+
return null;
|
|
68
|
+
return parsed;
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
}
|