@treeseed/sdk 0.8.3 → 0.8.5
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/capacity.d.ts +33 -0
- package/dist/fixture-support.d.ts +1 -1
- package/dist/fixture-support.js +5 -5
- package/dist/managed-dependencies.js +132 -10
- package/dist/operations/services/bootstrap-runner.js +7 -1
- package/dist/operations/services/config-runtime.js +13 -4
- package/dist/operations/services/github-actions-verification.d.ts +3 -0
- package/dist/operations/services/github-actions-verification.js +3 -0
- package/dist/operations/services/github-api.d.ts +4 -1
- package/dist/operations/services/github-api.js +26 -8
- package/dist/operations/services/github-automation.d.ts +14 -5
- package/dist/operations/services/github-automation.js +45 -11
- package/dist/operations/services/hub-provider-launch.js +9 -8
- package/dist/operations/services/project-platform.d.ts +93 -210
- package/dist/operations/services/project-platform.js +74 -34
- package/dist/operations/services/railway-deploy.d.ts +25 -2
- package/dist/operations/services/railway-deploy.js +312 -20
- package/dist/operations/services/repository-save-orchestrator.d.ts +8 -0
- package/dist/operations/services/repository-save-orchestrator.js +40 -3
- package/dist/operations/services/runtime-paths.d.ts +1 -0
- package/dist/operations/services/runtime-paths.js +3 -1
- package/dist/operations/services/runtime-tools.d.ts +1 -0
- package/dist/operations/services/runtime-tools.js +2 -0
- package/dist/operations/services/template-registry.js +3 -0
- package/dist/platform/contracts.d.ts +9 -0
- package/dist/platform/deploy-config.js +28 -0
- package/dist/platform/env.yaml +1 -745
- package/dist/platform/environment.js +69 -9
- package/dist/reconcile/builtin-adapters.js +7 -2
- package/dist/scripts/install-managed-dependencies.js +12 -0
- package/dist/scripts/tenant-workflow-action.js +11 -9
- package/dist/scripts/test-scaffold.js +3 -1
- package/dist/scripts/workflow-commands.test.js +10 -6
- package/dist/scripts/workspace-command-e2e.js +1 -1
- package/dist/treeseed/template-catalog/templates/starter-basic/template/package.json +1 -0
- package/dist/treeseed/template-catalog/templates/starter-basic/template/src/api/server.js +1 -1
- package/dist/treeseed/template-catalog/templates/starter-basic/template/treeseed.site.yaml +7 -6
- package/dist/treeseed/template-catalog/templates/starter-basic/template.config.json +6 -0
- package/dist/workflow/operations.d.ts +41 -8
- package/dist/workflow/operations.js +119 -24
- package/dist/workflow/runs.js +31 -0
- package/package.json +1 -1
- package/templates/github/deploy-processing.workflow.yml +120 -0
- package/templates/github/deploy-web.workflow.yml +116 -0
- package/templates/github/hosted-project.workflow.yml +4 -4
- package/templates/github/deploy.managed.workflow.yml +0 -208
- package/templates/github/deploy.workflow.yml +0 -746
package/dist/capacity.d.ts
CHANGED
|
@@ -1,5 +1,38 @@
|
|
|
1
1
|
import type { CapacityEstimateConfidence, CapacityGrant, CapacityPlan, CapacityProviderLane, CapacityReservation, TaskEstimateProfile } from './sdk-types.ts';
|
|
2
2
|
import type { AgentProviderProfile } from './types/agents.ts';
|
|
3
|
+
export type ProcessingEnvironment = 'local' | 'staging' | 'prod';
|
|
4
|
+
export interface CapacityProviderRegistration {
|
|
5
|
+
id: string;
|
|
6
|
+
teamId: string;
|
|
7
|
+
providerKind: 'processing-host';
|
|
8
|
+
serviceBaseUrl: string;
|
|
9
|
+
environments: ProcessingEnvironment[];
|
|
10
|
+
capabilities: string[];
|
|
11
|
+
status: 'pending' | 'active' | 'degraded' | 'disabled';
|
|
12
|
+
heartbeatAt: string;
|
|
13
|
+
limits: {
|
|
14
|
+
maxWorkers: number;
|
|
15
|
+
dailyTaskCreditBudget: number;
|
|
16
|
+
maxQueuedTasks: number;
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
export interface CapacityProviderHeartbeat {
|
|
20
|
+
providerId: string;
|
|
21
|
+
status: CapacityProviderRegistration['status'];
|
|
22
|
+
heartbeatAt: string;
|
|
23
|
+
queueDepth?: number | null;
|
|
24
|
+
activeWorkers?: number | null;
|
|
25
|
+
draining?: boolean;
|
|
26
|
+
}
|
|
27
|
+
export interface CapacityProviderHealth {
|
|
28
|
+
ok: boolean;
|
|
29
|
+
status: CapacityProviderRegistration['status'];
|
|
30
|
+
capabilities: string[];
|
|
31
|
+
queueDepth: number;
|
|
32
|
+
activeWorkers: number;
|
|
33
|
+
draining: boolean;
|
|
34
|
+
checkedAt: string;
|
|
35
|
+
}
|
|
3
36
|
export interface CapacityEstimateInput {
|
|
4
37
|
taskSignature?: string | null;
|
|
5
38
|
taskKind?: string | null;
|
|
@@ -4,7 +4,7 @@ export type FixtureSupportDeclaration = {
|
|
|
4
4
|
modes: readonly FixtureInjectionMode[];
|
|
5
5
|
workspaceDirName?: string;
|
|
6
6
|
entrySpecifier?: string;
|
|
7
|
-
contractsShim?: '
|
|
7
|
+
contractsShim?: 'agent-contracts';
|
|
8
8
|
};
|
|
9
9
|
export type ResolveSharedFixtureOptions = {
|
|
10
10
|
packageRoot?: string;
|
package/dist/fixture-support.js
CHANGED
|
@@ -106,15 +106,15 @@ function ensureFixtureLinkedPackage(fixtureRoot, packageName, resolvedPackageRoo
|
|
|
106
106
|
rmSync(packageDir, { recursive: true, force: true });
|
|
107
107
|
symlinkSync(resolvedPackageRoot, packageDir, "dir");
|
|
108
108
|
}
|
|
109
|
-
function
|
|
110
|
-
const packageDir = resolve(fixtureRoot, "node_modules", "@treeseed", "
|
|
109
|
+
function buildAgentContractsShimPackage(fixtureRoot) {
|
|
110
|
+
const packageDir = resolve(fixtureRoot, "node_modules", "@treeseed", "agent");
|
|
111
111
|
rmSync(packageDir, { recursive: true, force: true });
|
|
112
112
|
mkdirSync(resolve(packageDir, "contracts"), { recursive: true });
|
|
113
113
|
writeFileSync(
|
|
114
114
|
resolve(packageDir, "package.json"),
|
|
115
115
|
JSON.stringify(
|
|
116
116
|
{
|
|
117
|
-
name: "@treeseed/
|
|
117
|
+
name: "@treeseed/agent",
|
|
118
118
|
type: "module",
|
|
119
119
|
exports: {
|
|
120
120
|
"./runtime-types": {
|
|
@@ -315,8 +315,8 @@ function prepareFixturePackages(options) {
|
|
|
315
315
|
satisfied = true;
|
|
316
316
|
break;
|
|
317
317
|
}
|
|
318
|
-
if (mode === "contracts-only" && declaration.contractsShim === "
|
|
319
|
-
|
|
318
|
+
if (mode === "contracts-only" && declaration.contractsShim === "agent-contracts") {
|
|
319
|
+
buildAgentContractsShimPackage(options.fixtureRoot);
|
|
320
320
|
satisfied = true;
|
|
321
321
|
break;
|
|
322
322
|
}
|
|
@@ -18,7 +18,13 @@ const GH_ASSETS = [
|
|
|
18
18
|
];
|
|
19
19
|
const NPM_TOOLS = [
|
|
20
20
|
{ name: "wrangler", packageName: "wrangler", binName: "wrangler", version: "4.86.0" },
|
|
21
|
-
{
|
|
21
|
+
{
|
|
22
|
+
name: "railway",
|
|
23
|
+
packageName: "@railway/cli",
|
|
24
|
+
binName: "railway",
|
|
25
|
+
version: "4.44.0",
|
|
26
|
+
runtimeBinary: (packageRoot) => resolve(packageRoot, "bin", process.platform === "win32" ? "railway.exe" : "railway")
|
|
27
|
+
},
|
|
22
28
|
{ name: "copilot", packageName: "@github/copilot", binName: "copilot", version: "1.0.39" },
|
|
23
29
|
{ name: "copilot-language-server", packageName: "@github/copilot-language-server", binName: "copilot-language-server", version: "1.480.0" }
|
|
24
30
|
];
|
|
@@ -186,11 +192,45 @@ function resolvePackageRoot(packageName) {
|
|
|
186
192
|
function findNpmTool(name) {
|
|
187
193
|
return NPM_TOOLS.find((tool) => tool.name === name) ?? null;
|
|
188
194
|
}
|
|
195
|
+
function resolveNpmToolRuntimeBinary(tool) {
|
|
196
|
+
const packageJsonPath = resolvePackageJsonPathOptional(tool.packageName);
|
|
197
|
+
if (!packageJsonPath) {
|
|
198
|
+
return null;
|
|
199
|
+
}
|
|
200
|
+
const packageRoot = dirname(packageJsonPath);
|
|
201
|
+
const packageBin = resolvePackageBinaryOptional(tool.packageName, tool.binName);
|
|
202
|
+
if (!packageBin || !existsSync(packageBin)) {
|
|
203
|
+
return null;
|
|
204
|
+
}
|
|
205
|
+
if (!tool.runtimeBinary) {
|
|
206
|
+
return packageBin;
|
|
207
|
+
}
|
|
208
|
+
const runtimeBinary = tool.runtimeBinary(packageRoot);
|
|
209
|
+
return existsSync(runtimeBinary) ? runtimeBinary : null;
|
|
210
|
+
}
|
|
211
|
+
function npmToolMissingDetail(tool) {
|
|
212
|
+
const packageJsonPath = resolvePackageJsonPathOptional(tool.packageName);
|
|
213
|
+
if (!packageJsonPath) {
|
|
214
|
+
return `${tool.packageName} is missing from the installed package graph.`;
|
|
215
|
+
}
|
|
216
|
+
const packageRoot = dirname(packageJsonPath);
|
|
217
|
+
const packageBin = resolvePackageBinaryOptional(tool.packageName, tool.binName);
|
|
218
|
+
if (!packageBin || !existsSync(packageBin)) {
|
|
219
|
+
return `${tool.packageName} binary ${tool.binName} is missing from the installed package.`;
|
|
220
|
+
}
|
|
221
|
+
if (tool.runtimeBinary) {
|
|
222
|
+
const runtimeBinary = tool.runtimeBinary(packageRoot);
|
|
223
|
+
if (!existsSync(runtimeBinary)) {
|
|
224
|
+
return `${tool.packageName} runtime binary ${runtimeBinary} is missing. Run \`npx trsd install --json\` or npm install without --ignore-scripts.`;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
return `${tool.packageName} is unavailable.`;
|
|
228
|
+
}
|
|
189
229
|
function npmBackedDependenciesAvailable() {
|
|
190
230
|
try {
|
|
191
231
|
for (const tool of NPM_TOOLS) {
|
|
192
|
-
const binaryPath =
|
|
193
|
-
if (!existsSync(binaryPath)) {
|
|
232
|
+
const binaryPath = resolveNpmToolRuntimeBinary(tool);
|
|
233
|
+
if (!binaryPath || !existsSync(binaryPath)) {
|
|
194
234
|
return false;
|
|
195
235
|
}
|
|
196
236
|
}
|
|
@@ -228,6 +268,21 @@ function resolveNpmInstallCommand(env = process.env) {
|
|
|
228
268
|
display: ["npm", "install", "--no-audit", "--no-fund"]
|
|
229
269
|
};
|
|
230
270
|
}
|
|
271
|
+
function resolveNpmRebuildCommand(env = process.env, packageNames) {
|
|
272
|
+
const npmExecPath = env.npm_execpath || env.NPM_EXEC_PATH;
|
|
273
|
+
if (npmExecPath?.trim()) {
|
|
274
|
+
return {
|
|
275
|
+
command: process.execPath,
|
|
276
|
+
args: [npmExecPath, "rebuild", ...packageNames],
|
|
277
|
+
display: [process.execPath, npmExecPath, "rebuild", ...packageNames]
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
return {
|
|
281
|
+
command: "npm",
|
|
282
|
+
args: ["rebuild", ...packageNames],
|
|
283
|
+
display: ["npm", "rebuild", ...packageNames]
|
|
284
|
+
};
|
|
285
|
+
}
|
|
231
286
|
function runNpmBootstrap(options) {
|
|
232
287
|
const tenantRoot = options.tenantRoot ? resolve(options.tenantRoot) : null;
|
|
233
288
|
const npmCommand = resolveNpmInstallCommand(options.env);
|
|
@@ -251,6 +306,16 @@ function runNpmBootstrap(options) {
|
|
|
251
306
|
}
|
|
252
307
|
const nodeModulesMissing = !existsSync(resolve(tenantRoot, "node_modules"));
|
|
253
308
|
const npmDepsMissing = !npmBackedDependenciesAvailable();
|
|
309
|
+
const missingRuntimeTools = npmToolsMissingRuntime();
|
|
310
|
+
if (!options.force && !nodeModulesMissing && npmDepsMissing && missingRuntimeTools.length > 0) {
|
|
311
|
+
return [{
|
|
312
|
+
root: tenantRoot,
|
|
313
|
+
command: npmCommand.display,
|
|
314
|
+
status: "already-present",
|
|
315
|
+
exitCode: 0,
|
|
316
|
+
detail: `npm dependencies are installed; rebuilding missing runtime tools: ${missingRuntimeTools.map((tool) => tool.packageName).join(", ")}.`
|
|
317
|
+
}];
|
|
318
|
+
}
|
|
254
319
|
if (!options.force && !nodeModulesMissing && !npmDepsMissing) {
|
|
255
320
|
return [{
|
|
256
321
|
root: tenantRoot,
|
|
@@ -292,11 +357,65 @@ ${result.stdout ?? ""}`.trim() || result.error?.message || "";
|
|
|
292
357
|
detail: ok ? detail || "npm install completed successfully." : detail || "npm install failed."
|
|
293
358
|
}];
|
|
294
359
|
}
|
|
360
|
+
function npmToolsMissingRuntime() {
|
|
361
|
+
return NPM_TOOLS.filter((tool) => !resolveNpmToolRuntimeBinary(tool));
|
|
362
|
+
}
|
|
363
|
+
function runNpmToolRebuilds(options) {
|
|
364
|
+
const missingRuntimeTools = npmToolsMissingRuntime();
|
|
365
|
+
if (missingRuntimeTools.length === 0) {
|
|
366
|
+
return [];
|
|
367
|
+
}
|
|
368
|
+
const tenantRoot = options.tenantRoot ? resolve(options.tenantRoot) : null;
|
|
369
|
+
const npmCommand = resolveNpmRebuildCommand(options.env, missingRuntimeTools.map((tool) => tool.packageName));
|
|
370
|
+
if (!tenantRoot || !existsSync(resolve(tenantRoot, "package.json"))) {
|
|
371
|
+
return [{
|
|
372
|
+
root: tenantRoot,
|
|
373
|
+
command: npmCommand.display,
|
|
374
|
+
status: "skipped",
|
|
375
|
+
exitCode: null,
|
|
376
|
+
detail: tenantRoot ? `No package.json found in ${tenantRoot}; npm rebuild skipped.` : "No tenant root was provided; npm rebuild skipped."
|
|
377
|
+
}];
|
|
378
|
+
}
|
|
379
|
+
if (options.env.TREESEED_MANAGED_NPM_INSTALL === "1") {
|
|
380
|
+
return [{
|
|
381
|
+
root: tenantRoot,
|
|
382
|
+
command: npmCommand.display,
|
|
383
|
+
status: "skipped",
|
|
384
|
+
exitCode: null,
|
|
385
|
+
detail: "npm rebuild skipped because TREESEED_MANAGED_NPM_INSTALL=1 is set."
|
|
386
|
+
}];
|
|
387
|
+
}
|
|
388
|
+
options.write?.(`Rebuilding npm-backed Treeseed tools in ${tenantRoot}...`);
|
|
389
|
+
const result = options.spawn(npmCommand.command, npmCommand.args, {
|
|
390
|
+
cwd: tenantRoot,
|
|
391
|
+
env: {
|
|
392
|
+
...options.env,
|
|
393
|
+
TREESEED_MANAGED_NPM_INSTALL: "1"
|
|
394
|
+
},
|
|
395
|
+
stdio: "pipe",
|
|
396
|
+
encoding: "utf8"
|
|
397
|
+
});
|
|
398
|
+
const detail = `${result.stderr ?? ""}
|
|
399
|
+
${result.stdout ?? ""}`.trim() || result.error?.message || "";
|
|
400
|
+
const stillMissing = npmToolsMissingRuntime().map((tool) => tool.packageName);
|
|
401
|
+
const ok = result.status === 0 && !result.error && stillMissing.length === 0;
|
|
402
|
+
return [{
|
|
403
|
+
root: tenantRoot,
|
|
404
|
+
command: npmCommand.display,
|
|
405
|
+
status: ok ? "installed" : "failed",
|
|
406
|
+
exitCode: result.status ?? (ok ? 0 : 1),
|
|
407
|
+
detail: ok ? detail || "npm-backed Treeseed tools rebuilt successfully." : [
|
|
408
|
+
detail || "npm-backed Treeseed tool rebuild failed.",
|
|
409
|
+
stillMissing.length > 0 ? `Missing runtime tools after rebuild: ${stillMissing.join(", ")}` : ""
|
|
410
|
+
].filter(Boolean).join("\n")
|
|
411
|
+
}];
|
|
412
|
+
}
|
|
295
413
|
function formatTreeseedDependencyFailureDetails(result) {
|
|
296
414
|
const npmFailures = result.npmInstalls.filter((entry) => entry.status === "failed").map((entry) => {
|
|
297
415
|
const root = entry.root ?? "no tenant root";
|
|
298
416
|
const exit = entry.exitCode === null ? "unknown exit code" : `exit code ${entry.exitCode}`;
|
|
299
|
-
|
|
417
|
+
const operation = entry.command.includes("rebuild") ? "npm rebuild" : "npm install";
|
|
418
|
+
return `${operation} in ${root}: ${entry.command.join(" ")} failed with ${exit}${entry.detail ? `: ${entry.detail}` : ""}`;
|
|
300
419
|
});
|
|
301
420
|
const toolFailures = result.reports.filter((entry) => entry.required && ["failed", "missing", "unsupported"].includes(entry.status)).map((entry) => `${entry.name}: ${entry.detail}`);
|
|
302
421
|
return [...npmFailures, ...toolFailures].join("\n- ") || "No dependency failure details were reported.";
|
|
@@ -311,7 +430,7 @@ function resolveTreeseedToolBinary(toolName, options = {}) {
|
|
|
311
430
|
}
|
|
312
431
|
const npmTool = findNpmTool(toolName);
|
|
313
432
|
if (npmTool) {
|
|
314
|
-
return
|
|
433
|
+
return resolveNpmToolRuntimeBinary(npmTool);
|
|
315
434
|
}
|
|
316
435
|
if (toolName === "git" || toolName === "docker") {
|
|
317
436
|
return locateSystemBinary(toolName, spawnSync, options.env ?? process.env);
|
|
@@ -323,7 +442,7 @@ function resolveTreeseedToolCommand(toolName, options = {}) {
|
|
|
323
442
|
if (!binaryPath) {
|
|
324
443
|
return null;
|
|
325
444
|
}
|
|
326
|
-
if (findNpmTool(toolName)) {
|
|
445
|
+
if (findNpmTool(toolName) && /\.(?:cjs|mjs|js)$/u.test(binaryPath)) {
|
|
327
446
|
return { command: process.execPath, argsPrefix: [binaryPath], binaryPath };
|
|
328
447
|
}
|
|
329
448
|
return { command: binaryPath, argsPrefix: [], binaryPath };
|
|
@@ -339,7 +458,7 @@ function invocationForTool(toolName, env = process.env) {
|
|
|
339
458
|
};
|
|
340
459
|
}
|
|
341
460
|
return {
|
|
342
|
-
mode: findNpmTool(toolName) ? "node" : "direct",
|
|
461
|
+
mode: findNpmTool(toolName) && /\.(?:cjs|mjs|js)$/u.test(command.binaryPath) ? "node" : "direct",
|
|
343
462
|
command: command.command,
|
|
344
463
|
argsPrefix: command.argsPrefix,
|
|
345
464
|
binaryPath: command.binaryPath
|
|
@@ -546,7 +665,7 @@ async function installGh(options) {
|
|
|
546
665
|
}
|
|
547
666
|
function statusForNpmTool(tool, options) {
|
|
548
667
|
try {
|
|
549
|
-
const binaryPath =
|
|
668
|
+
const binaryPath = resolveNpmToolRuntimeBinary(tool);
|
|
550
669
|
return report({
|
|
551
670
|
name: tool.name,
|
|
552
671
|
kind: "npm",
|
|
@@ -555,7 +674,7 @@ function statusForNpmTool(tool, options) {
|
|
|
555
674
|
binaryPath,
|
|
556
675
|
status: binaryPath && existsSync(binaryPath) ? "already-present" : "missing",
|
|
557
676
|
required: true,
|
|
558
|
-
detail: binaryPath && existsSync(binaryPath) ? `${tool.packageName} is available from the Treeseed SDK dependency graph.` :
|
|
677
|
+
detail: binaryPath && existsSync(binaryPath) ? `${tool.packageName} is available from the Treeseed SDK dependency graph.` : npmToolMissingDetail(tool)
|
|
559
678
|
});
|
|
560
679
|
} catch (error) {
|
|
561
680
|
return report({
|
|
@@ -681,7 +800,10 @@ async function installTreeseedDependencies(options = {}) {
|
|
|
681
800
|
};
|
|
682
801
|
mkdirSync(resolveToolsHome(env), { recursive: true });
|
|
683
802
|
mkdirSync(createTreeseedManagedToolEnv(env).GH_CONFIG_DIR, { recursive: true });
|
|
684
|
-
const npmInstalls =
|
|
803
|
+
const npmInstalls = [
|
|
804
|
+
...runNpmBootstrap(effectiveOptions),
|
|
805
|
+
...runNpmToolRebuilds(effectiveOptions)
|
|
806
|
+
];
|
|
685
807
|
const reports = [
|
|
686
808
|
systemStatus("git", true, effectiveOptions),
|
|
687
809
|
await installGh(effectiveOptions),
|
|
@@ -73,9 +73,15 @@ async function runPrefixedCommand(command, args, {
|
|
|
73
73
|
prefix
|
|
74
74
|
}) {
|
|
75
75
|
return await new Promise((resolvePromise, reject) => {
|
|
76
|
+
const childEnv = {};
|
|
77
|
+
for (const [key, value] of Object.entries({ ...process.env, ...env })) {
|
|
78
|
+
if (value !== void 0) {
|
|
79
|
+
childEnv[key] = String(value);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
76
82
|
const child = spawn(command, args, {
|
|
77
83
|
cwd,
|
|
78
|
-
env:
|
|
84
|
+
env: childEnv,
|
|
79
85
|
stdio: ["pipe", "pipe", "pipe"]
|
|
80
86
|
});
|
|
81
87
|
let stdout = "";
|
|
@@ -1225,11 +1225,9 @@ function ensureTreeseedRailwayIgnoreEntries(tenantRoot) {
|
|
|
1225
1225
|
".treeseed/",
|
|
1226
1226
|
".wrangler/",
|
|
1227
1227
|
"coverage/",
|
|
1228
|
-
"dist/",
|
|
1229
1228
|
"node_modules/",
|
|
1230
1229
|
"npm-debug.log*",
|
|
1231
1230
|
"packages/*/.git/",
|
|
1232
|
-
"packages/*/dist/",
|
|
1233
1231
|
"packages/*/node_modules/",
|
|
1234
1232
|
"public/__treeseed/*.json",
|
|
1235
1233
|
"public/books/*.json",
|
|
@@ -1239,9 +1237,20 @@ function ensureTreeseedRailwayIgnoreEntries(tenantRoot) {
|
|
|
1239
1237
|
"*.log",
|
|
1240
1238
|
"*.tgz"
|
|
1241
1239
|
];
|
|
1240
|
+
const removedEntries = /* @__PURE__ */ new Set([
|
|
1241
|
+
"dist/",
|
|
1242
|
+
"**/dist/",
|
|
1243
|
+
"packages/*/dist/"
|
|
1244
|
+
]);
|
|
1242
1245
|
const current = existsSync(railwayIgnorePath) ? readFileSync(railwayIgnorePath, "utf8") : "";
|
|
1243
|
-
const lines = current.split(/\r?\n/);
|
|
1244
1246
|
let changed = false;
|
|
1247
|
+
const lines = current.split(/\r?\n/).filter((line) => {
|
|
1248
|
+
const keep = !removedEntries.has(line.trim());
|
|
1249
|
+
if (!keep) {
|
|
1250
|
+
changed = true;
|
|
1251
|
+
}
|
|
1252
|
+
return keep;
|
|
1253
|
+
});
|
|
1245
1254
|
for (const entry of requiredEntries) {
|
|
1246
1255
|
if (!lines.includes(entry)) {
|
|
1247
1256
|
lines.push(entry);
|
|
@@ -2363,7 +2372,7 @@ function createConfigReadiness(values, validation) {
|
|
|
2363
2372
|
configured: providerIssues("cloudflare").length === 0
|
|
2364
2373
|
},
|
|
2365
2374
|
railway: {
|
|
2366
|
-
configured: providerIssues("railway").length === 0
|
|
2375
|
+
configured: validConfigValue("RAILWAY_API_TOKEN") && providerIssues("railway").length === 0
|
|
2367
2376
|
},
|
|
2368
2377
|
localDevelopment: {
|
|
2369
2378
|
configured: localDevelopmentIssues.length === 0
|
|
@@ -18,6 +18,9 @@ export type GitHubActionsWorkflowGate = {
|
|
|
18
18
|
headSha: string;
|
|
19
19
|
timeoutSeconds?: number;
|
|
20
20
|
pollSeconds?: number;
|
|
21
|
+
dispatchIfMissing?: boolean;
|
|
22
|
+
dispatchAfterSeconds?: number;
|
|
23
|
+
dispatchInputs?: Record<string, string>;
|
|
21
24
|
};
|
|
22
25
|
export type GitHubActionsWorkflowJobStep = {
|
|
23
26
|
name: string;
|
|
@@ -541,6 +541,9 @@ async function waitForGitHubActionsGate(gate, options = {}) {
|
|
|
541
541
|
branch: gate.branch,
|
|
542
542
|
timeoutSeconds: gate.timeoutSeconds ?? options.timeoutSeconds,
|
|
543
543
|
pollSeconds: gate.pollSeconds ?? options.pollSeconds,
|
|
544
|
+
dispatchIfMissing: gate.dispatchIfMissing ?? gate.workflow === "verify.yml",
|
|
545
|
+
dispatchAfterSeconds: gate.dispatchAfterSeconds ?? 75,
|
|
546
|
+
dispatchInputs: gate.dispatchInputs,
|
|
544
547
|
onProgress: reportProgress
|
|
545
548
|
});
|
|
546
549
|
}
|
|
@@ -150,13 +150,16 @@ export declare function upsertGitHubRepositoryVariableWithGhCli(repository: stri
|
|
|
150
150
|
export declare function waitForGitHubWorkflowRunCompletion(repository: string | {
|
|
151
151
|
owner: string;
|
|
152
152
|
name: string;
|
|
153
|
-
}, { client, workflow, headSha, branch, timeoutSeconds, pollSeconds, onProgress, }?: {
|
|
153
|
+
}, { client, workflow, headSha, branch, timeoutSeconds, pollSeconds, dispatchIfMissing, dispatchAfterSeconds, dispatchInputs, onProgress, }?: {
|
|
154
154
|
client?: GitHubApiClient;
|
|
155
155
|
workflow?: string;
|
|
156
156
|
headSha?: string | null;
|
|
157
157
|
branch?: string | null;
|
|
158
158
|
timeoutSeconds?: number;
|
|
159
159
|
pollSeconds?: number;
|
|
160
|
+
dispatchIfMissing?: boolean;
|
|
161
|
+
dispatchAfterSeconds?: number;
|
|
162
|
+
dispatchInputs?: Record<string, string>;
|
|
160
163
|
onProgress?: (event: GitHubWorkflowProgressEvent) => void;
|
|
161
164
|
}): Promise<{
|
|
162
165
|
status: string;
|
|
@@ -196,13 +196,13 @@ async function paginateNames(request) {
|
|
|
196
196
|
async function listGitHubRepositorySecretNames(repository, { client = createGitHubApiClient() } = {}) {
|
|
197
197
|
const { owner, name } = typeof repository === "string" ? parseGitHubRepositorySlug(repository) : repository;
|
|
198
198
|
try {
|
|
199
|
-
return await paginateNames(
|
|
199
|
+
return await withGitHubApiRetries(() => paginateNames(
|
|
200
200
|
() => client.paginate(client.rest.actions.listRepoSecrets, {
|
|
201
201
|
owner,
|
|
202
202
|
repo: name,
|
|
203
203
|
per_page: 100
|
|
204
204
|
})
|
|
205
|
-
);
|
|
205
|
+
));
|
|
206
206
|
} catch (error) {
|
|
207
207
|
throw normalizeGitHubApiError(error, `Unable to list GitHub secrets for ${owner}/${name}`);
|
|
208
208
|
}
|
|
@@ -210,13 +210,13 @@ async function listGitHubRepositorySecretNames(repository, { client = createGitH
|
|
|
210
210
|
async function listGitHubRepositoryVariableNames(repository, { client = createGitHubApiClient() } = {}) {
|
|
211
211
|
const { owner, name } = typeof repository === "string" ? parseGitHubRepositorySlug(repository) : repository;
|
|
212
212
|
try {
|
|
213
|
-
return await paginateNames(
|
|
213
|
+
return await withGitHubApiRetries(() => paginateNames(
|
|
214
214
|
() => client.paginate(client.rest.actions.listRepoVariables, {
|
|
215
215
|
owner,
|
|
216
216
|
repo: name,
|
|
217
217
|
per_page: 100
|
|
218
218
|
})
|
|
219
|
-
);
|
|
219
|
+
));
|
|
220
220
|
} catch (error) {
|
|
221
221
|
throw normalizeGitHubApiError(error, `Unable to list GitHub variables for ${owner}/${name}`);
|
|
222
222
|
}
|
|
@@ -322,11 +322,11 @@ async function paginateGitHubEnvironmentNames(client, route, params) {
|
|
|
322
322
|
async function listGitHubEnvironmentSecretNames(repository, environmentName, { client = createGitHubApiClient() } = {}) {
|
|
323
323
|
const { owner, name } = typeof repository === "string" ? parseGitHubRepositorySlug(repository) : repository;
|
|
324
324
|
try {
|
|
325
|
-
return await paginateGitHubEnvironmentNames(
|
|
325
|
+
return await withGitHubApiRetries(() => paginateGitHubEnvironmentNames(
|
|
326
326
|
client,
|
|
327
327
|
"GET /repos/{owner}/{repo}/environments/{environment_name}/secrets",
|
|
328
328
|
{ owner, repo: name, environment_name: environmentName }
|
|
329
|
-
);
|
|
329
|
+
));
|
|
330
330
|
} catch (error) {
|
|
331
331
|
throw normalizeGitHubApiError(error, `Unable to list GitHub environment secrets for ${owner}/${name}:${environmentName}`);
|
|
332
332
|
}
|
|
@@ -334,11 +334,11 @@ async function listGitHubEnvironmentSecretNames(repository, environmentName, { c
|
|
|
334
334
|
async function listGitHubEnvironmentVariableNames(repository, environmentName, { client = createGitHubApiClient() } = {}) {
|
|
335
335
|
const { owner, name } = typeof repository === "string" ? parseGitHubRepositorySlug(repository) : repository;
|
|
336
336
|
try {
|
|
337
|
-
return await paginateGitHubEnvironmentNames(
|
|
337
|
+
return await withGitHubApiRetries(() => paginateGitHubEnvironmentNames(
|
|
338
338
|
client,
|
|
339
339
|
"GET /repos/{owner}/{repo}/environments/{environment_name}/variables",
|
|
340
340
|
{ owner, repo: name, environment_name: environmentName }
|
|
341
|
-
);
|
|
341
|
+
));
|
|
342
342
|
} catch (error) {
|
|
343
343
|
throw normalizeGitHubApiError(error, `Unable to list GitHub environment variables for ${owner}/${name}:${environmentName}`);
|
|
344
344
|
}
|
|
@@ -571,10 +571,14 @@ async function waitForGitHubWorkflowRunCompletion(repository, {
|
|
|
571
571
|
branch,
|
|
572
572
|
timeoutSeconds = 600,
|
|
573
573
|
pollSeconds = 5,
|
|
574
|
+
dispatchIfMissing = false,
|
|
575
|
+
dispatchAfterSeconds = 60,
|
|
576
|
+
dispatchInputs,
|
|
574
577
|
onProgress
|
|
575
578
|
} = {}) {
|
|
576
579
|
const { owner, name } = typeof repository === "string" ? parseGitHubRepositorySlug(repository) : repository;
|
|
577
580
|
const startedAt = Date.now();
|
|
581
|
+
let dispatchedMissingRun = false;
|
|
578
582
|
let lastProgress = null;
|
|
579
583
|
const emitProgress = (type, run = null, jobs = []) => {
|
|
580
584
|
const completedJobs = jobs.filter((job) => job.status === "completed");
|
|
@@ -610,6 +614,20 @@ async function waitForGitHubWorkflowRunCompletion(repository, {
|
|
|
610
614
|
const match = listed.data.workflow_runs.map((run) => normalizeWorkflowRun(run)).find((run) => (!headSha || run.headSha === headSha) && (!branch || run.headBranch === branch));
|
|
611
615
|
if (!match?.id) {
|
|
612
616
|
emitProgress("waiting");
|
|
617
|
+
if (dispatchIfMissing && branch && !dispatchedMissingRun && Date.now() - startedAt >= dispatchAfterSeconds * 1e3) {
|
|
618
|
+
try {
|
|
619
|
+
await client.rest.actions.createWorkflowDispatch({
|
|
620
|
+
owner,
|
|
621
|
+
repo: name,
|
|
622
|
+
workflow_id: workflow,
|
|
623
|
+
ref: branch,
|
|
624
|
+
inputs: dispatchInputs
|
|
625
|
+
});
|
|
626
|
+
dispatchedMissingRun = true;
|
|
627
|
+
} catch (error) {
|
|
628
|
+
throw normalizeGitHubApiError(error, `Unable to dispatch GitHub workflow ${workflow} in ${owner}/${name}`);
|
|
629
|
+
}
|
|
630
|
+
}
|
|
613
631
|
await sleep(pollSeconds * 1e3);
|
|
614
632
|
continue;
|
|
615
633
|
}
|
|
@@ -77,18 +77,26 @@ export declare function requiredGitHubEnvironment(tenantRoot: any, { scope, purp
|
|
|
77
77
|
variables: string[];
|
|
78
78
|
};
|
|
79
79
|
export declare function requiredGitHubSecrets(tenantRoot: any): string[];
|
|
80
|
-
export declare function
|
|
80
|
+
export declare function renderDeployWebWorkflow({ workingDirectory }: {
|
|
81
|
+
workingDirectory: any;
|
|
82
|
+
}): string;
|
|
83
|
+
export declare function renderDeployProcessingWorkflow({ workingDirectory }: {
|
|
81
84
|
workingDirectory: any;
|
|
82
|
-
executionBoundary?: string | undefined;
|
|
83
85
|
}): string;
|
|
84
86
|
export declare function renderHostedProjectWorkflow({ workingDirectory }: {
|
|
85
87
|
workingDirectory: any;
|
|
86
88
|
}): string;
|
|
87
89
|
export declare function ensureDeployWorkflow(tenantRoot: any): {
|
|
88
|
-
workingDirectory: string;
|
|
89
|
-
executionBoundary: string;
|
|
90
90
|
workflowPath: string;
|
|
91
91
|
changed: boolean;
|
|
92
|
+
workingDirectory: string;
|
|
93
|
+
executionBoundary: string;
|
|
94
|
+
additionalWorkflows: {
|
|
95
|
+
workingDirectory: string;
|
|
96
|
+
executionBoundary: string;
|
|
97
|
+
workflowPath: string;
|
|
98
|
+
changed: boolean;
|
|
99
|
+
}[];
|
|
92
100
|
};
|
|
93
101
|
export declare function ensureHostedProjectWorkflow(tenantRoot: any): {
|
|
94
102
|
workingDirectory: string;
|
|
@@ -197,10 +205,11 @@ export declare function ensureGitHubDeployAutomation(tenantRoot: any, { dryRun,
|
|
|
197
205
|
skipped?: undefined;
|
|
198
206
|
};
|
|
199
207
|
}>;
|
|
200
|
-
export declare function waitForGitHubWorkflowCompletion(tenantRoot: any, { repository, workflow, headSha, branch, timeoutSeconds, pollSeconds, onProgress, }?: {
|
|
208
|
+
export declare function waitForGitHubWorkflowCompletion(tenantRoot: any, { repository, workflow, headSha, branch, timeoutSeconds, pollSeconds, dispatchIfMissing, dispatchAfterSeconds, dispatchInputs, onProgress, }?: {
|
|
201
209
|
workflow?: string | undefined;
|
|
202
210
|
timeoutSeconds?: number | undefined;
|
|
203
211
|
pollSeconds?: number | undefined;
|
|
212
|
+
dispatchIfMissing?: boolean | undefined;
|
|
204
213
|
}): Promise<{
|
|
205
214
|
status: string;
|
|
206
215
|
repository: string;
|
|
@@ -279,7 +279,6 @@ function requiredGitHubSecrets(tenantRoot) {
|
|
|
279
279
|
function renderTenantWorkflowActionCommand() {
|
|
280
280
|
return [
|
|
281
281
|
"EXTRA_ARGS=()",
|
|
282
|
-
'if [[ "${TREESEED_WORKFLOW_SKIP_PROVISION:-}" == "1" ]]; then EXTRA_ARGS+=(--skip-provision); fi',
|
|
283
282
|
'if [[ -n "${TREESEED_WORKFLOW_PROJECT:-}" ]]; then EXTRA_ARGS+=(--project-id "${TREESEED_WORKFLOW_PROJECT}"); fi',
|
|
284
283
|
'if [[ -n "${TREESEED_WORKFLOW_PREVIEW_ID:-}" ]]; then EXTRA_ARGS+=(--preview-id "${TREESEED_WORKFLOW_PREVIEW_ID}"); fi',
|
|
285
284
|
"if test -f ./packages/sdk/scripts/tenant-workflow-action.ts; then",
|
|
@@ -305,11 +304,11 @@ function renderWorkflowTemplate(templateName, { workingDirectory }) {
|
|
|
305
304
|
normalizedWorkingDirectory === "." ? "package-lock.json" : `${normalizedWorkingDirectory}/package-lock.json`
|
|
306
305
|
);
|
|
307
306
|
}
|
|
308
|
-
function
|
|
309
|
-
return
|
|
307
|
+
function renderDeployWebWorkflow({ workingDirectory }) {
|
|
308
|
+
return renderWorkflowTemplate("deploy-web.workflow.yml", { workingDirectory });
|
|
310
309
|
}
|
|
311
|
-
function
|
|
312
|
-
return renderWorkflowTemplate(
|
|
310
|
+
function renderDeployProcessingWorkflow({ workingDirectory }) {
|
|
311
|
+
return renderWorkflowTemplate("deploy-processing.workflow.yml", { workingDirectory });
|
|
313
312
|
}
|
|
314
313
|
function renderHostedProjectWorkflow({ workingDirectory }) {
|
|
315
314
|
return renderWorkflowTemplate("hosted-project.workflow.yml", { workingDirectory });
|
|
@@ -324,16 +323,44 @@ function ensureWorkflowFile(tenantRoot, fileName, expected) {
|
|
|
324
323
|
writeFileSync(workflowPath, expected, "utf8");
|
|
325
324
|
return { workflowPath, changed: true };
|
|
326
325
|
}
|
|
326
|
+
function hasConcreteProcessingServices(deployConfig) {
|
|
327
|
+
return Object.entries(deployConfig.services ?? {}).some(
|
|
328
|
+
([serviceKey, service]) => ["api", "manager", "worker", "workerRunner", "workdayStart", "workdayReport"].includes(serviceKey) && service && service.enabled !== false
|
|
329
|
+
);
|
|
330
|
+
}
|
|
331
|
+
function shouldWriteProcessingWorkflow(deployConfig) {
|
|
332
|
+
if ((deployConfig.hosting?.kind ?? "self_hosted_project") === "market_control_plane") {
|
|
333
|
+
return true;
|
|
334
|
+
}
|
|
335
|
+
const mode = deployConfig.processing?.mode ?? "market-assigned";
|
|
336
|
+
return mode === "project-owned" || mode === "local" || hasConcreteProcessingServices(deployConfig);
|
|
337
|
+
}
|
|
327
338
|
function ensureDeployWorkflow(tenantRoot) {
|
|
328
339
|
const repositoryRoot = resolveGitRepositoryRoot(tenantRoot);
|
|
329
340
|
const workingDirectory = relative(repositoryRoot, tenantRoot).replaceAll("\\", "/") || ".";
|
|
330
341
|
const deployConfig = loadCliDeployConfig(tenantRoot);
|
|
331
|
-
const
|
|
332
|
-
const
|
|
342
|
+
const web = ensureWorkflowFile(tenantRoot, "deploy-web.yml", renderDeployWebWorkflow({ workingDirectory }));
|
|
343
|
+
const additionalWorkflows = [];
|
|
344
|
+
let changed = web.changed;
|
|
345
|
+
if (shouldWriteProcessingWorkflow(deployConfig)) {
|
|
346
|
+
const processing = ensureWorkflowFile(
|
|
347
|
+
tenantRoot,
|
|
348
|
+
"deploy-processing.yml",
|
|
349
|
+
renderDeployProcessingWorkflow({ workingDirectory })
|
|
350
|
+
);
|
|
351
|
+
changed = changed || processing.changed;
|
|
352
|
+
additionalWorkflows.push({
|
|
353
|
+
...processing,
|
|
354
|
+
workingDirectory,
|
|
355
|
+
executionBoundary: "split-plane"
|
|
356
|
+
});
|
|
357
|
+
}
|
|
333
358
|
return {
|
|
334
|
-
|
|
359
|
+
workflowPath: web.workflowPath,
|
|
360
|
+
changed,
|
|
335
361
|
workingDirectory,
|
|
336
|
-
executionBoundary
|
|
362
|
+
executionBoundary: "split-plane",
|
|
363
|
+
additionalWorkflows
|
|
337
364
|
};
|
|
338
365
|
}
|
|
339
366
|
function ensureHostedProjectWorkflow(tenantRoot) {
|
|
@@ -348,7 +375,7 @@ function ensureHostedProjectWorkflow(tenantRoot) {
|
|
|
348
375
|
function ensureStandardizedGitHubWorkflows(tenantRoot) {
|
|
349
376
|
const deployConfig = loadCliDeployConfig(tenantRoot);
|
|
350
377
|
const deploy = ensureDeployWorkflow(tenantRoot);
|
|
351
|
-
const workflows = [deploy];
|
|
378
|
+
const workflows = [deploy, ...deploy.additionalWorkflows ?? []];
|
|
352
379
|
if ((deployConfig.hosting?.kind ?? "self_hosted_project") === "market_control_plane") {
|
|
353
380
|
workflows.push(ensureHostedProjectWorkflow(tenantRoot));
|
|
354
381
|
}
|
|
@@ -465,6 +492,9 @@ async function waitForGitHubWorkflowCompletion(tenantRoot, {
|
|
|
465
492
|
branch,
|
|
466
493
|
timeoutSeconds = 600,
|
|
467
494
|
pollSeconds = 5,
|
|
495
|
+
dispatchIfMissing = false,
|
|
496
|
+
dispatchAfterSeconds,
|
|
497
|
+
dispatchInputs,
|
|
468
498
|
onProgress
|
|
469
499
|
} = {}) {
|
|
470
500
|
const repo = repository ?? resolveGitHubRepositorySlug(tenantRoot);
|
|
@@ -475,6 +505,9 @@ async function waitForGitHubWorkflowCompletion(tenantRoot, {
|
|
|
475
505
|
branch,
|
|
476
506
|
timeoutSeconds,
|
|
477
507
|
pollSeconds,
|
|
508
|
+
dispatchIfMissing,
|
|
509
|
+
dispatchAfterSeconds,
|
|
510
|
+
dispatchInputs,
|
|
478
511
|
onProgress
|
|
479
512
|
});
|
|
480
513
|
}
|
|
@@ -494,7 +527,8 @@ export {
|
|
|
494
527
|
listGitHubVariableNames,
|
|
495
528
|
maybeResolveGitHubRepositorySlug,
|
|
496
529
|
parseGitHubRepositoryFromRemote,
|
|
497
|
-
|
|
530
|
+
renderDeployProcessingWorkflow,
|
|
531
|
+
renderDeployWebWorkflow,
|
|
498
532
|
renderHostedProjectWorkflow,
|
|
499
533
|
requiredGitHubEnvironment,
|
|
500
534
|
requiredGitHubSecrets,
|