@treeseed/sdk 0.6.16 → 0.6.17
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/db/d1.d.ts +3493 -0
- package/dist/db/d1.js +8 -0
- package/dist/db/index.d.ts +2 -0
- package/dist/db/index.js +2 -0
- package/dist/db/node-sqlite.d.ts +3544 -0
- package/dist/db/node-sqlite.js +119 -0
- package/dist/db/schema.d.ts +6272 -0
- package/dist/db/schema.js +231 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/operations/providers/default.js +1 -0
- package/dist/operations/services/commit-message-provider.d.ts +33 -1
- package/dist/operations/services/commit-message-provider.js +228 -51
- package/dist/operations/services/config-runtime.js +0 -1
- package/dist/operations/services/deploy.d.ts +19 -5
- package/dist/operations/services/deploy.js +75 -36
- package/dist/operations/services/github-actions-verification.d.ts +123 -0
- package/dist/operations/services/github-actions-verification.js +440 -0
- package/dist/operations/services/mailpit-runtime.d.ts +5 -0
- package/dist/operations/services/mailpit-runtime.js +2 -2
- package/dist/operations/services/repository-save-orchestrator.js +64 -8
- package/dist/operations/services/runtime-tools.d.ts +6 -0
- package/dist/operations/services/runtime-tools.js +11 -0
- package/dist/operations-registry.js +1 -0
- package/dist/platform/contracts.d.ts +6 -0
- package/dist/platform/deploy-config.js +17 -0
- package/dist/reconcile/builtin-adapters.js +2 -16
- package/dist/reconcile/contracts.d.ts +1 -1
- package/dist/reconcile/desired-state.d.ts +6 -0
- package/dist/reconcile/desired-state.js +1 -13
- package/dist/reconcile/engine.d.ts +12 -0
- package/dist/reconcile/state.js +2 -1
- package/dist/reconcile/units.js +0 -1
- package/dist/scripts/tenant-d1-migrate-local.js +5 -2
- package/dist/scripts/tenant-destroy.js +3 -1
- package/dist/sdk.js +2 -6
- package/dist/types/cloudflare.d.ts +0 -1
- package/dist/workflow/operations.d.ts +2 -1
- package/dist/workflow/operations.js +115 -35
- package/dist/workflow-support.d.ts +1 -0
- package/dist/workflow-support.js +6 -0
- package/dist/workflow.d.ts +24 -2
- package/dist/workflow.js +6 -0
- package/package.json +19 -5
- package/templates/github/deploy.workflow.yml +4 -0
- package/dist/wrangler-d1.d.ts +0 -25
- package/dist/wrangler-d1.js +0 -89
|
@@ -57,6 +57,9 @@ const webSurfaceCacheFieldAliases = {
|
|
|
57
57
|
contentPages: { key: "contentPages", aliases: ["content_pages"] },
|
|
58
58
|
r2PublishedObjects: { key: "r2PublishedObjects", aliases: ["r2_published_objects"] }
|
|
59
59
|
};
|
|
60
|
+
const localRuntimeFieldAliases = {
|
|
61
|
+
runtime: { key: "runtime", aliases: ["runtime", "runtime_mode", "runtimeMode"] }
|
|
62
|
+
};
|
|
60
63
|
const webCachePolicyFieldAliases = {
|
|
61
64
|
browserTtlSeconds: { key: "browserTtlSeconds", aliases: ["browser_ttl_seconds"] },
|
|
62
65
|
edgeTtlSeconds: { key: "edgeTtlSeconds", aliases: ["edge_ttl_seconds"] },
|
|
@@ -326,6 +329,18 @@ function parseServiceEnvironmentConfig(value, label) {
|
|
|
326
329
|
railwayEnvironment: optionalString(record.railwayEnvironment)
|
|
327
330
|
};
|
|
328
331
|
}
|
|
332
|
+
function parseLocalRuntimeConfig(value, label) {
|
|
333
|
+
const record = normalizeAliasedRecord(
|
|
334
|
+
localRuntimeFieldAliases,
|
|
335
|
+
optionalRecord(value, label) ?? {}
|
|
336
|
+
);
|
|
337
|
+
if (!value || Object.keys(record).length === 0) {
|
|
338
|
+
return void 0;
|
|
339
|
+
}
|
|
340
|
+
return {
|
|
341
|
+
runtime: optionalEnum(record.runtime, `${label}.runtime`, ["auto", "provider", "local"])
|
|
342
|
+
};
|
|
343
|
+
}
|
|
329
344
|
function parseManagedServiceConfig(value, label) {
|
|
330
345
|
const record = optionalRecord(value, label);
|
|
331
346
|
if (!record) {
|
|
@@ -359,6 +374,7 @@ function parseManagedServiceConfig(value, label) {
|
|
|
359
374
|
runtimeMode: optionalString(railway.runtimeMode),
|
|
360
375
|
schedule: Array.isArray(railway.schedule) ? railway.schedule.map((entry) => optionalString(entry)).filter(Boolean) : optionalString(railway.schedule)
|
|
361
376
|
},
|
|
377
|
+
local: parseLocalRuntimeConfig(record.local, `${label}.local`),
|
|
362
378
|
environments: {
|
|
363
379
|
local: parseServiceEnvironmentConfig(environments.local, `${label}.environments.local`),
|
|
364
380
|
staging: parseServiceEnvironmentConfig(environments.staging, `${label}.environments.staging`),
|
|
@@ -394,6 +410,7 @@ function parsePlatformSurfaceConfig(value, label) {
|
|
|
394
410
|
rootDir: optionalString(record.rootDir),
|
|
395
411
|
publicBaseUrl: optionalString(record.publicBaseUrl),
|
|
396
412
|
localBaseUrl: optionalString(record.localBaseUrl),
|
|
413
|
+
local: parseLocalRuntimeConfig(record.local, `${label}.local`),
|
|
397
414
|
environments: (() => {
|
|
398
415
|
const environments = optionalRecord(record.environments, `${label}.environments`);
|
|
399
416
|
if (!environments) {
|
|
@@ -835,7 +835,6 @@ function reconcileCloudflareTarget(input, { dryRun = false } = {}) {
|
|
|
835
835
|
current.url = `https://${current.projectName}.pages.dev`;
|
|
836
836
|
};
|
|
837
837
|
runStep("kv-form-guard", () => ensureKv("FORM_GUARD_KV"));
|
|
838
|
-
runStep("kv-session", () => ensureKv("SESSION"));
|
|
839
838
|
runStep("d1", ensureD1);
|
|
840
839
|
runStep("queue", ensureQueue);
|
|
841
840
|
runStep("r2", ensureR2Bucket);
|
|
@@ -932,16 +931,6 @@ function observeCloudflareUnit(input) {
|
|
|
932
931
|
warnings: []
|
|
933
932
|
};
|
|
934
933
|
}
|
|
935
|
-
case "kv-session": {
|
|
936
|
-
const liveNamespace = kvNamespaces.find((entry) => entry?.title === state.kvNamespaces?.SESSION?.name);
|
|
937
|
-
return {
|
|
938
|
-
exists: Boolean(liveNamespace || hasLiveResourceId(state.kvNamespaces?.SESSION?.id)),
|
|
939
|
-
status: liveNamespace ? "ready" : "pending",
|
|
940
|
-
live: { ...state.kvNamespaces?.SESSION ?? {} },
|
|
941
|
-
locators: { id: liveNamespace?.id ?? state.kvNamespaces?.SESSION?.id ?? null },
|
|
942
|
-
warnings: []
|
|
943
|
-
};
|
|
944
|
-
}
|
|
945
934
|
case "pages-project": {
|
|
946
935
|
const liveProject = pagesProjects.find((entry) => entry?.name === state.pages?.projectName);
|
|
947
936
|
return {
|
|
@@ -1029,9 +1018,8 @@ function verifyCloudflareUnitOnce(input, postconditions) {
|
|
|
1029
1018
|
})
|
|
1030
1019
|
]);
|
|
1031
1020
|
}
|
|
1032
|
-
case "kv-form-guard":
|
|
1033
|
-
|
|
1034
|
-
const binding = input.unit.unitType === "kv-form-guard" ? "FORM_GUARD_KV" : "SESSION";
|
|
1021
|
+
case "kv-form-guard": {
|
|
1022
|
+
const binding = "FORM_GUARD_KV";
|
|
1035
1023
|
const namespace = state.kvNamespaces?.[binding];
|
|
1036
1024
|
const live = getCloudflareKvById(env, namespace?.id) ?? kvNamespaces.find((entry) => entry?.title === namespace?.name);
|
|
1037
1025
|
return summarizeVerification(input.unit.unitId, [
|
|
@@ -1209,7 +1197,6 @@ function buildCloudflareAdapter(unitType) {
|
|
|
1209
1197
|
{ key: "database.binding", description: "D1 binding matches desired config" }
|
|
1210
1198
|
];
|
|
1211
1199
|
case "kv-form-guard":
|
|
1212
|
-
case "kv-session":
|
|
1213
1200
|
return [
|
|
1214
1201
|
{ key: "kv.exists", description: "KV namespace exists by title and id" },
|
|
1215
1202
|
{ key: "kv.binding", description: "KV binding matches desired config" }
|
|
@@ -2109,7 +2096,6 @@ function createCloudflareReconcileAdapters() {
|
|
|
2109
2096
|
buildCloudflareAdapter("database"),
|
|
2110
2097
|
buildCloudflareAdapter("content-store"),
|
|
2111
2098
|
buildCloudflareAdapter("kv-form-guard"),
|
|
2112
|
-
buildCloudflareAdapter("kv-session"),
|
|
2113
2099
|
buildCloudflareAdapter("pages-project"),
|
|
2114
2100
|
buildCloudflareAdapter("edge-worker"),
|
|
2115
2101
|
buildCustomDomainAdapter("custom-domain:web", "cloudflare"),
|
|
@@ -3,7 +3,7 @@ export type TreeseedReconcileProviderId = string;
|
|
|
3
3
|
export type TreeseedReconcileActionKind = 'noop' | 'create' | 'update' | 'reuse' | 'drift_correct' | 'destroy';
|
|
4
4
|
export type TreeseedReconcileStatusKind = 'pending' | 'ready' | 'drifted' | 'error';
|
|
5
5
|
export type TreeseedReconcileVerificationSource = 'cli' | 'api' | 'sdk' | 'derived';
|
|
6
|
-
export type TreeseedReconcileUnitType = 'web-ui' | 'api-runtime' | 'manager-runtime' | 'worker-runtime' | 'workday-start-runtime' | 'workday-report-runtime' | 'edge-worker' | 'content-store' | 'queue' | 'database' | 'kv-form-guard' | '
|
|
6
|
+
export type TreeseedReconcileUnitType = 'web-ui' | 'api-runtime' | 'manager-runtime' | 'worker-runtime' | 'workday-start-runtime' | 'workday-report-runtime' | 'edge-worker' | 'content-store' | 'queue' | 'database' | 'kv-form-guard' | 'pages-project' | 'custom-domain:web' | 'custom-domain:api' | 'dns-record' | 'railway-service:api' | 'railway-service:manager' | 'railway-service:worker' | 'railway-service:workday-start' | 'railway-service:workday-report';
|
|
7
7
|
export type TreeseedReconcileTarget = {
|
|
8
8
|
kind: 'persistent';
|
|
9
9
|
scope: 'local' | 'staging' | 'prod';
|
|
@@ -84,6 +84,9 @@ export declare function deriveTreeseedDesiredUnits({ tenantRoot, target, }: {
|
|
|
84
84
|
rootDir: string | undefined;
|
|
85
85
|
publicBaseUrl: string | undefined;
|
|
86
86
|
localBaseUrl: string | undefined;
|
|
87
|
+
local: {
|
|
88
|
+
runtime: string | undefined;
|
|
89
|
+
} | undefined;
|
|
87
90
|
environments: {
|
|
88
91
|
local: {
|
|
89
92
|
baseUrl: string | undefined;
|
|
@@ -135,6 +138,9 @@ export declare function deriveTreeseedDesiredUnits({ tenantRoot, target, }: {
|
|
|
135
138
|
provider: string | undefined;
|
|
136
139
|
rootDir: string | undefined;
|
|
137
140
|
publicBaseUrl: string | undefined;
|
|
141
|
+
local: {
|
|
142
|
+
runtime: string | undefined;
|
|
143
|
+
} | undefined;
|
|
138
144
|
cloudflare: {
|
|
139
145
|
workerName: string | undefined;
|
|
140
146
|
};
|
|
@@ -78,18 +78,6 @@ function deriveTreeseedDesiredUnits({
|
|
|
78
78
|
secrets: {},
|
|
79
79
|
metadata: { bootstrapSystem: "web" }
|
|
80
80
|
});
|
|
81
|
-
const sessionKvId = add({
|
|
82
|
-
unitId: createTreeseedReconcileUnitId("kv-session", legacyState.kvNamespaces.SESSION.name),
|
|
83
|
-
unitType: "kv-session",
|
|
84
|
-
provider: "cloudflare",
|
|
85
|
-
identity,
|
|
86
|
-
target,
|
|
87
|
-
logicalName: legacyState.kvNamespaces.SESSION.name,
|
|
88
|
-
dependencies: [],
|
|
89
|
-
spec: { binding: "SESSION", name: legacyState.kvNamespaces.SESSION.name },
|
|
90
|
-
secrets: {},
|
|
91
|
-
metadata: { bootstrapSystem: "web" }
|
|
92
|
-
});
|
|
93
81
|
const contentStoreId = add({
|
|
94
82
|
unitId: createTreeseedReconcileUnitId("content-store", legacyState.content.bucketName ?? deployConfig.slug),
|
|
95
83
|
unitType: "content-store",
|
|
@@ -132,7 +120,7 @@ function deriveTreeseedDesiredUnits({
|
|
|
132
120
|
identity,
|
|
133
121
|
target,
|
|
134
122
|
logicalName: legacyState.workerName,
|
|
135
|
-
dependencies: [queueId, databaseId, formGuardKvId,
|
|
123
|
+
dependencies: [queueId, databaseId, formGuardKvId, contentStoreId, pagesProjectId],
|
|
136
124
|
spec: {
|
|
137
125
|
workerName: legacyState.workerName
|
|
138
126
|
},
|
|
@@ -91,6 +91,9 @@ export declare function observeTreeseedUnits({ tenantRoot, target, env, systems,
|
|
|
91
91
|
rootDir: string | undefined;
|
|
92
92
|
publicBaseUrl: string | undefined;
|
|
93
93
|
localBaseUrl: string | undefined;
|
|
94
|
+
local: {
|
|
95
|
+
runtime: string | undefined;
|
|
96
|
+
} | undefined;
|
|
94
97
|
environments: {
|
|
95
98
|
local: {
|
|
96
99
|
baseUrl: string | undefined;
|
|
@@ -142,6 +145,9 @@ export declare function observeTreeseedUnits({ tenantRoot, target, env, systems,
|
|
|
142
145
|
provider: string | undefined;
|
|
143
146
|
rootDir: string | undefined;
|
|
144
147
|
publicBaseUrl: string | undefined;
|
|
148
|
+
local: {
|
|
149
|
+
runtime: string | undefined;
|
|
150
|
+
} | undefined;
|
|
145
151
|
cloudflare: {
|
|
146
152
|
workerName: string | undefined;
|
|
147
153
|
};
|
|
@@ -274,6 +280,9 @@ export declare function planTreeseedReconciliation({ tenantRoot, target, env, sy
|
|
|
274
280
|
rootDir: string | undefined;
|
|
275
281
|
publicBaseUrl: string | undefined;
|
|
276
282
|
localBaseUrl: string | undefined;
|
|
283
|
+
local: {
|
|
284
|
+
runtime: string | undefined;
|
|
285
|
+
} | undefined;
|
|
277
286
|
environments: {
|
|
278
287
|
local: {
|
|
279
288
|
baseUrl: string | undefined;
|
|
@@ -325,6 +334,9 @@ export declare function planTreeseedReconciliation({ tenantRoot, target, env, sy
|
|
|
325
334
|
provider: string | undefined;
|
|
326
335
|
rootDir: string | undefined;
|
|
327
336
|
publicBaseUrl: string | undefined;
|
|
337
|
+
local: {
|
|
338
|
+
runtime: string | undefined;
|
|
339
|
+
} | undefined;
|
|
328
340
|
cloudflare: {
|
|
329
341
|
workerName: string | undefined;
|
|
330
342
|
};
|
package/dist/reconcile/state.js
CHANGED
|
@@ -118,9 +118,10 @@ function migrateLegacyDeployStateUnits(legacyState, target) {
|
|
|
118
118
|
};
|
|
119
119
|
}
|
|
120
120
|
for (const [binding, namespace] of Object.entries(legacyState.kvNamespaces ?? {})) {
|
|
121
|
+
if (binding !== "FORM_GUARD_KV") continue;
|
|
121
122
|
const record = namespace;
|
|
122
123
|
if (!record?.name) continue;
|
|
123
|
-
const unitType =
|
|
124
|
+
const unitType = "kv-form-guard";
|
|
124
125
|
units[`${unitType}:${record.name}`] = {
|
|
125
126
|
unitId: `${unitType}:${record.name}`,
|
|
126
127
|
unitType,
|
package/dist/reconcile/units.js
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import { resolve } from 'node:path';
|
|
2
2
|
import { runLocalD1Migrations } from '../operations/services/d1-migration.js';
|
|
3
|
-
import { ensureGeneratedWranglerConfig } from '../operations/services/deploy.js';
|
|
3
|
+
import { createPersistentDeployTarget, ensureGeneratedWranglerConfig } from '../operations/services/deploy.js';
|
|
4
4
|
const tenantRoot = process.cwd();
|
|
5
5
|
const migrationsRoot = resolve(tenantRoot, 'migrations');
|
|
6
|
-
const { wranglerPath: wranglerConfig } = ensureGeneratedWranglerConfig(tenantRoot
|
|
6
|
+
const { wranglerPath: wranglerConfig } = ensureGeneratedWranglerConfig(tenantRoot, {
|
|
7
|
+
target: createPersistentDeployTarget('local'),
|
|
8
|
+
env: process.env,
|
|
9
|
+
});
|
|
7
10
|
runLocalD1Migrations({
|
|
8
11
|
cwd: tenantRoot,
|
|
9
12
|
wranglerConfig,
|
|
@@ -61,7 +61,9 @@ function printDangerMessage(deployConfig, state, expectedConfirmation) {
|
|
|
61
61
|
console.error(` Worker: ${state.workerName ?? deriveCloudflareWorkerName(deployConfig)}`);
|
|
62
62
|
console.error(` D1: ${state.d1Databases.SITE_DATA_DB.databaseName}`);
|
|
63
63
|
console.error(` KV FORM_GUARD_KV: ${state.kvNamespaces.FORM_GUARD_KV.name}`);
|
|
64
|
-
|
|
64
|
+
if (state.kvNamespaces.SESSION?.name) {
|
|
65
|
+
console.error(` KV SESSION (deprecated): ${state.kvNamespaces.SESSION.name}`);
|
|
66
|
+
}
|
|
65
67
|
console.error(' This action is irreversible.');
|
|
66
68
|
console.error(` To continue, type exactly: ${expectedConfirmation}`);
|
|
67
69
|
}
|
package/dist/sdk.js
CHANGED
|
@@ -9,7 +9,7 @@ import { findDispatchCapability } from "./dispatch.js";
|
|
|
9
9
|
import { RemoteTreeseedClient, RemoteTreeseedDispatchClient } from "./remote.js";
|
|
10
10
|
import { executeSdkOperation } from "./sdk-dispatch.js";
|
|
11
11
|
import { TreeseedOperationsSdk } from "./operations/runtime.js";
|
|
12
|
-
import {
|
|
12
|
+
import { NodeSqliteD1Database } from "./db/node-sqlite.js";
|
|
13
13
|
function normalizeAgentSpec(entry) {
|
|
14
14
|
if (!entry) {
|
|
15
15
|
return null;
|
|
@@ -59,11 +59,7 @@ class AgentSdk {
|
|
|
59
59
|
}
|
|
60
60
|
static createLocal(options) {
|
|
61
61
|
const repoRoot = resolveSdkRepoRoot(options.repoRoot);
|
|
62
|
-
const d1 = new
|
|
63
|
-
options.databaseName ?? "karyon-docs-site-data",
|
|
64
|
-
repoRoot,
|
|
65
|
-
options.persistTo
|
|
66
|
-
);
|
|
62
|
+
const d1 = new NodeSqliteD1Database(options.persistTo ?? options.databaseName ?? ".treeseed/generated/environments/local/site-data.sqlite");
|
|
67
63
|
return new AgentSdk({
|
|
68
64
|
repoRoot,
|
|
69
65
|
database: new CloudflareD1AgentDatabase(d1),
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { resolveTreeseedWorkflowState, type TreeseedWorkflowStatusOptions } from '../workflow-state.ts';
|
|
2
2
|
import { type TreeseedWorkflowRunCommand, type TreeseedWorkflowRunJournal } from './runs.ts';
|
|
3
3
|
import { type TreeseedWorkflowMode } from './session.ts';
|
|
4
|
-
import type { TreeseedCloseInput, TreeseedConfigInput, TreeseedDestroyInput, TreeseedExportInput, TreeseedReleaseInput, TreeseedRecoverInput, TreeseedResumeInput, TreeseedSaveInput, TreeseedStageInput, TreeseedSwitchInput, TreeseedTaskBranchMetadata, TreeseedWorkflowContext, TreeseedWorkflowDevInput, TreeseedWorkflowOperationId, TreeseedWorkflowResult, TreeseedWorkflowWorktreeMode } from '../workflow.ts';
|
|
4
|
+
import type { TreeseedCloseInput, TreeseedCiInput, TreeseedConfigInput, TreeseedDestroyInput, TreeseedExportInput, TreeseedReleaseInput, TreeseedRecoverInput, TreeseedResumeInput, TreeseedSaveInput, TreeseedStageInput, TreeseedSwitchInput, TreeseedTaskBranchMetadata, TreeseedWorkflowContext, TreeseedWorkflowDevInput, TreeseedWorkflowOperationId, TreeseedWorkflowResult, TreeseedWorkflowWorktreeMode } from '../workflow.ts';
|
|
5
5
|
type WorkflowWrite = NonNullable<TreeseedWorkflowContext['write']>;
|
|
6
6
|
type WorkflowStatePayload = ReturnType<typeof resolveTreeseedWorkflowState>;
|
|
7
7
|
export type TreeseedWorkflowErrorCode = 'validation_failed' | 'merge_conflict' | 'missing_runtime_auth' | 'deployment_timeout' | 'confirmation_required' | 'unsupported_transport' | 'unsupported_state' | 'workflow_locked' | 'resume_unavailable' | 'workflow_contract_missing' | 'github_workflow_failed' | 'github_auth_unavailable';
|
|
@@ -44,6 +44,7 @@ type WorkflowRepoReport = {
|
|
|
44
44
|
workflowGates: Array<Record<string, unknown>>;
|
|
45
45
|
};
|
|
46
46
|
export declare function workflowStatus(helpers: WorkflowOperationHelpers, input?: TreeseedWorkflowStatusOptions): Promise<TreeseedWorkflowResult<import("../workflow-state.ts").TreeseedWorkflowState>>;
|
|
47
|
+
export declare function workflowCi(helpers: WorkflowOperationHelpers, input?: TreeseedCiInput): Promise<TreeseedWorkflowResult<TreeseedCiResult>>;
|
|
47
48
|
export declare function workflowTasks(helpers: WorkflowOperationHelpers): Promise<TreeseedWorkflowResult<{
|
|
48
49
|
tasks: TreeseedTaskBranchMetadata[];
|
|
49
50
|
workstreams: Array<{
|
|
@@ -66,7 +66,13 @@ import {
|
|
|
66
66
|
squashMergeBranchIntoStaging,
|
|
67
67
|
syncBranchWithOrigin
|
|
68
68
|
} from "../operations/services/git-workflow.js";
|
|
69
|
-
import { getGitHubAutomationMode, resolveGitHubRepositorySlug
|
|
69
|
+
import { getGitHubAutomationMode, resolveGitHubRepositorySlug } from "../operations/services/github-automation.js";
|
|
70
|
+
import {
|
|
71
|
+
formatGitHubActionsGateFailure,
|
|
72
|
+
inspectGitHubActionsVerification,
|
|
73
|
+
skippedGitHubActionsGate,
|
|
74
|
+
waitForGitHubActionsGate
|
|
75
|
+
} from "../operations/services/github-actions-verification.js";
|
|
70
76
|
import { loadCliDeployConfig, packageScriptPath, resolveWranglerBin } from "../operations/services/runtime-tools.js";
|
|
71
77
|
import { runTenantDeployPreflight, runWorkspaceReleasePreflight, runWorkspaceSavePreflight } from "../operations/services/save-deploy-preflight.js";
|
|
72
78
|
import { collectCliPreflight } from "../operations/services/workspace-preflight.js";
|
|
@@ -309,32 +315,6 @@ function helpersForCwd(helpers, cwd) {
|
|
|
309
315
|
function shouldDispatchSwitchToManagedWorktree(root, input, env) {
|
|
310
316
|
return !isManagedWorkflowWorktree(root) && effectiveWorkflowWorktreeMode(input.worktreeMode, env) === "on";
|
|
311
317
|
}
|
|
312
|
-
function skippedWorkflowGate(gate, reason) {
|
|
313
|
-
return {
|
|
314
|
-
name: gate.name,
|
|
315
|
-
repository: gate.repository ?? null,
|
|
316
|
-
workflow: gate.workflow,
|
|
317
|
-
branch: gate.branch,
|
|
318
|
-
headSha: gate.headSha,
|
|
319
|
-
status: "skipped",
|
|
320
|
-
reason,
|
|
321
|
-
conclusion: null,
|
|
322
|
-
runId: null,
|
|
323
|
-
url: null
|
|
324
|
-
};
|
|
325
|
-
}
|
|
326
|
-
function workflowGateFailureMessage(gate, result) {
|
|
327
|
-
const repository = String(result.repository ?? gate.repository ?? gate.name);
|
|
328
|
-
const runId = typeof result.runId === "number" || typeof result.runId === "string" ? String(result.runId) : "";
|
|
329
|
-
const url = typeof result.url === "string" && result.url ? `
|
|
330
|
-
${result.url}` : "";
|
|
331
|
-
const failedJobs = Array.isArray(result.failedJobs) ? result.failedJobs.map((job) => stringRecord(job)?.name).filter((name) => typeof name === "string" && name.length > 0) : [];
|
|
332
|
-
const jobLine = failedJobs.length > 0 ? `
|
|
333
|
-
Failed jobs: ${failedJobs.join(", ")}` : "";
|
|
334
|
-
const command = runId ? `
|
|
335
|
-
Inspect with: gh run view ${runId} --repo ${repository} --log-failed` : "";
|
|
336
|
-
return `${gate.name} ${gate.workflow} completed with conclusion ${String(result.conclusion ?? "unknown")} in ${repository}.${url}${jobLine}${command}`;
|
|
337
|
-
}
|
|
338
318
|
function assertHostedGitHubWorkflowAuthReady(operation, root) {
|
|
339
319
|
if (getGitHubAutomationMode() === "stub") {
|
|
340
320
|
return null;
|
|
@@ -370,7 +350,7 @@ function assertHostedGitHubWorkflowAuthReady(operation, root) {
|
|
|
370
350
|
}
|
|
371
351
|
async function waitForWorkflowGates(operation, gates, ciMode, options = {}) {
|
|
372
352
|
if (ciMode === "off" || process.env.TREESEED_STAGE_WAIT_MODE === "skip") {
|
|
373
|
-
return gates.map((gate) =>
|
|
353
|
+
return gates.map((gate) => skippedGitHubActionsGate(gate, ciMode === "off" ? "disabled" : "stubbed"));
|
|
374
354
|
}
|
|
375
355
|
if (gates.length === 0) {
|
|
376
356
|
return [];
|
|
@@ -394,12 +374,7 @@ async function waitForWorkflowGates(operation, gates, ciMode, options = {}) {
|
|
|
394
374
|
continue;
|
|
395
375
|
}
|
|
396
376
|
}
|
|
397
|
-
const result = await
|
|
398
|
-
repository: gate.repository,
|
|
399
|
-
workflow: gate.workflow,
|
|
400
|
-
headSha: gate.headSha,
|
|
401
|
-
branch: gate.branch
|
|
402
|
-
});
|
|
377
|
+
const result = await waitForGitHubActionsGate(gate);
|
|
403
378
|
const normalized = {
|
|
404
379
|
name: gate.name,
|
|
405
380
|
...result,
|
|
@@ -408,7 +383,7 @@ async function waitForWorkflowGates(operation, gates, ciMode, options = {}) {
|
|
|
408
383
|
headSha: String(result.headSha ?? gate.headSha)
|
|
409
384
|
};
|
|
410
385
|
if (normalized.status === "completed" && normalized.conclusion !== "success") {
|
|
411
|
-
workflowError(operation, "github_workflow_failed",
|
|
386
|
+
workflowError(operation, "github_workflow_failed", formatGitHubActionsGateFailure(gate, normalized), {
|
|
412
387
|
details: { gate, workflow: normalized }
|
|
413
388
|
});
|
|
414
389
|
}
|
|
@@ -711,6 +686,86 @@ function createStatusResult(cwd, options = {}) {
|
|
|
711
686
|
includeFinalState: false
|
|
712
687
|
});
|
|
713
688
|
}
|
|
689
|
+
function normalizeCiScope(value) {
|
|
690
|
+
return value === "root" || value === "packages" ? value : "workspace";
|
|
691
|
+
}
|
|
692
|
+
function normalizeCiLogLines(value) {
|
|
693
|
+
const parsed = typeof value === "number" ? value : typeof value === "string" ? Number.parseInt(value, 10) : 120;
|
|
694
|
+
return Number.isFinite(parsed) ? Math.max(20, Math.min(1e3, Math.floor(parsed))) : 120;
|
|
695
|
+
}
|
|
696
|
+
function normalizeCiWorkflows(input) {
|
|
697
|
+
const raw = input.workflows ?? input.workflow ?? [];
|
|
698
|
+
return (Array.isArray(raw) ? raw : [raw]).map((workflow) => String(workflow ?? "").trim()).filter(Boolean);
|
|
699
|
+
}
|
|
700
|
+
function defaultCiWorkflows(kind, branch) {
|
|
701
|
+
if (kind === "package") {
|
|
702
|
+
return ["verify.yml"];
|
|
703
|
+
}
|
|
704
|
+
const workflows = ["verify.yml"];
|
|
705
|
+
if (branch === STAGING_BRANCH || branch === PRODUCTION_BRANCH) {
|
|
706
|
+
workflows.push("deploy.yml");
|
|
707
|
+
}
|
|
708
|
+
return workflows;
|
|
709
|
+
}
|
|
710
|
+
function githubRepositoryForRepo(repoDir) {
|
|
711
|
+
try {
|
|
712
|
+
return resolveGitHubRepositorySlug(repoDir);
|
|
713
|
+
} catch {
|
|
714
|
+
return null;
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
function ciTargetForRepo(repo, kind, input, workflowOverrides) {
|
|
718
|
+
const branch = typeof input.branch === "string" && input.branch.trim() ? input.branch.trim() : repo.branchName;
|
|
719
|
+
const workflows = workflowOverrides.length > 0 ? workflowOverrides : defaultCiWorkflows(kind, branch);
|
|
720
|
+
return {
|
|
721
|
+
name: repo.name,
|
|
722
|
+
repoPath: repo.path,
|
|
723
|
+
repository: githubRepositoryForRepo(repo.path),
|
|
724
|
+
branch,
|
|
725
|
+
headSha: branch ? headCommit(repo.path) : null,
|
|
726
|
+
workflows,
|
|
727
|
+
kind
|
|
728
|
+
};
|
|
729
|
+
}
|
|
730
|
+
function ciTargetsForSession(session, input) {
|
|
731
|
+
const scope = normalizeCiScope(input.scope);
|
|
732
|
+
const workflows = normalizeCiWorkflows(input);
|
|
733
|
+
const targets = [];
|
|
734
|
+
if (scope === "workspace" || scope === "root") {
|
|
735
|
+
targets.push(ciTargetForRepo(session.rootRepo, "root", input, workflows));
|
|
736
|
+
}
|
|
737
|
+
if (scope === "workspace" || scope === "packages") {
|
|
738
|
+
targets.push(...session.packageRepos.map((repo) => ciTargetForRepo(repo, "package", input, workflows)));
|
|
739
|
+
}
|
|
740
|
+
return { scope, targets };
|
|
741
|
+
}
|
|
742
|
+
async function createCiResult(cwd, input) {
|
|
743
|
+
const session = resolveTreeseedWorkflowSession(cwd);
|
|
744
|
+
const { scope, targets } = ciTargetsForSession(session, input);
|
|
745
|
+
const strict = input.strict === true;
|
|
746
|
+
const includeLogs = input.logs === true || input.includeLogs === true;
|
|
747
|
+
const report = await inspectGitHubActionsVerification(targets, {
|
|
748
|
+
includeLogs,
|
|
749
|
+
logLines: normalizeCiLogLines(input.logLines)
|
|
750
|
+
});
|
|
751
|
+
const hasFailures = report.failures.length > 0;
|
|
752
|
+
const hasPending = report.summary.pending > 0;
|
|
753
|
+
const exitCode = hasFailures || strict && hasPending ? 1 : 0;
|
|
754
|
+
const payload = {
|
|
755
|
+
...report,
|
|
756
|
+
mode: session.mode,
|
|
757
|
+
branch: typeof input.branch === "string" && input.branch.trim() ? input.branch.trim() : session.branchName,
|
|
758
|
+
scope,
|
|
759
|
+
strict,
|
|
760
|
+
hasFailures,
|
|
761
|
+
hasPending,
|
|
762
|
+
exitCode
|
|
763
|
+
};
|
|
764
|
+
return buildWorkflowResult("ci", cwd, payload, {
|
|
765
|
+
includeFinalState: false,
|
|
766
|
+
summary: hasFailures ? "Treeseed CI found remote GitHub Actions failures." : strict && hasPending ? "Treeseed CI found pending remote GitHub Actions runs." : "Treeseed CI status is clear."
|
|
767
|
+
});
|
|
768
|
+
}
|
|
714
769
|
function createTasksResult(cwd) {
|
|
715
770
|
const tenantRoot = cwd;
|
|
716
771
|
const repoDir = gitWorkflowRoot(tenantRoot);
|
|
@@ -1642,6 +1697,30 @@ async function workflowStatus(helpers, input = {}) {
|
|
|
1642
1697
|
});
|
|
1643
1698
|
});
|
|
1644
1699
|
}
|
|
1700
|
+
async function workflowCi(helpers, input = {}) {
|
|
1701
|
+
return withContextEnv(helpers.context.env, async () => {
|
|
1702
|
+
try {
|
|
1703
|
+
const resolved = resolveTreeseedWorkflowPaths(helpers.cwd());
|
|
1704
|
+
const branch = currentBranch(repoRoot(resolved.cwd)) || null;
|
|
1705
|
+
const scope = branch === PRODUCTION_BRANCH ? "prod" : branch === STAGING_BRANCH ? "staging" : "local";
|
|
1706
|
+
const env = resolved.tenantRoot ? resolveTreeseedLaunchEnvironment({
|
|
1707
|
+
tenantRoot: resolved.cwd,
|
|
1708
|
+
scope,
|
|
1709
|
+
baseEnv: { ...process.env, ...helpers.context.env ?? {} }
|
|
1710
|
+
}) : { ...process.env, ...helpers.context.env ?? {} };
|
|
1711
|
+
return await withContextEnv(env, () => createCiResult(helpers.cwd(), input));
|
|
1712
|
+
} catch (error) {
|
|
1713
|
+
if (error instanceof TreeseedWorkflowError) {
|
|
1714
|
+
throw error;
|
|
1715
|
+
}
|
|
1716
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1717
|
+
if (/GH_TOKEN|GITHUB_TOKEN|GitHub authentication|authenticated|Bad credentials|Requires authentication/iu.test(message)) {
|
|
1718
|
+
workflowError("ci", "github_auth_unavailable", message, { exitCode: 2 });
|
|
1719
|
+
}
|
|
1720
|
+
workflowError("ci", "validation_failed", message, { exitCode: 2 });
|
|
1721
|
+
}
|
|
1722
|
+
});
|
|
1723
|
+
}
|
|
1645
1724
|
async function workflowTasks(helpers) {
|
|
1646
1725
|
return withContextEnv(helpers.context.env, () => createTasksResult(helpers.cwd()));
|
|
1647
1726
|
}
|
|
@@ -3826,6 +3905,7 @@ async function workflowDestroy(helpers, input) {
|
|
|
3826
3905
|
}
|
|
3827
3906
|
export {
|
|
3828
3907
|
TreeseedWorkflowError,
|
|
3908
|
+
workflowCi,
|
|
3829
3909
|
workflowClose,
|
|
3830
3910
|
workflowConfig,
|
|
3831
3911
|
workflowDestroy,
|
|
@@ -2,6 +2,7 @@ export { applyTreeseedConfigValues, applyTreeseedEnvironmentToProcess, applyTree
|
|
|
2
2
|
export { exportTreeseedCodebase } from './operations/services/export-runtime.ts';
|
|
3
3
|
export { assertDeploymentInitialized, cleanupDestroyedState, createBranchPreviewDeployTarget, createPersistentDeployTarget, deployTargetLabel, destroyCloudflareResources, ensureGeneratedWranglerConfig, finalizeDeploymentState, loadDeployState, printDeploySummary, printDestroySummary, provisionCloudflareResources, runRemoteD1Migrations, syncCloudflareSecrets, validateDeployPrerequisites, validateDestroyPrerequisites, } from './operations/services/deploy.ts';
|
|
4
4
|
export { assertCleanWorktree, assertFeatureBranch, branchExists, checkoutBranch, createDeprecatedTaskTag, createFeatureBranchFromStaging, currentManagedBranch, deleteLocalBranch, deleteRemoteBranch, ensureLocalBranchTracking, gitWorkflowRoot, listTaskBranches, mergeCurrentBranchIntoStaging, mergeStagingIntoMain, prepareReleaseBranches, PRODUCTION_BRANCH, pushBranch, remoteBranchExists, STAGING_BRANCH, syncBranchWithOrigin, waitForStagingAutomation, } from './operations/services/git-workflow.ts';
|
|
5
|
+
export { dockerIsAvailable, stopKnownMailpitContainers, type TreeseedMailpitContainer, } from './operations/services/mailpit-runtime.ts';
|
|
5
6
|
export { loadCliDeployConfig, packageScriptPath, resolveWranglerBin, } from './operations/services/runtime-tools.ts';
|
|
6
7
|
export { configuredRailwayServices, deployRailwayService, validateRailwayDeployPrerequisites, } from './operations/services/railway-deploy.ts';
|
|
7
8
|
export { ensureRailwayEnvironment, ensureRailwayProject, ensureRailwayService, getRailwayAuthProfile, listRailwayEnvironments, listRailwayProjects, listRailwayServices, listRailwayVariables, railwayGraphqlRequest, resolveRailwayApiToken, resolveRailwayApiUrl, resolveRailwayWorkspace, resolveRailwayWorkspaceContext, upsertRailwayVariables, } from './operations/services/railway-api.ts';
|
package/dist/workflow-support.js
CHANGED
|
@@ -80,6 +80,10 @@ import {
|
|
|
80
80
|
syncBranchWithOrigin,
|
|
81
81
|
waitForStagingAutomation
|
|
82
82
|
} from "./operations/services/git-workflow.js";
|
|
83
|
+
import {
|
|
84
|
+
dockerIsAvailable,
|
|
85
|
+
stopKnownMailpitContainers
|
|
86
|
+
} from "./operations/services/mailpit-runtime.js";
|
|
83
87
|
import {
|
|
84
88
|
loadCliDeployConfig,
|
|
85
89
|
packageScriptPath,
|
|
@@ -206,6 +210,7 @@ export {
|
|
|
206
210
|
destroyCloudflareResources,
|
|
207
211
|
destroyTreeseedTargetUnits,
|
|
208
212
|
discoverWorkspaceLinks,
|
|
213
|
+
dockerIsAvailable,
|
|
209
214
|
ensureGeneratedWranglerConfig,
|
|
210
215
|
ensureLocalBranchTracking,
|
|
211
216
|
ensureLocalWorkspaceLinks,
|
|
@@ -283,6 +288,7 @@ export {
|
|
|
283
288
|
runWorkspaceReleasePreflight,
|
|
284
289
|
runWorkspaceSavePreflight,
|
|
285
290
|
setTreeseedRemoteSession,
|
|
291
|
+
stopKnownMailpitContainers,
|
|
286
292
|
syncBranchWithOrigin,
|
|
287
293
|
syncCloudflareSecrets,
|
|
288
294
|
unlinkLocalWorkspaceLinks,
|
package/dist/workflow.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { resolveTreeseedWorkflowState, type TreeseedWorkflowStatusOptions } from './workflow-state.ts';
|
|
2
2
|
import { listTaskBranches } from './operations/services/git-workflow.ts';
|
|
3
|
+
import type { GitHubActionsVerificationReport } from './operations/services/github-actions-verification.ts';
|
|
3
4
|
import { TreeseedWorkflowError, type TreeseedWorkflowErrorCode } from './workflow/operations.ts';
|
|
4
|
-
export type TreeseedWorkflowOperationId = 'status' | 'config' | 'tasks' | 'switch' | 'dev' | 'save' | 'close' | 'stage' | 'release' | 'resume' | 'recover' | 'destroy' | 'export';
|
|
5
|
+
export type TreeseedWorkflowOperationId = 'status' | 'ci' | 'config' | 'tasks' | 'switch' | 'dev' | 'save' | 'close' | 'stage' | 'release' | 'resume' | 'recover' | 'destroy' | 'export';
|
|
5
6
|
export type TreeseedWorkflowNextStep = {
|
|
6
7
|
operation: string;
|
|
7
8
|
reason?: string;
|
|
@@ -112,6 +113,26 @@ export type TreeseedSaveInput = {
|
|
|
112
113
|
plan?: boolean;
|
|
113
114
|
dryRun?: boolean;
|
|
114
115
|
};
|
|
116
|
+
export type TreeseedCiInput = {
|
|
117
|
+
failed?: boolean;
|
|
118
|
+
logs?: boolean;
|
|
119
|
+
includeLogs?: boolean;
|
|
120
|
+
logLines?: number | string;
|
|
121
|
+
scope?: 'workspace' | 'root' | 'packages';
|
|
122
|
+
workflow?: string | string[];
|
|
123
|
+
workflows?: string | string[];
|
|
124
|
+
branch?: string;
|
|
125
|
+
strict?: boolean;
|
|
126
|
+
};
|
|
127
|
+
export type TreeseedCiResult = GitHubActionsVerificationReport & {
|
|
128
|
+
mode: 'root-only' | 'recursive-workspace';
|
|
129
|
+
branch: string | null;
|
|
130
|
+
scope: 'workspace' | 'root' | 'packages';
|
|
131
|
+
strict: boolean;
|
|
132
|
+
hasFailures: boolean;
|
|
133
|
+
hasPending: boolean;
|
|
134
|
+
exitCode: number;
|
|
135
|
+
};
|
|
115
136
|
export type TreeseedCloseInput = {
|
|
116
137
|
message: string;
|
|
117
138
|
deletePreview?: boolean;
|
|
@@ -227,11 +248,12 @@ export declare class TreeseedWorkflowSdk {
|
|
|
227
248
|
private readonly context;
|
|
228
249
|
constructor(context?: TreeseedWorkflowContext);
|
|
229
250
|
private helpers;
|
|
230
|
-
execute(operation: TreeseedWorkflowOperationId, input?: Record<string, unknown>): Promise<TreeseedWorkflowResult<import("./workflow-state.ts").TreeseedWorkflowState> | TreeseedWorkflowResult<{
|
|
251
|
+
execute(operation: TreeseedWorkflowOperationId, input?: Record<string, unknown>): Promise<TreeseedWorkflowResult<import("./workflow-state.ts").TreeseedWorkflowState> | TreeseedWorkflowResult<TreeseedCiResult> | TreeseedWorkflowResult<{
|
|
231
252
|
tasks: TreeseedTaskBranchMetadata[];
|
|
232
253
|
workstreams: TreeseedWorkflowWorkstreamSummary[];
|
|
233
254
|
}> | TreeseedWorkflowResult<Record<string, unknown>>>;
|
|
234
255
|
status(input?: TreeseedWorkflowStatusOptions): Promise<TreeseedWorkflowResult<ReturnType<typeof resolveTreeseedWorkflowState>>>;
|
|
256
|
+
ci(input?: TreeseedCiInput): Promise<TreeseedWorkflowResult<TreeseedCiResult>>;
|
|
235
257
|
tasks(): Promise<TreeseedWorkflowResult<{
|
|
236
258
|
tasks: TreeseedTaskBranchMetadata[];
|
|
237
259
|
workstreams: TreeseedWorkflowWorkstreamSummary[];
|
package/dist/workflow.js
CHANGED
|
@@ -2,6 +2,7 @@ import { resolveTreeseedWorkflowPaths } from "./workflow/policy.js";
|
|
|
2
2
|
import {
|
|
3
3
|
TreeseedWorkflowError,
|
|
4
4
|
workflowClose,
|
|
5
|
+
workflowCi,
|
|
5
6
|
workflowConfig,
|
|
6
7
|
workflowDestroy,
|
|
7
8
|
workflowDev,
|
|
@@ -42,6 +43,8 @@ class TreeseedWorkflowSdk {
|
|
|
42
43
|
switch (operation) {
|
|
43
44
|
case "status":
|
|
44
45
|
return this.status(input);
|
|
46
|
+
case "ci":
|
|
47
|
+
return this.ci(input);
|
|
45
48
|
case "tasks":
|
|
46
49
|
return this.tasks();
|
|
47
50
|
case "config":
|
|
@@ -73,6 +76,9 @@ class TreeseedWorkflowSdk {
|
|
|
73
76
|
async status(input = {}) {
|
|
74
77
|
return workflowStatus(this.helpers(), input);
|
|
75
78
|
}
|
|
79
|
+
async ci(input = {}) {
|
|
80
|
+
return workflowCi(this.helpers(), input);
|
|
81
|
+
}
|
|
76
82
|
async tasks() {
|
|
77
83
|
return workflowTasks(this.helpers());
|
|
78
84
|
}
|