@treeseed/sdk 0.10.28 → 0.11.0

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.
Files changed (148) hide show
  1. package/README.md +207 -6
  2. package/dist/capacity-provider.d.ts +3 -1
  3. package/dist/capacity-provider.js +25 -5
  4. package/dist/control-plane.d.ts +1 -0
  5. package/dist/control-plane.js +38 -13
  6. package/dist/db/market-schema.d.ts +8860 -6172
  7. package/dist/db/market-schema.js +108 -0
  8. package/dist/db/node-sqlite.js +7 -2
  9. package/dist/hosting/apps.d.ts +12 -0
  10. package/dist/hosting/apps.js +107 -0
  11. package/dist/hosting/builtins.d.ts +25 -0
  12. package/dist/hosting/builtins.js +791 -0
  13. package/dist/hosting/contracts.d.ts +207 -0
  14. package/dist/hosting/contracts.js +0 -0
  15. package/dist/hosting/graph.d.ts +192 -0
  16. package/dist/hosting/graph.js +1106 -0
  17. package/dist/hosting/index.d.ts +4 -0
  18. package/dist/hosting/index.js +4 -0
  19. package/dist/index.d.ts +10 -3
  20. package/dist/index.js +63 -6
  21. package/dist/managed-dependencies.js +1 -2
  22. package/dist/market-client.d.ts +63 -3
  23. package/dist/market-client.js +83 -11
  24. package/dist/operations/services/bootstrap-runner.d.ts +3 -1
  25. package/dist/operations/services/bootstrap-runner.js +22 -2
  26. package/dist/operations/services/config-runtime.d.ts +10 -5
  27. package/dist/operations/services/config-runtime.js +209 -66
  28. package/dist/operations/services/deploy.d.ts +70 -7
  29. package/dist/operations/services/deploy.js +579 -64
  30. package/dist/operations/services/deployment-readiness.d.ts +30 -0
  31. package/dist/operations/services/deployment-readiness.js +175 -0
  32. package/dist/operations/services/git-workflow.d.ts +2 -1
  33. package/dist/operations/services/git-workflow.js +9 -3
  34. package/dist/operations/services/github-actions-verification.d.ts +1 -0
  35. package/dist/operations/services/github-actions-verification.js +1 -0
  36. package/dist/operations/services/github-api.js +1 -1
  37. package/dist/operations/services/github-automation.d.ts +1 -1
  38. package/dist/operations/services/github-automation.js +4 -3
  39. package/dist/operations/services/github-credentials.d.ts +13 -0
  40. package/dist/operations/services/github-credentials.js +58 -0
  41. package/dist/operations/services/hosted-service-checks.d.ts +63 -0
  42. package/dist/operations/services/hosted-service-checks.js +327 -0
  43. package/dist/operations/services/hub-provider-launch.js +3 -3
  44. package/dist/operations/services/live-hosted-service-checks.d.ts +25 -0
  45. package/dist/operations/services/live-hosted-service-checks.js +350 -0
  46. package/dist/operations/services/managed-host-security.js +1 -1
  47. package/dist/operations/services/operations-runner-smoke.d.ts +30 -0
  48. package/dist/operations/services/operations-runner-smoke.js +180 -0
  49. package/dist/operations/services/package-adapters.d.ts +95 -0
  50. package/dist/operations/services/package-adapters.js +288 -0
  51. package/dist/operations/services/package-reference-policy.d.ts +1 -0
  52. package/dist/operations/services/package-reference-policy.js +15 -2
  53. package/dist/operations/services/project-platform.d.ts +80 -22
  54. package/dist/operations/services/project-platform.js +49 -8
  55. package/dist/operations/services/project-web-monitor.js +26 -4
  56. package/dist/operations/services/railway-api.d.ts +88 -5
  57. package/dist/operations/services/railway-api.js +626 -35
  58. package/dist/operations/services/railway-deploy.d.ts +46 -40
  59. package/dist/operations/services/railway-deploy.js +261 -293
  60. package/dist/operations/services/release-candidate.d.ts +19 -0
  61. package/dist/operations/services/release-candidate.js +375 -38
  62. package/dist/operations/services/repository-save-orchestrator.d.ts +3 -1
  63. package/dist/operations/services/repository-save-orchestrator.js +279 -66
  64. package/dist/operations/services/runtime-tools.d.ts +1 -0
  65. package/dist/operations/services/runtime-tools.js +10 -9
  66. package/dist/operations/services/verification-cache.d.ts +25 -0
  67. package/dist/operations/services/verification-cache.js +71 -0
  68. package/dist/operations/services/workspace-dependency-mode.js +9 -1
  69. package/dist/operations/services/workspace-save.js +1 -1
  70. package/dist/operations/services/workspace-tools.js +2 -1
  71. package/dist/platform/contracts.d.ts +32 -1
  72. package/dist/platform/deploy-config.js +73 -8
  73. package/dist/platform/env.yaml +163 -35
  74. package/dist/platform/environment.d.ts +1 -0
  75. package/dist/platform/environment.js +74 -5
  76. package/dist/platform/plugin.d.ts +9 -0
  77. package/dist/platform-operation-store.js +2 -2
  78. package/dist/platform-operations.js +1 -1
  79. package/dist/reconcile/bootstrap-systems.js +2 -2
  80. package/dist/reconcile/builtin-adapters.js +372 -189
  81. package/dist/reconcile/contracts.d.ts +9 -5
  82. package/dist/reconcile/desired-state.d.ts +1 -0
  83. package/dist/reconcile/desired-state.js +5 -5
  84. package/dist/reconcile/engine.d.ts +5 -2
  85. package/dist/reconcile/engine.js +53 -32
  86. package/dist/reconcile/index.d.ts +2 -0
  87. package/dist/reconcile/index.js +2 -0
  88. package/dist/reconcile/live-acceptance.d.ts +79 -0
  89. package/dist/reconcile/live-acceptance.js +1615 -0
  90. package/dist/reconcile/platform.d.ts +104 -0
  91. package/dist/reconcile/platform.js +100 -0
  92. package/dist/reconcile/state.js +4 -4
  93. package/dist/reconcile/units.js +2 -2
  94. package/dist/scripts/deployment-readiness.js +20 -0
  95. package/dist/scripts/generate-treedx-openapi-types.js +186 -0
  96. package/dist/scripts/operations-runner-smoke.js +16 -0
  97. package/dist/scripts/release-verify.js +4 -1
  98. package/dist/scripts/tenant-workflow-action.js +10 -1
  99. package/dist/sdk-types.d.ts +169 -4
  100. package/dist/sdk-types.js +20 -2
  101. package/dist/sdk.d.ts +35 -24
  102. package/dist/sdk.js +186 -17
  103. package/dist/template-launch-requirements.js +9 -0
  104. package/dist/treedx/adapters.d.ts +6 -0
  105. package/dist/treedx/adapters.js +36 -0
  106. package/dist/treedx/client.d.ts +222 -0
  107. package/dist/treedx/client.js +871 -0
  108. package/dist/treedx/errors.d.ts +13 -0
  109. package/dist/treedx/errors.js +17 -0
  110. package/dist/treedx/federated-client.d.ts +27 -0
  111. package/dist/treedx/federated-client.js +158 -0
  112. package/dist/treedx/generated/openapi-types.d.ts +3558 -0
  113. package/dist/treedx/generated/openapi-types.js +0 -0
  114. package/dist/treedx/graph-adapter.d.ts +33 -0
  115. package/dist/treedx/graph-adapter.js +156 -0
  116. package/dist/treedx/index.d.ts +14 -0
  117. package/dist/treedx/index.js +48 -0
  118. package/dist/treedx/market-integration.d.ts +27 -0
  119. package/dist/treedx/market-integration.js +131 -0
  120. package/dist/treedx/ports.d.ts +166 -0
  121. package/dist/treedx/ports.js +231 -0
  122. package/dist/treedx/query-adapter.d.ts +19 -0
  123. package/dist/treedx/query-adapter.js +62 -0
  124. package/dist/treedx/registry-client.d.ts +11 -0
  125. package/dist/treedx/registry-client.js +19 -0
  126. package/dist/treedx/repository-adapter.d.ts +45 -0
  127. package/dist/treedx/repository-adapter.js +308 -0
  128. package/dist/treedx/sdk-integration.d.ts +27 -0
  129. package/dist/treedx/sdk-integration.js +63 -0
  130. package/dist/treedx/types.d.ts +1084 -0
  131. package/dist/treedx/types.js +8 -0
  132. package/dist/treedx/workspace-adapter.d.ts +27 -0
  133. package/dist/treedx/workspace-adapter.js +65 -0
  134. package/dist/treedx-backends.d.ts +218 -0
  135. package/dist/treedx-backends.js +632 -0
  136. package/dist/treedx-client.d.ts +86 -0
  137. package/dist/treedx-client.js +175 -0
  138. package/dist/treeseed/template-catalog/catalog.fixture.json +23 -23
  139. package/dist/workflow/operations.d.ts +119 -13
  140. package/dist/workflow/operations.js +309 -53
  141. package/dist/workflow-state.d.ts +13 -0
  142. package/dist/workflow-state.js +43 -26
  143. package/dist/workflow-support.d.ts +11 -3
  144. package/dist/workflow-support.js +67 -3
  145. package/dist/workflow.d.ts +5 -0
  146. package/drizzle/market/0004_treedx_market_integration.sql +99 -0
  147. package/package.json +34 -3
  148. package/templates/github/deploy-web.workflow.yml +39 -6
@@ -0,0 +1,30 @@
1
+ import { type TreeseedHostingEnvironment } from '../../hosting/index.ts';
2
+ export type TreeseedDeploymentReadinessStatus = 'passed' | 'failed' | 'warning' | 'skipped';
3
+ export interface TreeseedDeploymentReadinessCheck {
4
+ id: string;
5
+ status: TreeseedDeploymentReadinessStatus;
6
+ expected?: Record<string, unknown>;
7
+ observed?: Record<string, unknown>;
8
+ message: string;
9
+ remediation?: string;
10
+ }
11
+ export interface TreeseedDeploymentReadinessReport {
12
+ environment: TreeseedHostingEnvironment;
13
+ ok: boolean;
14
+ generatedAt: string;
15
+ checks: TreeseedDeploymentReadinessCheck[];
16
+ summary: {
17
+ passed: number;
18
+ failed: number;
19
+ warning: number;
20
+ skipped: number;
21
+ };
22
+ }
23
+ export interface TreeseedDeploymentReadinessOptions {
24
+ tenantRoot: string;
25
+ environment: TreeseedHostingEnvironment;
26
+ appId?: string;
27
+ now?: Date;
28
+ }
29
+ export declare function collectTreeseedDeploymentReadiness(options: TreeseedDeploymentReadinessOptions): TreeseedDeploymentReadinessReport;
30
+ export declare function formatTreeseedReadinessReport(report: TreeseedDeploymentReadinessReport): string;
@@ -0,0 +1,175 @@
1
+ import { relative, resolve } from "node:path";
2
+ import { compileTreeseedHostingGraph, serializeHostingUnit } from "../../hosting/index.js";
3
+ import { configuredRailwayServices } from "./railway-deploy.js";
4
+ function relRoot(tenantRoot, path) {
5
+ const value = relative(tenantRoot, path).split("\\").join("/");
6
+ return value || ".";
7
+ }
8
+ function summary(checks) {
9
+ return {
10
+ passed: checks.filter((check2) => check2.status === "passed").length,
11
+ failed: checks.filter((check2) => check2.status === "failed").length,
12
+ warning: checks.filter((check2) => check2.status === "warning").length,
13
+ skipped: checks.filter((check2) => check2.status === "skipped").length
14
+ };
15
+ }
16
+ function check(id, actual, expected, message, remediation, key = id.split(":").at(-1) ?? "value") {
17
+ const passed = actual === expected;
18
+ return {
19
+ id,
20
+ status: passed ? "passed" : "failed",
21
+ expected: { [key]: expected },
22
+ observed: { [key]: actual ?? null },
23
+ message: passed ? message : `${message} Expected ${String(expected)}, observed ${String(actual ?? "(unset)")}.`,
24
+ remediation: passed ? void 0 : remediation
25
+ };
26
+ }
27
+ function checkIncludes(id, values, required, message, remediation) {
28
+ const observed = Array.isArray(values) ? values.map(String) : [];
29
+ const missing = required.filter((value) => !observed.includes(value));
30
+ return {
31
+ id,
32
+ status: missing.length === 0 ? "passed" : "failed",
33
+ expected: { includes: required },
34
+ observed: { values: observed },
35
+ message: missing.length === 0 ? message : `${message} Missing: ${missing.join(", ")}.`,
36
+ remediation: missing.length === 0 ? void 0 : remediation
37
+ };
38
+ }
39
+ function unitById(units, id) {
40
+ return units.find((unit) => unit.id === id) ?? null;
41
+ }
42
+ function railwayServiceByKey(services, key) {
43
+ return services.find((service) => service.key === key) ?? null;
44
+ }
45
+ function collectTreeseedDeploymentReadiness(options) {
46
+ const tenantRoot = resolve(options.tenantRoot);
47
+ const environment = options.environment;
48
+ const graph = compileTreeseedHostingGraph({ tenantRoot, environment, appId: options.appId });
49
+ const units = graph.units.map((unit) => serializeHostingUnit(unit));
50
+ const railwayServices = configuredRailwayServices(tenantRoot, environment).filter((service) => !options.appId || service.application?.id === options.appId);
51
+ const checks = [];
52
+ const web = unitById(units, "web");
53
+ const api = unitById(units, "api");
54
+ const runner = unitById(units, "operationsRunner");
55
+ const database = unitById(units, "treeseedDatabase");
56
+ const hasApiPlane = Boolean(api || runner || database);
57
+ const hasWorkspaceMarketControlPlane = graph.applications?.some((app) => app.config.hosting?.kind === "treeseed_control_plane") === true || graph.deployConfig.hosting?.kind === "treeseed_control_plane";
58
+ const railwayApi = railwayServiceByKey(railwayServices, "api");
59
+ const railwayRunner = railwayServiceByKey(railwayServices, "operationsRunner");
60
+ if (!web) {
61
+ if (!hasApiPlane) {
62
+ checks.push({
63
+ id: "hosting:web:present",
64
+ status: "failed",
65
+ message: "Web hosting unit is missing from the effective hosting graph.",
66
+ remediation: "Restore surfaces.web in treeseed.site.yaml."
67
+ });
68
+ }
69
+ } else {
70
+ checks.push(check("hosting:web:host", web.hostId, environment === "local" ? "local-process" : "cloudflare", "Web host matches the expected environment host.", "Set surfaces.web.provider to cloudflare for hosted environments.", "hostId"));
71
+ checks.push(check("hosting:web:rootDir", web.config?.rootDir, ".", "Web rootDir is the top-level UI application.", 'Set surfaces.web.rootDir to ".".', "rootDir"));
72
+ const apiConnection = graph.deployConfig.connections?.api;
73
+ const configuredBaseUrl = environment === "local" ? apiConnection?.localBaseUrl : apiConnection?.environments?.[environment]?.baseUrl ?? apiConnection?.environments?.[environment]?.domain;
74
+ checks.push({
75
+ id: "connection:api",
76
+ status: hasApiPlane || configuredBaseUrl ? "passed" : "failed",
77
+ expected: { apiConnection: true },
78
+ observed: {
79
+ localApiAppPresent: hasApiPlane,
80
+ baseUrl: configuredBaseUrl ?? null,
81
+ proxyPrefix: apiConnection?.proxyPrefix ?? null
82
+ },
83
+ message: hasApiPlane || configuredBaseUrl ? "Web app has an API app or configured API connection." : "Web app requires an API connection when no local API app manifest is present.",
84
+ remediation: hasApiPlane || configuredBaseUrl ? void 0 : "Add connections.api to treeseed.site.yaml or include packages/api/treeseed.site.yaml in the workspace."
85
+ });
86
+ }
87
+ if (!hasApiPlane && !hasWorkspaceMarketControlPlane) {
88
+ const counts2 = summary(checks);
89
+ return {
90
+ environment,
91
+ ok: counts2.failed === 0,
92
+ generatedAt: (options.now ?? /* @__PURE__ */ new Date()).toISOString(),
93
+ checks,
94
+ summary: counts2
95
+ };
96
+ }
97
+ const apiIsNestedApp = Boolean(api?.application?.relativeRoot && api.application.relativeRoot !== ".");
98
+ const runnerIsNestedApp = Boolean(runner?.application?.relativeRoot && runner.application.relativeRoot !== ".");
99
+ const expectedApiUnitRoot = apiIsNestedApp ? "." : "packages/api";
100
+ const expectedRunnerUnitRoot = runnerIsNestedApp ? "." : "packages/api";
101
+ const expectedApiRailwayRoot = api?.application?.relativeRoot && api.application.relativeRoot !== "." ? api.application.relativeRoot : expectedApiUnitRoot;
102
+ const expectedRunnerRailwayRoot = runner?.application?.relativeRoot && runner.application.relativeRoot !== "." ? runner.application.relativeRoot : expectedRunnerUnitRoot;
103
+ if (!api) {
104
+ checks.push({
105
+ id: "hosting:api:present",
106
+ status: "failed",
107
+ message: "API hosting unit is missing from the effective hosting graph.",
108
+ remediation: "Restore services.api in treeseed.site.yaml."
109
+ });
110
+ } else {
111
+ checks.push(check("hosting:api:host", api.hostId, environment === "local" ? "local-process" : "railway", "API host matches the expected environment host.", "Set services.api.provider to railway for hosted environments.", "hostId"));
112
+ checks.push(check("hosting:api:projectGroup", api.projectGroupId, "treeseed-control-plane", "API project group targets the Treeseed control plane.", "Bind services.api to the treeseed-control-plane project group.", "projectGroupId"));
113
+ checks.push(check("hosting:api:rootDir", api.config?.rootDir, expectedApiUnitRoot, "API effective rootDir points at the API package.", "Set services.api.rootDir and services.api.railway.rootDir relative to the owning API manifest.", "rootDir"));
114
+ checks.push(check("hosting:api:buildCommand", api.config?.buildCommand, "npm run build", "API build command is package-local.", 'Set services.api.railway.buildCommand to "npm run build".', "buildCommand"));
115
+ checks.push(check("hosting:api:startCommand", api.config?.startCommand, "npm run start:api", "API start command is package-local.", 'Set services.api.railway.startCommand to "npm run start:api".', "startCommand"));
116
+ checks.push(check("hosting:api:healthcheckPath", api.config?.healthcheckPath, "/healthz", "API healthcheck path is /healthz.", "Set services.api.railway.healthcheckPath to /healthz.", "healthcheckPath"));
117
+ }
118
+ if (railwayApi) {
119
+ checks.push(check("railway-config:api:serviceName", railwayApi.serviceName, "treeseed-api", "Railway API service uses the canonical name.", "Set services.api.railway.serviceName to treeseed-api.", "serviceName"));
120
+ checks.push(check("railway-config:api:rootDirectory", relRoot(tenantRoot, railwayApi.rootDir), expectedApiRailwayRoot, "Railway API effective rootDirectory points at the API app.", "Set services.api.railway.rootDir relative to the owning API manifest.", "rootDirectory"));
121
+ }
122
+ if (!runner) {
123
+ checks.push({
124
+ id: "hosting:operationsRunner:present",
125
+ status: "failed",
126
+ message: "Treeseed operations runner hosting unit is missing from the effective hosting graph.",
127
+ remediation: "Restore services.operationsRunner in treeseed.site.yaml."
128
+ });
129
+ } else {
130
+ checks.push(check("hosting:operationsRunner:host", runner.hostId, environment === "local" ? "local-docker" : "railway", "Runner host matches the expected environment host.", "Set services.operationsRunner.provider to railway for hosted environments.", "hostId"));
131
+ checks.push(check("hosting:operationsRunner:projectGroup", runner.projectGroupId, "treeseed-control-plane", "Runner project group targets the Treeseed control plane.", "Bind services.operationsRunner to the treeseed-control-plane project group.", "projectGroupId"));
132
+ checks.push(check("hosting:operationsRunner:rootDir", runner.config?.rootDir, expectedRunnerUnitRoot, "Runner effective rootDir points at the API package.", "Set services.operationsRunner.rootDir and services.operationsRunner.railway.rootDir relative to the owning API manifest.", "rootDir"));
133
+ checks.push(check("hosting:operationsRunner:buildCommand", runner.config?.buildCommand, "npm run build", "Runner build command is package-local.", 'Set services.operationsRunner.railway.buildCommand to "npm run build".', "buildCommand"));
134
+ checks.push(check("hosting:operationsRunner:startCommand", runner.config?.startCommand, "npm run start:runner", "Runner start command is package-local.", 'Set services.operationsRunner.railway.startCommand to "npm run start:runner".', "startCommand"));
135
+ checks.push(check("hosting:operationsRunner:healthcheckPath", runner.config?.healthcheckPath, "/healthz", "Runner healthcheck path is /healthz.", "Set services.operationsRunner.railway.healthcheckPath to /healthz.", "healthcheckPath"));
136
+ checks.push(check("hosting:operationsRunner:runtimeMode", runner.config?.runtimeMode, "service", "Runner runtime mode is a long-running service.", "Set services.operationsRunner.railway.runtimeMode to service.", "runtimeMode"));
137
+ checks.push(check("hosting:operationsRunner:volumeMountPath", runner.config?.volumeMountPath, "/data", "Runner volume mount path is stable.", "Set services.operationsRunner.railway.volumeMountPath to /data.", "volumeMountPath"));
138
+ }
139
+ if (railwayRunner) {
140
+ checks.push(check("railway-config:operationsRunner:serviceName", railwayRunner.serviceName, "treeseed-api-operations-runner-01", "Railway runner service instance uses the canonical name.", "Set the runner service instance name to treeseed-api-operations-runner-01.", "serviceName"));
141
+ checks.push(check("railway-config:operationsRunner:rootDirectory", relRoot(tenantRoot, railwayRunner.rootDir), expectedRunnerRailwayRoot, "Railway runner effective rootDirectory points at the API app.", "Set services.operationsRunner.railway.rootDir relative to the owning API manifest.", "rootDirectory"));
142
+ }
143
+ if (!database) {
144
+ checks.push({
145
+ id: "hosting:treeseedDatabase:present",
146
+ status: "failed",
147
+ message: "Treeseed database hosting unit is missing from the effective hosting graph.",
148
+ remediation: "Restore services.treeseedDatabase in treeseed.site.yaml."
149
+ });
150
+ } else {
151
+ checks.push(checkIncludes("hosting:treeseedDatabase:serviceTargets", database.config?.serviceTargets, ["api", "operationsRunner"], "Treeseed database targets API and runner services.", "Set services.treeseedDatabase.railway.serviceTargets to include api and operationsRunner."));
152
+ }
153
+ const counts = summary(checks);
154
+ return {
155
+ environment,
156
+ ok: counts.failed === 0,
157
+ generatedAt: (options.now ?? /* @__PURE__ */ new Date()).toISOString(),
158
+ checks,
159
+ summary: counts
160
+ };
161
+ }
162
+ function formatTreeseedReadinessReport(report) {
163
+ const lines = [
164
+ `Deployment readiness for ${report.environment}: ${report.ok ? "passed" : "failed"}`,
165
+ `passed=${report.summary.passed} failed=${report.summary.failed} warning=${report.summary.warning} skipped=${report.summary.skipped}`
166
+ ];
167
+ for (const check2 of report.checks.filter((entry) => entry.status !== "passed")) {
168
+ lines.push(`${check2.status.toUpperCase()} ${check2.id}: ${check2.message}${check2.remediation ? ` Remediation: ${check2.remediation}` : ""}`);
169
+ }
170
+ return lines.join("\n");
171
+ }
172
+ export {
173
+ collectTreeseedDeploymentReadiness,
174
+ formatTreeseedReadinessReport
175
+ };
@@ -179,9 +179,10 @@ export declare function mergeStagingIntoMain(cwd?: any): {
179
179
  commitSha: string;
180
180
  pushed: boolean;
181
181
  };
182
- export declare function mergeBranchIntoTarget(cwd?: any, { sourceBranch, targetBranch, message, pushTarget, quietMerge }?: {
182
+ export declare function mergeBranchIntoTarget(cwd?: any, { sourceBranch, targetBranch, message, pushTarget, quietMerge, allowUnrelatedHistories }?: {
183
183
  pushTarget?: boolean | undefined;
184
184
  quietMerge?: boolean | undefined;
185
+ allowUnrelatedHistories?: boolean | undefined;
185
186
  }): {
186
187
  repoDir: string;
187
188
  targetBranch: any;
@@ -456,16 +456,22 @@ function mergeStagingIntoMain(cwd = workspaceRoot()) {
456
456
  sourceBranch: STAGING_BRANCH,
457
457
  targetBranch: PRODUCTION_BRANCH,
458
458
  message: `release: ${STAGING_BRANCH} -> ${PRODUCTION_BRANCH}`,
459
- pushTarget: true
459
+ pushTarget: true,
460
+ allowUnrelatedHistories: false
460
461
  });
461
462
  }
462
- function mergeBranchIntoTarget(cwd = workspaceRoot(), { sourceBranch, targetBranch, message, pushTarget = true, quietMerge = false } = {}) {
463
+ function mergeBranchIntoTarget(cwd = workspaceRoot(), { sourceBranch, targetBranch, message, pushTarget = true, quietMerge = false, allowUnrelatedHistories = false } = {}) {
463
464
  const repoDir = prepareReleaseBranches(cwd);
464
465
  checkoutBranch(repoDir, targetBranch);
465
466
  if (remoteBranchExists(repoDir, targetBranch)) {
466
467
  runGit(["merge", "--ff-only", `origin/${targetBranch}`], { cwd: repoDir });
467
468
  }
468
- runGit(["merge", "--no-ff", sourceBranch, "-m", message], { cwd: repoDir, capture: quietMerge });
469
+ const mergeArgs = ["merge", "--no-ff"];
470
+ if (allowUnrelatedHistories) {
471
+ mergeArgs.push("--allow-unrelated-histories");
472
+ }
473
+ mergeArgs.push(sourceBranch, "-m", message);
474
+ runGit(mergeArgs, { cwd: repoDir, capture: quietMerge });
469
475
  pushBranch(repoDir, STAGING_BRANCH);
470
476
  if (pushTarget) {
471
477
  pushBranch(repoDir, targetBranch);
@@ -135,6 +135,7 @@ export declare function waitForGitHubActionsGate(gate: GitHubActionsWorkflowGate
135
135
  timeoutSeconds?: number;
136
136
  pollSeconds?: number;
137
137
  operation?: string;
138
+ env?: NodeJS.ProcessEnv | Record<string, string | undefined>;
138
139
  onProgress?: (message: string, stream?: 'stdout' | 'stderr') => void;
139
140
  }): Promise<Record<string, unknown>>;
140
141
  export {};
@@ -544,6 +544,7 @@ async function waitForGitHubActionsGate(gate, options = {}) {
544
544
  dispatchIfMissing: gate.dispatchIfMissing ?? gate.workflow === "verify.yml",
545
545
  dispatchAfterSeconds: gate.dispatchAfterSeconds ?? 75,
546
546
  dispatchInputs: gate.dispatchInputs,
547
+ env: options.env,
547
548
  onProgress: reportProgress
548
549
  });
549
550
  }
@@ -794,7 +794,7 @@ async function waitForGitHubWorkflowRunCompletion(repository, {
794
794
  continue;
795
795
  }
796
796
  for (; ; ) {
797
- if (Date.now() - startedAt >= timeoutSeconds * 1e3) {
797
+ if (Date.now() - startedAt >= timeoutSeconds * 1e3 && lastProgress?.runId === match.id) {
798
798
  break;
799
799
  }
800
800
  const current = await client.rest.actions.getWorkflowRun({
@@ -209,7 +209,7 @@ export declare function ensureGitHubDeployAutomation(tenantRoot: any, { dryRun,
209
209
  skipped?: undefined;
210
210
  };
211
211
  }>;
212
- export declare function waitForGitHubWorkflowCompletion(tenantRoot: any, { repository, workflow, headSha, branch, timeoutSeconds, pollSeconds, dispatchIfMissing, dispatchAfterSeconds, dispatchInputs, onProgress, }?: {
212
+ export declare function waitForGitHubWorkflowCompletion(tenantRoot: any, { repository, workflow, headSha, branch, timeoutSeconds, pollSeconds, dispatchIfMissing, dispatchAfterSeconds, dispatchInputs, onProgress, env, }?: {
213
213
  workflow?: string | undefined;
214
214
  timeoutSeconds?: number | undefined;
215
215
  pollSeconds?: number | undefined;
@@ -358,7 +358,7 @@ function ensureStandardizedGitHubWorkflows(tenantRoot) {
358
358
  const deployConfig = loadCliDeployConfig(tenantRoot);
359
359
  const deploy = ensureDeployWorkflow(tenantRoot);
360
360
  const workflows = [deploy, ...deploy.additionalWorkflows ?? []];
361
- if ((deployConfig.hosting?.kind ?? "self_hosted_project") === "market_control_plane") {
361
+ if ((deployConfig.hosting?.kind ?? "self_hosted_project") === "treeseed_control_plane") {
362
362
  workflows.push(ensureHostedProjectWorkflow(tenantRoot));
363
363
  }
364
364
  return workflows;
@@ -477,11 +477,12 @@ async function waitForGitHubWorkflowCompletion(tenantRoot, {
477
477
  dispatchIfMissing = false,
478
478
  dispatchAfterSeconds,
479
479
  dispatchInputs,
480
- onProgress
480
+ onProgress,
481
+ env
481
482
  } = {}) {
482
483
  const repo = repository ?? resolveGitHubRepositorySlug(tenantRoot);
483
484
  return await waitForGitHubWorkflowRunCompletion(repo, {
484
- client: createGitHubApiClient(),
485
+ client: createGitHubApiClient({ env }),
485
486
  workflow,
486
487
  headSha,
487
488
  branch,
@@ -0,0 +1,13 @@
1
+ export type TreeseedGitHubCredentialResolution = {
2
+ repository: string;
3
+ envName: string;
4
+ configured: boolean;
5
+ fallbackUsed: boolean;
6
+ source: 'repository' | 'fallback' | 'missing';
7
+ token: string | null;
8
+ };
9
+ export declare function githubRepositoryCredentialEnvName(repository: string): string;
10
+ export declare function resolveGitHubCredentialForRepository(repository: string, { values, env, }?: {
11
+ values?: Record<string, string | undefined>;
12
+ env?: NodeJS.ProcessEnv | Record<string, string | undefined>;
13
+ }): TreeseedGitHubCredentialResolution;
@@ -0,0 +1,58 @@
1
+ function cleanRepositorySlug(repository) {
2
+ const raw = String(repository ?? "").trim().replace(/^https?:\/\/github\.com\//iu, "").replace(/^git@github\.com:/iu, "").replace(/^ssh:\/\/git@github\.com\//iu, "").replace(/\.git$/iu, "").replace(/^\/+|\/+$/gu, "");
3
+ const [owner, repo, ...extra] = raw.split("/").filter(Boolean);
4
+ if (!owner || !repo || extra.length > 0) {
5
+ throw new Error(`Invalid GitHub repository "${repository}". Expected owner/name.`);
6
+ }
7
+ return `${owner}/${repo}`;
8
+ }
9
+ function githubRepositoryCredentialEnvName(repository) {
10
+ const slug = cleanRepositorySlug(repository);
11
+ const suffix = slug.toUpperCase().replace(/[^A-Z0-9]+/gu, "_").replace(/^_+|_+$/gu, "").replace(/_+/gu, "_");
12
+ return `TREESEED_GITHUB_TOKEN_${suffix}`;
13
+ }
14
+ function configuredValue(values, key) {
15
+ const value = values?.[key];
16
+ return typeof value === "string" && value.trim() ? value.trim() : "";
17
+ }
18
+ function resolveGitHubCredentialForRepository(repository, {
19
+ values,
20
+ env = process.env
21
+ } = {}) {
22
+ const normalizedRepository = cleanRepositorySlug(repository);
23
+ const envName = githubRepositoryCredentialEnvName(normalizedRepository);
24
+ const repositoryToken = configuredValue(values, envName) || configuredValue(env, envName);
25
+ if (repositoryToken) {
26
+ return {
27
+ repository: normalizedRepository,
28
+ envName,
29
+ configured: true,
30
+ fallbackUsed: false,
31
+ source: "repository",
32
+ token: repositoryToken
33
+ };
34
+ }
35
+ const fallbackToken = configuredValue(values, "GH_TOKEN") || configuredValue(values, "GITHUB_TOKEN") || configuredValue(env, "GH_TOKEN") || configuredValue(env, "GITHUB_TOKEN");
36
+ if (fallbackToken) {
37
+ return {
38
+ repository: normalizedRepository,
39
+ envName,
40
+ configured: true,
41
+ fallbackUsed: true,
42
+ source: "fallback",
43
+ token: fallbackToken
44
+ };
45
+ }
46
+ return {
47
+ repository: normalizedRepository,
48
+ envName,
49
+ configured: false,
50
+ fallbackUsed: false,
51
+ source: "missing",
52
+ token: null
53
+ };
54
+ }
55
+ export {
56
+ githubRepositoryCredentialEnvName,
57
+ resolveGitHubCredentialForRepository
58
+ };
@@ -0,0 +1,63 @@
1
+ export type TreeseedHostedServiceCheckStatus = 'passed' | 'failed' | 'skipped' | 'warning';
2
+ export type TreeseedHostedServiceTarget = 'local' | 'staging' | 'prod';
3
+ export type TreeseedHostedServiceProvider = 'cloudflare' | 'railway' | 'http' | 'dns' | 'github';
4
+ export type TreeseedHostedServiceType = 'web' | 'api' | 'operationsRunner' | 'treeseedDatabase' | 'customDomain' | 'dnsRecord' | 'githubWorkflow' | 'unknown';
5
+ export interface TreeseedHostedServiceCheck {
6
+ id: string;
7
+ provider: TreeseedHostedServiceProvider;
8
+ serviceKey?: string;
9
+ serviceType: TreeseedHostedServiceType;
10
+ target: TreeseedHostedServiceTarget;
11
+ description: string;
12
+ expected?: Record<string, unknown>;
13
+ observed?: Record<string, unknown>;
14
+ status: TreeseedHostedServiceCheckStatus;
15
+ issues: string[];
16
+ remediation?: string;
17
+ }
18
+ export interface TreeseedHostedServiceCheckReport {
19
+ target: TreeseedHostedServiceTarget;
20
+ tenantRoot: string;
21
+ generatedAt: string;
22
+ summary: {
23
+ passed: number;
24
+ failed: number;
25
+ skipped: number;
26
+ warning: number;
27
+ };
28
+ checks: TreeseedHostedServiceCheck[];
29
+ }
30
+ export interface TreeseedObservedRailwayServiceState {
31
+ projectName?: string | null;
32
+ environmentName?: string | null;
33
+ serviceName?: string | null;
34
+ rootDirectory?: string | null;
35
+ buildCommand?: string | null;
36
+ startCommand?: string | null;
37
+ healthcheckPath?: string | null;
38
+ healthcheckTimeoutSeconds?: number | null;
39
+ runtimeMode?: string | null;
40
+ volumeMountPath?: string | null;
41
+ variables?: Record<string, unknown> | string[];
42
+ secrets?: Record<string, unknown> | string[];
43
+ health?: 'ready' | 'failed' | 'unknown' | string | null;
44
+ }
45
+ export interface TreeseedHostedServiceCheckOptions {
46
+ tenantRoot: string;
47
+ target?: TreeseedHostedServiceTarget;
48
+ appId?: string;
49
+ now?: Date;
50
+ valuesOverlay?: Record<string, string | undefined>;
51
+ observedRailwayServices?: Record<string, TreeseedObservedRailwayServiceState | undefined>;
52
+ httpChecks?: Record<string, {
53
+ status?: number;
54
+ ok?: boolean;
55
+ skipped?: boolean;
56
+ error?: string;
57
+ fallbackUrl?: string;
58
+ fallbackStatus?: number;
59
+ fallbackOk?: boolean;
60
+ fallbackError?: string;
61
+ } | undefined>;
62
+ }
63
+ export declare function collectTreeseedHostedServiceChecks(options: TreeseedHostedServiceCheckOptions): TreeseedHostedServiceCheckReport;