@stacksjs/buddy 0.70.227 → 0.70.228
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/dist/commands/deploy.d.ts +1 -0
- package/dist/commands/deploy.js +51 -0
- package/package.json +1 -1
|
@@ -76,6 +76,7 @@ export declare function loadTsCloudDeployApi(): Promise<typeof import('@stacksjs
|
|
|
76
76
|
* port declaration is left alone.
|
|
77
77
|
*/
|
|
78
78
|
export declare function shouldInjectManagementDashboard(tsCloudConfig: any): boolean;
|
|
79
|
+
export declare function reconcilePartialDeployManagementDashboards(tsCloudConfig: any, livePorts: Record<string, number>): { preserved: string[], removed: string[] };
|
|
79
80
|
/**
|
|
80
81
|
* Read a tenant's existing shared-server pin without requiring provider API
|
|
81
82
|
* credentials. The pin contains only compute metadata and is sufficient for
|
package/dist/commands/deploy.js
CHANGED
|
@@ -567,6 +567,55 @@ export async function loadTsCloudDeployApi() {
|
|
|
567
567
|
export function shouldInjectManagementDashboard(tsCloudConfig) {
|
|
568
568
|
return !tsCloudConfig.cloud?.attachTo;
|
|
569
569
|
}
|
|
570
|
+
export function reconcilePartialDeployManagementDashboards(tsCloudConfig, livePorts) {
|
|
571
|
+
const sites = tsCloudConfig.sites, preserved = [], removed = [];
|
|
572
|
+
if (!sites)
|
|
573
|
+
return { preserved, removed };
|
|
574
|
+
for (const [siteName, site] of Object.entries(sites)) {
|
|
575
|
+
if (siteName !== "dashboard" && !siteName.startsWith("dashboard-"))
|
|
576
|
+
continue;
|
|
577
|
+
const livePort = livePorts[siteName];
|
|
578
|
+
if (typeof livePort !== "number" || !Number.isInteger(livePort) || livePort < 1 || livePort > 65535) {
|
|
579
|
+
delete sites[siteName];
|
|
580
|
+
removed.push(siteName);
|
|
581
|
+
continue;
|
|
582
|
+
}
|
|
583
|
+
const next = { ...site, port: livePort };
|
|
584
|
+
if (typeof next.start === "string")
|
|
585
|
+
next.start = next.start.replace(/(--port(?:=|\s+))\d+/, `$1${livePort}`);
|
|
586
|
+
sites[siteName] = next;
|
|
587
|
+
preserved.push(siteName);
|
|
588
|
+
}
|
|
589
|
+
return { preserved, removed };
|
|
590
|
+
}
|
|
591
|
+
async function reconcilePartialDeployManagementDashboardsWithLiveBox(tsCloudConfig, ip) {
|
|
592
|
+
const slug = String(tsCloudConfig.project?.slug || "app").replace(/[^a-z0-9._-]+/gi, "-"), siteNames = Object.keys(tsCloudConfig.sites ?? {}).filter((name) => name === "dashboard" || name.startsWith("dashboard-"));
|
|
593
|
+
if (siteNames.length === 0)
|
|
594
|
+
return;
|
|
595
|
+
const units = siteNames.map((siteName) => ({
|
|
596
|
+
siteName,
|
|
597
|
+
unit: `${slug}-${siteName}.service`
|
|
598
|
+
})), probe = `
|
|
599
|
+
const units = ${JSON.stringify(units)}
|
|
600
|
+
const text = bytes => new TextDecoder().decode(bytes).trim()
|
|
601
|
+
const run = args => text(Bun.spawnSync(args).stdout)
|
|
602
|
+
const ports = {}
|
|
603
|
+
for (const entry of units) {
|
|
604
|
+
if (run(['systemctl', 'show', entry.unit, '--property=ActiveState', '--value']) !== 'active')
|
|
605
|
+
continue
|
|
606
|
+
const command = run(['systemctl', 'show', entry.unit, '--property=ExecStart', '--value'])
|
|
607
|
+
const match = command.match(/--port(?:=|\\s+)(\\d+)/)
|
|
608
|
+
if (match)
|
|
609
|
+
ports[entry.siteName] = Number(match[1])
|
|
610
|
+
}
|
|
611
|
+
console.log(JSON.stringify(ports))
|
|
612
|
+
`.trim(), encoded = Buffer.from(probe).toString("base64"), { sshExecOrThrow } = await import("@stacksjs/ts-cloud"), line = (await sshExecOrThrow(ip, `/usr/local/bin/bun -e "eval(Buffer.from('${encoded}','base64').toString())"`, { user: "root", connectTimeoutSec: 10 })).trim().split(`
|
|
613
|
+
`).at(-1) || "{}", livePorts = JSON.parse(line), result = reconcilePartialDeployManagementDashboards(tsCloudConfig, livePorts);
|
|
614
|
+
for (const siteName of result.preserved)
|
|
615
|
+
log.info(`Partial deploy: preserving the live management dashboard service '${siteName}' on port ${livePorts[siteName]}`);
|
|
616
|
+
for (const siteName of result.removed)
|
|
617
|
+
log.info(`Partial deploy: omitting management dashboard route '${siteName}' because no active service owns it`);
|
|
618
|
+
}
|
|
570
619
|
export function resolvePersistedAttachTargetBox(tsCloudConfig, environment, cwd = process.cwd()) {
|
|
571
620
|
const owner = tsCloudConfig.cloud?.attachTo;
|
|
572
621
|
if (!owner)
|
|
@@ -751,6 +800,8 @@ async function runHetznerDeploy(args) {
|
|
|
751
800
|
process.exit(ExitCode.FatalError);
|
|
752
801
|
}
|
|
753
802
|
await waitForRemoteReady(ip);
|
|
803
|
+
if (onlySite && !attachTo)
|
|
804
|
+
await reconcilePartialDeployManagementDashboardsWithLiveBox(tsCloudConfig, ip);
|
|
754
805
|
const { execSync } = await import("node:child_process"), { tmpdir } = await import("node:os"), sites = applyEnvironmentToSites(tsCloudConfig.sites || {}, environment, tsCloudConfig), slug = tsCloudConfig.project?.slug || "app";
|
|
755
806
|
let sha;
|
|
756
807
|
try {
|