@treeseed/sdk 0.12.26 → 0.12.28
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/README.md +1 -1
- package/dist/guarantees/index.js +15 -1
- package/dist/hosting/builtins.js +4 -4
- package/dist/hosting/contracts.d.ts +3 -3
- package/dist/hosting/graph.d.ts +4 -4
- package/dist/hosting/graph.js +17 -17
- package/dist/operations/agent-tools.d.ts +1 -1
- package/dist/operations/agent-tools.js +1 -1
- package/dist/operations/providers/default.js +2 -2
- package/dist/operations/services/config-runtime.d.ts +9 -9
- package/dist/operations/services/config-runtime.js +10 -10
- package/dist/operations/services/deploy.d.ts +8 -8
- package/dist/operations/services/deploy.js +151 -151
- package/dist/operations/services/github-automation.d.ts +6 -6
- package/dist/operations/services/github-automation.js +8 -8
- package/dist/operations/services/hub-launch.d.ts +1 -1
- package/dist/operations/services/hub-launch.js +1 -1
- package/dist/operations/services/package-adapters.d.ts +2 -2
- package/dist/operations/services/package-adapters.js +2 -2
- package/dist/operations/services/project-host-operations.d.ts +1 -1
- package/dist/operations/services/project-host-operations.js +1 -1
- package/dist/operations/services/project-platform.d.ts +8 -8
- package/dist/operations/services/project-platform.js +31 -31
- package/dist/operations/services/project-web-monitor.d.ts +1 -1
- package/dist/operations/services/project-web-monitor.js +4 -4
- package/dist/operations/services/railway-deploy.d.ts +4 -4
- package/dist/operations/services/railway-deploy.js +7 -7
- package/dist/operations/services/repository-save-orchestrator.js +3 -3
- package/dist/operations/services/template-secret-sync.d.ts +1 -1
- package/dist/operations/services/template-secret-sync.js +3 -3
- package/dist/reconcile/builtin-adapters.js +86 -40
- package/dist/reconcile/contracts.d.ts +1 -1
- package/dist/reconcile/engine.d.ts +4 -4
- package/dist/reconcile/engine.js +12 -12
- package/dist/reconcile/providers/release-private.d.ts +1 -1
- package/dist/scripts/tenant-deploy.js +9 -9
- package/dist/scripts/tenant-destroy.js +7 -7
- package/dist/scripts/tenant-workflow-action.js +4 -4
- package/dist/scripts/test-scaffold.js +2 -2
- package/dist/scripts/workspace-command-e2e.js +4 -4
- package/dist/sdk-types.d.ts +2 -2
- package/dist/treedx/generated/openapi-types.d.ts +11 -11
- package/dist/treedx/types.d.ts +15 -15
- package/dist/workflow/operations.d.ts +6 -4
- package/dist/workflow/operations.js +46 -23
- package/dist/workflow.d.ts +0 -9
- package/package.json +1 -1
|
@@ -114,8 +114,8 @@ export declare function ensureStandardizedGitHubWorkflows(tenantRoot: any): {
|
|
|
114
114
|
export declare function listGitHubSecretNames(repository: any, tenantRoot: any): Promise<Set<string>>;
|
|
115
115
|
export declare function listGitHubVariableNames(repository: any, tenantRoot: any): Promise<Set<string>>;
|
|
116
116
|
export declare function formatMissingSecretsReport(repository: any, missingSecrets: any, reason?: string): string;
|
|
117
|
-
export declare function ensureGitHubSecrets(tenantRoot: any, {
|
|
118
|
-
|
|
117
|
+
export declare function ensureGitHubSecrets(tenantRoot: any, { planOnly }?: {
|
|
118
|
+
planOnly?: boolean | undefined;
|
|
119
119
|
}): Promise<{
|
|
120
120
|
existing: never[];
|
|
121
121
|
created: never[];
|
|
@@ -123,8 +123,8 @@ export declare function ensureGitHubSecrets(tenantRoot: any, { dryRun }?: {
|
|
|
123
123
|
existing: string[];
|
|
124
124
|
created: string[];
|
|
125
125
|
}>;
|
|
126
|
-
export declare function ensureGitHubEnvironment(tenantRoot: any, {
|
|
127
|
-
|
|
126
|
+
export declare function ensureGitHubEnvironment(tenantRoot: any, { planOnly, scope, purpose, valuesOverlay, managedHostMode }?: {
|
|
127
|
+
planOnly?: boolean | undefined;
|
|
128
128
|
scope?: string | undefined;
|
|
129
129
|
purpose?: string | undefined;
|
|
130
130
|
valuesOverlay?: {} | undefined;
|
|
@@ -152,8 +152,8 @@ export declare function ensureGitHubEnvironment(tenantRoot: any, { dryRun, scope
|
|
|
152
152
|
};
|
|
153
153
|
skipped?: undefined;
|
|
154
154
|
}>;
|
|
155
|
-
export declare function ensureGitHubDeployAutomation(tenantRoot: any, {
|
|
156
|
-
|
|
155
|
+
export declare function ensureGitHubDeployAutomation(tenantRoot: any, { planOnly, valuesOverlay }?: {
|
|
156
|
+
planOnly?: boolean | undefined;
|
|
157
157
|
valuesOverlay?: {} | undefined;
|
|
158
158
|
}): Promise<{
|
|
159
159
|
mode: string;
|
|
@@ -380,18 +380,18 @@ function formatMissingSecretsReport(repository, missingSecrets, reason = "missin
|
|
|
380
380
|
}
|
|
381
381
|
return lines.join("\n");
|
|
382
382
|
}
|
|
383
|
-
async function ensureGitHubSecrets(tenantRoot, {
|
|
384
|
-
return (await ensureGitHubEnvironment(tenantRoot, {
|
|
383
|
+
async function ensureGitHubSecrets(tenantRoot, { planOnly = false } = {}) {
|
|
384
|
+
return (await ensureGitHubEnvironment(tenantRoot, { planOnly })).secrets;
|
|
385
385
|
}
|
|
386
386
|
function nonEmptyValues(values = {}) {
|
|
387
387
|
return Object.fromEntries(
|
|
388
388
|
Object.entries(values).filter(([, value]) => typeof value === "string" && value.length > 0)
|
|
389
389
|
);
|
|
390
390
|
}
|
|
391
|
-
async function ensureGitHubEnvironment(tenantRoot, {
|
|
391
|
+
async function ensureGitHubEnvironment(tenantRoot, { planOnly = false, scope = "prod", purpose = "save", valuesOverlay = {}, managedHostMode = "auto" } = {}) {
|
|
392
392
|
const repository = maybeResolveGitHubRepositorySlug(tenantRoot);
|
|
393
393
|
if (!repository) {
|
|
394
|
-
if (
|
|
394
|
+
if (planOnly) {
|
|
395
395
|
return {
|
|
396
396
|
repository: null,
|
|
397
397
|
secrets: { existing: [], created: [] },
|
|
@@ -424,7 +424,7 @@ async function ensureGitHubEnvironment(tenantRoot, { dryRun = false, scope = "pr
|
|
|
424
424
|
}
|
|
425
425
|
const createdSecrets = [];
|
|
426
426
|
for (const name of missingRemote) {
|
|
427
|
-
if (
|
|
427
|
+
if (planOnly) {
|
|
428
428
|
createdSecrets.push(name);
|
|
429
429
|
continue;
|
|
430
430
|
}
|
|
@@ -433,7 +433,7 @@ async function ensureGitHubEnvironment(tenantRoot, { dryRun = false, scope = "pr
|
|
|
433
433
|
}
|
|
434
434
|
const createdVariables = [];
|
|
435
435
|
for (const name of missingRemoteVariables) {
|
|
436
|
-
if (
|
|
436
|
+
if (planOnly) {
|
|
437
437
|
createdVariables.push(name);
|
|
438
438
|
continue;
|
|
439
439
|
}
|
|
@@ -452,9 +452,9 @@ async function ensureGitHubEnvironment(tenantRoot, { dryRun = false, scope = "pr
|
|
|
452
452
|
}
|
|
453
453
|
};
|
|
454
454
|
}
|
|
455
|
-
async function ensureGitHubDeployAutomation(tenantRoot, {
|
|
455
|
+
async function ensureGitHubDeployAutomation(tenantRoot, { planOnly = false, valuesOverlay = {} } = {}) {
|
|
456
456
|
const workflows = ensureStandardizedGitHubWorkflows(tenantRoot);
|
|
457
|
-
const environment = await ensureGitHubEnvironment(tenantRoot, {
|
|
457
|
+
const environment = await ensureGitHubEnvironment(tenantRoot, { planOnly, valuesOverlay });
|
|
458
458
|
return {
|
|
459
459
|
mode: getGitHubAutomationMode(),
|
|
460
460
|
workflow: workflows[0],
|
|
@@ -164,7 +164,7 @@ export declare function validateRepositoryHost(host: RepositoryHost): {
|
|
|
164
164
|
};
|
|
165
165
|
export declare function createKnowledgeHubRepositories(input: {
|
|
166
166
|
plan: KnowledgeHubRepositoryPlan;
|
|
167
|
-
|
|
167
|
+
planOnly?: boolean;
|
|
168
168
|
description?: string | null;
|
|
169
169
|
homepageUrl?: string | null;
|
|
170
170
|
}): Promise<{
|
|
@@ -157,7 +157,7 @@ async function createKnowledgeHubRepositories(input) {
|
|
|
157
157
|
const githubEnv = githubToken ? { ...process.env, GH_TOKEN: githubToken, GITHUB_TOKEN: githubToken } : process.env;
|
|
158
158
|
const created = [];
|
|
159
159
|
for (const repository of input.plan.repositories) {
|
|
160
|
-
if (!repository.create || input.
|
|
160
|
+
if (!repository.create || input.planOnly !== false) {
|
|
161
161
|
created.push({ ...repository, status: repository.create ? "planned" : "connected" });
|
|
162
162
|
continue;
|
|
163
163
|
}
|
|
@@ -32,7 +32,7 @@ export type TreeseedPackageAdapter = {
|
|
|
32
32
|
architectures?: string[];
|
|
33
33
|
}>;
|
|
34
34
|
releaseChecks: Array<{
|
|
35
|
-
kind: 'npm-pack-
|
|
35
|
+
kind: 'npm-pack-plan' | 'github-workflow' | 'docker-manifest';
|
|
36
36
|
name: string;
|
|
37
37
|
detail: string;
|
|
38
38
|
}>;
|
|
@@ -140,7 +140,7 @@ export declare function packageAdapterPlanSummary(root?: any): {
|
|
|
140
140
|
architectures?: string[];
|
|
141
141
|
}[];
|
|
142
142
|
releaseChecks: {
|
|
143
|
-
kind: "npm-pack-
|
|
143
|
+
kind: "npm-pack-plan" | "github-workflow" | "docker-manifest";
|
|
144
144
|
name: string;
|
|
145
145
|
detail: string;
|
|
146
146
|
}[];
|
|
@@ -142,7 +142,7 @@ function nodeTypeScriptAdapter(pkg) {
|
|
|
142
142
|
})) : [{ provider: "npm", name: String(pkg.name) }],
|
|
143
143
|
releaseChecks: [
|
|
144
144
|
{ kind: "github-workflow", name: "publish workflow", detail: dockerImageReleaseWorkflow ? `.github/workflows/${dockerImageReleaseWorkflow}` : ".github/workflows/publish.yml" },
|
|
145
|
-
...dockerArtifacts.length > 0 ? dockerArtifacts.map((artifact) => ({ kind: "docker-manifest", name: `${artifact.name} Docker image manifest`, detail: `${artifact.name}:<version>` })) : [{ kind: "npm-pack-
|
|
145
|
+
...dockerArtifacts.length > 0 ? dockerArtifacts.map((artifact) => ({ kind: "docker-manifest", name: `${artifact.name} Docker image manifest`, detail: `${artifact.name}:<version>` })) : [{ kind: "npm-pack-plan", name: "npm pack", detail: "npm pack --plan" }]
|
|
146
146
|
],
|
|
147
147
|
metadata: {
|
|
148
148
|
...repository ? { repository } : {},
|
|
@@ -406,7 +406,7 @@ function planTreeseedPackageDevelopmentImage(root = workspaceRoot(), idOrName, {
|
|
|
406
406
|
environment,
|
|
407
407
|
overrideEnvVar,
|
|
408
408
|
override: { [overrideEnvVar]: hostingImageRef },
|
|
409
|
-
command: `${overrideEnvVar}=${hostingImageRef} npx trsd hosting apply --environment ${environment} --app ${app} --
|
|
409
|
+
command: `${overrideEnvVar}=${hostingImageRef} npx trsd hosting apply --environment ${environment} --app ${app} --json`
|
|
410
410
|
} : null
|
|
411
411
|
};
|
|
412
412
|
}
|
|
@@ -109,7 +109,7 @@ export interface ExecuteProjectHostBindingOperationInput {
|
|
|
109
109
|
commitMessage?: string | null;
|
|
110
110
|
approvalRequired?: boolean;
|
|
111
111
|
approvalId?: string | null;
|
|
112
|
-
|
|
112
|
+
planOnly?: boolean;
|
|
113
113
|
}
|
|
114
114
|
export interface ExecuteProjectHostBindingOperationContext {
|
|
115
115
|
workspaceRoot: string;
|
|
@@ -324,7 +324,7 @@ async function executeProjectHostBindingOperation(input, context) {
|
|
|
324
324
|
valuesOverlay: context.valuesOverlay,
|
|
325
325
|
valuesByScope: context.valuesByScope,
|
|
326
326
|
processEnv: context.processEnv,
|
|
327
|
-
|
|
327
|
+
planOnly: input.planOnly,
|
|
328
328
|
onProgress: context.onProgress
|
|
329
329
|
});
|
|
330
330
|
} catch (error) {
|
|
@@ -10,7 +10,7 @@ export interface ProjectPlatformActionOptions {
|
|
|
10
10
|
scope: ProjectPlatformScope;
|
|
11
11
|
projectId?: string | null;
|
|
12
12
|
previewId?: string | null;
|
|
13
|
-
|
|
13
|
+
planOnly?: boolean;
|
|
14
14
|
reporter?: ControlPlaneReporter;
|
|
15
15
|
skipProvision?: boolean;
|
|
16
16
|
bootstrapSystems?: TreeseedRunnableBootstrapSystem[];
|
|
@@ -24,7 +24,7 @@ export type TenantCloudflareDeployContext = {
|
|
|
24
24
|
tenantRoot: string;
|
|
25
25
|
scope: ProjectPlatformScope;
|
|
26
26
|
target: any;
|
|
27
|
-
|
|
27
|
+
planOnly?: boolean;
|
|
28
28
|
wranglerPath: string;
|
|
29
29
|
databaseName: string;
|
|
30
30
|
pagesProjectName: string | null;
|
|
@@ -32,23 +32,23 @@ export type TenantCloudflareDeployContext = {
|
|
|
32
32
|
env: NodeJS.ProcessEnv | Record<string, string | undefined>;
|
|
33
33
|
write?: TreeseedBootstrapWriter;
|
|
34
34
|
};
|
|
35
|
-
export declare function prepareTenantCloudflareDeploy({ tenantRoot, scope, target: explicitTarget,
|
|
35
|
+
export declare function prepareTenantCloudflareDeploy({ tenantRoot, scope, target: explicitTarget, planOnly, write, env, }: {
|
|
36
36
|
tenantRoot: string;
|
|
37
37
|
scope: ProjectPlatformScope;
|
|
38
38
|
target?: any;
|
|
39
|
-
|
|
39
|
+
planOnly?: boolean;
|
|
40
40
|
write?: TreeseedBootstrapWriter;
|
|
41
41
|
env?: NodeJS.ProcessEnv | Record<string, string | undefined>;
|
|
42
42
|
}): TenantCloudflareDeployContext;
|
|
43
43
|
export declare function runTenantDataMigration(context: TenantCloudflareDeployContext): Promise<{
|
|
44
44
|
databaseName: string;
|
|
45
|
-
|
|
45
|
+
planOnly: boolean;
|
|
46
46
|
}>;
|
|
47
|
-
export declare function runTenantWebBuild(context: Pick<TenantCloudflareDeployContext, 'tenantRoot' | 'scope' | '
|
|
48
|
-
|
|
47
|
+
export declare function runTenantWebBuild(context: Pick<TenantCloudflareDeployContext, 'tenantRoot' | 'scope' | 'planOnly' | 'env' | 'write'>): Promise<{
|
|
48
|
+
planOnly: boolean;
|
|
49
49
|
}>;
|
|
50
50
|
export declare function runTenantWebPublish(context: TenantCloudflareDeployContext): Promise<{
|
|
51
|
-
|
|
51
|
+
planOnly: boolean;
|
|
52
52
|
}>;
|
|
53
53
|
export declare function provisionProjectPlatform(options: ProjectPlatformActionOptions): Promise<{
|
|
54
54
|
ok: boolean;
|
|
@@ -240,7 +240,7 @@ function prepareTenantCloudflareDeploy({
|
|
|
240
240
|
tenantRoot,
|
|
241
241
|
scope,
|
|
242
242
|
target: explicitTarget,
|
|
243
|
-
|
|
243
|
+
planOnly,
|
|
244
244
|
write,
|
|
245
245
|
env: env2 = process.env
|
|
246
246
|
}) {
|
|
@@ -251,7 +251,7 @@ function prepareTenantCloudflareDeploy({
|
|
|
251
251
|
}
|
|
252
252
|
const { wranglerPath, deployConfig, state } = ensureGeneratedWranglerConfig(tenantRoot, { target });
|
|
253
253
|
if (scope !== "local") {
|
|
254
|
-
syncCloudflareSecrets(tenantRoot, { target,
|
|
254
|
+
syncCloudflareSecrets(tenantRoot, { target, planOnly });
|
|
255
255
|
}
|
|
256
256
|
const deployState = loadDeployState(tenantRoot, deployConfig, { target });
|
|
257
257
|
const pagesProjectName = target.kind === "persistent" ? deployState.pages?.projectName ?? null : null;
|
|
@@ -260,7 +260,7 @@ function prepareTenantCloudflareDeploy({
|
|
|
260
260
|
tenantRoot,
|
|
261
261
|
scope,
|
|
262
262
|
target,
|
|
263
|
-
|
|
263
|
+
planOnly,
|
|
264
264
|
wranglerPath,
|
|
265
265
|
databaseName: state.d1Databases.SITE_DATA_DB.databaseName,
|
|
266
266
|
pagesProjectName,
|
|
@@ -275,14 +275,14 @@ function prepareTenantCloudflareDeploy({
|
|
|
275
275
|
};
|
|
276
276
|
}
|
|
277
277
|
async function runTenantDataMigration(context) {
|
|
278
|
-
if (context.
|
|
278
|
+
if (context.planOnly) {
|
|
279
279
|
writeTreeseedBootstrapLine(context.write, {
|
|
280
280
|
scope: context.scope,
|
|
281
281
|
system: "data",
|
|
282
282
|
task: "d1-migrate",
|
|
283
283
|
stage: "deploy"
|
|
284
|
-
}, `
|
|
285
|
-
return { databaseName: context.databaseName,
|
|
284
|
+
}, `Plan: would apply remote migrations for ${context.databaseName}.`);
|
|
285
|
+
return { databaseName: context.databaseName, planOnly: true };
|
|
286
286
|
}
|
|
287
287
|
await runPrefixedWranglerWithRetry(context.tenantRoot, [
|
|
288
288
|
"d1",
|
|
@@ -302,7 +302,7 @@ async function runTenantDataMigration(context) {
|
|
|
302
302
|
stage: "deploy"
|
|
303
303
|
}
|
|
304
304
|
});
|
|
305
|
-
return { databaseName: context.databaseName,
|
|
305
|
+
return { databaseName: context.databaseName, planOnly: false };
|
|
306
306
|
}
|
|
307
307
|
async function runTenantWebBuild(context) {
|
|
308
308
|
const prefix = {
|
|
@@ -311,9 +311,9 @@ async function runTenantWebBuild(context) {
|
|
|
311
311
|
task: "build",
|
|
312
312
|
stage: "deploy"
|
|
313
313
|
};
|
|
314
|
-
if (context.
|
|
315
|
-
writeTreeseedBootstrapLine(context.write, prefix, "
|
|
316
|
-
return {
|
|
314
|
+
if (context.planOnly) {
|
|
315
|
+
writeTreeseedBootstrapLine(context.write, prefix, "Plan: skipped tenant build.");
|
|
316
|
+
return { planOnly: true };
|
|
317
317
|
}
|
|
318
318
|
const result = await runPrefixedCommand(process.execPath, [packageScriptPath("tenant-build")], {
|
|
319
319
|
cwd: context.tenantRoot,
|
|
@@ -324,7 +324,7 @@ async function runTenantWebBuild(context) {
|
|
|
324
324
|
if (result.status !== 0) {
|
|
325
325
|
throw new Error("tenant-build failed.");
|
|
326
326
|
}
|
|
327
|
-
return {
|
|
327
|
+
return { planOnly: false };
|
|
328
328
|
}
|
|
329
329
|
async function runTenantWebPublish(context) {
|
|
330
330
|
const prefix = {
|
|
@@ -333,13 +333,13 @@ async function runTenantWebPublish(context) {
|
|
|
333
333
|
task: "publish",
|
|
334
334
|
stage: "deploy"
|
|
335
335
|
};
|
|
336
|
-
if (context.
|
|
336
|
+
if (context.planOnly) {
|
|
337
337
|
if (context.pagesProjectName) {
|
|
338
|
-
writeTreeseedBootstrapLine(context.write, prefix, `
|
|
338
|
+
writeTreeseedBootstrapLine(context.write, prefix, `Plan: would deploy ${deployTargetLabel(context.target)} to Pages project ${context.pagesProjectName} from ${resolve(context.tenantRoot, "dist")}.`);
|
|
339
339
|
} else {
|
|
340
|
-
writeTreeseedBootstrapLine(context.write, prefix, `
|
|
340
|
+
writeTreeseedBootstrapLine(context.write, prefix, `Plan: would deploy ${deployTargetLabel(context.target)} with generated Wrangler config at ${resolve(context.wranglerPath)}.`);
|
|
341
341
|
}
|
|
342
|
-
return {
|
|
342
|
+
return { planOnly: true };
|
|
343
343
|
}
|
|
344
344
|
if (context.pagesProjectName) {
|
|
345
345
|
const args = [
|
|
@@ -364,7 +364,7 @@ async function runTenantWebPublish(context) {
|
|
|
364
364
|
prefix
|
|
365
365
|
});
|
|
366
366
|
}
|
|
367
|
-
return {
|
|
367
|
+
return { planOnly: false };
|
|
368
368
|
}
|
|
369
369
|
function inferContentType(filePath) {
|
|
370
370
|
const extension = extname(filePath).toLowerCase();
|
|
@@ -462,11 +462,11 @@ async function repairHostingAfterSuccessfulDeploy(options, systems) {
|
|
|
462
462
|
reason: "local_environment"
|
|
463
463
|
};
|
|
464
464
|
}
|
|
465
|
-
if (options.
|
|
465
|
+
if (options.planOnly) {
|
|
466
466
|
return {
|
|
467
467
|
ok: true,
|
|
468
468
|
skipped: true,
|
|
469
|
-
reason: "
|
|
469
|
+
reason: "plan"
|
|
470
470
|
};
|
|
471
471
|
}
|
|
472
472
|
if (process.env.TREESEED_POST_DEPLOY_HOSTING_REPAIR === "off") {
|
|
@@ -908,7 +908,7 @@ async function publishContent(options, reporter, publishOptions = {}) {
|
|
|
908
908
|
}
|
|
909
909
|
const tempRoot = mkdtempSync(join(projectPlatformTempRoot(options.tenantRoot, "content-publish"), "treeseed-content-publish-"));
|
|
910
910
|
try {
|
|
911
|
-
if (!options.
|
|
911
|
+
if (!options.planOnly) {
|
|
912
912
|
const uploadOptions = {
|
|
913
913
|
write: options.write,
|
|
914
914
|
prefix: {
|
|
@@ -969,7 +969,7 @@ async function publishContent(options, reporter, publishOptions = {}) {
|
|
|
969
969
|
}
|
|
970
970
|
}
|
|
971
971
|
const state = loadDeployState(options.tenantRoot, siteConfig, { target });
|
|
972
|
-
if (!options.
|
|
972
|
+
if (!options.planOnly) {
|
|
973
973
|
state.content.lastPublishedManifestRevision = "overlay" in built ? built.overlay.previewId : built.manifest.revision;
|
|
974
974
|
state.content.lastPublishedManifestSha256 = stableHash(
|
|
975
975
|
JSON.stringify("overlay" in built ? built.overlay : built.manifest)
|
|
@@ -1035,7 +1035,7 @@ async function provisionProjectPlatform(options) {
|
|
|
1035
1035
|
env: env2,
|
|
1036
1036
|
systems: bootstrapSystems,
|
|
1037
1037
|
write: options.write,
|
|
1038
|
-
|
|
1038
|
+
planOnly: options.planOnly
|
|
1039
1039
|
}));
|
|
1040
1040
|
writeWorkflowStatus("provision:reconcile:done");
|
|
1041
1041
|
timings.push(...summary.timings ?? []);
|
|
@@ -1204,7 +1204,7 @@ async function provisionProjectPlatform(options) {
|
|
|
1204
1204
|
};
|
|
1205
1205
|
}
|
|
1206
1206
|
async function deployProjectPlatform(options) {
|
|
1207
|
-
writeWorkflowStatus(`deploy:start scope=${options.scope}
|
|
1207
|
+
writeWorkflowStatus(`deploy:start scope=${options.scope} planOnly=${options.planOnly ? "true" : "false"}`);
|
|
1208
1208
|
const timings = [];
|
|
1209
1209
|
const deployStartMs = performance.now();
|
|
1210
1210
|
writeWorkflowStatus("deploy:resolve-reporter");
|
|
@@ -1244,7 +1244,7 @@ async function deployProjectPlatform(options) {
|
|
|
1244
1244
|
run: () => runTenantWebBuild({
|
|
1245
1245
|
tenantRoot: options.tenantRoot,
|
|
1246
1246
|
scope: "local",
|
|
1247
|
-
|
|
1247
|
+
planOnly: options.planOnly,
|
|
1248
1248
|
env: env2,
|
|
1249
1249
|
write
|
|
1250
1250
|
})
|
|
@@ -1253,7 +1253,7 @@ async function deployProjectPlatform(options) {
|
|
|
1253
1253
|
cloudflareContext = prepareTenantCloudflareDeploy({
|
|
1254
1254
|
tenantRoot: options.tenantRoot,
|
|
1255
1255
|
scope: options.scope,
|
|
1256
|
-
|
|
1256
|
+
planOnly: options.planOnly,
|
|
1257
1257
|
write,
|
|
1258
1258
|
env: env2
|
|
1259
1259
|
});
|
|
@@ -1309,7 +1309,7 @@ async function deployProjectPlatform(options) {
|
|
|
1309
1309
|
}),
|
|
1310
1310
|
run: async () => {
|
|
1311
1311
|
const result = await deployRailwayService(options.tenantRoot, service, {
|
|
1312
|
-
|
|
1312
|
+
planOnly: options.planOnly,
|
|
1313
1313
|
write,
|
|
1314
1314
|
env: env2,
|
|
1315
1315
|
prefix: {
|
|
@@ -1330,7 +1330,7 @@ async function deployProjectPlatform(options) {
|
|
|
1330
1330
|
}
|
|
1331
1331
|
const managesRailwaySchedules = options.scope === "staging" || options.scope === "prod";
|
|
1332
1332
|
let railwaySchedules = [];
|
|
1333
|
-
let railwayScheduleVerification = { ok: true, checks: [], skipped: true, reason: !selectedSystems.has("agents") ? "agents_not_selected" : !managesRailwaySchedules ? "scope_not_scheduled" : "
|
|
1333
|
+
let railwayScheduleVerification = { ok: true, checks: [], skipped: true, reason: !selectedSystems.has("agents") ? "agents_not_selected" : !managesRailwaySchedules ? "scope_not_scheduled" : "plan" };
|
|
1334
1334
|
if (managesRailwaySchedules && selectedSystems.has("agents")) {
|
|
1335
1335
|
const agentDeployNodeIds = nodes.filter((node) => node.id.startsWith("agents:") && node.id.endsWith("-railway-deploy")).map((node) => node.id);
|
|
1336
1336
|
nodes.push({
|
|
@@ -1343,8 +1343,8 @@ async function deployProjectPlatform(options) {
|
|
|
1343
1343
|
task: "schedules",
|
|
1344
1344
|
stage: "deploy"
|
|
1345
1345
|
}, "Reconciling Railway schedules...");
|
|
1346
|
-
railwaySchedules = await ensureRailwayScheduledJobs(options.tenantRoot, options.scope, {
|
|
1347
|
-
railwayScheduleVerification = !options.
|
|
1346
|
+
railwaySchedules = await ensureRailwayScheduledJobs(options.tenantRoot, options.scope, { planOnly: options.planOnly, env: env2 });
|
|
1347
|
+
railwayScheduleVerification = !options.planOnly ? await verifyRailwayScheduledJobs(options.tenantRoot, options.scope) : { ok: true, checks: railwaySchedules, skipped: true, reason: "plan" };
|
|
1348
1348
|
return {
|
|
1349
1349
|
service: "railway-schedules",
|
|
1350
1350
|
status: railwayScheduleVerification.ok ? "verified" : "failed",
|
|
@@ -1362,7 +1362,7 @@ async function deployProjectPlatform(options) {
|
|
|
1362
1362
|
for (const result of serviceResults) {
|
|
1363
1363
|
timings.push(...result.timings ?? []);
|
|
1364
1364
|
}
|
|
1365
|
-
if (options.scope !== "local" && !options.
|
|
1365
|
+
if (options.scope !== "local" && !options.planOnly && (selectedSystems.has("web") || serviceResults.length > 0)) {
|
|
1366
1366
|
finalizeDeploymentState(options.tenantRoot, {
|
|
1367
1367
|
target: createPersistentDeployTarget(options.scope),
|
|
1368
1368
|
serviceResults,
|
|
@@ -1371,7 +1371,7 @@ async function deployProjectPlatform(options) {
|
|
|
1371
1371
|
}
|
|
1372
1372
|
if (!managesRailwaySchedules || !selectedSystems.has("agents")) {
|
|
1373
1373
|
railwaySchedules = [];
|
|
1374
|
-
railwayScheduleVerification = { ok: true, checks: railwaySchedules, skipped: true, reason: !selectedSystems.has("agents") ? "agents_not_selected" : !managesRailwaySchedules ? "scope_not_scheduled" : "
|
|
1374
|
+
railwayScheduleVerification = { ok: true, checks: railwaySchedules, skipped: true, reason: !selectedSystems.has("agents") ? "agents_not_selected" : !managesRailwaySchedules ? "scope_not_scheduled" : "plan" };
|
|
1375
1375
|
}
|
|
1376
1376
|
if (selectedSystems.has("agents")) {
|
|
1377
1377
|
serviceResults.push({
|
|
@@ -1456,7 +1456,7 @@ async function monitorProjectPlatform(options) {
|
|
|
1456
1456
|
apiReady: apiSelected && apiMonitorEndpoints.apiReady ? timedPhase(timings, "monitor:probe-api-ready", () => probeHttp(apiMonitorEndpoints.apiReady, { attempts: 8, delayMs: 1e4 })) : Promise.resolve(skippedApiCheck),
|
|
1457
1457
|
d1Health: apiSelected && apiMonitorEndpoints.d1Health ? timedPhase(timings, "monitor:probe-d1-health", () => probeHttp(apiMonitorEndpoints.d1Health, { attempts: 8, delayMs: 1e4 })) : Promise.resolve(skippedD1Check),
|
|
1458
1458
|
agentHealth: agentsSelected && apiMonitorEndpoints.agentHealth ? timedPhase(timings, "monitor:probe-agent-health", () => probeHttp(apiMonitorEndpoints.agentHealth, { attempts: 8, delayMs: 1e4 })) : Promise.resolve(skippedAgentCheck),
|
|
1459
|
-
r2: options.
|
|
1459
|
+
r2: options.planOnly ? Promise.resolve({ ok: true, skipped: true, reason: "plan" }) : timedPhase(timings, "monitor:probe-r2", () => probeR2(options.tenantRoot, siteConfig, state, target)),
|
|
1460
1460
|
scaleProbe: timedPhase(timings, "monitor:probe-scale", () => probeScaleConfiguration(siteConfig, state)),
|
|
1461
1461
|
railwayResources: Promise.resolve(railwayResourcesPromise),
|
|
1462
1462
|
readiness: state.readiness,
|
|
@@ -175,7 +175,7 @@ async function workflowFileCheck(input) {
|
|
|
175
175
|
summary: "GitHub repository is not configured."
|
|
176
176
|
});
|
|
177
177
|
}
|
|
178
|
-
if (input.mockExternal || input.
|
|
178
|
+
if (input.mockExternal || input.planOnly) {
|
|
179
179
|
return check({
|
|
180
180
|
key: "workflow_file",
|
|
181
181
|
label: "Workflow file",
|
|
@@ -226,7 +226,7 @@ async function httpCheck(input) {
|
|
|
226
226
|
summary: "No public URL is available for HTTP probing."
|
|
227
227
|
});
|
|
228
228
|
}
|
|
229
|
-
if (input.mockExternal || input.
|
|
229
|
+
if (input.mockExternal || input.planOnly || !input.fetchImpl) {
|
|
230
230
|
return check({
|
|
231
231
|
key: "http_response",
|
|
232
232
|
label: "HTTP response",
|
|
@@ -296,7 +296,7 @@ async function buildProjectWebMonitorResult(input) {
|
|
|
296
296
|
workflowFile,
|
|
297
297
|
githubClient: input.githubClient,
|
|
298
298
|
mockExternal: input.mockExternal,
|
|
299
|
-
|
|
299
|
+
planOnly: input.planOnly,
|
|
300
300
|
treeDxPublish
|
|
301
301
|
}),
|
|
302
302
|
check({
|
|
@@ -318,7 +318,7 @@ async function buildProjectWebMonitorResult(input) {
|
|
|
318
318
|
url: targetUrl,
|
|
319
319
|
fetchImpl: input.fetchImpl,
|
|
320
320
|
mockExternal: input.mockExternal,
|
|
321
|
-
|
|
321
|
+
planOnly: input.planOnly
|
|
322
322
|
}),
|
|
323
323
|
contentRuntimeCheck({ resolution: contentRuntime, hasRuntimeMetadata }),
|
|
324
324
|
check({
|
|
@@ -81,8 +81,8 @@ export declare function validateRailwayDeployPrerequisites(tenantRoot: any, scop
|
|
|
81
81
|
hostingKind: import("../../platform/contracts.js").TreeseedHostingKind;
|
|
82
82
|
managedTopology: string[];
|
|
83
83
|
};
|
|
84
|
-
export declare function ensureRailwayScheduledJobs(tenantRoot: any, scope: any, {
|
|
85
|
-
|
|
84
|
+
export declare function ensureRailwayScheduledJobs(tenantRoot: any, scope: any, { planOnly, fetchImpl, apiToken, apiUrl, env }?: {
|
|
85
|
+
planOnly?: boolean | undefined;
|
|
86
86
|
fetchImpl?: typeof fetch | undefined;
|
|
87
87
|
env?: NodeJS.ProcessEnv | undefined;
|
|
88
88
|
}): Promise<any[]>;
|
|
@@ -111,8 +111,8 @@ export declare function verifyRailwayManagedResources(tenantRoot: any, scope: an
|
|
|
111
111
|
checks: any[];
|
|
112
112
|
}>;
|
|
113
113
|
export declare function shouldRunRailwayPredeployBuild(env?: NodeJS.ProcessEnv): boolean;
|
|
114
|
-
export declare function deployRailwayService(tenantRoot: any, service: any, {
|
|
115
|
-
|
|
114
|
+
export declare function deployRailwayService(tenantRoot: any, service: any, { planOnly, write, prefix, env, }?: {
|
|
115
|
+
planOnly?: boolean;
|
|
116
116
|
write?: TreeseedBootstrapWriter;
|
|
117
117
|
prefix?: TreeseedBootstrapTaskPrefix;
|
|
118
118
|
env?: NodeJS.ProcessEnv | Record<string, string | undefined>;
|
|
@@ -854,8 +854,8 @@ function resolveRailwayServiceSourcePolicy({ tenantRoot, scope, serviceKey, serv
|
|
|
854
854
|
const configuredSource = service.railway?.source && typeof service.railway.source === "object" && !Array.isArray(service.railway.source) ? service.railway.source : {};
|
|
855
855
|
const configuredRepo = typeof service.railway?.sourceRepo === "string" ? service.railway.sourceRepo : typeof configuredSource.repository === "string" ? configuredSource.repository : typeof configuredSource.repo === "string" ? configuredSource.repo : null;
|
|
856
856
|
const packageRepository = configuredRepo ?? readTreeseedPackageRepository(serviceRoot) ?? readTreeseedPackageRepository(tenantRoot);
|
|
857
|
-
const
|
|
858
|
-
const sourceMode = scope === "prod" ? "image" :
|
|
857
|
+
const apiPackageSourceEligible = ["api", "operationsRunner"].includes(serviceKey);
|
|
858
|
+
const sourceMode = scope === "prod" ? "image" : scope === "staging" && apiPackageSourceEligible ? "git" : configuredMode === "git" || configuredMode === "image" ? configuredMode : imageRef ? "image" : "git";
|
|
859
859
|
if (sourceMode !== "git") {
|
|
860
860
|
return {
|
|
861
861
|
sourceMode: "image",
|
|
@@ -1028,7 +1028,7 @@ function validateRailwayDeployPrerequisites(tenantRoot, scope, { env = process.e
|
|
|
1028
1028
|
}
|
|
1029
1029
|
return validation;
|
|
1030
1030
|
}
|
|
1031
|
-
async function ensureRailwayScheduledJobs(tenantRoot, scope, {
|
|
1031
|
+
async function ensureRailwayScheduledJobs(tenantRoot, scope, { planOnly = false, fetchImpl = fetch, apiToken, apiUrl, env = process.env } = {}) {
|
|
1032
1032
|
const { schedules } = validateRailwayServiceConfiguration(tenantRoot, scope);
|
|
1033
1033
|
if (schedules.length === 0) {
|
|
1034
1034
|
return [];
|
|
@@ -1044,7 +1044,7 @@ async function ensureRailwayScheduledJobs(tenantRoot, scope, { dryRun = false, f
|
|
|
1044
1044
|
const target = await resolveRailwayScheduleTarget(schedule, {
|
|
1045
1045
|
env,
|
|
1046
1046
|
fetchImpl,
|
|
1047
|
-
ensure: !
|
|
1047
|
+
ensure: !planOnly
|
|
1048
1048
|
});
|
|
1049
1049
|
if (!target.project || !target.environment || !target.service) {
|
|
1050
1050
|
results.push({
|
|
@@ -1074,7 +1074,7 @@ async function ensureRailwayScheduledJobs(tenantRoot, scope, { dryRun = false, f
|
|
|
1074
1074
|
const drifted = Boolean(
|
|
1075
1075
|
current.id && (current.cronSchedule !== desired.schedule || (current.startCommand ?? null) !== (desired.command ?? null))
|
|
1076
1076
|
);
|
|
1077
|
-
if (
|
|
1077
|
+
if (planOnly) {
|
|
1078
1078
|
results.push({
|
|
1079
1079
|
...schedule,
|
|
1080
1080
|
projectId: target.project.id,
|
|
@@ -1596,13 +1596,13 @@ async function syncRailwayServiceRuntimeConfigurationAfterDeploy(tenantRoot, ser
|
|
|
1596
1596
|
};
|
|
1597
1597
|
}
|
|
1598
1598
|
async function deployRailwayService(tenantRoot, service, {
|
|
1599
|
-
|
|
1599
|
+
planOnly = false,
|
|
1600
1600
|
write,
|
|
1601
1601
|
prefix,
|
|
1602
1602
|
env = process.env
|
|
1603
1603
|
} = {}) {
|
|
1604
1604
|
const timings = [];
|
|
1605
|
-
if (
|
|
1605
|
+
if (planOnly) {
|
|
1606
1606
|
return {
|
|
1607
1607
|
service: service.key,
|
|
1608
1608
|
status: "planned",
|
|
@@ -922,7 +922,7 @@ async function runProjectVerificationInstallWithRetry(node, options) {
|
|
|
922
922
|
const packageJson = node.packageJson ?? (existsSync(resolve(node.path, "package.json")) ? readJson(resolve(node.path, "package.json")) : null);
|
|
923
923
|
const rootWorkspaceInstall = node.path === options.root && Array.isArray(packageJson?.workspaces);
|
|
924
924
|
if (rootWorkspaceInstall) {
|
|
925
|
-
emitProgress(options, node, "install", "Skipped root npm ci project verification install; lockfile
|
|
925
|
+
emitProgress(options, node, "install", "Skipped root npm ci project verification install; lockfile plan and restored workspace links provide save-time dependency proof.");
|
|
926
926
|
return;
|
|
927
927
|
}
|
|
928
928
|
const args = rootWorkspaceInstall ? ["ci"] : ["ci", "--workspaces=false"];
|
|
@@ -945,7 +945,7 @@ ${lastError ?? ""}`);
|
|
|
945
945
|
function lockfileValidationCommand(node, options) {
|
|
946
946
|
const packageJson = node.packageJson ?? (existsSync(resolve(node.path, "package.json")) ? readJson(resolve(node.path, "package.json")) : null);
|
|
947
947
|
const rootWorkspaceInstall = node.path === options.root && Array.isArray(packageJson?.workspaces);
|
|
948
|
-
const args = rootWorkspaceInstall ? ["ci", "--ignore-scripts", "--
|
|
948
|
+
const args = rootWorkspaceInstall ? ["ci", "--ignore-scripts", "--plan"] : ["ci", "--ignore-scripts", "--plan", "--workspaces=false"];
|
|
949
949
|
return { command: "npm", args };
|
|
950
950
|
}
|
|
951
951
|
async function validateRepositoryLockfile(node, options) {
|
|
@@ -1509,7 +1509,7 @@ function repoPlanCommands(node, options, plannedVersion, plannedDependencySpec,
|
|
|
1509
1509
|
commands.push(rootWorkspaceInstall ? "npm install --package-lock-only --ignore-scripts # refresh root workspace lockfile without installing git dependencies" : "npm install --workspaces=false # refresh project lockfile after internal dependency updates");
|
|
1510
1510
|
}
|
|
1511
1511
|
if (hasNpmLockfile(node.path) && (node.kind === "project" || plannedVersion || dependencyUpdates.length > 0 || node.submoduleDependencies.length > 0)) {
|
|
1512
|
-
commands.push(rootWorkspaceInstall ? "npm ci --ignore-scripts --
|
|
1512
|
+
commands.push(rootWorkspaceInstall ? "npm ci --ignore-scripts --plan # validate root manifest, workspaces, and lockfile before commit" : "npm ci --ignore-scripts --plan --workspaces=false # validate deployment lockfile before commit");
|
|
1513
1513
|
}
|
|
1514
1514
|
commands.push("git add -A");
|
|
1515
1515
|
commands.push("generate commit message # Cloudflare AI when configured, fallback otherwise");
|
|
@@ -84,7 +84,7 @@ export interface ResolveProjectLaunchSecretValueOverlayOptions {
|
|
|
84
84
|
export interface SyncProjectLaunchHostBindingSecretsOptions extends ResolveProjectLaunchSecretValueOverlayOptions {
|
|
85
85
|
projectRoot: string;
|
|
86
86
|
repository?: string | null;
|
|
87
|
-
|
|
87
|
+
planOnly?: boolean;
|
|
88
88
|
providers?: ProjectLaunchSecretSyncProvider[];
|
|
89
89
|
onProgress?: (event: ProjectLaunchSecretSyncProgressEvent) => void | Promise<void>;
|
|
90
90
|
adapters?: ProjectLaunchSecretSyncAdapters;
|
|
@@ -229,7 +229,7 @@ async function syncProjectLaunchHostBindingSecrets(options) {
|
|
|
229
229
|
await adapters.github({
|
|
230
230
|
tenantRoot: options.projectRoot,
|
|
231
231
|
scope,
|
|
232
|
-
|
|
232
|
+
planOnly: options.planOnly,
|
|
233
233
|
repository: options.repository ?? null,
|
|
234
234
|
valuesOverlay,
|
|
235
235
|
entryIds,
|
|
@@ -239,7 +239,7 @@ async function syncProjectLaunchHostBindingSecrets(options) {
|
|
|
239
239
|
adapters.cloudflare({
|
|
240
240
|
tenantRoot: options.projectRoot,
|
|
241
241
|
scope,
|
|
242
|
-
|
|
242
|
+
planOnly: options.planOnly,
|
|
243
243
|
valuesOverlay,
|
|
244
244
|
entryIds
|
|
245
245
|
});
|
|
@@ -247,7 +247,7 @@ async function syncProjectLaunchHostBindingSecrets(options) {
|
|
|
247
247
|
adapters.railway({
|
|
248
248
|
tenantRoot: options.projectRoot,
|
|
249
249
|
scope,
|
|
250
|
-
|
|
250
|
+
planOnly: options.planOnly,
|
|
251
251
|
valuesOverlay,
|
|
252
252
|
entryIds
|
|
253
253
|
});
|