@treeseed/sdk 0.12.20 → 0.12.21
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/operations/services/deploy.d.ts +6 -2
- package/dist/operations/services/deploy.js +31 -8
- package/dist/operations/services/hosted-service-checks.js +5 -3
- package/dist/operations/services/live-hosted-service-checks.js +3 -2
- package/dist/operations/services/project-platform.js +23 -22
- package/dist/workflow/operations.d.ts +1 -0
- package/dist/workflow/operations.js +29 -0
- package/package.json +1 -1
|
@@ -294,7 +294,8 @@ export declare function queueName(entry: any): any;
|
|
|
294
294
|
export declare function queueId(entry: any): any;
|
|
295
295
|
export declare function hasProvisionedCloudflareResources(state: any): boolean;
|
|
296
296
|
export declare function purgeSourcePageCaches(tenantRoot: any, options?: {}): {
|
|
297
|
-
skipped:
|
|
297
|
+
skipped: boolean;
|
|
298
|
+
reason: string;
|
|
298
299
|
urls: (string | null)[];
|
|
299
300
|
results: never[];
|
|
300
301
|
} | {
|
|
@@ -305,9 +306,11 @@ export declare function purgeSourcePageCaches(tenantRoot: any, options?: {}): {
|
|
|
305
306
|
success: boolean;
|
|
306
307
|
}[];
|
|
307
308
|
skipped?: undefined;
|
|
309
|
+
reason?: undefined;
|
|
308
310
|
};
|
|
309
311
|
export declare function purgePublishedContentCaches(tenantRoot: any, urls: any, options?: {}): {
|
|
310
|
-
skipped:
|
|
312
|
+
skipped: boolean;
|
|
313
|
+
reason: string;
|
|
311
314
|
urls: any;
|
|
312
315
|
results: never[];
|
|
313
316
|
} | {
|
|
@@ -318,6 +321,7 @@ export declare function purgePublishedContentCaches(tenantRoot: any, urls: any,
|
|
|
318
321
|
success: boolean;
|
|
319
322
|
}[];
|
|
320
323
|
skipped?: undefined;
|
|
324
|
+
reason?: undefined;
|
|
321
325
|
};
|
|
322
326
|
export declare function resolveConfiguredCloudflareAccountId(deployConfig: any): any;
|
|
323
327
|
export declare function collectMissingDeployInputs(tenantRoot: any): {
|
|
@@ -1683,19 +1683,30 @@ function recordCachePurgeResult(targetState, results, error = null) {
|
|
|
1683
1683
|
targetState.purgeCount = Array.isArray(results) ? results.reduce((sum, result) => sum + (result?.count ?? 0), 0) : 0;
|
|
1684
1684
|
targetState.lastError = null;
|
|
1685
1685
|
}
|
|
1686
|
+
function resolveCloudflareCachePurgeEnv(options = {}) {
|
|
1687
|
+
const env = options.env ?? {};
|
|
1688
|
+
const token = env.TREESEED_CLOUDFLARE_API_TOKEN ?? env.CLOUDFLARE_API_TOKEN ?? process.env.TREESEED_CLOUDFLARE_API_TOKEN ?? process.env.CLOUDFLARE_API_TOKEN;
|
|
1689
|
+
return token ? { ...env, TREESEED_CLOUDFLARE_API_TOKEN: token, CLOUDFLARE_API_TOKEN: token } : null;
|
|
1690
|
+
}
|
|
1686
1691
|
function purgeSourcePageCaches(tenantRoot, options = {}) {
|
|
1687
1692
|
const target = normalizeTarget(options.scope ?? options.target ?? "prod");
|
|
1688
1693
|
const deployConfig = loadTenantDeployConfig(tenantRoot);
|
|
1689
1694
|
const state = loadDeployState(tenantRoot, deployConfig, { target });
|
|
1690
1695
|
const urls = resolveSourcePagePurgeUrls(deployConfig);
|
|
1691
|
-
|
|
1696
|
+
const env = resolveCloudflareCachePurgeEnv(options);
|
|
1697
|
+
if ((options.dryRun ?? false) || urls.length === 0 || !env) {
|
|
1692
1698
|
recordCachePurgeResult(state.webCache.deployPurge, urls.map((url) => ({ count: url ? 1 : 0 })));
|
|
1693
1699
|
writeDeployState(tenantRoot, state, { target });
|
|
1694
|
-
return {
|
|
1700
|
+
return {
|
|
1701
|
+
skipped: true,
|
|
1702
|
+
reason: options.dryRun ? "dry_run" : urls.length === 0 ? "no_urls" : "missing_cloudflare_token",
|
|
1703
|
+
urls,
|
|
1704
|
+
results: []
|
|
1705
|
+
};
|
|
1695
1706
|
}
|
|
1696
1707
|
try {
|
|
1697
1708
|
const results = purgeCloudflareCacheByUrls(urls, deployConfig, {
|
|
1698
|
-
env
|
|
1709
|
+
env
|
|
1699
1710
|
});
|
|
1700
1711
|
recordCachePurgeResult(state.webCache.deployPurge, results);
|
|
1701
1712
|
writeDeployState(tenantRoot, state, { target });
|
|
@@ -1710,14 +1721,20 @@ function purgePublishedContentCaches(tenantRoot, urls, options = {}) {
|
|
|
1710
1721
|
const target = normalizeTarget(options.scope ?? options.target ?? "prod");
|
|
1711
1722
|
const deployConfig = loadTenantDeployConfig(tenantRoot);
|
|
1712
1723
|
const state = loadDeployState(tenantRoot, deployConfig, { target });
|
|
1713
|
-
|
|
1724
|
+
const env = resolveCloudflareCachePurgeEnv(options);
|
|
1725
|
+
if ((options.dryRun ?? false) || !urls?.length || !env) {
|
|
1714
1726
|
recordCachePurgeResult(state.webCache.contentPurge, (urls ?? []).map((url) => ({ count: url ? 1 : 0 })));
|
|
1715
1727
|
writeDeployState(tenantRoot, state, { target });
|
|
1716
|
-
return {
|
|
1728
|
+
return {
|
|
1729
|
+
skipped: true,
|
|
1730
|
+
reason: options.dryRun ? "dry_run" : !urls?.length ? "no_urls" : "missing_cloudflare_token",
|
|
1731
|
+
urls: urls ?? [],
|
|
1732
|
+
results: []
|
|
1733
|
+
};
|
|
1717
1734
|
}
|
|
1718
1735
|
try {
|
|
1719
1736
|
const results = purgeCloudflareCacheByUrls(urls, deployConfig, {
|
|
1720
|
-
env
|
|
1737
|
+
env
|
|
1721
1738
|
});
|
|
1722
1739
|
recordCachePurgeResult(state.webCache.contentPurge, results);
|
|
1723
1740
|
writeDeployState(tenantRoot, state, { target });
|
|
@@ -3657,8 +3674,14 @@ function finalizeDeploymentState(tenantRoot, options = {}) {
|
|
|
3657
3674
|
writeDeployState(tenantRoot, state, { target });
|
|
3658
3675
|
if (target.kind === "persistent") {
|
|
3659
3676
|
try {
|
|
3660
|
-
purgeSourcePageCaches(tenantRoot, { target });
|
|
3661
|
-
|
|
3677
|
+
const purgeResult = purgeSourcePageCaches(tenantRoot, { target, env: options.env });
|
|
3678
|
+
if (target.scope === "prod" && purgeResult?.skipped) {
|
|
3679
|
+
throw new Error(`Production source-page cache purge was skipped: ${purgeResult.reason ?? "unknown"}.`);
|
|
3680
|
+
}
|
|
3681
|
+
} catch (error) {
|
|
3682
|
+
if (target.scope === "prod") {
|
|
3683
|
+
throw error;
|
|
3684
|
+
}
|
|
3662
3685
|
}
|
|
3663
3686
|
return loadDeployState(tenantRoot, deployConfig, { target });
|
|
3664
3687
|
}
|
|
@@ -160,11 +160,12 @@ function collectTreeseedHostedServiceChecks(options) {
|
|
|
160
160
|
const selectedAppId = options.appId?.trim() || null;
|
|
161
161
|
const applications = discoverTreeseedApplications(tenantRoot);
|
|
162
162
|
const selectedApplication = selectedAppId ? applications.find((application) => application.id === selectedAppId || application.relativeRoot === selectedAppId) : null;
|
|
163
|
+
const selectedService = (serviceKey) => selectedServiceKeys.size === 0 || selectedServiceKeys.has(serviceKey);
|
|
163
164
|
const workspaceHasApiApplication = applications.some(
|
|
164
165
|
(application) => application.roles.includes("api") || application.config.surfaces?.api?.enabled === true || application.config.services?.api?.enabled !== false && Boolean(application.config.services?.api)
|
|
165
166
|
);
|
|
166
|
-
const includeWeb = !selectedAppId || selectedAppId === "web" || selectedApplication?.roles.includes("web") === true;
|
|
167
|
-
const includeApi = !selectedAppId || selectedAppId === "api" || selectedApplication?.roles.includes("api") === true;
|
|
167
|
+
const includeWeb = selectedService("web") && (!selectedAppId || selectedAppId === "web" || selectedApplication?.roles.includes("web") === true);
|
|
168
|
+
const includeApi = selectedService("api") && (!selectedAppId || selectedAppId === "api" || selectedApplication?.roles.includes("api") === true);
|
|
168
169
|
const selectedAppHasApi = Boolean(
|
|
169
170
|
selectedApplication?.roles.includes("api") || selectedApplication?.config.surfaces?.api?.enabled === true || selectedApplication?.config.services?.api?.enabled !== false && selectedApplication?.config.services?.api || selectedAppId === "web" && workspaceHasApiApplication || selectedAppId === "web" && (deployConfig.surfaces?.api?.enabled === true || deployConfig.services?.api?.enabled !== false && deployConfig.services?.api)
|
|
170
171
|
);
|
|
@@ -190,12 +191,13 @@ function collectTreeseedHostedServiceChecks(options) {
|
|
|
190
191
|
if (domain) {
|
|
191
192
|
const url = String(domain).startsWith("http") ? String(domain) : `https://${domain}`;
|
|
192
193
|
checks.push({ ...httpStatus(url, options), id: `http:${selectedWeb.appId}`, serviceKey: selectedWeb.appId, serviceType: "web", description: "Web public URL responds." });
|
|
193
|
-
if (!selectedAppId || selectedAppHasApi) {
|
|
194
|
+
if (selectedServiceKeys.size === 0 && (!selectedAppId || selectedAppHasApi)) {
|
|
194
195
|
checks.push({ ...httpStatus(`${url.replace(/\/+$/u, "")}/v1/healthz`, options), id: `http:${selectedWeb.appId}:v1-healthz`, serviceKey: selectedWeb.appId, serviceType: "web", description: "Web proxy reaches API health." });
|
|
195
196
|
}
|
|
196
197
|
}
|
|
197
198
|
}
|
|
198
199
|
for (const [surfaceKey, surface] of Object.entries(deployConfig.surfaces ?? {})) {
|
|
200
|
+
if (!selectedService(surfaceKey)) continue;
|
|
199
201
|
if (selectedAppId === "api" && surfaceKey === "web") continue;
|
|
200
202
|
if (surface && typeof surface === "object" && surface.enabled !== false && surface.provider && !["cloudflare", "railway"].includes(surface.provider)) {
|
|
201
203
|
checks.push(check({
|
|
@@ -423,13 +423,14 @@ async function collectHttpObservations(options) {
|
|
|
423
423
|
const fallbacks = /* @__PURE__ */ new Map();
|
|
424
424
|
const selectedServiceKeys = selectedServiceKeySet(options);
|
|
425
425
|
const selectedApplication = options.appId ? discoverTreeseedApplications(options.tenantRoot).find((application) => application.id === options.appId || application.relativeRoot === options.appId) : null;
|
|
426
|
-
|
|
426
|
+
const webHttpSelected = selectedServiceKeys.size === 0 || selectedServiceKeys.has("web");
|
|
427
|
+
if (webHttpSelected && (!options.appId || options.appId === "web" || selectedApplication?.roles.includes("web"))) {
|
|
427
428
|
const webConfig = selectedWebConfig(deployConfig, selectedApplication);
|
|
428
429
|
const webDomain = webConfig.surfaces?.web?.environments?.[options.target]?.domain ?? webConfig.surfaces?.web?.publicBaseUrl ?? webConfig.siteUrl;
|
|
429
430
|
const webUrl = urlForDomain(webDomain);
|
|
430
431
|
if (webUrl) {
|
|
431
432
|
urls.add(webUrl);
|
|
432
|
-
if (!options.appId || options.appId === "web" || selectedApplication?.roles.includes("api")) {
|
|
433
|
+
if (selectedServiceKeys.size === 0 && (!options.appId || options.appId === "web" || selectedApplication?.roles.includes("api"))) {
|
|
433
434
|
urls.add(`${webUrl}/v1/healthz`);
|
|
434
435
|
}
|
|
435
436
|
const pagesProjectName = webConfig.cloudflare?.pages?.projectName;
|
|
@@ -196,7 +196,7 @@ function isTransientWranglerOutput(output) {
|
|
|
196
196
|
return /fetch failed|timed out|etimedout|econnreset|enetunreach|temporarily unavailable|connectivity issue|internal error|aborted/i.test(output);
|
|
197
197
|
}
|
|
198
198
|
async function runPrefixedWranglerWithRetry(tenantRoot, args, {
|
|
199
|
-
env = {},
|
|
199
|
+
env: env2 = {},
|
|
200
200
|
write,
|
|
201
201
|
prefix
|
|
202
202
|
}) {
|
|
@@ -208,7 +208,7 @@ async function runPrefixedWranglerWithRetry(tenantRoot, args, {
|
|
|
208
208
|
}
|
|
209
209
|
const result = await runPrefixedCommand(wrangler.command, [...wrangler.argsPrefix, ...args], {
|
|
210
210
|
cwd: tenantRoot,
|
|
211
|
-
env,
|
|
211
|
+
env: env2,
|
|
212
212
|
write,
|
|
213
213
|
prefix
|
|
214
214
|
});
|
|
@@ -235,7 +235,7 @@ function prepareTenantCloudflareDeploy({
|
|
|
235
235
|
target: explicitTarget,
|
|
236
236
|
dryRun,
|
|
237
237
|
write,
|
|
238
|
-
env = process.env
|
|
238
|
+
env: env2 = process.env
|
|
239
239
|
}) {
|
|
240
240
|
const target = explicitTarget ?? createPersistentDeployTarget(scope === "local" ? "staging" : scope);
|
|
241
241
|
if (scope !== "local") {
|
|
@@ -260,7 +260,7 @@ function prepareTenantCloudflareDeploy({
|
|
|
260
260
|
pagesBranchName,
|
|
261
261
|
env: {
|
|
262
262
|
...process.env,
|
|
263
|
-
...
|
|
263
|
+
...env2,
|
|
264
264
|
CLOUDFLARE_ACCOUNT_ID: resolveConfiguredCloudflareAccountId(deployConfig),
|
|
265
265
|
...target.kind === "persistent" && target.scope !== "local" ? { TREESEED_CONTENT_SERVING_MODE: "published_runtime" } : {}
|
|
266
266
|
},
|
|
@@ -478,12 +478,12 @@ async function repairHostingAfterSuccessfulDeploy(options, systems) {
|
|
|
478
478
|
};
|
|
479
479
|
}
|
|
480
480
|
const environment = options.scope === "prod" ? "prod" : "staging";
|
|
481
|
-
const
|
|
481
|
+
const env2 = { ...process.env, ...options.env ?? {} };
|
|
482
482
|
const audit = await runTreeseedHostingAudit({
|
|
483
483
|
tenantRoot: options.tenantRoot,
|
|
484
484
|
environment,
|
|
485
485
|
repair: false,
|
|
486
|
-
env,
|
|
486
|
+
env: env2,
|
|
487
487
|
hostKinds: [...hostKinds]
|
|
488
488
|
});
|
|
489
489
|
if (audit.ok) {
|
|
@@ -494,7 +494,7 @@ async function repairHostingAfterSuccessfulDeploy(options, systems) {
|
|
|
494
494
|
tenantRoot: options.tenantRoot,
|
|
495
495
|
environment,
|
|
496
496
|
repair: true,
|
|
497
|
-
env,
|
|
497
|
+
env: env2,
|
|
498
498
|
hostKinds: [...hostKinds],
|
|
499
499
|
write: (line) => options.write?.(`[${environment}][hosting][repair] ${line}`)
|
|
500
500
|
});
|
|
@@ -955,7 +955,7 @@ async function publishContent(options, reporter, publishOptions = {}) {
|
|
|
955
955
|
}, manifestFile, uploadOptions);
|
|
956
956
|
if (contentPurgeUrls.size > 0) {
|
|
957
957
|
try {
|
|
958
|
-
purgePublishedContentCaches(options.tenantRoot, [...contentPurgeUrls].filter(Boolean), { target });
|
|
958
|
+
purgePublishedContentCaches(options.tenantRoot, [...contentPurgeUrls].filter(Boolean), { target, env });
|
|
959
959
|
} catch {
|
|
960
960
|
}
|
|
961
961
|
}
|
|
@@ -1020,12 +1020,12 @@ async function provisionProjectPlatform(options) {
|
|
|
1020
1020
|
writeWorkflowStatus("provision:resolve-bootstrap-systems");
|
|
1021
1021
|
const bootstrapSystems = resolveProjectPlatformBootstrapSystems(options, siteConfig);
|
|
1022
1022
|
const selectedSystems = new Set(bootstrapSystems);
|
|
1023
|
-
const
|
|
1023
|
+
const env2 = { ...process.env, ...options.env ?? {} };
|
|
1024
1024
|
writeWorkflowStatus(`provision:reconcile:start systems=${bootstrapSystems.join(",") || "(none)"}`);
|
|
1025
1025
|
const summary = await timedPhase(timings, "provision:reconcile", () => reconcileTreeseedTarget({
|
|
1026
1026
|
tenantRoot: options.tenantRoot,
|
|
1027
1027
|
target,
|
|
1028
|
-
env,
|
|
1028
|
+
env: env2,
|
|
1029
1029
|
systems: bootstrapSystems,
|
|
1030
1030
|
write: options.write,
|
|
1031
1031
|
dryRun: options.dryRun
|
|
@@ -1036,7 +1036,7 @@ async function provisionProjectPlatform(options) {
|
|
|
1036
1036
|
const verification = await timedPhase(timings, "provision:collect-reconcile-status", () => collectTreeseedReconcileStatus({
|
|
1037
1037
|
tenantRoot: options.tenantRoot,
|
|
1038
1038
|
target,
|
|
1039
|
-
env,
|
|
1039
|
+
env: env2,
|
|
1040
1040
|
systems: bootstrapSystems
|
|
1041
1041
|
}));
|
|
1042
1042
|
writeWorkflowStatus("provision:collect-reconcile-status:done");
|
|
@@ -1050,7 +1050,7 @@ async function provisionProjectPlatform(options) {
|
|
|
1050
1050
|
writeWorkflowStatus("provision:ensure-wrangler-config:skipped");
|
|
1051
1051
|
}
|
|
1052
1052
|
const shouldValidateRailway = selectedSystems.has("api") || selectedSystems.has("agents");
|
|
1053
|
-
const railwayValidation = shouldValidateRailway ? options.scope === "local" ? validateRailwayServiceConfiguration(options.tenantRoot, options.scope) : validateRailwayDeployPrerequisites(options.tenantRoot, options.scope, { env }) : { services: [] };
|
|
1053
|
+
const railwayValidation = shouldValidateRailway ? options.scope === "local" ? validateRailwayServiceConfiguration(options.tenantRoot, options.scope) : validateRailwayDeployPrerequisites(options.tenantRoot, options.scope, { env: env2 }) : { services: [] };
|
|
1054
1054
|
const railwaySchedules = [];
|
|
1055
1055
|
const railwayScheduleVerification = {
|
|
1056
1056
|
ok: true,
|
|
@@ -1210,7 +1210,7 @@ async function deployProjectPlatform(options) {
|
|
|
1210
1210
|
const selectedSystems = new Set(bootstrapSystems);
|
|
1211
1211
|
const execution = options.bootstrapExecution ?? "parallel";
|
|
1212
1212
|
const write = options.write;
|
|
1213
|
-
const
|
|
1213
|
+
const env2 = { ...process.env, ...options.env ?? {} };
|
|
1214
1214
|
writeWorkflowStatus(`deploy:bootstrap-systems ${bootstrapSystems.join(",") || "(none)"}`);
|
|
1215
1215
|
writeWorkflowStatus("deploy:report-running");
|
|
1216
1216
|
await reportDeployment(reporter, {
|
|
@@ -1238,7 +1238,7 @@ async function deployProjectPlatform(options) {
|
|
|
1238
1238
|
tenantRoot: options.tenantRoot,
|
|
1239
1239
|
scope: "local",
|
|
1240
1240
|
dryRun: options.dryRun,
|
|
1241
|
-
env,
|
|
1241
|
+
env: env2,
|
|
1242
1242
|
write
|
|
1243
1243
|
})
|
|
1244
1244
|
});
|
|
@@ -1248,7 +1248,7 @@ async function deployProjectPlatform(options) {
|
|
|
1248
1248
|
scope: options.scope,
|
|
1249
1249
|
dryRun: options.dryRun,
|
|
1250
1250
|
write,
|
|
1251
|
-
env
|
|
1251
|
+
env: env2
|
|
1252
1252
|
});
|
|
1253
1253
|
}
|
|
1254
1254
|
if (cloudflareContext && selectedSystems.has("data")) {
|
|
@@ -1283,11 +1283,11 @@ async function deployProjectPlatform(options) {
|
|
|
1283
1283
|
const serviceResultsByKey = /* @__PURE__ */ new Map();
|
|
1284
1284
|
let selectedRailwayServiceKeys = [];
|
|
1285
1285
|
if (options.scope !== "local" && (selectedSystems.has("api") || selectedSystems.has("agents"))) {
|
|
1286
|
-
const validation = validateRailwayDeployPrerequisites(options.tenantRoot, options.scope, { env });
|
|
1286
|
+
const validation = validateRailwayDeployPrerequisites(options.tenantRoot, options.scope, { env: env2 });
|
|
1287
1287
|
const selectedServices = validation.services.filter(
|
|
1288
1288
|
(service) => service.key === "api" ? selectedSystems.has("api") : selectedSystems.has("agents")
|
|
1289
1289
|
);
|
|
1290
|
-
const sequentialRailwayDeploys = String(
|
|
1290
|
+
const sequentialRailwayDeploys = String(env2.TREESEED_RAILWAY_DEPLOY_SEQUENTIAL ?? "").trim() === "1";
|
|
1291
1291
|
let previousRailwayDeployNodeId = null;
|
|
1292
1292
|
for (const service of selectedServices) {
|
|
1293
1293
|
const system = service.key === "api" ? "api" : "agents";
|
|
@@ -1304,7 +1304,7 @@ async function deployProjectPlatform(options) {
|
|
|
1304
1304
|
const result = await deployRailwayService(options.tenantRoot, service, {
|
|
1305
1305
|
dryRun: options.dryRun,
|
|
1306
1306
|
write,
|
|
1307
|
-
env,
|
|
1307
|
+
env: env2,
|
|
1308
1308
|
prefix: {
|
|
1309
1309
|
scope: options.scope,
|
|
1310
1310
|
system,
|
|
@@ -1336,7 +1336,7 @@ async function deployProjectPlatform(options) {
|
|
|
1336
1336
|
task: "schedules",
|
|
1337
1337
|
stage: "deploy"
|
|
1338
1338
|
}, "Reconciling Railway schedules...");
|
|
1339
|
-
railwaySchedules = await ensureRailwayScheduledJobs(options.tenantRoot, options.scope, { dryRun: options.dryRun, env });
|
|
1339
|
+
railwaySchedules = await ensureRailwayScheduledJobs(options.tenantRoot, options.scope, { dryRun: options.dryRun, env: env2 });
|
|
1340
1340
|
railwayScheduleVerification = !options.dryRun ? await verifyRailwayScheduledJobs(options.tenantRoot, options.scope) : { ok: true, checks: railwaySchedules, skipped: true, reason: "dry_run" };
|
|
1341
1341
|
return {
|
|
1342
1342
|
service: "railway-schedules",
|
|
@@ -1358,7 +1358,8 @@ async function deployProjectPlatform(options) {
|
|
|
1358
1358
|
if (options.scope !== "local" && !options.dryRun && (selectedSystems.has("web") || serviceResults.length > 0)) {
|
|
1359
1359
|
finalizeDeploymentState(options.tenantRoot, {
|
|
1360
1360
|
target: createPersistentDeployTarget(options.scope),
|
|
1361
|
-
serviceResults
|
|
1361
|
+
serviceResults,
|
|
1362
|
+
env: env2
|
|
1362
1363
|
});
|
|
1363
1364
|
}
|
|
1364
1365
|
if (!managesRailwaySchedules || !selectedSystems.has("agents")) {
|
|
@@ -1423,7 +1424,7 @@ async function publishProjectContent(options) {
|
|
|
1423
1424
|
async function monitorProjectPlatform(options) {
|
|
1424
1425
|
const timings = [];
|
|
1425
1426
|
const reporter = resolveReporter(options.tenantRoot, options.reporter);
|
|
1426
|
-
const
|
|
1427
|
+
const env2 = { ...process.env, ...options.env ?? {} };
|
|
1427
1428
|
const target = createPersistentDeployTarget(options.scope === "local" ? "staging" : options.scope);
|
|
1428
1429
|
const siteConfig = loadCliDeployConfig(options.tenantRoot);
|
|
1429
1430
|
const selectedSystems = new Set(resolveProjectPlatformBootstrapSystems(options, siteConfig));
|
|
@@ -1434,7 +1435,7 @@ async function monitorProjectPlatform(options) {
|
|
|
1434
1435
|
const apiBaseUrl = resolveImmediateApiProbeUrl(siteConfig, state, target);
|
|
1435
1436
|
const apiMonitorEndpoints = resolveApiMonitorEndpoints(siteConfig, apiBaseUrl);
|
|
1436
1437
|
const railwayResourcesPromise = options.scope === "local" || !apiSelected && !agentsSelected ? { ok: true, skipped: true, reason: options.scope === "local" ? "local_scope" : "railway_not_selected" } : timedPhase(timings, "monitor:railway-resources", () => verifyRailwayManagedResources(options.tenantRoot, options.scope, {
|
|
1437
|
-
env,
|
|
1438
|
+
env: env2,
|
|
1438
1439
|
settleDeployments: true,
|
|
1439
1440
|
onProgress: options.write
|
|
1440
1441
|
}));
|
|
@@ -1167,6 +1167,7 @@ export declare function workflowRelease(helpers: WorkflowOperationHelpers, input
|
|
|
1167
1167
|
releaseBlockingFailures: number;
|
|
1168
1168
|
};
|
|
1169
1169
|
};
|
|
1170
|
+
productionWebVerification: Record<string, unknown> | null;
|
|
1170
1171
|
backMerge: {
|
|
1171
1172
|
packages: {
|
|
1172
1173
|
status: string;
|
|
@@ -617,6 +617,33 @@ ${liveFailures.join("\n")}`, {
|
|
|
617
617
|
liveVerification: live
|
|
618
618
|
};
|
|
619
619
|
}
|
|
620
|
+
async function runReleaseWebLiveVerification(root, environment, helpers, operation) {
|
|
621
|
+
const env = {
|
|
622
|
+
...helpers.context.env,
|
|
623
|
+
...collectTreeseedConfigSeedValues(root, environment, helpers.context.env)
|
|
624
|
+
};
|
|
625
|
+
const live = await collectTreeseedLiveHostedServiceChecks({
|
|
626
|
+
tenantRoot: root,
|
|
627
|
+
target: environment,
|
|
628
|
+
appId: "web",
|
|
629
|
+
serviceKeys: ["web"],
|
|
630
|
+
strict: true,
|
|
631
|
+
requireLiveRailway: false,
|
|
632
|
+
requireLiveHttp: true,
|
|
633
|
+
env
|
|
634
|
+
});
|
|
635
|
+
const liveFailures = [
|
|
636
|
+
...live.checks.filter((check) => check.status === "failed").map((check) => `${check.id}: ${check.issues.join("; ") || "failed"}`),
|
|
637
|
+
...live.liveObservation.issues
|
|
638
|
+
];
|
|
639
|
+
if (liveFailures.length > 0) {
|
|
640
|
+
workflowError(operation, "hosted_live_verification_failed", `Production web live verification failed after root deployment:
|
|
641
|
+
${liveFailures.join("\n")}`, {
|
|
642
|
+
details: { environment, live }
|
|
643
|
+
});
|
|
644
|
+
}
|
|
645
|
+
return live;
|
|
646
|
+
}
|
|
620
647
|
function productionReleaseImageRefEnv(selectedVersions) {
|
|
621
648
|
const refs = {};
|
|
622
649
|
const apiVersion = selectedVersions.get("@treeseed/api");
|
|
@@ -5664,6 +5691,7 @@ ${rendered}`);
|
|
|
5664
5691
|
runId: workflowRun.runId,
|
|
5665
5692
|
onProgress: (line, stream) => helpers.write(line, stream)
|
|
5666
5693
|
}).then((workflowGates) => ({ workflowGates })));
|
|
5694
|
+
const productionWebVerification = await executeJournalStep(root, workflowRun.runId, "production-web-live-verification", () => runReleaseWebLiveVerification(root, "prod", helpers, "release"));
|
|
5667
5695
|
const backMerge = await executeJournalStep(root, workflowRun.runId, "release-back-merge", () => {
|
|
5668
5696
|
const packageBackMerges = checkedOutWorkspacePackageRepos(root).filter((pkg) => selectedPackageSet.has(pkg.name)).map((pkg) => backMergeProductionIntoStaging(pkg.dir, pkg.name, releaseAdminMessage({
|
|
5669
5697
|
subject: `release: back-merge ${PRODUCTION_BRANCH} into ${STAGING_BRANCH}`,
|
|
@@ -5692,6 +5720,7 @@ ${rendered}`);
|
|
|
5692
5720
|
publishedArtifacts,
|
|
5693
5721
|
productionHosting,
|
|
5694
5722
|
productionApiGuarantees,
|
|
5723
|
+
productionWebVerification,
|
|
5695
5724
|
backMerge,
|
|
5696
5725
|
workspaceLinks,
|
|
5697
5726
|
releasedCommit: String(rootRelease.commit.commitSha ?? ""),
|