@treeseed/sdk 0.10.11 → 0.10.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/api/auth/d1-store.js +20 -1
- package/dist/capacity-provider.d.ts +53 -1
- package/dist/capacity.d.ts +80 -1
- package/dist/capacity.js +687 -8
- package/dist/db/d1.d.ts +109 -3227
- package/dist/db/index.d.ts +1 -0
- package/dist/db/index.js +1 -0
- package/dist/db/market-schema.d.ts +43769 -0
- package/dist/db/market-schema.js +1878 -0
- package/dist/db/node-sqlite.d.ts +109 -3227
- package/dist/db/schema.d.ts +226 -5757
- package/dist/db/schema.js +35 -226
- package/dist/index.d.ts +6 -4
- package/dist/index.js +32 -0
- package/dist/market-client.d.ts +135 -0
- package/dist/market-client.js +134 -1
- package/dist/operations/services/commit-message-provider.js +1 -1
- package/dist/operations/services/d1-migration.js +0 -59
- package/dist/operations/services/deploy.js +5 -1
- package/dist/operations/services/github-api.d.ts +83 -0
- package/dist/operations/services/github-api.js +167 -0
- package/dist/operations/services/local-dev.js +3 -3
- package/dist/operations/services/mailpit-runtime.d.ts +13 -2
- package/dist/operations/services/mailpit-runtime.js +19 -14
- package/dist/operations/services/project-web-monitor.d.ts +15 -0
- package/dist/operations/services/project-web-monitor.js +260 -0
- package/dist/operations/services/railway-api.js +2 -2
- package/dist/operations/services/release-candidate.js +9 -9
- package/dist/operations/services/runtime-paths.d.ts +1 -1
- package/dist/operations/services/runtime-paths.js +2 -2
- package/dist/operations/services/template-registry.js +10 -1
- package/dist/operations.d.ts +1 -0
- package/dist/operations.js +11 -1
- package/dist/platform-operation-store.d.ts +4 -0
- package/dist/platform-operation-store.js +29 -3
- package/dist/platform-operations.d.ts +8 -0
- package/dist/platform-operations.js +19 -0
- package/dist/remote.js +6 -6
- package/dist/scripts/tenant-d1-migrate-local.js +2 -3
- package/dist/scripts/test-cloudflare-local.js +1 -1
- package/dist/scripts/workspace-command-e2e.js +3 -1
- package/dist/sdk-types.d.ts +281 -3
- package/dist/sdk-types.js +5 -1
- package/dist/seeds/normalize.js +6 -0
- package/dist/seeds/schema.js +61 -1
- package/dist/seeds/types.d.ts +32 -0
- package/drizzle/d1/0000_treeseed_d1.sql +37 -0
- package/drizzle/market/0000_market_control_plane.sql +2929 -0
- package/drizzle/market/0001_capacity_budget_mode_default.sql +4 -0
- package/drizzle/market/0002_user_email_addresses.sql +26 -0
- package/package.json +8 -1
package/dist/market-client.js
CHANGED
|
@@ -272,7 +272,8 @@ class MarketClient {
|
|
|
272
272
|
});
|
|
273
273
|
const payload = await response.json().catch(() => ({}));
|
|
274
274
|
if (!response.ok) {
|
|
275
|
-
const
|
|
275
|
+
const payloadError = payload.error;
|
|
276
|
+
const error = typeof payloadError === "string" ? String(payloadError) : payloadError && typeof payloadError === "object" && typeof payloadError.message === "string" ? String(payloadError.message) : `Market request failed with ${response.status}.`;
|
|
276
277
|
throw new MarketApiError(error, response.status, payload);
|
|
277
278
|
}
|
|
278
279
|
return payload;
|
|
@@ -331,11 +332,45 @@ class MarketClient {
|
|
|
331
332
|
body
|
|
332
333
|
});
|
|
333
334
|
}
|
|
335
|
+
confirmWebEmail(body) {
|
|
336
|
+
return this.request("/v1/auth/web/confirm-email", {
|
|
337
|
+
method: "POST",
|
|
338
|
+
body
|
|
339
|
+
});
|
|
340
|
+
}
|
|
334
341
|
checkWebUsername(username) {
|
|
335
342
|
return this.request(
|
|
336
343
|
`/v1/auth/web/username/check?username=${encodeURIComponent(username)}`
|
|
337
344
|
);
|
|
338
345
|
}
|
|
346
|
+
webEmails() {
|
|
347
|
+
return this.request("/v1/auth/web/emails", { requireAuth: true });
|
|
348
|
+
}
|
|
349
|
+
addWebEmail(body) {
|
|
350
|
+
return this.request("/v1/auth/web/emails", {
|
|
351
|
+
method: "POST",
|
|
352
|
+
body,
|
|
353
|
+
requireAuth: true
|
|
354
|
+
});
|
|
355
|
+
}
|
|
356
|
+
verifyWebEmail(emailId) {
|
|
357
|
+
return this.request(
|
|
358
|
+
`/v1/auth/web/emails/${encodeURIComponent(emailId)}/verify`,
|
|
359
|
+
{ method: "POST", requireAuth: true }
|
|
360
|
+
);
|
|
361
|
+
}
|
|
362
|
+
setPrimaryWebEmail(emailId) {
|
|
363
|
+
return this.request(
|
|
364
|
+
`/v1/auth/web/emails/${encodeURIComponent(emailId)}/primary`,
|
|
365
|
+
{ method: "POST", requireAuth: true }
|
|
366
|
+
);
|
|
367
|
+
}
|
|
368
|
+
deleteWebEmail(emailId) {
|
|
369
|
+
return this.request(
|
|
370
|
+
`/v1/auth/web/emails/${encodeURIComponent(emailId)}`,
|
|
371
|
+
{ method: "DELETE", requireAuth: true }
|
|
372
|
+
);
|
|
373
|
+
}
|
|
339
374
|
webSessions() {
|
|
340
375
|
return this.request("/v1/auth/web/sessions", { requireAuth: true });
|
|
341
376
|
}
|
|
@@ -426,12 +461,73 @@ class MarketClient {
|
|
|
426
461
|
{ requireAuth: true }
|
|
427
462
|
);
|
|
428
463
|
}
|
|
464
|
+
projectDeploymentState(projectId) {
|
|
465
|
+
return this.request(
|
|
466
|
+
`/v1/projects/${encodeURIComponent(projectId)}/deployment-state`,
|
|
467
|
+
{ requireAuth: true }
|
|
468
|
+
);
|
|
469
|
+
}
|
|
470
|
+
projectDeployments(projectId, filters = {}) {
|
|
471
|
+
const query = new URLSearchParams();
|
|
472
|
+
if (filters.environment) query.set("environment", String(filters.environment));
|
|
473
|
+
if (filters.action) query.set("action", String(filters.action));
|
|
474
|
+
if (filters.status) query.set("status", String(filters.status));
|
|
475
|
+
if (filters.limit) query.set("limit", String(filters.limit));
|
|
476
|
+
const suffix = query.size > 0 ? `?${query.toString()}` : "";
|
|
477
|
+
return this.request(
|
|
478
|
+
`/v1/projects/${encodeURIComponent(projectId)}/deployments${suffix}`,
|
|
479
|
+
{ requireAuth: true }
|
|
480
|
+
);
|
|
481
|
+
}
|
|
482
|
+
projectDeployment(projectId, deploymentId) {
|
|
483
|
+
return this.request(
|
|
484
|
+
`/v1/projects/${encodeURIComponent(projectId)}/deployments/${encodeURIComponent(deploymentId)}`,
|
|
485
|
+
{ requireAuth: true }
|
|
486
|
+
);
|
|
487
|
+
}
|
|
488
|
+
projectDeploymentEvents(projectId, deploymentId, options = {}) {
|
|
489
|
+
const query = options.limit ? `?limit=${encodeURIComponent(String(options.limit))}` : "";
|
|
490
|
+
return this.request(
|
|
491
|
+
`/v1/projects/${encodeURIComponent(projectId)}/deployments/${encodeURIComponent(deploymentId)}/events${query}`,
|
|
492
|
+
{ requireAuth: true }
|
|
493
|
+
);
|
|
494
|
+
}
|
|
495
|
+
createProjectWebDeployment(projectId, body) {
|
|
496
|
+
return this.request(
|
|
497
|
+
`/v1/projects/${encodeURIComponent(projectId)}/deployments/web`,
|
|
498
|
+
{ method: "POST", body, requireAuth: true }
|
|
499
|
+
);
|
|
500
|
+
}
|
|
501
|
+
retryProjectDeployment(projectId, deploymentId, body = {}) {
|
|
502
|
+
return this.request(
|
|
503
|
+
`/v1/projects/${encodeURIComponent(projectId)}/deployments/${encodeURIComponent(deploymentId)}/retry`,
|
|
504
|
+
{ method: "POST", body, requireAuth: true }
|
|
505
|
+
);
|
|
506
|
+
}
|
|
507
|
+
resumeProjectDeployment(projectId, deploymentId, body = {}) {
|
|
508
|
+
return this.request(
|
|
509
|
+
`/v1/projects/${encodeURIComponent(projectId)}/deployments/${encodeURIComponent(deploymentId)}/resume`,
|
|
510
|
+
{ method: "POST", body, requireAuth: true }
|
|
511
|
+
);
|
|
512
|
+
}
|
|
513
|
+
cancelProjectDeployment(projectId, deploymentId, body = {}) {
|
|
514
|
+
return this.request(
|
|
515
|
+
`/v1/projects/${encodeURIComponent(projectId)}/deployments/${encodeURIComponent(deploymentId)}/cancel`,
|
|
516
|
+
{ method: "POST", body, requireAuth: true }
|
|
517
|
+
);
|
|
518
|
+
}
|
|
429
519
|
teamCapacity(teamId) {
|
|
430
520
|
return this.request(
|
|
431
521
|
`/v1/teams/${encodeURIComponent(teamId)}/capacity`,
|
|
432
522
|
{ requireAuth: true }
|
|
433
523
|
);
|
|
434
524
|
}
|
|
525
|
+
teamCapacityProviders(teamId) {
|
|
526
|
+
return this.request(
|
|
527
|
+
`/v1/teams/${encodeURIComponent(teamId)}/capacity-providers`,
|
|
528
|
+
{ requireAuth: true }
|
|
529
|
+
);
|
|
530
|
+
}
|
|
435
531
|
launchManagedCapacityProvider(teamId, body = {}) {
|
|
436
532
|
return this.request(
|
|
437
533
|
`/v1/teams/${encodeURIComponent(teamId)}/capacity/providers/managed`,
|
|
@@ -456,12 +552,49 @@ class MarketClient {
|
|
|
456
552
|
{ requireAuth: true }
|
|
457
553
|
);
|
|
458
554
|
}
|
|
555
|
+
updateCapacityProvider(teamId, providerId, body) {
|
|
556
|
+
return this.request(
|
|
557
|
+
`/v1/teams/${encodeURIComponent(teamId)}/capacity-providers/${encodeURIComponent(providerId)}`,
|
|
558
|
+
{ method: "PATCH", body, requireAuth: true }
|
|
559
|
+
);
|
|
560
|
+
}
|
|
459
561
|
createCapacityGrant(teamId, body) {
|
|
460
562
|
return this.request(
|
|
461
563
|
`/v1/teams/${encodeURIComponent(teamId)}/capacity-grants`,
|
|
462
564
|
{ method: "POST", body, requireAuth: true }
|
|
463
565
|
);
|
|
464
566
|
}
|
|
567
|
+
executionProviders(teamId, providerId) {
|
|
568
|
+
return this.request(
|
|
569
|
+
`/v1/teams/${encodeURIComponent(teamId)}/capacity-providers/${encodeURIComponent(providerId)}/execution-providers`,
|
|
570
|
+
{ requireAuth: true }
|
|
571
|
+
);
|
|
572
|
+
}
|
|
573
|
+
createExecutionProvider(teamId, providerId, body) {
|
|
574
|
+
return this.request(
|
|
575
|
+
`/v1/teams/${encodeURIComponent(teamId)}/capacity-providers/${encodeURIComponent(providerId)}/execution-providers`,
|
|
576
|
+
{ method: "POST", body, requireAuth: true }
|
|
577
|
+
);
|
|
578
|
+
}
|
|
579
|
+
updateExecutionProvider(teamId, providerId, executionProviderId, body) {
|
|
580
|
+
return this.request(
|
|
581
|
+
`/v1/teams/${encodeURIComponent(teamId)}/capacity-providers/${encodeURIComponent(providerId)}/execution-providers/${encodeURIComponent(executionProviderId)}`,
|
|
582
|
+
{ method: "PATCH", body, requireAuth: true }
|
|
583
|
+
);
|
|
584
|
+
}
|
|
585
|
+
createExecutionProviderNativeLimit(teamId, providerId, executionProviderId, body) {
|
|
586
|
+
return this.request(
|
|
587
|
+
`/v1/teams/${encodeURIComponent(teamId)}/capacity-providers/${encodeURIComponent(providerId)}/execution-providers/${encodeURIComponent(executionProviderId)}/native-limits`,
|
|
588
|
+
{ method: "POST", body, requireAuth: true }
|
|
589
|
+
);
|
|
590
|
+
}
|
|
591
|
+
projectCapacityPlan(projectId, environment) {
|
|
592
|
+
const query = environment ? `?environment=${encodeURIComponent(environment)}` : "";
|
|
593
|
+
return this.request(
|
|
594
|
+
`/v1/projects/${encodeURIComponent(projectId)}/capacity-plan${query}`,
|
|
595
|
+
{ requireAuth: true }
|
|
596
|
+
);
|
|
597
|
+
}
|
|
465
598
|
planSeed(seedName, body) {
|
|
466
599
|
return this.request(
|
|
467
600
|
`/v1/seeds/${encodeURIComponent(seedName)}/plan`,
|
|
@@ -62,7 +62,7 @@ function changedPaths(changedFiles) {
|
|
|
62
62
|
function changedFileGroups(paths) {
|
|
63
63
|
const counts = /* @__PURE__ */ new Map();
|
|
64
64
|
for (const path of paths) {
|
|
65
|
-
const group = path.startsWith(".github/") || path.includes("/workflows/") ? "ci" : /(^|\/)(test|tests|__tests__)\/|\.test\.|\.spec\./u.test(path) ? "tests" : path.startsWith("docs/") || /\.(md|mdx|txt)$/u.test(path) ? "docs" : path.includes("workflow") || path.includes("repository-save-orchestrator") ? "workflow" : path.includes("package-reference") || path.includes("release") || path.includes("publish") ? "release" : /(^|\/)(package|package-lock)\.json$/u.test(path) || path.includes("build") || path.includes("scripts/") ? "build" : path.includes("config") || path.endsWith(".yaml") || path.endsWith(".yml") ? "config" : path.startsWith("
|
|
65
|
+
const group = path.startsWith(".github/") || path.includes("/workflows/") ? "ci" : /(^|\/)(test|tests|__tests__)\/|\.test\.|\.spec\./u.test(path) ? "tests" : path.startsWith("docs/") || /\.(md|mdx|txt)$/u.test(path) ? "docs" : path.includes("workflow") || path.includes("repository-save-orchestrator") ? "workflow" : path.includes("package-reference") || path.includes("release") || path.includes("publish") ? "release" : /(^|\/)(package|package-lock)\.json$/u.test(path) || path.includes("build") || path.includes("scripts/") ? "build" : path.includes("config") || path.endsWith(".yaml") || path.endsWith(".yml") ? "config" : path.startsWith("packages/sdk/drizzle/") || path.includes("/db/") ? "database" : path.startsWith("src/pages/") || path.startsWith("src/layouts/") || path.includes("/ui/") ? "ui" : path.startsWith("src/api/") || path.includes("/api/") ? "api" : "source";
|
|
66
66
|
counts.set(group, (counts.get(group) ?? 0) + 1);
|
|
67
67
|
}
|
|
68
68
|
return [...counts.entries()].sort((left, right) => right[1] - left[1] || left[0].localeCompare(right[0]));
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import { existsSync, readdirSync } from "node:fs";
|
|
2
|
-
import { readFileSync } from "node:fs";
|
|
3
2
|
import { resolve } from "node:path";
|
|
4
3
|
import { spawnSync } from "node:child_process";
|
|
5
|
-
import { NodeSqliteD1Database } from "../../db/node-sqlite.js";
|
|
6
4
|
import { resolveWranglerBin } from "./runtime-tools.js";
|
|
7
5
|
const DATABASE_BINDING = "SITE_DATA_DB";
|
|
8
6
|
const WRANGLER_D1_TIMEOUT_MS = 12e4;
|
|
@@ -100,58 +98,6 @@ function markMigrationApplied({ cwd, wranglerConfig, persistTo, migration }) {
|
|
|
100
98
|
command: `INSERT OR REPLACE INTO treeseed_schema_migrations (name, applied_at) VALUES ('${migration.replace(/'/g, "''")}', datetime('now'));`
|
|
101
99
|
});
|
|
102
100
|
}
|
|
103
|
-
function tableColumns({ cwd, wranglerConfig, persistTo, tableName }) {
|
|
104
|
-
const result = executeSqlCommand({
|
|
105
|
-
cwd,
|
|
106
|
-
wranglerConfig,
|
|
107
|
-
persistTo,
|
|
108
|
-
capture: true,
|
|
109
|
-
command: `PRAGMA table_info(${tableName.replace(/[^A-Za-z0-9_]/g, "")});`
|
|
110
|
-
});
|
|
111
|
-
const parsed = parseWranglerJsonOutput(result.stdout);
|
|
112
|
-
const rows = (Array.isArray(parsed) ? parsed : [parsed]).flatMap((entry) => entry.results ?? []);
|
|
113
|
-
return new Set(rows.map((row) => row.name).filter(Boolean));
|
|
114
|
-
}
|
|
115
|
-
function migrationAlreadySatisfied({ cwd, wranglerConfig, persistTo, filePath }) {
|
|
116
|
-
const sql = readFileSync(filePath, "utf8");
|
|
117
|
-
if (!sql.includes("execution_profile_id") || !sql.includes("task_estimate_profiles")) {
|
|
118
|
-
return false;
|
|
119
|
-
}
|
|
120
|
-
const estimateColumns = tableColumns({ cwd, wranglerConfig, persistTo, tableName: "task_estimates" });
|
|
121
|
-
const actualColumns = tableColumns({ cwd, wranglerConfig, persistTo, tableName: "task_usage_actuals" });
|
|
122
|
-
const profileColumns = tableColumns({ cwd, wranglerConfig, persistTo, tableName: "task_estimate_profiles" });
|
|
123
|
-
return estimateColumns.has("execution_profile_id") && actualColumns.has("execution_profile_id") && profileColumns.has("execution_profile_id") && profileColumns.has("completed_sample_count") && profileColumns.has("interrupted_sample_count") && profileColumns.has("confidence_score");
|
|
124
|
-
}
|
|
125
|
-
function ensureLocalSdkRuntimeState({ migrationsRoot, persistTo }) {
|
|
126
|
-
if (!persistTo) {
|
|
127
|
-
return;
|
|
128
|
-
}
|
|
129
|
-
const migrationPath = resolve(migrationsRoot, "0025_agent_runtime_state.sql");
|
|
130
|
-
if (!existsSync(migrationPath)) {
|
|
131
|
-
return;
|
|
132
|
-
}
|
|
133
|
-
const sql = readFileSync(migrationPath, "utf8");
|
|
134
|
-
const targets = /* @__PURE__ */ new Set();
|
|
135
|
-
const sdkDb = new NodeSqliteD1Database(persistTo);
|
|
136
|
-
targets.add(sdkDb.path);
|
|
137
|
-
sdkDb.close();
|
|
138
|
-
const miniflareRoot = resolve(persistTo, "miniflare-D1DatabaseObject");
|
|
139
|
-
if (existsSync(miniflareRoot)) {
|
|
140
|
-
for (const entry of readdirSync(miniflareRoot)) {
|
|
141
|
-
if (entry.endsWith(".sqlite") && entry !== "metadata.sqlite") {
|
|
142
|
-
targets.add(resolve(miniflareRoot, entry));
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
for (const target of targets) {
|
|
147
|
-
const db = new NodeSqliteD1Database(target);
|
|
148
|
-
try {
|
|
149
|
-
db.client.exec(sql);
|
|
150
|
-
} finally {
|
|
151
|
-
db.close();
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
101
|
function runLocalD1Migrations({ cwd, wranglerConfig, migrationsRoot, persistTo }) {
|
|
156
102
|
ensureSchemaMigrationsTable({ cwd, wranglerConfig, persistTo });
|
|
157
103
|
const appliedMigrations = loadAppliedMigrations({ cwd, wranglerConfig, persistTo });
|
|
@@ -165,14 +111,9 @@ function runLocalD1Migrations({ cwd, wranglerConfig, migrationsRoot, persistTo }
|
|
|
165
111
|
console.error(`Unable to find migration file at ${filePath}.`);
|
|
166
112
|
process.exit(1);
|
|
167
113
|
}
|
|
168
|
-
if (migrationAlreadySatisfied({ cwd, wranglerConfig, persistTo, filePath })) {
|
|
169
|
-
markMigrationApplied({ cwd, wranglerConfig, persistTo, migration });
|
|
170
|
-
continue;
|
|
171
|
-
}
|
|
172
114
|
executeSqlFile({ cwd, wranglerConfig, filePath, persistTo });
|
|
173
115
|
markMigrationApplied({ cwd, wranglerConfig, persistTo, migration });
|
|
174
116
|
}
|
|
175
|
-
ensureLocalSdkRuntimeState({ migrationsRoot, persistTo });
|
|
176
117
|
}
|
|
177
118
|
export {
|
|
178
119
|
runLocalD1Migrations
|
|
@@ -6,6 +6,7 @@ import { createInterface } from "node:readline/promises";
|
|
|
6
6
|
import { resolveTreeseedWebCachePolicy } from "../../platform/deploy-config.js";
|
|
7
7
|
import { normalizeRailwayEnvironmentName } from "./railway-api.js";
|
|
8
8
|
import { loadCliDeployConfig, resolveWranglerBin } from "./runtime-tools.js";
|
|
9
|
+
import { sdkD1MigrationsRoot } from "./runtime-paths.js";
|
|
9
10
|
const DEFAULT_COMPATIBILITY_DATE = "2026-04-05";
|
|
10
11
|
const DEFAULT_COMPATIBILITY_FLAGS = ["nodejs_compat"];
|
|
11
12
|
const DEFAULT_TREESEED_MARKET_BASE_URL = "https://api.treeseed.ai";
|
|
@@ -339,7 +340,10 @@ const LOCAL_RUNTIME_AUTH_ENV_KEYS = [
|
|
|
339
340
|
"TREESEED_API_DEVICE_CODE_TTL",
|
|
340
341
|
"TREESEED_API_DEVICE_ACCESS_TOKEN_TTL",
|
|
341
342
|
"TREESEED_API_DEVICE_POLL_INTERVAL",
|
|
343
|
+
"TREESEED_API_WEB_SERVICE_ID",
|
|
344
|
+
"TREESEED_API_WEB_SERVICE_SECRET",
|
|
342
345
|
"TREESEED_API_WEB_EXCHANGE_TTL",
|
|
346
|
+
"TREESEED_PLATFORM_RUNNER_SECRET",
|
|
343
347
|
"TREESEED_AUTH_GITHUB_CLIENT_ID",
|
|
344
348
|
"TREESEED_AUTH_GITHUB_CLIENT_SECRET",
|
|
345
349
|
"TREESEED_AUTH_GOOGLE_CLIENT_ID",
|
|
@@ -737,7 +741,7 @@ function buildWranglerConfigContents(tenantRoot, deployConfig, state, options =
|
|
|
737
741
|
const workerName = state.workerName ?? targetWorkerName(deployConfig, target);
|
|
738
742
|
const mainPath = relativeFromGeneratedRoot(resolve(tenantRoot, "dist/_worker.js/index.js"), generatedRoot);
|
|
739
743
|
const assetsDirectory = relativeFromGeneratedRoot(resolve(tenantRoot, "dist"), generatedRoot);
|
|
740
|
-
const migrationsDir = relativeFromGeneratedRoot(
|
|
744
|
+
const migrationsDir = relativeFromGeneratedRoot(sdkD1MigrationsRoot, generatedRoot);
|
|
741
745
|
const vars = {
|
|
742
746
|
...buildPublicVars(deployConfig),
|
|
743
747
|
...buildLocalRuntimeVars(deployConfig, state, target, options.env)
|
|
@@ -58,6 +58,59 @@ export type GitHubWorkflowProgressEvent = {
|
|
|
58
58
|
completedJobs?: GitHubWorkflowJobSummary[];
|
|
59
59
|
failedJobs?: GitHubWorkflowJobSummary[];
|
|
60
60
|
};
|
|
61
|
+
export interface GitHubWorkflowDispatchResult {
|
|
62
|
+
repository: string;
|
|
63
|
+
workflow: string;
|
|
64
|
+
branch: string;
|
|
65
|
+
inputs: Record<string, string> | undefined;
|
|
66
|
+
status: number | null;
|
|
67
|
+
dispatchedAt: string;
|
|
68
|
+
}
|
|
69
|
+
export interface GitHubWorkflowCancellationResult {
|
|
70
|
+
ok: boolean;
|
|
71
|
+
supported: boolean;
|
|
72
|
+
repository: string | null;
|
|
73
|
+
runId: number | null;
|
|
74
|
+
url?: string | null;
|
|
75
|
+
message: string;
|
|
76
|
+
cancelledAt?: string | null;
|
|
77
|
+
}
|
|
78
|
+
export interface GitHubWorkflowFileStatus {
|
|
79
|
+
ok: boolean;
|
|
80
|
+
exists: boolean | null;
|
|
81
|
+
repository: string;
|
|
82
|
+
workflow: string;
|
|
83
|
+
url: string | null;
|
|
84
|
+
message: string;
|
|
85
|
+
}
|
|
86
|
+
export interface GitHubWorkflowFailureSummaryInput {
|
|
87
|
+
repository?: string | null;
|
|
88
|
+
workflow?: string | null;
|
|
89
|
+
runId?: number | string | null;
|
|
90
|
+
runUrl?: string | null;
|
|
91
|
+
conclusion?: string | null;
|
|
92
|
+
failedJobName?: string | null;
|
|
93
|
+
lastActiveStep?: string | null;
|
|
94
|
+
message?: string | null;
|
|
95
|
+
blockerCode?: string | null;
|
|
96
|
+
retrySafe?: boolean;
|
|
97
|
+
resumeSafe?: boolean;
|
|
98
|
+
}
|
|
99
|
+
export interface GitHubWorkflowFailureSummary {
|
|
100
|
+
summary: string;
|
|
101
|
+
provider: 'github';
|
|
102
|
+
repository: string | null;
|
|
103
|
+
workflow: string | null;
|
|
104
|
+
runId: number | null;
|
|
105
|
+
runUrl: string | null;
|
|
106
|
+
inspectCommand: string | null;
|
|
107
|
+
failedJobName: string | null;
|
|
108
|
+
lastActiveStep: string | null;
|
|
109
|
+
conclusion: string | null;
|
|
110
|
+
retrySafe: boolean;
|
|
111
|
+
resumeSafe: boolean;
|
|
112
|
+
blockerCode: string;
|
|
113
|
+
}
|
|
61
114
|
export declare function resolveGitHubApiToken(env?: NodeJS.ProcessEnv | Record<string, string | undefined>): string;
|
|
62
115
|
export declare function parseGitHubRepositorySlug(value: string): {
|
|
63
116
|
owner: string;
|
|
@@ -147,6 +200,36 @@ export declare function upsertGitHubRepositoryVariableWithGhCli(repository: stri
|
|
|
147
200
|
}, name: string, value: string, { env, }?: {
|
|
148
201
|
env?: NodeJS.ProcessEnv | Record<string, string | undefined>;
|
|
149
202
|
}): void;
|
|
203
|
+
export declare function formatGitHubWorkflowFailure(input?: GitHubWorkflowFailureSummaryInput): GitHubWorkflowFailureSummary;
|
|
204
|
+
export declare function dispatchGitHubWorkflowRun(repository: string | {
|
|
205
|
+
owner: string;
|
|
206
|
+
name: string;
|
|
207
|
+
}, { client, workflow, branch, inputs, }: {
|
|
208
|
+
client?: GitHubApiClient;
|
|
209
|
+
workflow?: string;
|
|
210
|
+
branch: string;
|
|
211
|
+
inputs?: Record<string, string>;
|
|
212
|
+
}): Promise<GitHubWorkflowDispatchResult>;
|
|
213
|
+
export declare function cancelGitHubWorkflowRun(repository: string | {
|
|
214
|
+
owner: string;
|
|
215
|
+
name: string;
|
|
216
|
+
} | null | undefined, runId: number | string | null | undefined, { client }?: {
|
|
217
|
+
client?: GitHubApiClient;
|
|
218
|
+
}): Promise<GitHubWorkflowCancellationResult>;
|
|
219
|
+
export declare function getGitHubWorkflowFileStatus(repository: string | {
|
|
220
|
+
owner: string;
|
|
221
|
+
name: string;
|
|
222
|
+
}, workflow?: string, { client }?: {
|
|
223
|
+
client?: GitHubApiClient;
|
|
224
|
+
}): Promise<GitHubWorkflowFileStatus>;
|
|
225
|
+
export declare function getLatestGitHubWorkflowRun(repository: string | {
|
|
226
|
+
owner: string;
|
|
227
|
+
name: string;
|
|
228
|
+
}, { client, workflow, branch, }?: {
|
|
229
|
+
client?: GitHubApiClient;
|
|
230
|
+
workflow?: string;
|
|
231
|
+
branch?: string | null;
|
|
232
|
+
}): Promise<GitHubWorkflowRunSummary | null>;
|
|
150
233
|
export declare function waitForGitHubWorkflowRunCompletion(repository: string | {
|
|
151
234
|
owner: string;
|
|
152
235
|
name: string;
|
|
@@ -548,6 +548,168 @@ function normalizeWorkflowJob(job) {
|
|
|
548
548
|
})) : []
|
|
549
549
|
};
|
|
550
550
|
}
|
|
551
|
+
function workflowInspectCommand(repository, runId) {
|
|
552
|
+
return repository && runId ? `gh run view ${runId} --repo ${repository} --log-failed` : null;
|
|
553
|
+
}
|
|
554
|
+
function formatGitHubWorkflowFailure(input = {}) {
|
|
555
|
+
const repository = typeof input.repository === "string" && input.repository.trim() ? input.repository.trim() : null;
|
|
556
|
+
const workflow = typeof input.workflow === "string" && input.workflow.trim() ? input.workflow.trim() : null;
|
|
557
|
+
const numericRunId = Number(input.runId);
|
|
558
|
+
const runId = Number.isFinite(numericRunId) && numericRunId > 0 ? numericRunId : null;
|
|
559
|
+
const runUrl = typeof input.runUrl === "string" && input.runUrl.trim() ? input.runUrl.trim() : null;
|
|
560
|
+
const conclusion = typeof input.conclusion === "string" && input.conclusion.trim() ? input.conclusion.trim() : null;
|
|
561
|
+
const failedJobName = typeof input.failedJobName === "string" && input.failedJobName.trim() ? input.failedJobName.trim() : null;
|
|
562
|
+
const lastActiveStep = typeof input.lastActiveStep === "string" && input.lastActiveStep.trim() ? input.lastActiveStep.trim() : null;
|
|
563
|
+
const blockerCode = typeof input.blockerCode === "string" && input.blockerCode.trim() ? input.blockerCode.trim() : conclusion === "cancelled" ? "github_workflow_cancelled" : conclusion === "timed_out" ? "github_workflow_timed_out" : "github_workflow_failed";
|
|
564
|
+
const detail = failedJobName ? ` Failed job: ${failedJobName}.` : lastActiveStep ? ` Last active step: ${lastActiveStep}.` : "";
|
|
565
|
+
const summary = typeof input.message === "string" && input.message.trim() ? input.message.trim() : `${workflow ?? "GitHub workflow"} ${conclusion ? `completed with conclusion ${conclusion}` : "failed"}.${detail}`;
|
|
566
|
+
return {
|
|
567
|
+
summary,
|
|
568
|
+
provider: "github",
|
|
569
|
+
repository,
|
|
570
|
+
workflow,
|
|
571
|
+
runId,
|
|
572
|
+
runUrl,
|
|
573
|
+
inspectCommand: workflowInspectCommand(repository, runId),
|
|
574
|
+
failedJobName,
|
|
575
|
+
lastActiveStep,
|
|
576
|
+
conclusion,
|
|
577
|
+
retrySafe: input.retrySafe ?? true,
|
|
578
|
+
resumeSafe: input.resumeSafe ?? false,
|
|
579
|
+
blockerCode
|
|
580
|
+
};
|
|
581
|
+
}
|
|
582
|
+
async function dispatchGitHubWorkflowRun(repository, {
|
|
583
|
+
client = createGitHubApiClient(),
|
|
584
|
+
workflow = "deploy-web.yml",
|
|
585
|
+
branch,
|
|
586
|
+
inputs
|
|
587
|
+
}) {
|
|
588
|
+
const { owner, name } = typeof repository === "string" ? parseGitHubRepositorySlug(repository) : repository;
|
|
589
|
+
try {
|
|
590
|
+
const result = await client.rest.actions.createWorkflowDispatch({
|
|
591
|
+
owner,
|
|
592
|
+
repo: name,
|
|
593
|
+
workflow_id: workflow,
|
|
594
|
+
ref: branch,
|
|
595
|
+
inputs
|
|
596
|
+
});
|
|
597
|
+
return {
|
|
598
|
+
repository: `${owner}/${name}`,
|
|
599
|
+
workflow,
|
|
600
|
+
branch,
|
|
601
|
+
inputs,
|
|
602
|
+
status: typeof result.status === "number" ? result.status : null,
|
|
603
|
+
dispatchedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
604
|
+
};
|
|
605
|
+
} catch (error) {
|
|
606
|
+
throw normalizeGitHubApiError(error, `Unable to dispatch GitHub workflow ${workflow} in ${owner}/${name}`);
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
async function cancelGitHubWorkflowRun(repository, runId, { client = createGitHubApiClient() } = {}) {
|
|
610
|
+
if (!repository || !runId) {
|
|
611
|
+
return {
|
|
612
|
+
ok: false,
|
|
613
|
+
supported: false,
|
|
614
|
+
repository: typeof repository === "string" ? repository : null,
|
|
615
|
+
runId: null,
|
|
616
|
+
message: "GitHub workflow cancellation requires a repository and run id."
|
|
617
|
+
};
|
|
618
|
+
}
|
|
619
|
+
const { owner, name } = typeof repository === "string" ? parseGitHubRepositorySlug(repository) : repository;
|
|
620
|
+
const numericRunId = Number(runId);
|
|
621
|
+
if (!Number.isFinite(numericRunId) || numericRunId <= 0) {
|
|
622
|
+
return {
|
|
623
|
+
ok: false,
|
|
624
|
+
supported: false,
|
|
625
|
+
repository: `${owner}/${name}`,
|
|
626
|
+
runId: null,
|
|
627
|
+
message: "GitHub workflow cancellation requires a numeric run id."
|
|
628
|
+
};
|
|
629
|
+
}
|
|
630
|
+
try {
|
|
631
|
+
await client.rest.actions.cancelWorkflowRun({
|
|
632
|
+
owner,
|
|
633
|
+
repo: name,
|
|
634
|
+
run_id: numericRunId
|
|
635
|
+
});
|
|
636
|
+
return {
|
|
637
|
+
ok: true,
|
|
638
|
+
supported: true,
|
|
639
|
+
repository: `${owner}/${name}`,
|
|
640
|
+
runId: numericRunId,
|
|
641
|
+
url: `https://github.com/${owner}/${name}/actions/runs/${numericRunId}`,
|
|
642
|
+
message: "GitHub workflow cancellation requested.",
|
|
643
|
+
cancelledAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
644
|
+
};
|
|
645
|
+
} catch (error) {
|
|
646
|
+
const message = error instanceof Error ? error.message : String(error ?? "");
|
|
647
|
+
if (/not supported|not found|404/iu.test(message)) {
|
|
648
|
+
return {
|
|
649
|
+
ok: false,
|
|
650
|
+
supported: false,
|
|
651
|
+
repository: `${owner}/${name}`,
|
|
652
|
+
runId: numericRunId,
|
|
653
|
+
message: "GitHub workflow cancellation is not supported for this run."
|
|
654
|
+
};
|
|
655
|
+
}
|
|
656
|
+
throw normalizeGitHubApiError(error, `Unable to cancel GitHub workflow run ${numericRunId} in ${owner}/${name}`);
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
async function getGitHubWorkflowFileStatus(repository, workflow = "deploy-web.yml", { client = createGitHubApiClient() } = {}) {
|
|
660
|
+
const { owner, name } = typeof repository === "string" ? parseGitHubRepositorySlug(repository) : repository;
|
|
661
|
+
const normalizedWorkflow = workflow.replace(/^\.github\/workflows\//u, "");
|
|
662
|
+
const path = `.github/workflows/${normalizedWorkflow}`;
|
|
663
|
+
try {
|
|
664
|
+
const result = await client.rest.repos.getContent({
|
|
665
|
+
owner,
|
|
666
|
+
repo: name,
|
|
667
|
+
path
|
|
668
|
+
});
|
|
669
|
+
const data = result.data;
|
|
670
|
+
return {
|
|
671
|
+
ok: true,
|
|
672
|
+
exists: true,
|
|
673
|
+
repository: `${owner}/${name}`,
|
|
674
|
+
workflow: normalizedWorkflow,
|
|
675
|
+
url: typeof data.html_url === "string" ? data.html_url : `https://github.com/${owner}/${name}/blob/HEAD/${path}`,
|
|
676
|
+
message: `${normalizedWorkflow} is present.`
|
|
677
|
+
};
|
|
678
|
+
} catch (error) {
|
|
679
|
+
const status = typeof error?.status === "number" ? error.status : null;
|
|
680
|
+
if (status === 404) {
|
|
681
|
+
return {
|
|
682
|
+
ok: true,
|
|
683
|
+
exists: false,
|
|
684
|
+
repository: `${owner}/${name}`,
|
|
685
|
+
workflow: normalizedWorkflow,
|
|
686
|
+
url: null,
|
|
687
|
+
message: `${normalizedWorkflow} is missing from ${owner}/${name}.`
|
|
688
|
+
};
|
|
689
|
+
}
|
|
690
|
+
throw normalizeGitHubApiError(error, `Unable to inspect GitHub workflow file ${path} in ${owner}/${name}`);
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
async function getLatestGitHubWorkflowRun(repository, {
|
|
694
|
+
client = createGitHubApiClient(),
|
|
695
|
+
workflow = "deploy-web.yml",
|
|
696
|
+
branch
|
|
697
|
+
} = {}) {
|
|
698
|
+
const { owner, name } = typeof repository === "string" ? parseGitHubRepositorySlug(repository) : repository;
|
|
699
|
+
try {
|
|
700
|
+
const listed = await client.rest.actions.listWorkflowRuns({
|
|
701
|
+
owner,
|
|
702
|
+
repo: name,
|
|
703
|
+
workflow_id: workflow,
|
|
704
|
+
...branch ? { branch } : {},
|
|
705
|
+
per_page: 1
|
|
706
|
+
});
|
|
707
|
+
const run = listed.data.workflow_runs[0] ?? null;
|
|
708
|
+
return run ? normalizeWorkflowRun(run) : null;
|
|
709
|
+
} catch (error) {
|
|
710
|
+
throw normalizeGitHubApiError(error, `Unable to inspect latest GitHub workflow run ${workflow} in ${owner}/${name}`);
|
|
711
|
+
}
|
|
712
|
+
}
|
|
551
713
|
async function listWorkflowJobsForProgress(client, owner, repo, runId) {
|
|
552
714
|
try {
|
|
553
715
|
const jobs = await client.rest.actions.listJobsForWorkflowRun({
|
|
@@ -717,11 +879,16 @@ async function ensureGitHubBranchFromBase(repository, branch, {
|
|
|
717
879
|
}
|
|
718
880
|
}
|
|
719
881
|
export {
|
|
882
|
+
cancelGitHubWorkflowRun,
|
|
720
883
|
createGitHubApiClient,
|
|
884
|
+
dispatchGitHubWorkflowRun,
|
|
721
885
|
ensureGitHubActionsEnvironment,
|
|
722
886
|
ensureGitHubBranchFromBase,
|
|
723
887
|
ensureGitHubRepository,
|
|
888
|
+
formatGitHubWorkflowFailure,
|
|
724
889
|
getGitHubRepository,
|
|
890
|
+
getGitHubWorkflowFileStatus,
|
|
891
|
+
getLatestGitHubWorkflowRun,
|
|
725
892
|
listGitHubEnvironmentSecretNames,
|
|
726
893
|
listGitHubEnvironmentVariableNames,
|
|
727
894
|
listGitHubRepositorySecretNames,
|
|
@@ -2,10 +2,10 @@ import { spawn, spawnSync } from "node:child_process";
|
|
|
2
2
|
import { resolve } from "node:path";
|
|
3
3
|
import { runLocalD1Migrations as applyLocalD1Migrations } from "./d1-migration.js";
|
|
4
4
|
import {
|
|
5
|
-
fixtureMigrationsRoot,
|
|
6
5
|
fixtureRoot,
|
|
7
6
|
fixtureWranglerConfig,
|
|
8
|
-
corePackageRoot
|
|
7
|
+
corePackageRoot,
|
|
8
|
+
sdkD1MigrationsRoot
|
|
9
9
|
} from "./runtime-paths.js";
|
|
10
10
|
import { resolveTreeseedToolCommand } from "../../managed-dependencies.js";
|
|
11
11
|
function mergeEnv(extraEnv = {}) {
|
|
@@ -41,7 +41,7 @@ function runLocalD1Migration(persistTo) {
|
|
|
41
41
|
applyLocalD1Migrations({
|
|
42
42
|
cwd: fixtureRoot,
|
|
43
43
|
wranglerConfig: fixtureWranglerConfig,
|
|
44
|
-
migrationsRoot:
|
|
44
|
+
migrationsRoot: sdkD1MigrationsRoot,
|
|
45
45
|
persistTo
|
|
46
46
|
});
|
|
47
47
|
}
|
|
@@ -3,7 +3,18 @@ export type TreeseedMailpitContainer = {
|
|
|
3
3
|
image: string;
|
|
4
4
|
ports: string;
|
|
5
5
|
};
|
|
6
|
+
type RunDocker = typeof runDocker;
|
|
7
|
+
declare function runDocker(args: string[], options?: {}): import("child_process").SpawnSyncReturns<string>;
|
|
6
8
|
export declare function dockerIsAvailable(): boolean;
|
|
7
|
-
export declare function findRunningMailpitContainer(
|
|
8
|
-
|
|
9
|
+
export declare function findRunningMailpitContainer(options?: {
|
|
10
|
+
run?: RunDocker;
|
|
11
|
+
}): {
|
|
12
|
+
name: string;
|
|
13
|
+
image: string;
|
|
14
|
+
ports: string;
|
|
15
|
+
} | null;
|
|
16
|
+
export declare function stopKnownMailpitContainers(options?: {
|
|
17
|
+
run?: RunDocker;
|
|
18
|
+
}): boolean;
|
|
9
19
|
export declare function streamKnownMailpitLogs(): void;
|
|
20
|
+
export {};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { spawnSync } from "node:child_process";
|
|
2
2
|
const EXPECTED_PORTS = ["1025->1025/tcp", "8025->8025/tcp"];
|
|
3
|
-
const KNOWN_MAILPIT_NAMES = ["treeseed_mailpit", "
|
|
3
|
+
const KNOWN_MAILPIT_NAMES = ["treeseed_mailpit", "docs_mailpit"];
|
|
4
4
|
function runDocker(args, options = {}) {
|
|
5
5
|
return spawnSync("docker", args, {
|
|
6
6
|
encoding: "utf8",
|
|
@@ -23,24 +23,29 @@ function dockerIsAvailable() {
|
|
|
23
23
|
const result = runDocker(["ps", "--format", "{{.Names}} {{.Image}} {{.Ports}}"]);
|
|
24
24
|
return result.status === 0;
|
|
25
25
|
}
|
|
26
|
-
function
|
|
27
|
-
const
|
|
26
|
+
function findKnownMailpitContainers({ all = false, run = runDocker } = {}) {
|
|
27
|
+
const args = [
|
|
28
|
+
"ps",
|
|
29
|
+
...all ? ["-a"] : [],
|
|
30
|
+
"--format",
|
|
31
|
+
"{{.Names}} {{.Image}} {{.Ports}}"
|
|
32
|
+
];
|
|
33
|
+
const result = run(args);
|
|
28
34
|
if (result.status !== 0) {
|
|
29
|
-
return
|
|
35
|
+
return [];
|
|
30
36
|
}
|
|
31
|
-
return parseDockerPsOutput(result.stdout).
|
|
37
|
+
return parseDockerPsOutput(result.stdout).filter(isCompatibleMailpitContainer);
|
|
32
38
|
}
|
|
33
|
-
function
|
|
34
|
-
|
|
35
|
-
|
|
39
|
+
function findRunningMailpitContainer(options = {}) {
|
|
40
|
+
return findKnownMailpitContainers(options).at(0) ?? null;
|
|
41
|
+
}
|
|
42
|
+
function stopKnownMailpitContainers(options = {}) {
|
|
43
|
+
const run = options.run ?? runDocker;
|
|
44
|
+
const containers = findKnownMailpitContainers({ all: true, run });
|
|
45
|
+
if (containers.length === 0) {
|
|
36
46
|
return true;
|
|
37
47
|
}
|
|
38
|
-
|
|
39
|
-
if (stopResult.status !== 0) {
|
|
40
|
-
return false;
|
|
41
|
-
}
|
|
42
|
-
const removeResult = runDocker(["rm", "-f", container.name]);
|
|
43
|
-
return removeResult.status === 0;
|
|
48
|
+
return containers.every((container) => run(["rm", "-f", container.name]).status === 0);
|
|
44
49
|
}
|
|
45
50
|
function streamKnownMailpitLogs() {
|
|
46
51
|
const container = findRunningMailpitContainer();
|