@dbx-tools/projen 0.9.18 → 0.9.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (105) hide show
  1. package/._LICENSE +0 -0
  2. package/._README.md +0 -0
  3. package/._index.ts +0 -0
  4. package/._package.json +0 -0
  5. package/README.md +72 -88
  6. package/index.ts +2 -7
  7. package/package.json +9 -5
  8. package/src/.__lazy-require.ts +0 -0
  9. package/src/.__release-platform.ts +0 -0
  10. package/src/.__rust-project.ts +0 -0
  11. package/src/.__rust-release-workflow.ts +0 -0
  12. package/src/.__task-command.ts +0 -0
  13. package/src/._barrels.ts +0 -0
  14. package/src/._bun-app.ts +0 -0
  15. package/src/._bun-workflow.ts +0 -0
  16. package/src/._clean.ts +0 -0
  17. package/src/._codegen.ts +0 -0
  18. package/src/._dbx-tools-config.ts +0 -0
  19. package/src/._engine-root.ts +0 -0
  20. package/src/._generated.ts +0 -0
  21. package/src/._mixin.ts +0 -0
  22. package/src/._module-exports.ts +0 -0
  23. package/src/._openapi.ts +0 -0
  24. package/src/._packages.ts +0 -0
  25. package/src/._pnpm-workspace.ts +0 -0
  26. package/src/._project-js.ts +0 -0
  27. package/src/._project-predicate.ts +0 -0
  28. package/src/._project-py.ts +0 -0
  29. package/src/._project-rs.ts +0 -0
  30. package/src/._project.ts +0 -0
  31. package/src/._projen-version.ts +0 -0
  32. package/src/._publish.ts +0 -0
  33. package/src/._release-catalog.ts +0 -0
  34. package/src/._release-dispatch.ts +0 -0
  35. package/src/._release-github.ts +0 -0
  36. package/src/._release.ts +0 -0
  37. package/src/._scaffold.ts +0 -0
  38. package/src/._tags.ts +0 -0
  39. package/src/._tsconfig.ts +0 -0
  40. package/src/._uniffi.ts +0 -0
  41. package/src/._vscode.ts +0 -0
  42. package/src/._watch.ts +0 -0
  43. package/src/._workspace-lock.ts +0 -0
  44. package/src/._workspace-version.ts +0 -0
  45. package/src/_rust-release-workflow.ts +18 -582
  46. package/src/_task-command.ts +75 -0
  47. package/src/project-js.ts +2 -42
  48. package/src/project-py.ts +60 -162
  49. package/src/project-rs.ts +4 -100
  50. package/src/release-catalog.ts +37 -203
  51. package/src/release.ts +96 -775
  52. package/src/scaffold.ts +2 -6
  53. package/src/workspace-version.ts +9 -121
  54. package/tasks/._barrels.ts +0 -0
  55. package/tasks/._bump.ts +0 -0
  56. package/tasks/._clean.ts +0 -0
  57. package/tasks/._compile-workspace.ts +0 -0
  58. package/tasks/._local-publish.ts +0 -0
  59. package/tasks/._openapi.ts +0 -0
  60. package/tasks/._projenrc.ts +0 -0
  61. package/tasks/._publish-npm.ts +0 -0
  62. package/tasks/._publish-python.ts +0 -0
  63. package/tasks/._publish-uniffi-local.ts +0 -0
  64. package/tasks/._publish.ts +0 -0
  65. package/tasks/._python-registry.ts +0 -0
  66. package/tasks/._release-assets.ts +0 -0
  67. package/tasks/._release-candidate.ts +0 -0
  68. package/tasks/._release-manifest.ts +0 -0
  69. package/tasks/._release-pr.ts +0 -0
  70. package/tasks/._release-summary.ts +0 -0
  71. package/tasks/._release-version.ts +0 -0
  72. package/tasks/._rust.ts +0 -0
  73. package/tasks/._sync.ts +0 -0
  74. package/tasks/._uniffi-python.d.ts +0 -0
  75. package/tasks/._uniffi-python.js +0 -0
  76. package/tasks/._uniffi-release.mjs +0 -0
  77. package/tasks/._uniffi.ts +0 -0
  78. package/tasks/._version-check.ts +0 -0
  79. package/tasks/bump.ts +17 -34
  80. package/tasks/local-publish.ts +124 -117
  81. package/tasks/publish-npm.ts +125 -41
  82. package/tasks/publish-python.ts +79 -106
  83. package/tasks/publish-uniffi-local.ts +129 -84
  84. package/tasks/publish.ts +44 -139
  85. package/tasks/python-registry.ts +2 -9
  86. package/tasks/release-assets.ts +56 -65
  87. package/tasks/release-candidate.ts +416 -0
  88. package/tasks/release-manifest.ts +273 -0
  89. package/tasks/release-pr.ts +312 -369
  90. package/tasks/release-summary.ts +8 -10
  91. package/tasks/release-version.ts +23 -2
  92. package/tasks/rust.ts +8 -13
  93. package/tasks/uniffi-python.d.ts +2 -3
  94. package/tasks/uniffi-python.js +7 -20
  95. package/tasks/uniffi-release.mjs +19 -13
  96. package/tasks/uniffi.ts +15 -24
  97. package/tasks/version-check.ts +24 -56
  98. package/src/release-plan.ts +0 -269
  99. package/src/release-please.ts +0 -242
  100. package/tasks/release-bootstrap.ts +0 -59
  101. package/tasks/release-plan.ts +0 -89
  102. package/tasks/release-please.ts +0 -162
  103. package/tasks/release-request.ts +0 -166
  104. package/tasks/release-summary-units.ts +0 -75
  105. package/tasks/stamp-python.ts +0 -13
@@ -1,25 +1,21 @@
1
1
  /**
2
2
  * Cross-language release-unit ownership and dependency graph.
3
3
  *
4
- * The catalog is the version lookup and release-planning seam shared by every
5
- * generated project. Fixed mode preserves the root VERSION contract while the
6
- * same registrations and graph are exercised before independent versions are
7
- * enabled.
4
+ * The catalog owns cross-language package identity, release grouping, and
5
+ * publication order. Every registered project reads its version from the root
6
+ * VERSION file.
8
7
  *
9
8
  * @module
10
9
  */
11
10
 
12
- import { execFileSync } from "node:child_process";
13
11
  import { createHash } from "node:crypto";
14
12
  import { existsSync, readFileSync, readdirSync } from "node:fs";
15
13
  import { basename, join, relative, resolve } from "node:path";
16
- import { Component, JsonFile, type Project } from "projen";
14
+ import { Component, type Project } from "projen";
15
+ import { probeTaskCommand } from "./_task-command.ts";
17
16
  import { toPosix } from "./packages.ts";
18
17
  import { readWorkspaceVersion } from "./workspace-version.ts";
19
18
 
20
- /** Version source used by the generated workspace. */
21
- export type DBXToolsVersioningMode = "fixed" | "independent";
22
-
23
19
  /** Runtime family represented by a release project. */
24
20
  export type ReleaseProjectLanguage = "javascript" | "python" | "rust";
25
21
 
@@ -51,11 +47,6 @@ export interface ReleaseUnitRule {
51
47
 
52
48
  /** Root catalog configuration. */
53
49
  export interface DBXToolsReleaseCatalogOptions {
54
- readonly mode?: DBXToolsVersioningMode;
55
- readonly manifestFile?: string;
56
- readonly graphFile?: string;
57
- readonly releasePleaseConfigFile?: string;
58
- readonly bootstrapSha?: string;
59
50
  readonly units?: readonly ReleaseUnitRule[];
60
51
  readonly externalProjects?: readonly ExternalReleaseProjectRegistration[];
61
52
  }
@@ -151,7 +142,6 @@ export interface ReleaseUnit {
151
142
  /** Generated execution graph consumed by validation and release tasks. */
152
143
  export interface ReleaseUnitGraph {
153
144
  readonly schemaVersion: 1;
154
- readonly mode: DBXToolsVersioningMode;
155
145
  readonly units: readonly ReleaseUnit[];
156
146
  readonly projects: readonly ReleaseProjectNode[];
157
147
  readonly artifacts: readonly ReleaseArtifact[];
@@ -219,22 +209,6 @@ export function defaultPublishOrder(kind: ReleaseEdgeKind): boolean {
219
209
  return !["test", "development"].includes(kind);
220
210
  }
221
211
 
222
- /** Read one component version, falling back to fixed workspace compatibility. */
223
- export function readReleaseUnitVersion(
224
- root: string,
225
- component: string,
226
- manifestFile = ".release-please-manifest.json",
227
- ): string {
228
- const path = resolve(root, manifestFile);
229
- if (!existsSync(path)) return readWorkspaceVersion(root);
230
- const parsed = JSON.parse(readFileSync(path, "utf8")) as Record<string, unknown>;
231
- const version = parsed[`.release-units/${component}`];
232
- if (typeof version !== "string") {
233
- throw new Error(`Release Please manifest is missing .release-units/${component}`);
234
- }
235
- return version;
236
- }
237
-
238
212
  /**
239
213
  * Root release catalog shared by every generated language project.
240
214
  *
@@ -243,12 +217,6 @@ export function readReleaseUnitVersion(
243
217
  * after project construction.
244
218
  */
245
219
  export class DBXToolsReleaseCatalog extends Component {
246
- readonly mode: DBXToolsVersioningMode;
247
- readonly manifestFile: string;
248
- readonly graphFile: string;
249
- readonly releasePleaseConfigFile: string;
250
- readonly bootstrapSha?: string;
251
-
252
220
  private readonly projects = new Map<Project, RegisteredProject>();
253
221
  private readonly externalProjects = new Map<string, ExternalReleaseProjectRegistration>();
254
222
  private readonly artifacts = new Map<string, RegisteredArtifact>();
@@ -257,37 +225,11 @@ export class DBXToolsReleaseCatalog extends Component {
257
225
  dependency: ReleaseDependencyInput;
258
226
  }> = [];
259
227
  private readonly rules: ReleaseUnitRule[];
260
- private readonly markerFiles = new Set<string>();
261
228
 
262
229
  constructor(project: Project, options: DBXToolsReleaseCatalogOptions = {}) {
263
230
  super(project);
264
- this.mode = options.mode ?? "fixed";
265
- this.manifestFile = options.manifestFile ?? ".release-please-manifest.json";
266
- this.graphFile = options.graphFile ?? ".projen/release-units.json";
267
- this.releasePleaseConfigFile = options.releasePleaseConfigFile ?? "release-please-config.json";
268
- this.bootstrapSha = options.bootstrapSha;
269
231
  this.rules = [...(options.units ?? [])];
270
232
  for (const external of options.externalProjects ?? []) this.registerExternalProject(external);
271
- if (this.mode === "independent") {
272
- new JsonFile(project, this.graphFile, {
273
- marker: false,
274
- readonly: true,
275
- obj: () => this.graph(),
276
- });
277
- new JsonFile(project, this.releasePleaseConfigFile, {
278
- marker: false,
279
- readonly: true,
280
- obj: () => this.releasePleaseConfig(),
281
- });
282
- project
283
- .addTask("release:bootstrap", {
284
- description: "Bootstrap Release Please state from release units",
285
- })
286
- .exec("bun node_modules/@dbx-tools/projen/tasks/release-bootstrap.ts");
287
- } else {
288
- project.tryRemoveFile(this.graphFile);
289
- project.tryRemoveFile(this.releasePleaseConfigFile);
290
- }
291
233
  }
292
234
 
293
235
  /** Register one attached Projen project. */
@@ -349,10 +291,7 @@ export class DBXToolsReleaseCatalog extends Component {
349
291
  versionFor(project: Project): string {
350
292
  const registered = this.projects.get(project);
351
293
  if (!registered) throw new Error(`Release project is not registered: ${project.outdir}`);
352
- if (!resolveValue(registered.registration.publish ?? true)) {
353
- return this.mode === "independent" ? "0.0.0" : readWorkspaceVersion(this.project.outdir);
354
- }
355
- return this.versionForUnit(this.unitFor(registered));
294
+ return this.workspaceVersion();
356
295
  }
357
296
 
358
297
  /** Stable unit, component, and version identity for a registered project. */
@@ -372,58 +311,23 @@ export class DBXToolsReleaseCatalog extends Component {
372
311
  }
373
312
 
374
313
  /** Current version for a project selected by its repository-relative path. */
375
- versionForPath(path: string): string {
376
- const normalized = toPosix(path).replace(/^\.\//, "") || ".";
377
- const attached = [...this.projects.values()].find(
378
- (registered) => this.projectId(registered.project) === normalized,
379
- );
380
- if (attached) return this.versionFor(attached.project);
381
- const external = this.externalProjects.get(normalized);
382
- if (!external) {
383
- if (this.mode === "fixed") return readWorkspaceVersion(this.project.outdir);
384
- throw new Error(`Release project path is not registered: ${normalized}`);
385
- }
386
- if (external.publish === false) {
387
- return this.mode === "independent" ? "0.0.0" : readWorkspaceVersion(this.project.outdir);
388
- }
389
- const unit =
390
- external.unit ??
391
- this.rules.find(
392
- (rule) =>
393
- rule.projectPaths?.includes(normalized) ||
394
- rule.projectIdentities?.includes(external.identity),
395
- )?.id ??
396
- defaultReleaseUnitId(external.language, external.identity);
397
- return this.versionForUnit(unit);
314
+ versionForPath(_path: string): string {
315
+ return this.workspaceVersion();
398
316
  }
399
317
 
400
318
  /** Resolve a version before the corresponding Projen project is constructed. */
401
319
  versionForRegistration(
402
- language: ReleaseProjectLanguage,
403
- identity: string,
404
- path: string,
405
- unit?: string,
320
+ _language: ReleaseProjectLanguage,
321
+ _identity: string,
322
+ _path: string,
323
+ _unit?: string,
406
324
  ): string {
407
- const normalized = toPosix(path).replace(/^\.\//, "") || ".";
408
- const resolvedUnit =
409
- unit ??
410
- this.rules.find(
411
- (rule) =>
412
- rule.projectPaths?.includes(normalized) || rule.projectIdentities?.includes(identity),
413
- )?.id ??
414
- defaultReleaseUnitId(language, identity);
415
- return this.versionForUnit(resolvedUnit);
325
+ return this.workspaceVersion();
416
326
  }
417
327
 
418
328
  /** Current version for a release unit. */
419
- versionForUnit(unit: string): string {
420
- if (this.mode === "fixed") return readWorkspaceVersion(this.project.outdir);
421
- const component = this.rules.find((rule) => rule.id === unit)?.component ?? unit;
422
- const versions = this.readIndependentVersions();
423
- const key = `.release-units/${component}`;
424
- const version = versions[key];
425
- if (!version) throw new Error(`Release Please manifest is missing ${key}`);
426
- return version;
329
+ versionForUnit(_unit: string): string {
330
+ return this.workspaceVersion();
427
331
  }
428
332
 
429
333
  /** Build and validate the normalized release graph. */
@@ -510,7 +414,7 @@ export class DBXToolsReleaseCatalog extends Component {
510
414
  return {
511
415
  id,
512
416
  component: unit.component,
513
- version: this.versionForResolvedUnit(id, unit.component),
417
+ version: this.workspaceVersion(),
514
418
  projects: [...unit.projects].sort(),
515
419
  artifacts: [...unit.artifacts].sort(),
516
420
  sourceHash: hashStrings(hashes),
@@ -520,7 +424,6 @@ export class DBXToolsReleaseCatalog extends Component {
520
424
 
521
425
  return {
522
426
  schemaVersion: 1,
523
- mode: this.mode,
524
427
  units: serializedUnits,
525
428
  projects,
526
429
  artifacts,
@@ -529,55 +432,6 @@ export class DBXToolsReleaseCatalog extends Component {
529
432
  };
530
433
  }
531
434
 
532
- /** Generated Release Please manifest-mode configuration. */
533
- releasePleaseConfig(): Record<string, unknown> {
534
- return {
535
- $schema:
536
- "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json",
537
- "release-type": "simple",
538
- "separate-pull-requests": false,
539
- "include-component-in-tag": true,
540
- "include-v-in-tag": true,
541
- ...(this.bootstrapSha ? { "bootstrap-sha": this.bootstrapSha } : {}),
542
- packages: Object.fromEntries(
543
- this.graph().units.map((unit) => [
544
- `.release-units/${unit.component}`,
545
- {
546
- "release-type": "simple",
547
- "package-name": unit.id,
548
- component: unit.component,
549
- "changelog-path": "CHANGELOG.md",
550
- "version-file": "version.txt",
551
- },
552
- ]),
553
- ),
554
- };
555
- }
556
-
557
- public override preSynthesize(): void {
558
- const graph = this.graph();
559
- if (this.mode === "fixed") return;
560
- for (const unit of graph.units) {
561
- const path = `.release-units/${unit.component}/source.json`;
562
- if (this.markerFiles.has(path)) continue;
563
- this.markerFiles.add(path);
564
- new JsonFile(this.project, path, {
565
- marker: false,
566
- readonly: true,
567
- obj: () => {
568
- const current = this.graph().units.find((candidate) => candidate.id === unit.id);
569
- if (!current) throw new Error(`Release unit disappeared during synthesis: ${unit.id}`);
570
- return {
571
- schemaVersion: 1,
572
- unit: current.id,
573
- component: current.component,
574
- sourceHash: current.sourceHash,
575
- };
576
- },
577
- });
578
- }
579
- }
580
-
581
435
  private projectNode(registered: RegisteredProject): ReleaseProjectNode {
582
436
  const identity = resolveValue(registered.registration.identity);
583
437
  const publish = resolveValue(registered.registration.publish ?? true);
@@ -651,6 +505,10 @@ export class DBXToolsReleaseCatalog extends Component {
651
505
  return toPosix(relative(this.project.outdir, project.outdir)) || ".";
652
506
  }
653
507
 
508
+ private workspaceVersion(): string {
509
+ return readWorkspaceVersion(this.project.outdir);
510
+ }
511
+
654
512
  private dependencies(
655
513
  byIdentity: ReadonlyMap<string, readonly ReleaseProjectNode[]>,
656
514
  byId: ReadonlyMap<string, ReleaseProjectNode>,
@@ -719,31 +577,6 @@ export class DBXToolsReleaseCatalog extends Component {
719
577
  [a.from, a.to, a.kind].join("\0").localeCompare([b.from, b.to, b.kind].join("\0")),
720
578
  );
721
579
  }
722
-
723
- private versionForResolvedUnit(unit: string, component: string): string {
724
- if (this.mode === "fixed") return readWorkspaceVersion(this.project.outdir);
725
- const key = `.release-units/${component}`;
726
- const version = this.readIndependentVersions()[key];
727
- if (!version) throw new Error(`Release Please manifest is missing ${key} for ${unit}`);
728
- return version;
729
- }
730
-
731
- private readIndependentVersions(): Record<string, string> {
732
- const path = resolve(this.project.outdir, this.manifestFile);
733
- if (!existsSync(path)) throw new Error(`Independent version manifest not found: ${path}`);
734
- const parsed = JSON.parse(readFileSync(path, "utf8")) as unknown;
735
- if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
736
- throw new Error(`Independent version manifest must contain an object: ${path}`);
737
- }
738
- return Object.fromEntries(
739
- Object.entries(parsed).map(([key, value]) => {
740
- if (typeof value !== "string" || !/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/.test(value)) {
741
- throw new Error(`Invalid independent version for ${key}: ${String(value)}`);
742
- }
743
- return [key, value];
744
- }),
745
- );
746
- }
747
580
  }
748
581
 
749
582
  /** Publish dependency batches ordered with dependencies before dependents. */
@@ -795,22 +628,23 @@ function hashPaths(root: string, paths: readonly string[]): string {
795
628
 
796
629
  function gitReleaseFiles(root: string, paths: readonly string[]): string[] | undefined {
797
630
  const relativePaths = paths.map((path) => toPosix(relative(root, path)));
798
- try {
799
- const output = execFileSync(
800
- "git",
801
- ["ls-files", "--cached", "--others", "--exclude-standard", "-z", "--", ...relativePaths],
802
- { cwd: root, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] },
803
- );
804
- return output
805
- .split("\0")
806
- .filter(Boolean)
807
- .map((path) => resolve(root, path))
808
- .filter((path) => existsSync(path))
809
- .filter((path) => paths.some((sourcePath) => trackedReleaseFile(sourcePath, path)))
810
- .sort();
811
- } catch {
812
- return undefined;
813
- }
631
+ const output = probeTaskCommand(root, "git", [
632
+ "ls-files",
633
+ "--cached",
634
+ "--others",
635
+ "--exclude-standard",
636
+ "-z",
637
+ "--",
638
+ ...relativePaths,
639
+ ]);
640
+ if (output === undefined) return undefined;
641
+ return output
642
+ .split("\0")
643
+ .filter(Boolean)
644
+ .map((path) => resolve(root, path))
645
+ .filter((path) => existsSync(path))
646
+ .filter((path) => paths.some((sourcePath) => trackedReleaseFile(sourcePath, path)))
647
+ .sort();
814
648
  }
815
649
 
816
650
  function trackedReleaseFile(sourcePath: string, path: string): boolean {