@treeseed/sdk 0.10.23 → 0.10.25
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/index.d.ts +12 -2
- package/dist/index.js +42 -1
- package/dist/market-client.d.ts +23 -0
- package/dist/market-client.js +30 -0
- package/dist/operations/providers/default.js +103 -10
- package/dist/operations/repository-operations.d.ts +6 -1
- package/dist/operations/repository-operations.js +44 -0
- package/dist/operations/services/bootstrap-runner.d.ts +5 -1
- package/dist/operations/services/bootstrap-runner.js +34 -5
- package/dist/operations/services/config-runtime.d.ts +25 -9
- package/dist/operations/services/config-runtime.js +60 -12
- package/dist/operations/services/deploy.js +6 -1
- package/dist/operations/services/hub-launch.js +1 -0
- package/dist/operations/services/hub-provider-launch.d.ts +11 -1
- package/dist/operations/services/hub-provider-launch.js +81 -8
- package/dist/operations/services/project-host-operations.d.ts +153 -0
- package/dist/operations/services/project-host-operations.js +365 -0
- package/dist/operations/services/project-platform.d.ts +207 -177
- package/dist/operations/services/project-platform.js +96 -29
- package/dist/operations/services/railway-deploy.d.ts +33 -1
- package/dist/operations/services/railway-deploy.js +153 -44
- package/dist/operations/services/release-candidate.js +8 -2
- package/dist/operations/services/template-host-bindings.d.ts +68 -0
- package/dist/operations/services/template-host-bindings.js +400 -0
- package/dist/operations/services/template-registry.d.ts +22 -2
- package/dist/operations/services/template-registry.js +93 -6
- package/dist/operations/services/template-secret-sync.d.ts +97 -0
- package/dist/operations/services/template-secret-sync.js +292 -0
- package/dist/platform/contracts.d.ts +1 -0
- package/dist/platform/deploy-config.js +8 -1
- package/dist/platform/deploy-runtime.js +1 -0
- package/dist/platform/environment.d.ts +3 -0
- package/dist/project-workflow.d.ts +7 -1
- package/dist/reconcile/engine.d.ts +2 -0
- package/dist/reconcile/engine.js +58 -3
- package/dist/scripts/scaffold-site.js +3 -2
- package/dist/scripts/test-scaffold.js +2 -1
- package/dist/sdk-types.d.ts +87 -0
- package/dist/sdk-types.js +29 -0
- package/dist/template-catalog.js +3 -1
- package/dist/template-launch-requirements.d.ts +118 -0
- package/dist/template-launch-requirements.js +759 -0
- package/dist/template-launch-ui.d.ts +85 -0
- package/dist/template-launch-ui.js +189 -0
- package/dist/timing.d.ts +20 -0
- package/dist/timing.js +73 -0
- package/dist/treeseed/template-catalog/catalog.fixture.json +477 -0
- package/package.json +13 -1
- package/templates/github/deploy-web.workflow.yml +4 -0
|
@@ -24,6 +24,7 @@ import {
|
|
|
24
24
|
resolveRailwayWorkspaceContext,
|
|
25
25
|
upsertRailwayVariables
|
|
26
26
|
} from "./railway-api.js";
|
|
27
|
+
import { elapsedMs, formatDurationMs } from "../../timing.js";
|
|
27
28
|
function normalizeScope(scope) {
|
|
28
29
|
return scope === "prod" ? "prod" : scope === "staging" ? "staging" : "local";
|
|
29
30
|
}
|
|
@@ -86,6 +87,30 @@ function configuredEnvValue(env, name) {
|
|
|
86
87
|
const value = env?.[name];
|
|
87
88
|
return typeof value === "string" && value.trim() ? value.trim() : "";
|
|
88
89
|
}
|
|
90
|
+
async function timedRailwayPhase(timings, name, run, metadata) {
|
|
91
|
+
const startMs = performance.now();
|
|
92
|
+
try {
|
|
93
|
+
const result = await Promise.resolve(run());
|
|
94
|
+
timings.push({
|
|
95
|
+
name,
|
|
96
|
+
durationMs: elapsedMs(startMs),
|
|
97
|
+
status: "success",
|
|
98
|
+
...metadata ? { metadata } : {}
|
|
99
|
+
});
|
|
100
|
+
return result;
|
|
101
|
+
} catch (error) {
|
|
102
|
+
timings.push({
|
|
103
|
+
name,
|
|
104
|
+
durationMs: elapsedMs(startMs),
|
|
105
|
+
status: "failed",
|
|
106
|
+
metadata: {
|
|
107
|
+
...metadata ?? {},
|
|
108
|
+
error: error instanceof Error ? error.name || "Error" : "Error"
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
throw error;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
89
114
|
function parseRailwayJsonOutput(output) {
|
|
90
115
|
const trimmed = typeof output === "string" ? output.trim() : "";
|
|
91
116
|
if (!trimmed) {
|
|
@@ -171,6 +196,26 @@ function collectRailwayDeploymentStatusChecks(statusPayload, scope, services) {
|
|
|
171
196
|
};
|
|
172
197
|
}
|
|
173
198
|
const deployment = instance.latestDeployment ?? null;
|
|
199
|
+
if (!deployment) {
|
|
200
|
+
return {
|
|
201
|
+
type: "deployment-status",
|
|
202
|
+
service: service.key,
|
|
203
|
+
serviceName: service.serviceName,
|
|
204
|
+
environment: normalizeRailwayEnvironmentName(environment.name),
|
|
205
|
+
ok: true,
|
|
206
|
+
skipped: true,
|
|
207
|
+
status: "no_active_deployment",
|
|
208
|
+
observed: {
|
|
209
|
+
status: null,
|
|
210
|
+
deploymentId: null,
|
|
211
|
+
deploymentCreatedAt: null,
|
|
212
|
+
deploymentStopped: null,
|
|
213
|
+
instanceStatuses: [],
|
|
214
|
+
volumeMounts: []
|
|
215
|
+
},
|
|
216
|
+
message: `Railway service ${service.serviceName} has no active deployment to wait for.`
|
|
217
|
+
};
|
|
218
|
+
}
|
|
174
219
|
const status = String(deployment?.status ?? "").trim().toUpperCase();
|
|
175
220
|
const instanceStatuses = Array.isArray(deployment?.instances) ? deployment.instances.map((entry) => String(entry?.status ?? "").trim()).filter(Boolean) : [];
|
|
176
221
|
const ok = railwayStatusDeploymentSettled(status);
|
|
@@ -183,6 +228,8 @@ function collectRailwayDeploymentStatusChecks(statusPayload, scope, services) {
|
|
|
183
228
|
status: status || "missing_deployment",
|
|
184
229
|
observed: {
|
|
185
230
|
status: status || null,
|
|
231
|
+
deploymentId: deployment?.id ?? null,
|
|
232
|
+
deploymentCreatedAt: deployment?.createdAt ?? null,
|
|
186
233
|
deploymentStopped: deployment?.deploymentStopped ?? null,
|
|
187
234
|
instanceStatuses,
|
|
188
235
|
volumeMounts: Array.isArray(deployment?.meta?.volumeMounts) ? deployment.meta.volumeMounts : []
|
|
@@ -474,8 +521,10 @@ async function waitForRailwayManagedDeploymentsSettled(tenantRoot, scope, {
|
|
|
474
521
|
env = process.env,
|
|
475
522
|
timeoutMs = 6e5,
|
|
476
523
|
pollMs = 15e3,
|
|
524
|
+
fetchImpl = fetch,
|
|
477
525
|
onProgress
|
|
478
526
|
} = {}) {
|
|
527
|
+
const startMs = performance.now();
|
|
479
528
|
const deadline = Date.now() + timeoutMs;
|
|
480
529
|
const projectId = services.find((service) => typeof service.projectId === "string" && service.projectId.trim())?.projectId ?? null;
|
|
481
530
|
if (!projectId) {
|
|
@@ -488,6 +537,11 @@ async function waitForRailwayManagedDeploymentsSettled(tenantRoot, scope, {
|
|
|
488
537
|
environment: resolveRailwayEnvironmentForScope(scope),
|
|
489
538
|
ok: false,
|
|
490
539
|
status: "missing_project",
|
|
540
|
+
settle: {
|
|
541
|
+
durationMs: elapsedMs(startMs),
|
|
542
|
+
pollCount: 0,
|
|
543
|
+
finalStatus: "missing_project"
|
|
544
|
+
},
|
|
491
545
|
message: `Railway deployment status for ${service.serviceName} cannot be checked without a project id.`
|
|
492
546
|
}))
|
|
493
547
|
};
|
|
@@ -495,12 +549,15 @@ async function waitForRailwayManagedDeploymentsSettled(tenantRoot, scope, {
|
|
|
495
549
|
let checks = [];
|
|
496
550
|
let lastError = null;
|
|
497
551
|
let lastSummary = "";
|
|
552
|
+
let pollCount = 0;
|
|
498
553
|
for (; ; ) {
|
|
499
554
|
lastError = null;
|
|
555
|
+
pollCount += 1;
|
|
500
556
|
try {
|
|
501
557
|
const statusPayload = await fetchRailwayProjectDeploymentStatus({
|
|
502
558
|
projectId,
|
|
503
|
-
env
|
|
559
|
+
env,
|
|
560
|
+
fetchImpl
|
|
504
561
|
});
|
|
505
562
|
checks = collectRailwayDeploymentStatusChecks(statusPayload, scope, services);
|
|
506
563
|
} catch (error) {
|
|
@@ -512,28 +569,63 @@ async function waitForRailwayManagedDeploymentsSettled(tenantRoot, scope, {
|
|
|
512
569
|
environment: resolveRailwayEnvironmentForScope(scope),
|
|
513
570
|
ok: false,
|
|
514
571
|
status: "status_error",
|
|
572
|
+
settle: {
|
|
573
|
+
durationMs: elapsedMs(startMs),
|
|
574
|
+
pollCount,
|
|
575
|
+
finalStatus: "status_error"
|
|
576
|
+
},
|
|
515
577
|
message: error instanceof Error ? error.message : String(error)
|
|
516
578
|
}));
|
|
517
579
|
}
|
|
518
580
|
const summary = formatRailwayDeploymentStatusSummary(scope, checks);
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
581
|
+
const progress = `${summary} poll=${pollCount} elapsed=${formatDurationMs(elapsedMs(startMs))}`;
|
|
582
|
+
if (progress !== lastSummary || !checks.every((entry) => entry.ok === true || entry.skipped === true)) {
|
|
583
|
+
onProgress?.(progress, "stdout");
|
|
584
|
+
lastSummary = progress;
|
|
522
585
|
}
|
|
523
|
-
if (checks.every((entry) => entry.ok === true)) {
|
|
524
|
-
return {
|
|
586
|
+
if (checks.every((entry) => entry.ok === true || entry.skipped === true)) {
|
|
587
|
+
return {
|
|
588
|
+
ok: true,
|
|
589
|
+
checks: checks.map((check) => ({
|
|
590
|
+
...check,
|
|
591
|
+
settle: {
|
|
592
|
+
durationMs: elapsedMs(startMs),
|
|
593
|
+
pollCount,
|
|
594
|
+
finalStatus: check.status,
|
|
595
|
+
fastSkipped: check.skipped === true
|
|
596
|
+
}
|
|
597
|
+
})),
|
|
598
|
+
settle: {
|
|
599
|
+
durationMs: elapsedMs(startMs),
|
|
600
|
+
pollCount,
|
|
601
|
+
status: checks.every((entry) => entry.skipped === true) ? "skipped" : "settled"
|
|
602
|
+
}
|
|
603
|
+
};
|
|
525
604
|
}
|
|
526
605
|
if (Date.now() >= deadline) {
|
|
527
606
|
return {
|
|
528
607
|
ok: false,
|
|
529
|
-
checks
|
|
608
|
+
checks: checks.map((check) => ({
|
|
609
|
+
...check,
|
|
610
|
+
settle: {
|
|
611
|
+
durationMs: elapsedMs(startMs),
|
|
612
|
+
pollCount,
|
|
613
|
+
finalStatus: check.status,
|
|
614
|
+
timeout: true
|
|
615
|
+
}
|
|
616
|
+
})),
|
|
617
|
+
settle: {
|
|
618
|
+
durationMs: elapsedMs(startMs),
|
|
619
|
+
pollCount,
|
|
620
|
+
status: "timeout"
|
|
621
|
+
},
|
|
530
622
|
message: lastError instanceof Error ? lastError.message : "Railway deployments did not settle before the monitor timeout."
|
|
531
623
|
};
|
|
532
624
|
}
|
|
533
625
|
await sleep(pollMs);
|
|
534
626
|
}
|
|
535
627
|
}
|
|
536
|
-
async function fetchRailwayProjectDeploymentStatus({ projectId, env = process.env }) {
|
|
628
|
+
async function fetchRailwayProjectDeploymentStatus({ projectId, env = process.env, fetchImpl = fetch }) {
|
|
537
629
|
const payload = await railwayGraphqlRequest({
|
|
538
630
|
query: `
|
|
539
631
|
query TreeseedRailwayDeploymentStatus($projectId: String!) {
|
|
@@ -571,7 +663,8 @@ query TreeseedRailwayDeploymentStatus($projectId: String!) {
|
|
|
571
663
|
}
|
|
572
664
|
`.trim(),
|
|
573
665
|
variables: { projectId },
|
|
574
|
-
env
|
|
666
|
+
env,
|
|
667
|
+
fetchImpl
|
|
575
668
|
});
|
|
576
669
|
return payload.data?.project ?? null;
|
|
577
670
|
}
|
|
@@ -1300,6 +1393,7 @@ async function verifyRailwayManagedResources(tenantRoot, scope, {
|
|
|
1300
1393
|
const settled = await waitForRailwayManagedDeploymentsSettled(tenantRoot, scope, {
|
|
1301
1394
|
services: deploymentStatusServices.length > 0 ? deploymentStatusServices : services,
|
|
1302
1395
|
env: effectiveEnv,
|
|
1396
|
+
fetchImpl,
|
|
1303
1397
|
timeoutMs: settleTimeoutMs,
|
|
1304
1398
|
pollMs: settlePollMs,
|
|
1305
1399
|
onProgress
|
|
@@ -1897,6 +1991,7 @@ async function deployRailwayService(tenantRoot, service, {
|
|
|
1897
1991
|
prefix,
|
|
1898
1992
|
env = process.env
|
|
1899
1993
|
} = {}) {
|
|
1994
|
+
const timings = [];
|
|
1900
1995
|
if (dryRun) {
|
|
1901
1996
|
const plan2 = planRailwayServiceDeploy(service, { env });
|
|
1902
1997
|
return {
|
|
@@ -1904,10 +1999,13 @@ async function deployRailwayService(tenantRoot, service, {
|
|
|
1904
1999
|
status: "planned",
|
|
1905
2000
|
command: [plan2.command, ...plan2.args].join(" "),
|
|
1906
2001
|
cwd: plan2.cwd,
|
|
1907
|
-
publicBaseUrl: service.publicBaseUrl
|
|
2002
|
+
publicBaseUrl: service.publicBaseUrl,
|
|
2003
|
+
timings
|
|
1908
2004
|
};
|
|
1909
2005
|
}
|
|
1910
|
-
const deployService = await resolveRailwayDeployProjectContext(service, { env })
|
|
2006
|
+
const deployService = await timedRailwayPhase(timings, "railway:resolve-context", () => resolveRailwayDeployProjectContext(service, { env }), {
|
|
2007
|
+
service: service.key
|
|
2008
|
+
});
|
|
1911
2009
|
const commandEnv = buildRailwayCommandEnv({ ...process.env, ...env });
|
|
1912
2010
|
let railwayDeployEnv = buildRailwayDeployCommandEnv(commandEnv);
|
|
1913
2011
|
const railway = resolveTreeseedToolCommand("railway", { env: commandEnv });
|
|
@@ -1920,9 +2018,9 @@ async function deployRailwayService(tenantRoot, service, {
|
|
|
1920
2018
|
task: `${deployService.key}-railway-deploy`,
|
|
1921
2019
|
stage: "deploy"
|
|
1922
2020
|
};
|
|
1923
|
-
const runtimeConfiguration = await syncRailwayServiceRuntimeConfigurationAfterDeploy(tenantRoot, deployService, {
|
|
2021
|
+
const runtimeConfiguration = await timedRailwayPhase(timings, "railway:sync-runtime-config", () => syncRailwayServiceRuntimeConfigurationAfterDeploy(tenantRoot, deployService, {
|
|
1924
2022
|
env: commandEnv
|
|
1925
|
-
});
|
|
2023
|
+
}), { service: deployService.key });
|
|
1926
2024
|
const cliDeployService = {
|
|
1927
2025
|
...deployService,
|
|
1928
2026
|
projectId: runtimeConfiguration?.projectId ?? deployService.projectId,
|
|
@@ -1932,14 +2030,19 @@ async function deployRailwayService(tenantRoot, service, {
|
|
|
1932
2030
|
serviceName: runtimeConfiguration?.serviceName ?? deployService.serviceName,
|
|
1933
2031
|
railwayEnvironment: runtimeConfiguration?.environmentName ?? runtimeConfiguration?.environmentId ?? deployService.railwayEnvironment
|
|
1934
2032
|
};
|
|
1935
|
-
await syncRailwayApiDeviceLoginVariables(cliDeployService, commandEnv, write, taskPrefix)
|
|
2033
|
+
await timedRailwayPhase(timings, "railway:device-login-vars", () => syncRailwayApiDeviceLoginVariables(cliDeployService, commandEnv, write, taskPrefix), {
|
|
2034
|
+
service: cliDeployService.key
|
|
2035
|
+
});
|
|
1936
2036
|
railwayDeployEnv = buildRailwayCliContextEnv(railwayDeployEnv, cliDeployService);
|
|
1937
2037
|
const hasCommandApiToken = Boolean(configuredEnvValue(commandEnv, "RAILWAY_API_TOKEN"));
|
|
1938
2038
|
let usesProjectToken = Boolean(configuredEnvValue(railwayDeployEnv, "RAILWAY_TOKEN"));
|
|
1939
2039
|
if (usesProjectToken) {
|
|
1940
2040
|
railwayDeployEnv = { ...railwayDeployEnv, RAILWAY_API_TOKEN: void 0 };
|
|
1941
2041
|
}
|
|
1942
|
-
|
|
2042
|
+
await timedRailwayPhase(timings, "railway:project-token", async () => {
|
|
2043
|
+
if (usesProjectToken || hasCommandApiToken) {
|
|
2044
|
+
return null;
|
|
2045
|
+
}
|
|
1943
2046
|
const projectToken = await createRailwayCliProjectToken(cliDeployService, { env: commandEnv });
|
|
1944
2047
|
if (projectToken) {
|
|
1945
2048
|
railwayDeployEnv = buildRailwayCliContextEnv({ ...railwayDeployEnv, RAILWAY_API_TOKEN: void 0, RAILWAY_TOKEN: projectToken }, cliDeployService);
|
|
@@ -1947,16 +2050,17 @@ async function deployRailwayService(tenantRoot, service, {
|
|
|
1947
2050
|
} else if (configuredEnvValue(commandEnv, "CI") === "true") {
|
|
1948
2051
|
throw new Error(`Railway CI deploy requires a project token for ${cliDeployService.serviceName ?? cliDeployService.key}. Automatic project token creation did not return a token.`);
|
|
1949
2052
|
}
|
|
1950
|
-
|
|
2053
|
+
return null;
|
|
2054
|
+
}, { service: cliDeployService.key });
|
|
1951
2055
|
const linkPlan = planRailwayServiceLink(cliDeployService, { env: commandEnv });
|
|
1952
2056
|
const plan = planRailwayServiceDeploy(cliDeployService, { env, projectTokenMode: usesProjectToken });
|
|
1953
2057
|
if (deployService.buildCommand && shouldRunRailwayPredeployBuild(commandEnv)) {
|
|
1954
|
-
const buildResult = await runPrefixedCommand("bash", ["-lc", deployService.buildCommand], {
|
|
2058
|
+
const buildResult = await timedRailwayPhase(timings, "railway:predeploy-build", () => runPrefixedCommand("bash", ["-lc", deployService.buildCommand], {
|
|
1955
2059
|
cwd: deployService.rootDir,
|
|
1956
2060
|
env: commandEnv,
|
|
1957
2061
|
write,
|
|
1958
2062
|
prefix: { ...taskPrefix, stage: "build" }
|
|
1959
|
-
});
|
|
2063
|
+
}), { service: deployService.key });
|
|
1960
2064
|
if (buildResult.status !== 0) {
|
|
1961
2065
|
throw new Error(`Railway ${deployService.key} build command failed.`);
|
|
1962
2066
|
}
|
|
@@ -1965,9 +2069,11 @@ async function deployRailwayService(tenantRoot, service, {
|
|
|
1965
2069
|
const cliConfig = configuredEnvValue(commandEnv, "CI") === "true" ? writeRailwayCliProjectConfig(cliDeployService, { env: railwayDeployEnv, cwd: plan.cwd }) : null;
|
|
1966
2070
|
const effectiveLinkPlan = hasRailwayApiToken ? linkPlan : usesProjectToken ? planRailwayProjectEnvironmentLink(cliDeployService) : linkPlan;
|
|
1967
2071
|
const railwayLinkEnv = hasRailwayApiToken ? buildRailwayLinkCommandEnv(commandEnv, cliDeployService) : railwayDeployEnv;
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
2072
|
+
await timedRailwayPhase(timings, "railway:link", async () => {
|
|
2073
|
+
if (cliConfig) {
|
|
2074
|
+
write ? write(`[${taskPrefix.scope}][${taskPrefix.system}][${taskPrefix.task}][link] Wrote Railway CLI project context for ${cliConfig.projectPath}.`, "stdout") : null;
|
|
2075
|
+
return;
|
|
2076
|
+
}
|
|
1971
2077
|
const linkResult = await runPrefixedCommand(railway.command, [...railway.argsPrefix, ...effectiveLinkPlan.args], {
|
|
1972
2078
|
cwd: effectiveLinkPlan.cwd,
|
|
1973
2079
|
env: railwayLinkEnv,
|
|
@@ -1977,37 +2083,40 @@ async function deployRailwayService(tenantRoot, service, {
|
|
|
1977
2083
|
if (linkResult.status !== 0) {
|
|
1978
2084
|
throw new Error(linkResult.stderr?.trim() || linkResult.stdout?.trim() || `railway ${effectiveLinkPlan.args.join(" ")} failed with exit code ${linkResult.status ?? "unknown"} in ${effectiveLinkPlan.cwd}`);
|
|
1979
2085
|
}
|
|
1980
|
-
}
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
2086
|
+
}, { service: cliDeployService.key });
|
|
2087
|
+
await timedRailwayPhase(timings, "railway:deploy", async () => {
|
|
2088
|
+
let lastFailure = null;
|
|
2089
|
+
for (let attempt = 1; attempt <= 5; attempt += 1) {
|
|
2090
|
+
const result = await runPrefixedCommand(railway.command, [...railway.argsPrefix, ...plan.args], {
|
|
2091
|
+
cwd: plan.cwd,
|
|
2092
|
+
env: railwayDeployEnv,
|
|
2093
|
+
write,
|
|
2094
|
+
prefix: taskPrefix
|
|
2095
|
+
});
|
|
2096
|
+
if (result.status === 0) {
|
|
2097
|
+
lastFailure = null;
|
|
2098
|
+
break;
|
|
2099
|
+
}
|
|
2100
|
+
lastFailure = result;
|
|
2101
|
+
if (!isRailwayTransientFailure(result) || attempt === 5) {
|
|
2102
|
+
throw new Error(result.stderr?.trim() || result.stdout?.trim() || `railway ${plan.args.join(" ")} failed with exit code ${result.status ?? "unknown"} in ${plan.cwd}`);
|
|
2103
|
+
}
|
|
2104
|
+
const backoffMs = 5e3 * attempt;
|
|
2105
|
+
const warning = `Railway deploy for ${deployService.serviceName ?? deployService.serviceId ?? deployService.key} hit a transient failure; retrying in ${Math.round(backoffMs / 1e3)}s...`;
|
|
2106
|
+
write ? write(`[${taskPrefix.scope}][${taskPrefix.system}][${taskPrefix.task}][retry] ${warning}`, "stderr") : console.warn(warning);
|
|
2107
|
+
await sleep(backoffMs);
|
|
1992
2108
|
}
|
|
1993
|
-
lastFailure
|
|
1994
|
-
|
|
1995
|
-
throw new Error(result.stderr?.trim() || result.stdout?.trim() || `railway ${plan.args.join(" ")} failed with exit code ${result.status ?? "unknown"} in ${plan.cwd}`);
|
|
2109
|
+
if (lastFailure) {
|
|
2110
|
+
throw new Error(lastFailure.stderr?.trim() || lastFailure.stdout?.trim() || `railway ${plan.args.join(" ")} failed`);
|
|
1996
2111
|
}
|
|
1997
|
-
|
|
1998
|
-
const warning = `Railway deploy for ${deployService.serviceName ?? deployService.serviceId ?? deployService.key} hit a transient failure; retrying in ${Math.round(backoffMs / 1e3)}s...`;
|
|
1999
|
-
write ? write(`[${taskPrefix.scope}][${taskPrefix.system}][${taskPrefix.task}][retry] ${warning}`, "stderr") : console.warn(warning);
|
|
2000
|
-
await sleep(backoffMs);
|
|
2001
|
-
}
|
|
2002
|
-
if (lastFailure) {
|
|
2003
|
-
throw new Error(lastFailure.stderr?.trim() || lastFailure.stdout?.trim() || `railway ${plan.args.join(" ")} failed`);
|
|
2004
|
-
}
|
|
2112
|
+
}, { service: cliDeployService.key });
|
|
2005
2113
|
return {
|
|
2006
2114
|
service: deployService.key,
|
|
2007
2115
|
status: "deployed",
|
|
2008
2116
|
command: [plan.command, ...plan.args].join(" "),
|
|
2009
2117
|
cwd: plan.cwd,
|
|
2010
2118
|
publicBaseUrl: deployService.publicBaseUrl,
|
|
2119
|
+
timings,
|
|
2011
2120
|
runtimeConfiguration: runtimeConfiguration ? {
|
|
2012
2121
|
updated: runtimeConfiguration.updated,
|
|
2013
2122
|
healthcheckPath: runtimeConfiguration.instance?.healthcheckPath ?? null,
|
|
@@ -244,7 +244,7 @@ function rehearsalVerifyScript(root) {
|
|
|
244
244
|
function runNpmRehearsalCommand(args, options) {
|
|
245
245
|
const result = spawnSync("npm", args, {
|
|
246
246
|
cwd: options.cwd,
|
|
247
|
-
env: process.env,
|
|
247
|
+
env: options.env ?? process.env,
|
|
248
248
|
stdio: "pipe",
|
|
249
249
|
encoding: "utf8",
|
|
250
250
|
timeout: options.timeoutMs
|
|
@@ -320,7 +320,13 @@ function runProductionDependencyRehearsal(root, plannedVersions, selectedPackage
|
|
|
320
320
|
buildRehearsalWorkspacePackageArtifacts(copied.tempRoot);
|
|
321
321
|
const scriptName = rehearsalVerifyScript(copied.tempRoot);
|
|
322
322
|
if (scriptName) {
|
|
323
|
-
|
|
323
|
+
const packageJson = safePackageJson(resolve(copied.tempRoot, "package.json"));
|
|
324
|
+
const parallelMarketVerify = packageJson?.name === "@treeseed/market" && (scriptName === "verify:direct" || scriptName === "verify:local" || scriptName === "verify");
|
|
325
|
+
runNpmRehearsalCommand(["run", scriptName], {
|
|
326
|
+
cwd: copied.tempRoot,
|
|
327
|
+
timeoutMs: 9e5,
|
|
328
|
+
env: parallelMarketVerify ? { ...process.env, TREESEED_VERIFY_PARALLEL: "1" } : process.env
|
|
329
|
+
});
|
|
324
330
|
}
|
|
325
331
|
const postInstallIssues = collectInternalDevReferenceIssues(copied.tempRoot, selectedPackageSet);
|
|
326
332
|
if (postInstallIssues.length > 0) {
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { type ProjectEnvironmentName, type TemplateConfigMergeStrategy, type TemplateConfigWriteTarget, type TemplateSecretSensitivity } from '../../sdk-types.ts';
|
|
2
|
+
import type { ProjectLaunchConfigWritePlanItem, ProjectLaunchResolvedHostBinding, ProjectLaunchSecretDeploymentPlanItem } from '../../template-launch-requirements.ts';
|
|
3
|
+
export interface ApplyProjectLaunchHostBindingConfigOptions {
|
|
4
|
+
projectRoot: string;
|
|
5
|
+
hostBindings?: Record<string, ProjectLaunchResolvedHostBinding> | null;
|
|
6
|
+
hostBindingPlans?: {
|
|
7
|
+
configWrites?: ProjectLaunchConfigWritePlanItem[];
|
|
8
|
+
secretDeployment?: {
|
|
9
|
+
items?: ProjectLaunchSecretDeploymentPlanItem[];
|
|
10
|
+
};
|
|
11
|
+
} | null;
|
|
12
|
+
launchInput?: {
|
|
13
|
+
projectSlug?: string | null;
|
|
14
|
+
projectName?: string | null;
|
|
15
|
+
repoName?: string | null;
|
|
16
|
+
domains?: Record<string, unknown> | null;
|
|
17
|
+
} | null;
|
|
18
|
+
derived?: {
|
|
19
|
+
projectSlug?: string | null;
|
|
20
|
+
projectName?: string | null;
|
|
21
|
+
repositoryName?: string | null;
|
|
22
|
+
} | null;
|
|
23
|
+
}
|
|
24
|
+
export interface ProjectLaunchHostBindingConfigWriteSummary {
|
|
25
|
+
target: TemplateConfigWriteTarget;
|
|
26
|
+
path: string;
|
|
27
|
+
requirementKey: string;
|
|
28
|
+
requirementKind: string;
|
|
29
|
+
provider: string;
|
|
30
|
+
operation: TemplateConfigMergeStrategy;
|
|
31
|
+
valuePreview: string | number | boolean | null;
|
|
32
|
+
}
|
|
33
|
+
export interface ProjectLaunchHostBindingEnvironmentWriteSummary {
|
|
34
|
+
env: string;
|
|
35
|
+
requirementKey: string;
|
|
36
|
+
requirementKind: string;
|
|
37
|
+
sourceHostType?: string | null;
|
|
38
|
+
sourceProvider?: string | null;
|
|
39
|
+
sensitivity: TemplateSecretSensitivity | string;
|
|
40
|
+
targets: string[];
|
|
41
|
+
scopes: ProjectEnvironmentName[];
|
|
42
|
+
}
|
|
43
|
+
export interface ProjectLaunchHostBindingConfigApplyResult {
|
|
44
|
+
configWrites: ProjectLaunchHostBindingConfigWriteSummary[];
|
|
45
|
+
environmentWrites: ProjectLaunchHostBindingEnvironmentWriteSummary[];
|
|
46
|
+
targets: string[];
|
|
47
|
+
}
|
|
48
|
+
export interface ProjectLaunchHostBindingConfigAuditDiagnostic {
|
|
49
|
+
code: 'missing_config_target' | 'stale_config_target' | 'invalid_config_target';
|
|
50
|
+
status: 'ok' | 'warning' | 'blocked';
|
|
51
|
+
target: TemplateConfigWriteTarget;
|
|
52
|
+
message: string;
|
|
53
|
+
}
|
|
54
|
+
export interface ProjectLaunchHostBindingConfigAuditResult {
|
|
55
|
+
status: 'ok' | 'warning' | 'blocked';
|
|
56
|
+
checkedTargets: TemplateConfigWriteTarget[];
|
|
57
|
+
changedTargets: TemplateConfigWriteTarget[];
|
|
58
|
+
diagnostics: ProjectLaunchHostBindingConfigAuditDiagnostic[];
|
|
59
|
+
expected: ProjectLaunchHostBindingConfigApplyResult;
|
|
60
|
+
}
|
|
61
|
+
export declare function applyProjectLaunchHostBindingConfig(options: ApplyProjectLaunchHostBindingConfigOptions): ProjectLaunchHostBindingConfigApplyResult;
|
|
62
|
+
export declare function auditProjectLaunchHostBindingConfig(options: ApplyProjectLaunchHostBindingConfigOptions): ProjectLaunchHostBindingConfigAuditResult;
|
|
63
|
+
export declare function preserveProjectLaunchHostBindingConfigOverlay(options: {
|
|
64
|
+
target: TemplateConfigWriteTarget;
|
|
65
|
+
currentContent: string;
|
|
66
|
+
nextContent: string;
|
|
67
|
+
hostBindingPlans?: ApplyProjectLaunchHostBindingConfigOptions['hostBindingPlans'];
|
|
68
|
+
}): string;
|