@treeseed/sdk 0.6.8 → 0.6.9

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.
@@ -1411,9 +1411,14 @@ function collectTreeseedConfigSeedValues(tenantRoot, scope, env = process.env) {
1411
1411
  }
1412
1412
  return filterEnvironmentValuesByRegistry({
1413
1413
  ...machineValues,
1414
- ...Object.fromEntries(Object.entries(env).map(([key, value]) => [key, value ?? void 0]))
1414
+ ...nonEmptyEnvironmentValues(env)
1415
1415
  }, registry, scope);
1416
1416
  }
1417
+ function nonEmptyEnvironmentValues(env = process.env) {
1418
+ return Object.fromEntries(
1419
+ Object.entries(env).filter(([, value]) => typeof value === "string" && value.length > 0)
1420
+ );
1421
+ }
1417
1422
  function collectTreeseedConfigSeedValueSources(tenantRoot, scope, env = process.env) {
1418
1423
  warnDeprecatedTreeseedLocalEnvFiles(tenantRoot);
1419
1424
  const registry = collectTreeseedEnvironmentContext(tenantRoot);
@@ -1458,7 +1463,8 @@ function resolveTreeseedLaunchEnvironment({
1458
1463
  }
1459
1464
  }
1460
1465
  const registry = collectTreeseedEnvironmentContext(tenantRoot);
1461
- const seedValues = scope === "local" ? { ...baseEnv, ...machineValues } : { ...machineValues, ...baseEnv };
1466
+ const baseValues = nonEmptyEnvironmentValues(baseEnv);
1467
+ const seedValues = scope === "local" ? { ...baseValues, ...machineValues } : { ...machineValues, ...baseValues };
1462
1468
  const suggestedValues = getTreeseedEnvironmentSuggestedValues({
1463
1469
  scope,
1464
1470
  purpose: "deploy",
@@ -1470,7 +1476,7 @@ function resolveTreeseedLaunchEnvironment({
1470
1476
  const nonSecretSuggestedValues = Object.fromEntries(
1471
1477
  registry.entries.filter((entry) => entry.sensitivity !== "secret" && typeof suggestedValues[entry.id] === "string" && suggestedValues[entry.id].length > 0).map((entry) => [entry.id, suggestedValues[entry.id]])
1472
1478
  );
1473
- const scopedValues = scope === "local" ? { ...nonSecretSuggestedValues, ...baseEnv, ...machineValues } : { ...nonSecretSuggestedValues, ...machineValues, ...baseEnv };
1479
+ const scopedValues = scope === "local" ? { ...nonSecretSuggestedValues, ...baseValues, ...machineValues } : { ...nonSecretSuggestedValues, ...machineValues, ...baseValues };
1474
1480
  return {
1475
1481
  ...scopedValues,
1476
1482
  ...overrides
@@ -224,10 +224,26 @@ function collectInternalDevReferenceIssues(root = workspaceRoot(), packageNames
224
224
  for (const lockName of ["package-lock.json", "npm-shrinkwrap.json"]) {
225
225
  const lockPath = resolve(lockRoot.dir, lockName);
226
226
  if (!existsSync(lockPath)) continue;
227
- const source = readFileSync(lockPath, "utf8");
227
+ const lockfile = readJson(lockPath);
228
+ const packageEntries = lockfile.packages && typeof lockfile.packages === "object" ? Object.values(lockfile.packages) : [];
228
229
  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" });
230
+ const entries = [
231
+ lockfile.dependencies?.[packageName],
232
+ ...packageEntries.map((entry) => entry && typeof entry === "object" ? entry.dependencies?.[packageName] : null)
233
+ ];
234
+ for (const entry of entries) {
235
+ if (!entry || typeof entry !== "object") continue;
236
+ const record = entry;
237
+ const spec = [
238
+ record.version,
239
+ record.resolved,
240
+ record.from
241
+ ].map((value) => typeof value === "string" ? value : "").find(
242
+ (value) => isGitDependencySpec(value) || devTagFromDependencySpec(value) || isPrereleaseVersion(value)
243
+ );
244
+ if (spec) {
245
+ issues.push({ repoName: lockRoot.name, filePath: lockPath, spec, reason: "lockfile-dev-ref", dependencyName: packageName });
246
+ }
231
247
  }
232
248
  }
233
249
  }
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { spawnSync } from 'node:child_process';
4
+
5
+ if (process.env.TREESEED_SKIP_PACKAGE_PREPARE === '1') {
6
+ process.exit(0);
7
+ }
8
+
9
+ const result = spawnSync('npm', ['run', 'build:dist'], {
10
+ stdio: 'inherit',
11
+ shell: process.platform === 'win32',
12
+ });
13
+
14
+ process.exit(result.status ?? 1);
@@ -51,6 +51,21 @@ function createActArgs(eventName, workflowPath) {
51
51
  }
52
52
  function createWorkspaceActWorkflow(options) {
53
53
  const relativePackageRoot = relative(options.workspaceRoot, options.packageRoot).replace(/\\/g, "/");
54
+ const siblingLinkCommands = options.localTreeseedSiblingDependencies.map((packageName) => {
55
+ const [, packageShortName] = packageName.split("/");
56
+ const packageDir = `packages/${packageShortName}`;
57
+ const packageScope = packageName.split("/")[0];
58
+ const linkParent = `node_modules/${packageScope}`;
59
+ const linkTarget = relative(
60
+ resolve(options.packageRoot, linkParent),
61
+ resolve(options.workspaceRoot, packageDir)
62
+ ).replace(/\\/g, "/");
63
+ return [
64
+ `mkdir -p ${linkParent}`,
65
+ `rm -rf ${linkParent}/${packageShortName}`,
66
+ `ln -s ${linkTarget} ${linkParent}/${packageShortName}`
67
+ ].join("\n");
68
+ }).join("\n");
54
69
  const siblingPreparationCommands = options.localTreeseedSiblingDependencies.map((packageName) => {
55
70
  const packageDir = `packages/${packageName.split("/")[1]}`;
56
71
  const manifest = readPackageManifest(resolve(options.workspaceRoot, packageDir, "package.json"));
@@ -111,13 +126,19 @@ ${siblingPreparationCommands.split("\n").map((line) => ` ${line}`).join
111
126
 
112
127
  ` : ""} - name: Install dependencies
113
128
  run: |
129
+ node -e "const fs = require('fs'); const p = JSON.parse(fs.readFileSync('package.json', 'utf8')); if (p.scripts) delete p.scripts.prepare; fs.writeFileSync('package.json', JSON.stringify(p, null, '\\t') + '\\n');"
114
130
  if test -f package-lock.json; then
115
131
  npm ci --workspaces=false
116
132
  else
117
133
  npm install --workspaces=false --no-audit --no-fund
118
134
  fi
135
+ git checkout -- package.json || true
119
136
 
120
- - name: Verify package
137
+ ${siblingLinkCommands ? ` - name: Link local Treeseed dependencies
138
+ run: |
139
+ ${siblingLinkCommands.split("\n").map((line) => ` ${line}`).join("\n")}
140
+
141
+ ` : ""} - name: Verify package
121
142
  run: npm run verify:direct
122
143
  `,
123
144
  "utf8"
@@ -752,20 +752,24 @@ function findAutoResumableReleaseRun(root, branch, rootRepo, packageReports) {
752
752
  if (journal.command !== "release" || !journal.resumable || journal.session.branchName !== STAGING_BRANCH) {
753
753
  return false;
754
754
  }
755
+ const releasePlan = stringRecord(journal.steps.find((step) => step.id === "release-plan")?.data);
756
+ const nextStep = nextPendingJournalStep(journal);
755
757
  if (releaseRunHasCompletedMutation(journal)) {
758
+ if (nextStep?.id === "release-root" && releasePlanHead(releasePlan ?? {}, rootRepo.name) !== rootRepo.commitSha) {
759
+ return false;
760
+ }
756
761
  return true;
757
762
  }
758
- const releasePlan = stringRecord(journal.steps.find((step) => step.id === "release-plan")?.data);
759
763
  return releasePlan ? releasePlanMatchesCurrentHeads(releasePlan, rootRepo, packageReports) : true;
760
764
  }) ?? null;
761
765
  }
762
- async function executeJournalStep(root, runId, stepId, action) {
766
+ async function executeJournalStep(root, runId, stepId, action, options = {}) {
763
767
  const current = readWorkflowRunJournal(root, runId);
764
768
  const step = current?.steps.find((entry) => entry.id === stepId) ?? null;
765
769
  if (!current || !step) {
766
770
  throw new Error(`Unknown workflow step "${stepId}" for run ${runId}.`);
767
771
  }
768
- if (step.status === "completed") {
772
+ if (step.status === "completed" && !options.rerunCompleted) {
769
773
  return step.data ?? null;
770
774
  }
771
775
  const data = await Promise.resolve(action());
@@ -918,7 +922,7 @@ function runReleaseNpmInstall(repoDir, options = {}) {
918
922
  if (shouldSkipReleaseInstall()) {
919
923
  return { status: "skipped", reason: "stubbed" };
920
924
  }
921
- const args = repoDir === options.workspaceRoot ? ["install"] : ["install", "--workspaces=false"];
925
+ const args = repoDir === options.workspaceRoot ? ["install", "--package-lock-only", "--ignore-scripts"] : ["install", "--package-lock-only", "--ignore-scripts", "--workspaces=false"];
922
926
  run("npm", args, { cwd: repoDir });
923
927
  return { status: "completed", reason: null };
924
928
  }
@@ -976,7 +980,7 @@ function buildReleasePlanSnapshot(input) {
976
980
  plannedPublishWaits: input.packageSelection.selected.map((name) => ({
977
981
  name,
978
982
  workflow: "publish.yml",
979
- branch: PRODUCTION_BRANCH,
983
+ branch: String(plannedVersions[name] ?? PRODUCTION_BRANCH),
980
984
  status: "planned"
981
985
  })),
982
986
  touchedPackages: input.packageSelection.selected,
@@ -2806,7 +2810,7 @@ async function workflowRelease(helpers, input) {
2806
2810
  replacedDevReferences: replacedDevReferences2,
2807
2811
  releaseInstalls: releaseInstalls2
2808
2812
  };
2809
- });
2813
+ }, { rerunCompleted: workflowRun.resumed });
2810
2814
  const replacedDevReferences = Array.isArray(metadata?.replacedDevReferences) ? metadata.replacedDevReferences : [];
2811
2815
  const releaseInstalls = Array.isArray(metadata?.releaseInstalls) ? metadata.releaseInstalls : [];
2812
2816
  const releasedPackageDevTags = new Map(Object.entries(metadata?.releasedPackageDevTags ?? {}).map(([name, version]) => [name, String(version)]));
@@ -2842,7 +2846,7 @@ async function workflowRelease(helpers, input) {
2842
2846
  const publish = await waitForGitHubWorkflowCompletion(pkg.dir, {
2843
2847
  workflow: "publish.yml",
2844
2848
  headSha: mergeResult.commitSha,
2845
- branch: PRODUCTION_BRANCH
2849
+ branch: tagName
2846
2850
  });
2847
2851
  assertReleaseGitHubWorkflowSucceeded(pkg.name, publish);
2848
2852
  syncBranchWithOrigin(pkg.dir, STAGING_BRANCH);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@treeseed/sdk",
3
- "version": "0.6.8",
3
+ "version": "0.6.9",
4
4
  "description": "Shared Treeseed SDK for content-backed and D1-backed object models.",
5
5
  "license": "AGPL-3.0-only",
6
6
  "repository": {
@@ -34,7 +34,7 @@
34
34
  "setup:ci": "npm ci",
35
35
  "build": "npm run build:dist",
36
36
  "build:dist": "node ./scripts/run-ts.mjs ./scripts/build-dist.ts",
37
- "prepare": "npm run build:dist",
37
+ "prepare": "node ./scripts/prepare.mjs",
38
38
  "prepack": "npm run build:dist",
39
39
  "test": "npm run test:unit",
40
40
  "test:unit": "vitest run --config ./vitest.config.ts",
@@ -150,7 +150,14 @@ jobs:
150
150
  cache-dependency-path: __CACHE_DEPENDENCY_PATH__
151
151
 
152
152
  - name: Install dependencies
153
- run: npm ci --ignore-scripts
153
+ run: |
154
+ node -e "const fs = require('fs'); for (const file of ['packages/sdk/package.json', 'packages/core/package.json', 'packages/cli/package.json']) { if (!fs.existsSync(file)) continue; const p = JSON.parse(fs.readFileSync(file, 'utf8')); if (p.scripts) delete p.scripts.prepare; fs.writeFileSync(file, JSON.stringify(p, null, '\t') + '\n'); }"
155
+ npm ci
156
+ for dir in packages/sdk packages/core packages/cli; do
157
+ if test -d "${dir}/.git" || git -C "${dir}" rev-parse --git-dir >/dev/null 2>&1; then
158
+ git -C "${dir}" checkout -- package.json
159
+ fi
160
+ done
154
161
 
155
162
  - name: Verify workspace
156
163
  shell: bash
@@ -263,7 +270,14 @@ __WORKING_DIRECTORY_BLOCK__
263
270
  cache-dependency-path: __CACHE_DEPENDENCY_PATH__
264
271
 
265
272
  - name: Install dependencies
266
- run: npm ci --ignore-scripts
273
+ run: |
274
+ node -e "const fs = require('fs'); for (const file of ['packages/sdk/package.json', 'packages/core/package.json', 'packages/cli/package.json']) { if (!fs.existsSync(file)) continue; const p = JSON.parse(fs.readFileSync(file, 'utf8')); if (p.scripts) delete p.scripts.prepare; fs.writeFileSync(file, JSON.stringify(p, null, '\t') + '\n'); }"
275
+ npm ci
276
+ for dir in packages/sdk packages/core packages/cli; do
277
+ if test -d "${dir}/.git" || git -C "${dir}" rev-parse --git-dir >/dev/null 2>&1; then
278
+ git -C "${dir}" checkout -- package.json
279
+ fi
280
+ done
267
281
 
268
282
  - name: Install Railway CLI
269
283
  run: npm install -g @railway/cli
@@ -384,7 +398,14 @@ __WORKING_DIRECTORY_BLOCK__
384
398
  cache-dependency-path: __CACHE_DEPENDENCY_PATH__
385
399
 
386
400
  - name: Install dependencies
387
- run: npm ci --ignore-scripts
401
+ run: |
402
+ node -e "const fs = require('fs'); for (const file of ['packages/sdk/package.json', 'packages/core/package.json', 'packages/cli/package.json']) { if (!fs.existsSync(file)) continue; const p = JSON.parse(fs.readFileSync(file, 'utf8')); if (p.scripts) delete p.scripts.prepare; fs.writeFileSync(file, JSON.stringify(p, null, '\t') + '\n'); }"
403
+ npm ci
404
+ for dir in packages/sdk packages/core packages/cli; do
405
+ if test -d "${dir}/.git" || git -C "${dir}" rev-parse --git-dir >/dev/null 2>&1; then
406
+ git -C "${dir}" checkout -- package.json
407
+ fi
408
+ done
388
409
 
389
410
  - name: Install Railway CLI
390
411
  run: npm install -g @railway/cli
@@ -497,7 +518,14 @@ __WORKING_DIRECTORY_BLOCK__
497
518
  cache-dependency-path: __CACHE_DEPENDENCY_PATH__
498
519
 
499
520
  - name: Install dependencies
500
- run: npm ci --ignore-scripts
521
+ run: |
522
+ node -e "const fs = require('fs'); for (const file of ['packages/sdk/package.json', 'packages/core/package.json', 'packages/cli/package.json']) { if (!fs.existsSync(file)) continue; const p = JSON.parse(fs.readFileSync(file, 'utf8')); if (p.scripts) delete p.scripts.prepare; fs.writeFileSync(file, JSON.stringify(p, null, '\t') + '\n'); }"
523
+ npm ci
524
+ for dir in packages/sdk packages/core packages/cli; do
525
+ if test -d "${dir}/.git" || git -C "${dir}" rev-parse --git-dir >/dev/null 2>&1; then
526
+ git -C "${dir}" checkout -- package.json
527
+ fi
528
+ done
501
529
 
502
530
  - name: Install Railway CLI
503
531
  run: npm install -g @railway/cli
@@ -590,7 +618,14 @@ __WORKING_DIRECTORY_BLOCK__
590
618
  cache-dependency-path: __CACHE_DEPENDENCY_PATH__
591
619
 
592
620
  - name: Install dependencies
593
- run: npm ci --ignore-scripts
621
+ run: |
622
+ node -e "const fs = require('fs'); for (const file of ['packages/sdk/package.json', 'packages/core/package.json', 'packages/cli/package.json']) { if (!fs.existsSync(file)) continue; const p = JSON.parse(fs.readFileSync(file, 'utf8')); if (p.scripts) delete p.scripts.prepare; fs.writeFileSync(file, JSON.stringify(p, null, '\t') + '\n'); }"
623
+ npm ci
624
+ for dir in packages/sdk packages/core packages/cli; do
625
+ if test -d "${dir}/.git" || git -C "${dir}" rev-parse --git-dir >/dev/null 2>&1; then
626
+ git -C "${dir}" checkout -- package.json
627
+ fi
628
+ done
594
629
 
595
630
  - name: Download Treeseed deployment state
596
631
  uses: actions/download-artifact@v4