@treeseed/sdk 0.6.7 → 0.6.8
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/copilot.d.ts +15 -0
- package/dist/copilot.js +75 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +18 -0
- package/dist/managed-dependencies.d.ts +56 -0
- package/dist/managed-dependencies.js +668 -0
- package/dist/operations/providers/default.js +30 -1
- package/dist/operations/services/commit-message-provider.d.ts +33 -0
- package/dist/operations/services/commit-message-provider.js +319 -0
- package/dist/operations/services/config-runtime.js +41 -20
- package/dist/operations/services/git-remote-policy.d.ts +9 -0
- package/dist/operations/services/git-remote-policy.js +55 -0
- package/dist/operations/services/git-workflow.js +22 -3
- package/dist/operations/services/github-api.js +9 -4
- package/dist/operations/services/knowledge-coop-launch.js +4 -0
- package/dist/operations/services/local-dev.js +7 -2
- package/dist/operations/services/package-reference-policy.d.ts +70 -0
- package/dist/operations/services/package-reference-policy.js +314 -0
- package/dist/operations/services/project-platform.d.ts +4 -0
- package/dist/operations/services/project-platform.js +28 -4
- package/dist/operations/services/railway-deploy.d.ts +4 -1
- package/dist/operations/services/railway-deploy.js +76 -38
- package/dist/operations/services/repository-save-orchestrator.d.ts +172 -0
- package/dist/operations/services/repository-save-orchestrator.js +1462 -0
- package/dist/operations/services/workspace-dependency-mode.d.ts +70 -0
- package/dist/operations/services/workspace-dependency-mode.js +404 -0
- package/dist/operations/services/workspace-preflight.js +5 -0
- package/dist/operations/services/workspace-save.js +10 -6
- package/dist/operations-registry.js +5 -0
- package/dist/operations-types.d.ts +1 -0
- package/dist/platform/books-data.js +4 -1
- package/dist/platform/env.yaml +6 -3
- package/dist/reconcile/builtin-adapters.js +37 -7
- package/dist/scripts/cleanup-markdown.js +4 -0
- package/dist/scripts/publish-package.js +5 -0
- package/dist/scripts/tenant-workflow-action.js +11 -2
- package/dist/verification.js +24 -12
- package/dist/workflow/operations.d.ts +381 -55
- package/dist/workflow/operations.js +718 -258
- package/dist/workflow-state.d.ts +40 -1
- package/dist/workflow-state.js +220 -17
- package/dist/workflow-support.d.ts +3 -0
- package/dist/workflow-support.js +34 -0
- package/dist/workflow.d.ts +19 -3
- package/dist/workflow.js +3 -3
- package/dist/wrangler-d1.js +6 -1
- package/package.json +17 -1
- package/templates/github/deploy.workflow.yml +24 -14
|
@@ -1,11 +1,20 @@
|
|
|
1
1
|
import { run, workspaceRoot } from "./workspace-tools.js";
|
|
2
2
|
import { currentBranch, gitStatusPorcelain, repoRoot } from "./workspace-save.js";
|
|
3
|
+
import { ensureSshPushUrlForOrigin } from "./git-remote-policy.js";
|
|
4
|
+
import { createTreeseedManagedToolEnv, resolveTreeseedToolBinary } from "../../managed-dependencies.js";
|
|
3
5
|
const STAGING_BRANCH = "staging";
|
|
4
6
|
const PRODUCTION_BRANCH = "main";
|
|
5
7
|
const RESERVED_BRANCHES = /* @__PURE__ */ new Set([STAGING_BRANCH, PRODUCTION_BRANCH]);
|
|
6
8
|
function runGit(args, { cwd, capture = false } = {}) {
|
|
7
9
|
return run("git", args, { cwd, capture });
|
|
8
10
|
}
|
|
11
|
+
function ensureWritableOrigin(repoDir) {
|
|
12
|
+
try {
|
|
13
|
+
const remoteUrl = runGit(["remote", "get-url", "origin"], { cwd: repoDir, capture: true }).trim();
|
|
14
|
+
ensureSshPushUrlForOrigin(repoDir, remoteUrl);
|
|
15
|
+
} catch {
|
|
16
|
+
}
|
|
17
|
+
}
|
|
9
18
|
function repoHasStagedChanges(repoDir) {
|
|
10
19
|
try {
|
|
11
20
|
runGit(["diff", "--cached", "--quiet"], { cwd: repoDir });
|
|
@@ -130,7 +139,7 @@ function syncBranchWithOrigin(repoDir, branchName) {
|
|
|
130
139
|
checkoutBranch(repoDir, branchName);
|
|
131
140
|
}
|
|
132
141
|
if (remoteBranchExists(repoDir, branchName)) {
|
|
133
|
-
runGit(["
|
|
142
|
+
runGit(["merge", "--ff-only", `origin/${branchName}`], { cwd: repoDir });
|
|
134
143
|
}
|
|
135
144
|
}
|
|
136
145
|
function createFeatureBranchFromStaging(cwd, branchName) {
|
|
@@ -144,6 +153,7 @@ function createFeatureBranchFromStaging(cwd, branchName) {
|
|
|
144
153
|
return result;
|
|
145
154
|
}
|
|
146
155
|
function pushBranch(repoDir, branchName, { setUpstream = false } = {}) {
|
|
156
|
+
ensureWritableOrigin(repoDir);
|
|
147
157
|
const args = setUpstream ? ["push", "-u", "origin", branchName] : ["push", "origin", branchName];
|
|
148
158
|
runGit(args, { cwd: repoDir });
|
|
149
159
|
}
|
|
@@ -188,6 +198,7 @@ function deleteRemoteBranch(repoDir, branchName) {
|
|
|
188
198
|
if (!remoteBranchExists(repoDir, branchName)) {
|
|
189
199
|
return false;
|
|
190
200
|
}
|
|
201
|
+
ensureWritableOrigin(repoDir);
|
|
191
202
|
runGit(["push", "origin", "--delete", branchName], { cwd: repoDir });
|
|
192
203
|
return true;
|
|
193
204
|
}
|
|
@@ -268,6 +279,7 @@ function createDeprecatedTaskTag(repoDir, branchName, message) {
|
|
|
268
279
|
const shortSha = head.slice(0, 12);
|
|
269
280
|
const tagName = `deprecated/${taskTagSlug(branchName)}/${shortSha}`;
|
|
270
281
|
runGit(["tag", "-a", tagName, head, "-m", message], { cwd: repoDir });
|
|
282
|
+
ensureWritableOrigin(repoDir);
|
|
271
283
|
runGit(["push", "origin", tagName], { cwd: repoDir, capture: true });
|
|
272
284
|
return { tagName, head };
|
|
273
285
|
}
|
|
@@ -276,7 +288,14 @@ function waitForStagingAutomation(repoDir) {
|
|
|
276
288
|
return { status: "skipped", reason: "stubbed" };
|
|
277
289
|
}
|
|
278
290
|
try {
|
|
279
|
-
|
|
291
|
+
const gh = resolveTreeseedToolBinary("gh");
|
|
292
|
+
if (!gh) {
|
|
293
|
+
throw new Error("GitHub CLI `gh` is unavailable.");
|
|
294
|
+
}
|
|
295
|
+
run(gh, ["run", "watch", "--branch", STAGING_BRANCH, "--exit-status"], {
|
|
296
|
+
cwd: repoDir,
|
|
297
|
+
env: createTreeseedManagedToolEnv(process.env)
|
|
298
|
+
});
|
|
280
299
|
return { status: "completed", branch: STAGING_BRANCH };
|
|
281
300
|
} catch (error) {
|
|
282
301
|
throw new Error([
|
|
@@ -308,7 +327,7 @@ function mergeBranchIntoTarget(cwd = workspaceRoot(), { sourceBranch, targetBran
|
|
|
308
327
|
const repoDir = prepareReleaseBranches(cwd);
|
|
309
328
|
checkoutBranch(repoDir, targetBranch);
|
|
310
329
|
if (remoteBranchExists(repoDir, targetBranch)) {
|
|
311
|
-
runGit(["
|
|
330
|
+
runGit(["merge", "--ff-only", `origin/${targetBranch}`], { cwd: repoDir });
|
|
312
331
|
}
|
|
313
332
|
runGit(["merge", "--no-ff", sourceBranch, "-m", message], { cwd: repoDir });
|
|
314
333
|
pushBranch(repoDir, STAGING_BRANCH);
|
|
@@ -2,6 +2,7 @@ import { Buffer } from "node:buffer";
|
|
|
2
2
|
import { spawnSync } from "node:child_process";
|
|
3
3
|
import { createRequire } from "node:module";
|
|
4
4
|
import { Octokit } from "octokit";
|
|
5
|
+
import { createTreeseedManagedToolEnv, resolveTreeseedToolBinary } from "../../managed-dependencies.js";
|
|
5
6
|
const require2 = createRequire(import.meta.url);
|
|
6
7
|
const sodium = require2("libsodium-wrappers");
|
|
7
8
|
const DEFAULT_GITHUB_API_TIMEOUT_MS = 6e4;
|
|
@@ -465,8 +466,12 @@ function upsertGitHubRepositoryVariableWithGhCli(repository, name, value, {
|
|
|
465
466
|
GH_TOKEN: token,
|
|
466
467
|
GITHUB_TOKEN: token
|
|
467
468
|
};
|
|
469
|
+
const gh = resolveTreeseedToolBinary("gh", { env: ghEnv });
|
|
470
|
+
if (!gh) {
|
|
471
|
+
throw new Error("GitHub CLI `gh` is unavailable.");
|
|
472
|
+
}
|
|
468
473
|
const create = spawnSync(
|
|
469
|
-
|
|
474
|
+
gh,
|
|
470
475
|
[
|
|
471
476
|
"api",
|
|
472
477
|
`repos/${owner}/${repo}/actions/variables`,
|
|
@@ -477,7 +482,7 @@ function upsertGitHubRepositoryVariableWithGhCli(repository, name, value, {
|
|
|
477
482
|
"-f",
|
|
478
483
|
`value=${value}`
|
|
479
484
|
],
|
|
480
|
-
{ encoding: "utf8", env: ghEnv }
|
|
485
|
+
{ encoding: "utf8", env: createTreeseedManagedToolEnv(ghEnv) }
|
|
481
486
|
);
|
|
482
487
|
if (create.status === 0) {
|
|
483
488
|
return;
|
|
@@ -488,7 +493,7 @@ ${create.stderr ?? ""}`.trim();
|
|
|
488
493
|
throw new Error(combinedCreateOutput || `gh api exited with status ${create.status ?? 1}`);
|
|
489
494
|
}
|
|
490
495
|
const update = spawnSync(
|
|
491
|
-
|
|
496
|
+
gh,
|
|
492
497
|
[
|
|
493
498
|
"api",
|
|
494
499
|
`repos/${owner}/${repo}/actions/variables/${name}`,
|
|
@@ -499,7 +504,7 @@ ${create.stderr ?? ""}`.trim();
|
|
|
499
504
|
"-f",
|
|
500
505
|
`value=${value}`
|
|
501
506
|
],
|
|
502
|
-
{ encoding: "utf8", env: ghEnv }
|
|
507
|
+
{ encoding: "utf8", env: createTreeseedManagedToolEnv(ghEnv) }
|
|
503
508
|
);
|
|
504
509
|
if (update.status === 0) {
|
|
505
510
|
return;
|
|
@@ -17,6 +17,7 @@ import { loadCliDeployConfig } from "./runtime-tools.js";
|
|
|
17
17
|
import { templateCatalogRoot } from "./runtime-paths.js";
|
|
18
18
|
import { scaffoldTemplateProject } from "./template-registry.js";
|
|
19
19
|
import { buildKnowledgeCoopKnowledgePackPackage, buildKnowledgeCoopTemplatePackage, importKnowledgeCoopKnowledgePack } from "./knowledge-coop-packaging.js";
|
|
20
|
+
import { resolveTreeseedToolBinary } from "../../managed-dependencies.js";
|
|
20
21
|
class KnowledgeCoopLaunchError extends Error {
|
|
21
22
|
phase;
|
|
22
23
|
phases;
|
|
@@ -531,6 +532,9 @@ function loadProjectMetadata(projectId, input, seed, workstream, siteUrl, projec
|
|
|
531
532
|
};
|
|
532
533
|
}
|
|
533
534
|
function commandAvailable(command) {
|
|
535
|
+
if (command === "gh" || command === "wrangler" || command === "railway") {
|
|
536
|
+
return Boolean(resolveTreeseedToolBinary(command));
|
|
537
|
+
}
|
|
534
538
|
return spawnSync("bash", ["-lc", `command -v ${command}`], { stdio: "ignore" }).status === 0;
|
|
535
539
|
}
|
|
536
540
|
function appendPhase(phases, phase, status, detail) {
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
fixtureWranglerConfig,
|
|
8
8
|
corePackageRoot
|
|
9
9
|
} from "./runtime-paths.js";
|
|
10
|
+
import { resolveTreeseedToolCommand } from "../../managed-dependencies.js";
|
|
10
11
|
function mergeEnv(extraEnv = {}) {
|
|
11
12
|
return { ...process.env, ...extraEnv };
|
|
12
13
|
}
|
|
@@ -74,9 +75,13 @@ function prepareCloudflareLocalRuntime({ envOverrides = {}, persistTo, outDir }
|
|
|
74
75
|
});
|
|
75
76
|
}
|
|
76
77
|
function startWranglerDev(args = [], options = {}) {
|
|
78
|
+
const wrangler = resolveTreeseedToolCommand("wrangler");
|
|
79
|
+
if (!wrangler) {
|
|
80
|
+
throw new Error("Wrangler CLI is unavailable.");
|
|
81
|
+
}
|
|
77
82
|
return spawnProcess(
|
|
78
|
-
|
|
79
|
-
["dev", "--local", "--config", fixtureWranglerConfig, ...args],
|
|
83
|
+
wrangler.command,
|
|
84
|
+
[...wrangler.argsPrefix, "dev", "--local", "--config", fixtureWranglerConfig, ...args],
|
|
80
85
|
{
|
|
81
86
|
...options,
|
|
82
87
|
cwd: options.cwd ?? fixtureRoot
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
export type DevDependencyReferenceMode = 'git-tag' | 'registry-prerelease';
|
|
2
|
+
export type DevTagCleanupMode = 'safe-after-release' | 'off';
|
|
3
|
+
export type GitDependencyProtocol = 'preserve-origin' | 'https' | 'ssh';
|
|
4
|
+
export type PackageDependencyReference = {
|
|
5
|
+
packageName: string;
|
|
6
|
+
version: string;
|
|
7
|
+
spec: string;
|
|
8
|
+
manifestSpec: string;
|
|
9
|
+
installSpec: string;
|
|
10
|
+
tagName: string | null;
|
|
11
|
+
remoteUrl: string | null;
|
|
12
|
+
mode: 'stable-semver' | 'dev-git-tag' | 'dev-registry-prerelease';
|
|
13
|
+
};
|
|
14
|
+
export type RewrittenDevReference = {
|
|
15
|
+
packageName: string;
|
|
16
|
+
field: string;
|
|
17
|
+
from: string;
|
|
18
|
+
to: string;
|
|
19
|
+
tagName: string | null;
|
|
20
|
+
};
|
|
21
|
+
export declare function internalDependencyFields(packageJson: Record<string, unknown> | null): string[];
|
|
22
|
+
export declare function isPrereleaseVersion(version: string): boolean;
|
|
23
|
+
export declare function isStableVersion(version: string): boolean;
|
|
24
|
+
export declare function isGitDependencySpec(spec: string): boolean;
|
|
25
|
+
export declare function devTagFromDependencySpec(spec: string): string | null;
|
|
26
|
+
export declare function normalizeGitRemoteForDependency(remoteUrl: string, protocol?: GitDependencyProtocol): string | null;
|
|
27
|
+
export declare function normalizeGitRemoteForManifest(remoteUrl: string, protocol?: GitDependencyProtocol): string | null;
|
|
28
|
+
export declare function createPackageDependencyReference(input: {
|
|
29
|
+
packageName: string;
|
|
30
|
+
version: string;
|
|
31
|
+
branchMode: 'package-release-main' | 'package-dev-save';
|
|
32
|
+
remoteUrl?: string | null;
|
|
33
|
+
devDependencyReferenceMode?: DevDependencyReferenceMode;
|
|
34
|
+
gitDependencyProtocol?: GitDependencyProtocol;
|
|
35
|
+
}): PackageDependencyReference;
|
|
36
|
+
export declare function updateInternalDependencySpecs(packageJson: Record<string, unknown> | null, references: Map<string, PackageDependencyReference>): RewrittenDevReference[];
|
|
37
|
+
export declare function rewriteInternalDependenciesToStableVersions(root: any, versions: Map<string, string>): (RewrittenDevReference & {
|
|
38
|
+
repoName: string;
|
|
39
|
+
packageJsonPath: string;
|
|
40
|
+
})[];
|
|
41
|
+
export declare function rewriteProjectInternalDependenciesToStableVersions(root: any, versions: Map<string, string>): (RewrittenDevReference & {
|
|
42
|
+
repoName: string;
|
|
43
|
+
packageJsonPath: string;
|
|
44
|
+
})[];
|
|
45
|
+
export declare function collectInternalDevReferenceIssues(root?: any, packageNames?: Set<any>): {
|
|
46
|
+
repoName: string;
|
|
47
|
+
filePath: string;
|
|
48
|
+
field?: string;
|
|
49
|
+
dependencyName?: string;
|
|
50
|
+
spec: string;
|
|
51
|
+
reason: string;
|
|
52
|
+
}[];
|
|
53
|
+
export declare function assertNoInternalDevReferences(root?: any, packageNames?: Set<string>): void;
|
|
54
|
+
export declare function createDevTagMessage(input: {
|
|
55
|
+
packageName: string;
|
|
56
|
+
version: string;
|
|
57
|
+
branch: string;
|
|
58
|
+
commitSha: string;
|
|
59
|
+
workflowRunId?: string | null;
|
|
60
|
+
createdAt?: string;
|
|
61
|
+
}): string;
|
|
62
|
+
export declare function gitTagMessage(repoDir: string, tagName: string): string;
|
|
63
|
+
export declare function tagHasTreeseedDevMetadata(repoDir: string, tagName: string): boolean;
|
|
64
|
+
export declare function cleanupDevTags(repoDir: string, tagNames: string[], activeReferences?: string[]): {
|
|
65
|
+
cleaned: string[];
|
|
66
|
+
skipped: {
|
|
67
|
+
tagName: string;
|
|
68
|
+
reason: string;
|
|
69
|
+
}[];
|
|
70
|
+
};
|
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { resolve } from "node:path";
|
|
3
|
+
import { pathToFileURL } from "node:url";
|
|
4
|
+
import { run, workspacePackages, workspaceRoot } from "./workspace-tools.js";
|
|
5
|
+
const INTERNAL_DEPENDENCY_FIELDS = ["dependencies", "optionalDependencies", "peerDependencies", "devDependencies"];
|
|
6
|
+
function readJson(filePath) {
|
|
7
|
+
return JSON.parse(readFileSync(filePath, "utf8"));
|
|
8
|
+
}
|
|
9
|
+
function writeJson(filePath, value) {
|
|
10
|
+
writeFileSync(filePath, `${JSON.stringify(value, null, 2)}
|
|
11
|
+
`, "utf8");
|
|
12
|
+
}
|
|
13
|
+
function internalDependencyFields(packageJson) {
|
|
14
|
+
if (!packageJson) return [];
|
|
15
|
+
return INTERNAL_DEPENDENCY_FIELDS.filter((field) => packageJson[field] && typeof packageJson[field] === "object" && !Array.isArray(packageJson[field]));
|
|
16
|
+
}
|
|
17
|
+
function isPrereleaseVersion(version) {
|
|
18
|
+
return /^\d+\.\d+\.\d+-[0-9A-Za-z.-]+$/u.test(String(version).trim());
|
|
19
|
+
}
|
|
20
|
+
function isStableVersion(version) {
|
|
21
|
+
return /^\d+\.\d+\.\d+$/u.test(String(version).trim());
|
|
22
|
+
}
|
|
23
|
+
function isGitDependencySpec(spec) {
|
|
24
|
+
return /^(?:git\+|github:|gitlab:|bitbucket:|ssh:\/\/|https:\/\/|file:)/u.test(String(spec).trim()) && String(spec).includes("#");
|
|
25
|
+
}
|
|
26
|
+
function devTagFromDependencySpec(spec) {
|
|
27
|
+
const value = String(spec).trim();
|
|
28
|
+
const hashIndex = value.lastIndexOf("#");
|
|
29
|
+
if (hashIndex === -1) return null;
|
|
30
|
+
const ref = decodeURIComponent(value.slice(hashIndex + 1));
|
|
31
|
+
return ref.includes("-dev.") ? ref : null;
|
|
32
|
+
}
|
|
33
|
+
function normalizeGitRemoteForDependency(remoteUrl, protocol = "preserve-origin") {
|
|
34
|
+
const remote = String(remoteUrl).trim();
|
|
35
|
+
if (!remote) return null;
|
|
36
|
+
if (/^file:\/\//u.test(remote)) return remote;
|
|
37
|
+
if (remote.startsWith("/") || remote.startsWith("./") || remote.startsWith("../")) {
|
|
38
|
+
return `git+${pathToFileURL(remote).href}`;
|
|
39
|
+
}
|
|
40
|
+
if (remote.endsWith(".git") && existsSync(remote)) {
|
|
41
|
+
return `git+${pathToFileURL(remote).href}`;
|
|
42
|
+
}
|
|
43
|
+
const sshMatch = remote.match(/^git@([^:]+):(.+?)(?:\.git)?$/u);
|
|
44
|
+
if (sshMatch) {
|
|
45
|
+
if (protocol === "https") {
|
|
46
|
+
return `git+https://${sshMatch[1]}/${sshMatch[2]}.git`;
|
|
47
|
+
}
|
|
48
|
+
return `git+ssh://git@${sshMatch[1]}/${sshMatch[2]}.git`;
|
|
49
|
+
}
|
|
50
|
+
const httpsMatch = remote.match(/^https:\/\/([^/]+)\/(.+?)(?:\.git)?$/u);
|
|
51
|
+
if (httpsMatch) {
|
|
52
|
+
if (protocol === "ssh") {
|
|
53
|
+
return `git+ssh://git@${httpsMatch[1]}/${httpsMatch[2]}.git`;
|
|
54
|
+
}
|
|
55
|
+
return `git+https://${httpsMatch[1]}/${httpsMatch[2]}.git`;
|
|
56
|
+
}
|
|
57
|
+
if (/^ssh:\/\//u.test(remote)) return `git+${remote}`;
|
|
58
|
+
if (/^git\+/u.test(remote)) return remote;
|
|
59
|
+
return remote;
|
|
60
|
+
}
|
|
61
|
+
function normalizeGitRemoteForManifest(remoteUrl, protocol = "preserve-origin") {
|
|
62
|
+
const dependencyRemote = normalizeGitRemoteForDependency(remoteUrl, protocol);
|
|
63
|
+
if (!dependencyRemote) return null;
|
|
64
|
+
const githubSshMatch = dependencyRemote.match(/^git\+ssh:\/\/git@github\.com\/(.+?)(?:\.git)?$/u);
|
|
65
|
+
if (githubSshMatch) {
|
|
66
|
+
return `github:${githubSshMatch[1]}`;
|
|
67
|
+
}
|
|
68
|
+
const githubHttpsMatch = dependencyRemote.match(/^git\+https:\/\/github\.com\/(.+?)(?:\.git)?$/u);
|
|
69
|
+
if (githubHttpsMatch) {
|
|
70
|
+
return `github:${githubHttpsMatch[1]}`;
|
|
71
|
+
}
|
|
72
|
+
return dependencyRemote;
|
|
73
|
+
}
|
|
74
|
+
function createPackageDependencyReference(input) {
|
|
75
|
+
if (input.branchMode === "package-release-main") {
|
|
76
|
+
return {
|
|
77
|
+
packageName: input.packageName,
|
|
78
|
+
version: input.version,
|
|
79
|
+
spec: input.version,
|
|
80
|
+
manifestSpec: input.version,
|
|
81
|
+
installSpec: input.version,
|
|
82
|
+
tagName: input.version,
|
|
83
|
+
remoteUrl: input.remoteUrl ?? null,
|
|
84
|
+
mode: "stable-semver"
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
if ((input.devDependencyReferenceMode ?? "git-tag") === "registry-prerelease") {
|
|
88
|
+
return {
|
|
89
|
+
packageName: input.packageName,
|
|
90
|
+
version: input.version,
|
|
91
|
+
spec: input.version,
|
|
92
|
+
manifestSpec: input.version,
|
|
93
|
+
installSpec: input.version,
|
|
94
|
+
tagName: input.version,
|
|
95
|
+
remoteUrl: input.remoteUrl ?? null,
|
|
96
|
+
mode: "dev-registry-prerelease"
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
const installRemote = normalizeGitRemoteForDependency(input.remoteUrl ?? "", input.gitDependencyProtocol ?? "preserve-origin");
|
|
100
|
+
const manifestRemote = normalizeGitRemoteForManifest(input.remoteUrl ?? "", input.gitDependencyProtocol ?? "preserve-origin");
|
|
101
|
+
if (!installRemote || !manifestRemote) {
|
|
102
|
+
throw new Error(`Unable to create Git-tag dependency for ${input.packageName}; origin remote is missing.`);
|
|
103
|
+
}
|
|
104
|
+
const manifestSpec = `${manifestRemote}#${input.version}`;
|
|
105
|
+
return {
|
|
106
|
+
packageName: input.packageName,
|
|
107
|
+
version: input.version,
|
|
108
|
+
spec: manifestSpec,
|
|
109
|
+
manifestSpec,
|
|
110
|
+
installSpec: manifestSpec,
|
|
111
|
+
tagName: input.version,
|
|
112
|
+
remoteUrl: input.remoteUrl ?? null,
|
|
113
|
+
mode: "dev-git-tag"
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
function updateInternalDependencySpecs(packageJson, references) {
|
|
117
|
+
if (!packageJson) return [];
|
|
118
|
+
const changed = [];
|
|
119
|
+
for (const field of internalDependencyFields(packageJson)) {
|
|
120
|
+
const values = packageJson[field];
|
|
121
|
+
for (const [depName, reference] of references.entries()) {
|
|
122
|
+
if (!(depName in values)) continue;
|
|
123
|
+
const current = String(values[depName]);
|
|
124
|
+
const nextSpec = reference.manifestSpec ?? reference.spec;
|
|
125
|
+
if (current === nextSpec) continue;
|
|
126
|
+
values[depName] = nextSpec;
|
|
127
|
+
changed.push({
|
|
128
|
+
packageName: depName,
|
|
129
|
+
field,
|
|
130
|
+
from: current,
|
|
131
|
+
to: nextSpec,
|
|
132
|
+
tagName: devTagFromDependencySpec(current) ?? (isPrereleaseVersion(current) ? current : null)
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
return changed;
|
|
137
|
+
}
|
|
138
|
+
function rewriteInternalDependenciesToStableVersions(root = workspaceRoot(), versions) {
|
|
139
|
+
const rewrites = [];
|
|
140
|
+
for (const pkg of workspacePackages(root)) {
|
|
141
|
+
const packageJsonPath = resolve(pkg.dir, "package.json");
|
|
142
|
+
const packageJson = readJson(packageJsonPath);
|
|
143
|
+
const changed = updateInternalDependencySpecs(
|
|
144
|
+
packageJson,
|
|
145
|
+
new Map([...versions.entries()].map(([packageName, version]) => [packageName, {
|
|
146
|
+
packageName,
|
|
147
|
+
version,
|
|
148
|
+
spec: version,
|
|
149
|
+
manifestSpec: version,
|
|
150
|
+
installSpec: version,
|
|
151
|
+
tagName: version,
|
|
152
|
+
remoteUrl: null,
|
|
153
|
+
mode: "stable-semver"
|
|
154
|
+
}]))
|
|
155
|
+
);
|
|
156
|
+
if (changed.length === 0) continue;
|
|
157
|
+
writeJson(packageJsonPath, packageJson);
|
|
158
|
+
rewrites.push(...changed.map((entry) => ({
|
|
159
|
+
...entry,
|
|
160
|
+
repoName: pkg.name,
|
|
161
|
+
packageJsonPath
|
|
162
|
+
})));
|
|
163
|
+
}
|
|
164
|
+
return rewrites;
|
|
165
|
+
}
|
|
166
|
+
function rewriteProjectInternalDependenciesToStableVersions(root = workspaceRoot(), versions) {
|
|
167
|
+
const rewrites = [];
|
|
168
|
+
const targets = [
|
|
169
|
+
{ name: "@treeseed/market", dir: root },
|
|
170
|
+
...workspacePackages(root).map((pkg) => ({ name: pkg.name, dir: pkg.dir }))
|
|
171
|
+
];
|
|
172
|
+
for (const target of targets) {
|
|
173
|
+
const packageJsonPath = resolve(target.dir, "package.json");
|
|
174
|
+
if (!existsSync(packageJsonPath)) continue;
|
|
175
|
+
const packageJson = readJson(packageJsonPath);
|
|
176
|
+
const changed = updateInternalDependencySpecs(
|
|
177
|
+
packageJson,
|
|
178
|
+
new Map([...versions.entries()].map(([packageName, version]) => [packageName, {
|
|
179
|
+
packageName,
|
|
180
|
+
version,
|
|
181
|
+
spec: version,
|
|
182
|
+
manifestSpec: version,
|
|
183
|
+
installSpec: version,
|
|
184
|
+
tagName: version,
|
|
185
|
+
remoteUrl: null,
|
|
186
|
+
mode: "stable-semver"
|
|
187
|
+
}]))
|
|
188
|
+
);
|
|
189
|
+
if (changed.length === 0) continue;
|
|
190
|
+
writeJson(packageJsonPath, packageJson);
|
|
191
|
+
rewrites.push(...changed.map((entry) => ({
|
|
192
|
+
...entry,
|
|
193
|
+
repoName: target.name,
|
|
194
|
+
packageJsonPath
|
|
195
|
+
})));
|
|
196
|
+
}
|
|
197
|
+
return rewrites;
|
|
198
|
+
}
|
|
199
|
+
function collectInternalDevReferenceIssues(root = workspaceRoot(), packageNames = new Set(workspacePackages(root).map((pkg) => pkg.name))) {
|
|
200
|
+
const issues = [];
|
|
201
|
+
const manifestRoots = [
|
|
202
|
+
{ name: "@treeseed/market", dir: root },
|
|
203
|
+
...workspacePackages(root).map((pkg) => ({ name: pkg.name, dir: pkg.dir }))
|
|
204
|
+
];
|
|
205
|
+
for (const pkg of manifestRoots) {
|
|
206
|
+
const packageJsonPath = resolve(pkg.dir, "package.json");
|
|
207
|
+
if (!existsSync(packageJsonPath)) continue;
|
|
208
|
+
const packageJson = readJson(packageJsonPath);
|
|
209
|
+
for (const field of internalDependencyFields(packageJson)) {
|
|
210
|
+
const values = packageJson[field];
|
|
211
|
+
for (const [depName, specValue] of Object.entries(values)) {
|
|
212
|
+
if (!packageNames.has(depName)) continue;
|
|
213
|
+
const spec = String(specValue);
|
|
214
|
+
if (isGitDependencySpec(spec) || devTagFromDependencySpec(spec)) {
|
|
215
|
+
issues.push({ repoName: pkg.name, filePath: packageJsonPath, field, dependencyName: depName, spec, reason: "git-dev-ref" });
|
|
216
|
+
} else if (isPrereleaseVersion(spec)) {
|
|
217
|
+
issues.push({ repoName: pkg.name, filePath: packageJsonPath, field, dependencyName: depName, spec, reason: "prerelease-dev-ref" });
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
const lockRoots = [{ name: "@treeseed/market", dir: root }, ...workspacePackages(root).map((pkg) => ({ name: pkg.name, dir: pkg.dir }))];
|
|
223
|
+
for (const lockRoot of lockRoots) {
|
|
224
|
+
for (const lockName of ["package-lock.json", "npm-shrinkwrap.json"]) {
|
|
225
|
+
const lockPath = resolve(lockRoot.dir, lockName);
|
|
226
|
+
if (!existsSync(lockPath)) continue;
|
|
227
|
+
const source = readFileSync(lockPath, "utf8");
|
|
228
|
+
for (const packageName of packageNames) {
|
|
229
|
+
if (source.includes(`${packageName}.git#`) || source.includes(`${packageName}#`) || /-dev\.[0-9A-Za-z.-]+/u.test(source)) {
|
|
230
|
+
issues.push({ repoName: lockRoot.name, filePath: lockPath, spec: packageName, reason: "lockfile-dev-ref" });
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
return issues;
|
|
236
|
+
}
|
|
237
|
+
function assertNoInternalDevReferences(root = workspaceRoot(), packageNames) {
|
|
238
|
+
const issues = collectInternalDevReferenceIssues(root, packageNames);
|
|
239
|
+
if (issues.length === 0) return;
|
|
240
|
+
const rendered = issues.map((issue) => `${issue.filePath}${issue.field ? ` ${issue.field}.${issue.dependencyName}` : ""}: ${issue.reason} ${issue.spec}`).join("\n");
|
|
241
|
+
throw new Error(`Stable release still contains internal Git/dev dependency references.
|
|
242
|
+
${rendered}`);
|
|
243
|
+
}
|
|
244
|
+
function createDevTagMessage(input) {
|
|
245
|
+
const branchSlug = input.branch.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 40) || "dev";
|
|
246
|
+
return [
|
|
247
|
+
`save: ${input.packageName} ${input.version}`,
|
|
248
|
+
"",
|
|
249
|
+
"treeseed-dev-tag: true",
|
|
250
|
+
`package: ${input.packageName}`,
|
|
251
|
+
`version: ${input.version}`,
|
|
252
|
+
`branch: ${input.branch}`,
|
|
253
|
+
`branchSlug: ${branchSlug}`,
|
|
254
|
+
`createdAt: ${input.createdAt ?? (/* @__PURE__ */ new Date()).toISOString()}`,
|
|
255
|
+
`workflowRunId: ${input.workflowRunId ?? ""}`,
|
|
256
|
+
`commitSha: ${input.commitSha}`
|
|
257
|
+
].join("\n");
|
|
258
|
+
}
|
|
259
|
+
function gitTagMessage(repoDir, tagName) {
|
|
260
|
+
try {
|
|
261
|
+
return run("git", ["tag", "-l", tagName, "--format=%(contents)"], { cwd: repoDir, capture: true });
|
|
262
|
+
} catch {
|
|
263
|
+
return "";
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
function tagHasTreeseedDevMetadata(repoDir, tagName) {
|
|
267
|
+
return gitTagMessage(repoDir, tagName).includes("treeseed-dev-tag: true");
|
|
268
|
+
}
|
|
269
|
+
function cleanupDevTags(repoDir, tagNames, activeReferences = []) {
|
|
270
|
+
const active = new Set(activeReferences.filter(Boolean));
|
|
271
|
+
const cleaned = [];
|
|
272
|
+
const skipped = [];
|
|
273
|
+
for (const tagName of [...new Set(tagNames.filter(Boolean))].sort()) {
|
|
274
|
+
if (!tagName.includes("-dev.")) {
|
|
275
|
+
skipped.push({ tagName, reason: "not-dev-tag" });
|
|
276
|
+
continue;
|
|
277
|
+
}
|
|
278
|
+
if (active.has(tagName)) {
|
|
279
|
+
skipped.push({ tagName, reason: "still-referenced" });
|
|
280
|
+
continue;
|
|
281
|
+
}
|
|
282
|
+
if (!tagHasTreeseedDevMetadata(repoDir, tagName)) {
|
|
283
|
+
skipped.push({ tagName, reason: "missing-treeseed-metadata" });
|
|
284
|
+
continue;
|
|
285
|
+
}
|
|
286
|
+
try {
|
|
287
|
+
run("git", ["tag", "-d", tagName], { cwd: repoDir });
|
|
288
|
+
run("git", ["push", "origin", `:refs/tags/${tagName}`], { cwd: repoDir });
|
|
289
|
+
cleaned.push(tagName);
|
|
290
|
+
} catch (error) {
|
|
291
|
+
skipped.push({ tagName, reason: error instanceof Error ? error.message : String(error) });
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
return { cleaned, skipped };
|
|
295
|
+
}
|
|
296
|
+
export {
|
|
297
|
+
assertNoInternalDevReferences,
|
|
298
|
+
cleanupDevTags,
|
|
299
|
+
collectInternalDevReferenceIssues,
|
|
300
|
+
createDevTagMessage,
|
|
301
|
+
createPackageDependencyReference,
|
|
302
|
+
devTagFromDependencySpec,
|
|
303
|
+
gitTagMessage,
|
|
304
|
+
internalDependencyFields,
|
|
305
|
+
isGitDependencySpec,
|
|
306
|
+
isPrereleaseVersion,
|
|
307
|
+
isStableVersion,
|
|
308
|
+
normalizeGitRemoteForDependency,
|
|
309
|
+
normalizeGitRemoteForManifest,
|
|
310
|
+
rewriteInternalDependenciesToStableVersions,
|
|
311
|
+
rewriteProjectInternalDependenciesToStableVersions,
|
|
312
|
+
tagHasTreeseedDevMetadata,
|
|
313
|
+
updateInternalDependencySpecs
|
|
314
|
+
};
|
|
@@ -247,6 +247,10 @@ export declare function deployProjectPlatform(options: ProjectPlatformActionOpti
|
|
|
247
247
|
} | null;
|
|
248
248
|
} | undefined)[];
|
|
249
249
|
}>;
|
|
250
|
+
export declare function resolveRailwayServiceDeployDependencies({ includeDataDependency, previousRailwayDeployNodeId, }: {
|
|
251
|
+
includeDataDependency: boolean;
|
|
252
|
+
previousRailwayDeployNodeId?: string | null;
|
|
253
|
+
}): string[];
|
|
250
254
|
export declare function publishProjectContent(options: ProjectPlatformActionOptions): Promise<{
|
|
251
255
|
ok: boolean;
|
|
252
256
|
scope: ProjectPlatformScope;
|
|
@@ -37,7 +37,8 @@ import {
|
|
|
37
37
|
validateRailwayServiceConfiguration,
|
|
38
38
|
verifyRailwayScheduledJobs
|
|
39
39
|
} from "./railway-deploy.js";
|
|
40
|
-
import { loadCliDeployConfig, packageScriptPath
|
|
40
|
+
import { loadCliDeployConfig, packageScriptPath } from "./runtime-tools.js";
|
|
41
|
+
import { resolveTreeseedToolCommand } from "../../managed-dependencies.js";
|
|
41
42
|
import { CloudflareQueuePullClient, CloudflareQueuePushClient } from "../../remote.js";
|
|
42
43
|
import { runPrefixedCommand, runTreeseedBootstrapDag, sleep, writeTreeseedBootstrapLine } from "./bootstrap-runner.js";
|
|
43
44
|
import { runTenantDeployPreflight } from "./save-deploy-preflight.js";
|
|
@@ -130,7 +131,11 @@ function runTenantPublishContentPreflight(options) {
|
|
|
130
131
|
return target;
|
|
131
132
|
}
|
|
132
133
|
function runWrangler(tenantRoot, args, extraEnv = {}, options = {}) {
|
|
133
|
-
const
|
|
134
|
+
const wrangler = resolveTreeseedToolCommand("wrangler");
|
|
135
|
+
if (!wrangler) {
|
|
136
|
+
throw new Error("Wrangler CLI is unavailable.");
|
|
137
|
+
}
|
|
138
|
+
const result = spawnSync(wrangler.command, [...wrangler.argsPrefix, ...args], {
|
|
134
139
|
cwd: tenantRoot,
|
|
135
140
|
stdio: options.capture ? "pipe" : "inherit",
|
|
136
141
|
encoding: options.capture ? "utf8" : void 0,
|
|
@@ -151,7 +156,11 @@ async function runPrefixedWranglerWithRetry(tenantRoot, args, {
|
|
|
151
156
|
}) {
|
|
152
157
|
let lastOutput = "";
|
|
153
158
|
for (let attempt = 1; attempt <= 3; attempt += 1) {
|
|
154
|
-
const
|
|
159
|
+
const wrangler = resolveTreeseedToolCommand("wrangler");
|
|
160
|
+
if (!wrangler) {
|
|
161
|
+
throw new Error("Wrangler CLI is unavailable.");
|
|
162
|
+
}
|
|
163
|
+
const result = await runPrefixedCommand(wrangler.command, [...wrangler.argsPrefix, ...args], {
|
|
155
164
|
cwd: tenantRoot,
|
|
156
165
|
env,
|
|
157
166
|
write,
|
|
@@ -1080,13 +1089,17 @@ async function deployProjectPlatform(options) {
|
|
|
1080
1089
|
const selectedServices = validation.services.filter(
|
|
1081
1090
|
(service) => service.key === "api" ? selectedSystems.has("api") : selectedSystems.has("agents")
|
|
1082
1091
|
);
|
|
1092
|
+
let previousRailwayDeployNodeId = null;
|
|
1083
1093
|
for (const service of selectedServices) {
|
|
1084
1094
|
const system = service.key === "api" ? "api" : "agents";
|
|
1085
1095
|
const nodeId = `${system}:${service.key}-railway-deploy`;
|
|
1086
1096
|
selectedRailwayServiceKeys.push(service.key);
|
|
1087
1097
|
nodes.push({
|
|
1088
1098
|
id: nodeId,
|
|
1089
|
-
dependencies:
|
|
1099
|
+
dependencies: resolveRailwayServiceDeployDependencies({
|
|
1100
|
+
includeDataDependency: selectedSystems.has("data"),
|
|
1101
|
+
previousRailwayDeployNodeId
|
|
1102
|
+
}),
|
|
1090
1103
|
run: async () => {
|
|
1091
1104
|
const result = await deployRailwayService(options.tenantRoot, service, {
|
|
1092
1105
|
dryRun: options.dryRun,
|
|
@@ -1103,6 +1116,7 @@ async function deployProjectPlatform(options) {
|
|
|
1103
1116
|
return result;
|
|
1104
1117
|
}
|
|
1105
1118
|
});
|
|
1119
|
+
previousRailwayDeployNodeId = nodeId;
|
|
1106
1120
|
}
|
|
1107
1121
|
}
|
|
1108
1122
|
let railwaySchedules = [];
|
|
@@ -1178,6 +1192,15 @@ async function deployProjectPlatform(options) {
|
|
|
1178
1192
|
serviceResults
|
|
1179
1193
|
};
|
|
1180
1194
|
}
|
|
1195
|
+
function resolveRailwayServiceDeployDependencies({
|
|
1196
|
+
includeDataDependency,
|
|
1197
|
+
previousRailwayDeployNodeId
|
|
1198
|
+
}) {
|
|
1199
|
+
return [
|
|
1200
|
+
...includeDataDependency ? ["data:d1-migrate"] : [],
|
|
1201
|
+
...previousRailwayDeployNodeId ? [previousRailwayDeployNodeId] : []
|
|
1202
|
+
];
|
|
1203
|
+
}
|
|
1181
1204
|
async function publishProjectContent(options) {
|
|
1182
1205
|
const reporter = resolveReporter(options.tenantRoot, options.reporter);
|
|
1183
1206
|
return publishContent(options, reporter);
|
|
@@ -1303,6 +1326,7 @@ export {
|
|
|
1303
1326
|
prepareTenantCloudflareDeploy,
|
|
1304
1327
|
provisionProjectPlatform,
|
|
1305
1328
|
publishProjectContent,
|
|
1329
|
+
resolveRailwayServiceDeployDependencies,
|
|
1306
1330
|
resolveScope,
|
|
1307
1331
|
runProjectPlatformAction,
|
|
1308
1332
|
runTenantDataMigration,
|
|
@@ -4,6 +4,7 @@ export declare function resolveRailwayAuthToken(env?: NodeJS.ProcessEnv): string
|
|
|
4
4
|
export declare function buildRailwayCommandEnv(env?: NodeJS.ProcessEnv): {
|
|
5
5
|
[key: string]: string | undefined;
|
|
6
6
|
};
|
|
7
|
+
export declare function isRailwayTransientFailure(result: any): boolean;
|
|
7
8
|
export declare function runRailway(args: any, { cwd, capture, allowFailure, input, env }?: {
|
|
8
9
|
capture?: boolean | undefined;
|
|
9
10
|
allowFailure?: boolean | undefined;
|
|
@@ -265,7 +266,9 @@ export declare function verifyRailwayScheduledJobs(tenantRoot: any, scope: any,
|
|
|
265
266
|
unsupported?: undefined;
|
|
266
267
|
message?: undefined;
|
|
267
268
|
}>;
|
|
268
|
-
export declare function planRailwayServiceDeploy(service: any
|
|
269
|
+
export declare function planRailwayServiceDeploy(service: any, { env }?: {
|
|
270
|
+
env?: NodeJS.ProcessEnv | undefined;
|
|
271
|
+
}): {
|
|
269
272
|
command: string;
|
|
270
273
|
args: any[];
|
|
271
274
|
cwd: any;
|