@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
@@ -12,6 +12,13 @@ export interface CaptureTaskCommandOptions extends TaskCommandOptions {
12
12
  readonly check?: boolean;
13
13
  }
14
14
 
15
+ export type ProbeTaskCommandOptions = TaskCommandOptions;
16
+
17
+ export interface LoggedTaskCommandOptions extends TaskCommandOptions {
18
+ /** Receive normalized stdout and stderr lines after the command exits. */
19
+ readonly onLine: (line: string) => void;
20
+ }
21
+
15
22
  function executable(command: string): string {
16
23
  return command === "bun" && process.versions.bun ? process.execPath : command;
17
24
  }
@@ -69,6 +76,47 @@ export function captureTaskCommand(
69
76
  return result.exitCode === 0 ? (result.stdout?.trim() ?? "") : "";
70
77
  }
71
78
 
79
+ /** Capture trimmed stdout while preserving command failure as `undefined`. */
80
+ export function probeTaskCommand(
81
+ cwd: string,
82
+ command: string,
83
+ args: readonly string[],
84
+ options: ProbeTaskCommandOptions = {},
85
+ ): string | undefined {
86
+ const result = exec.spawnSync(executable(command), [...args], {
87
+ cwd,
88
+ env: options.env,
89
+ stdout: "capture",
90
+ stderr: options.stderr ?? "ignore",
91
+ stdin: "ignore",
92
+ check: false,
93
+ });
94
+ return result.exitCode === 0 ? (result.stdout?.trim() ?? "") : undefined;
95
+ }
96
+
97
+ /** Run a checked command while routing captured output through one line policy. */
98
+ export function runLoggedTaskCommand(
99
+ cwd: string,
100
+ command: string,
101
+ args: readonly string[],
102
+ options: LoggedTaskCommandOptions,
103
+ ): void {
104
+ const result = exec.spawnSync(executable(command), [...args], {
105
+ cwd,
106
+ env: options.env,
107
+ stdout: "capture",
108
+ stderr: "capture",
109
+ stdin: "ignore",
110
+ check: false,
111
+ });
112
+ for (const line of [...result.stdoutLines, ...result.stderrLines]) {
113
+ if (line) options.onLine(line);
114
+ }
115
+ if (result.exitCode !== 0) {
116
+ throw new Error(`${command} exited with ${result.exitCode}`);
117
+ }
118
+ }
119
+
72
120
  /** Whether a quiet, non-interactive command exits successfully. */
73
121
  export function taskCommandSucceeds(
74
122
  cwd: string,
@@ -87,3 +135,30 @@ export function taskCommandSucceeds(
87
135
  }).exitCode === 0
88
136
  );
89
137
  }
138
+
139
+ /** Run Git with the shared non-interactive checked command policy. */
140
+ export function runGitTaskCommand(
141
+ cwd: string,
142
+ args: readonly string[],
143
+ options: TaskCommandOptions = {},
144
+ ): void {
145
+ runTaskCommand(cwd, "git", args, options);
146
+ }
147
+
148
+ /** Capture Git output, returning an empty string for optional offline probes. */
149
+ export function captureGitTaskCommand(
150
+ cwd: string,
151
+ args: readonly string[],
152
+ options: CaptureTaskCommandOptions = {},
153
+ ): string {
154
+ return captureTaskCommand(cwd, "git", args, options);
155
+ }
156
+
157
+ /** Whether a quiet Git command succeeds. */
158
+ export function gitTaskCommandSucceeds(
159
+ cwd: string,
160
+ args: readonly string[],
161
+ options: Pick<TaskCommandOptions, "env"> = {},
162
+ ): boolean {
163
+ return taskCommandSucceeds(cwd, "git", args, options);
164
+ }
package/src/project-js.ts CHANGED
@@ -15,7 +15,7 @@ import { type IConstruct } from "constructs";
15
15
  import { Component, IgnoreFile, Project, type TaskOptions, javascript, typescript } from "projen";
16
16
  import { BuildWorkflow } from "projen/lib/build";
17
17
  import { AutoMerge } from "projen/lib/github";
18
- import { JobPermission, type JobStep, type Triggers } from "projen/lib/github/workflows-model";
18
+ import { JobPermission, type JobStep } from "projen/lib/github/workflows-model";
19
19
  import type { ReleaseProjectOptions } from "projen/lib/release";
20
20
  import { mixin } from "..";
21
21
  import { generateBarrels } from "./barrels.ts";
@@ -44,7 +44,6 @@ import { PROJEN_VERSION } from "./projen-version.ts";
44
44
  import { applyCompiledPublish } from "./publish.ts";
45
45
  import {
46
46
  DBXToolsReleaseCatalog,
47
- type DBXToolsVersioningMode,
48
47
  type ExternalReleaseProjectRegistration,
49
48
  type ReleaseDependencyInput,
50
49
  type ReleaseUnitRule,
@@ -628,18 +627,12 @@ export type DBXToolsJavaScriptProjectOptions = CommonProjectOptions &
628
627
  readonly releaseValidationTasks?: readonly string[];
629
628
  /** Optional AI-generated release summary. Defaults to enabled. */
630
629
  readonly releaseSummary?: boolean | ReleaseSummaryOptions;
631
- /** Existing source branch safely fast-forwarded after an independent release. */
632
- readonly releaseSyncBranch?: string | false;
633
630
  /** Set to `false` to omit normal npm workspace publication. */
634
631
  readonly nodeRelease?: boolean;
635
632
  /** Unified dbx-tools release workflow, or no release surface. Defaults to `dbx-tools`. */
636
633
  readonly releaseMode?: DBXToolsReleaseMode;
637
- /** Package version ownership mode. Defaults to `fixed`. */
638
- readonly versioningMode?: DBXToolsVersioningMode;
639
634
  /** Explicit stable release-unit grouping rules. */
640
635
  readonly releaseUnits?: readonly ReleaseUnitRule[];
641
- /** Commit before the first independent Release Please history. */
642
- readonly releaseBootstrapSha?: string;
643
636
  /** Publishable workspace members synthesized outside the attached project tree. */
644
637
  readonly externalReleaseProjects?: readonly ExternalReleaseProjectRegistration[];
645
638
  /** Prefix for generated release tags. Defaults to `v`. */
@@ -723,10 +716,8 @@ export class DBXToolsNodeProject
723
716
  // (its build phase installs with pnpm and reads catalog + `allowBuilds`).
724
717
  pnpmWorkspace.attachWorkspaceFile(this);
725
718
  this.releaseCatalog = new DBXToolsReleaseCatalog(this, {
726
- mode: options.versioningMode,
727
719
  units: options.releaseUnits,
728
720
  externalProjects: options.externalReleaseProjects,
729
- bootstrapSha: options.releaseBootstrapSha,
730
721
  });
731
722
  registerJavaScriptReleaseProject(this);
732
723
  this.package.addField("version", () => this.releaseCatalog.versionFor(this));
@@ -862,10 +853,8 @@ export class DBXToolsTypeScriptProject
862
853
  (parent instanceof DBXToolsNodeProject || parent instanceof DBXToolsTypeScriptProject)
863
854
  ? parent.releaseCatalog
864
855
  : new DBXToolsReleaseCatalog(this, {
865
- mode: options.versioningMode,
866
856
  units: options.releaseUnits,
867
857
  externalProjects: options.externalReleaseProjects,
868
- bootstrapSha: options.releaseBootstrapSha,
869
858
  });
870
859
  // Pairs with `jsx` in SHARED_COMPILER_OPTIONS: projen's default `include` is
871
860
  // `src/**/*.ts` only, which silently omits a `.tsx` file from the program
@@ -937,35 +926,7 @@ function configureBuildWorkflow(
937
926
  });
938
927
  validation.exec("bunx projen default");
939
928
  validation.exec("bun run compile");
940
- const configured = {
941
- ...options.buildWorkflowOptions,
942
- ...(!options.buildWorkflowOptions?.workflowTriggers &&
943
- project.releaseCatalog.mode === "independent"
944
- ? {
945
- workflowTriggers: {
946
- pullRequest: {
947
- types: ["opened", "synchronize", "reopened", "closed"],
948
- paths: [
949
- "**",
950
- "!.projen/release-units.json",
951
- "!.release-please-manifest.json",
952
- "!.release-units/**",
953
- "!.release-notes/**",
954
- "!docs/releases/**",
955
- "!Cargo.lock",
956
- "!**/Cargo.toml",
957
- "!**/package.json",
958
- "!**/pyproject.toml",
959
- "!**/index.ts",
960
- "!packages/js/node/rust-binary/src/_release-binaries.ts",
961
- "!packages/js/node/appkit-graphiti/src/_python-runtime.ts",
962
- ],
963
- },
964
- workflowDispatch: {},
965
- } satisfies Triggers,
966
- }
967
- : {}),
968
- };
929
+ const configured = { ...options.buildWorkflowOptions };
969
930
  const compatibility = nodeWorkflowCompatibility(project);
970
931
  const configuredRunner = configured.runsOn !== undefined || configured.runsOnGroup !== undefined;
971
932
  const workflow = new BuildWorkflow(project, {
@@ -1620,7 +1581,6 @@ function initProject(
1620
1581
  pythonRoot: options.releasePythonRoot,
1621
1582
  validationTasks: options.releaseValidationTasks,
1622
1583
  summary: options.releaseSummary,
1623
- syncBranch: options.releaseSyncBranch,
1624
1584
  });
1625
1585
  }
1626
1586
  }
package/src/project-py.ts CHANGED
@@ -4,14 +4,17 @@ import { stringUtils } from "@dbx-tools/shared-core";
4
4
  import { Component, License, TextFile, type Project, javascript, python, vscode } from "projen";
5
5
  import { JobPermission } from "projen/lib/github/workflows-model";
6
6
  import { BUN_VERSION, bunCacheRestoreSteps, bunCacheSaveStep } from "./bun-workflow.ts";
7
- import { DBX_TOOLS_LICENSE, projectReleaseBranch, projectRepositoryUrl } from "./project-js.ts";
7
+ import { DBX_TOOLS_LICENSE, projectRepositoryUrl } from "./project-js.ts";
8
8
  import { isDBXToolsJavaScriptProject } from "./project-predicate.ts";
9
9
  import type { DBXToolsProject, DBXToolsProjectOptions } from "./project.ts";
10
- import { RELEASE_TAG, RELEASE_VERSION, releaseSourceSteps } from "./release-dispatch.ts";
11
10
  import {
12
- independentReleaseSetupSteps,
13
- registerIndependentPublicationJob,
14
- releaseArtifactSteps,
11
+ RELEASE_SHA,
12
+ RELEASE_TAG,
13
+ RELEASE_VERSION,
14
+ releaseSourceSteps,
15
+ } from "./release-dispatch.ts";
16
+ import {
17
+ releaseTagPattern,
15
18
  releasePublishCondition,
16
19
  releaseStageCondition,
17
20
  tryReleaseWorkflow,
@@ -109,8 +112,6 @@ const DEFAULT_DEV_DEPENDENCIES = [
109
112
  "ruff>=0.12,<1",
110
113
  ] as const;
111
114
 
112
- const quote = (value: string): string => JSON.stringify(value);
113
-
114
115
  /** Repository-relative path for a Python package directory. */
115
116
  export function pythonPackagePath(repository: PythonRepositoryOptions, directory: string): string {
116
117
  return `${repository.root ?? "packages/py"}/${directory}`;
@@ -274,10 +275,7 @@ export class DBXToolsPythonWorkspace extends Component {
274
275
  }));
275
276
  const resolvedOptions = { ...options, packages };
276
277
  this.requiresPython = options.requiresPython ?? ">=3.10";
277
- this.version =
278
- isDBXToolsJavaScriptProject()(project) && project.releaseCatalog.mode === "independent"
279
- ? "0.0.0"
280
- : readWorkspaceVersion(project.outdir);
278
+ this.version = readWorkspaceVersion(project.outdir);
281
279
  this.file = this.emitWorkspace(project, resolvedOptions, scope);
282
280
  this.packages = packages.map(
283
281
  (pkg) =>
@@ -457,100 +455,15 @@ export class DBXToolsPythonWorkspace extends Component {
457
455
  const uniffiPublications = this.uniffiPublications(options);
458
456
  const allPublications = [...publications, ...uniffiPublications];
459
457
  if (allPublications.length === 0) return;
460
- const usesRustArtifacts = uniffiPublications.length > 0;
461
458
  const workflow = tryReleaseWorkflow(project);
462
459
  if (!workflow) {
463
460
  throw new Error("Python release requires the root dbx-tools release mode");
464
461
  }
465
- if (project.releaseCatalog.mode === "independent") {
466
- workflow.addJob("build-python", {
467
- if: usesRustArtifacts
468
- ? "${{ always() && needs.release-plan.outputs.python == 'true' && needs.rust-build.result != 'failure' && needs.rust-build.result != 'cancelled' && (github.event_name == 'push' || inputs.stage == 'all' || inputs.stage == 'python') }}"
469
- : "${{ needs.release-plan.outputs.python == 'true' && (github.event_name == 'push' || inputs.stage == 'all' || inputs.stage == 'python') }}",
470
- needs: ["release-plan", ...(usesRustArtifacts ? ["rust-build"] : [])],
471
- runsOn: ["ubuntu-latest"],
472
- permissions: { actions: JobPermission.READ, contents: JobPermission.READ },
473
- timeoutMinutes: 20,
474
- env: { BUN_VERSION },
475
- steps: [
476
- ...independentReleaseSetupSteps(project),
477
- { name: "Setup uv", uses: "astral-sh/setup-uv@v7" },
478
- ...uniffiPublications.map((publication) => ({
479
- name: `Download ${publication.distribution} native wheels`,
480
- uses: "actions/download-artifact@v8",
481
- with: {
482
- pattern: `${publication.distribution}--*--python-wheel`,
483
- path: `dist/${publication.directory}`,
484
- "merge-multiple": true,
485
- },
486
- })),
487
- {
488
- name: "Build affected distributions",
489
- shell: "bash",
490
- run: publications
491
- .map(
492
- (publication) =>
493
- `if jq -e --arg package ${quote(publication.distribution)} '.pythonPackages[] | select(.identity == $package)' dist/release-plan.json >/dev/null; then uv build --package ${quote(publication.distribution)} --out-dir dist/${publication.directory}; fi`,
494
- )
495
- .join("\n"),
496
- },
497
- {
498
- name: "Validate affected distributions",
499
- run: "find dist -type f \\( -name '*.whl' -o -name '*.tar.gz' \\) -print0 | xargs -0 uvx twine check",
500
- },
501
- {
502
- name: "Upload distributions",
503
- uses: "actions/upload-artifact@v7",
504
- with: { name: "python-distributions", path: "dist", "retention-days": 7 },
505
- },
506
- ],
507
- });
508
- for (const publication of allPublications) {
509
- const dependencyJobs = (publication.dependencies ?? []).map(
510
- (dependency) => `publish-pypi-${dependency}`,
511
- );
512
- const dependencyCondition = dependencyJobs
513
- .map((job) => `needs.${job}.result != 'failure' && needs.${job}.result != 'cancelled'`)
514
- .join(" && ");
515
- workflow.addJob(`publish-pypi-${publication.directory}`, {
516
- if: `\${{ always() && needs.build-python.result == 'success' && contains(needs.release-plan.outputs.python_packages, '"identity":"${publication.distribution}"') && (github.event_name == 'push' || inputs.stage == 'all' || inputs.stage == 'python')${dependencyCondition ? ` && ${dependencyCondition}` : ""} }}`,
517
- needs: ["release-plan", "build-python", ...dependencyJobs],
518
- environment: {
519
- name: publication.environment,
520
- url:
521
- options.environmentUrl ??
522
- `https://pypi.org/project/${publication.distribution.replaceAll("_", "-")}/`,
523
- },
524
- runsOn: ["ubuntu-latest"],
525
- permissions: { idToken: JobPermission.WRITE },
526
- timeoutMinutes: 10,
527
- steps: [
528
- {
529
- name: "Download distributions",
530
- uses: "actions/download-artifact@v8",
531
- with: { name: "python-distributions", path: "dist" },
532
- },
533
- {
534
- name: `Publish ${publication.distribution} to PyPI`,
535
- uses: "pypa/gh-action-pypi-publish@release/v1",
536
- with: {
537
- "packages-dir": `dist/${publication.directory}`,
538
- "skip-existing": true,
539
- },
540
- },
541
- ],
542
- });
543
- registerIndependentPublicationJob(workflow, `publish-pypi-${publication.directory}`);
544
- }
545
- return;
546
- }
547
462
  workflow.addJob("build-python", {
548
- if: usesRustArtifacts
549
- ? "${{ always() && needs.verify-context.result == 'success' && needs.rust-assets.result == 'success' && (github.event_name == 'push' || inputs.stage == 'all' || inputs.stage == 'python') }}"
550
- : releaseStageCondition("python"),
551
- needs: ["verify-context", ...(usesRustArtifacts ? ["rust-assets"] : [])],
463
+ if: releaseStageCondition("python", ["needs.verify-context.result == 'success'"]),
464
+ needs: ["verify-context"],
552
465
  runsOn: ["ubuntu-latest"],
553
- permissions: { actions: JobPermission.READ, contents: JobPermission.READ },
466
+ permissions: { contents: JobPermission.READ },
554
467
  timeoutMinutes: 20,
555
468
  env: { BUN_VERSION },
556
469
  steps: [
@@ -561,76 +474,60 @@ export class DBXToolsPythonWorkspace extends Component {
561
474
  { name: "Setup uv", uses: "astral-sh/setup-uv@v7" },
562
475
  { name: "Install release helpers", run: "bun install" },
563
476
  bunCacheSaveStep(),
564
- ...uniffiPublications.flatMap((publication) =>
565
- releaseArtifactSteps({
566
- currentName: `Download ${publication.distribution} native wheels`,
567
- recoveredName: `Download recovered ${publication.distribution} native wheels`,
568
- pattern: `${publication.distribution}--*--python-wheel`,
569
- path: `dist/${publication.directory}`,
570
- condition: "needs.verify-context.outputs.build_mode == 'remote'",
571
- }),
572
- ),
573
- ...(uniffiPublications.length
574
- ? [
575
- {
576
- name: "Download locally built native wheels",
577
- if: "${{ needs.verify-context.outputs.build_mode == 'local' }}",
578
- env: { GH_TOKEN: "${{ github.token }}", RELEASE_TAG },
579
- shell: "bash",
580
- run: [
581
- "mkdir -p dist/local-wheels",
582
- 'gh release download "$RELEASE_TAG" --pattern "*.whl" --dir dist/local-wheels',
583
- ...uniffiPublications.flatMap((publication) => [
584
- `mkdir -p dist/${publication.directory}`,
585
- `cp dist/local-wheels/${publication.distribution.replaceAll("-", "_")}-*.whl dist/${publication.directory}/`,
586
- ]),
587
- ].join("\n"),
588
- },
589
- ]
590
- : []),
591
- {
592
- name: "Stamp workspace versions",
593
- env: { VERSION: RELEASE_VERSION },
594
- run: `bun node_modules/@dbx-tools/projen/tasks/stamp-python.ts "$VERSION" --root ${quote(this.repository.root)}`,
595
- },
596
477
  {
597
- name: "Build distributions",
598
- run: publications
599
- .map(
600
- (publication) =>
601
- `uv build --package ${publication.distribution} --out-dir dist/${publication.directory}`,
602
- )
603
- .join("\n"),
478
+ name: "Download approved Python distributions",
479
+ env: { GH_TOKEN: "${{ github.token }}", RELEASE_TAG },
480
+ run: [
481
+ "rm -rf dist/release-download",
482
+ "mkdir -p dist/release-download",
483
+ 'gh release download "$RELEASE_TAG" --pattern release-manifest.json --pattern SHA256SUMS --pattern "*.whl" --pattern "*.tar.gz" --dir dist/release-download',
484
+ ].join("\n"),
604
485
  },
605
- {
606
- name: "Validate distributions",
486
+ ...allPublications.map((publication) => ({
487
+ name: `Select ${publication.distribution} distributions`,
488
+ env: {
489
+ PACKAGE_NAME: publication.distribution.replaceAll("_", "-").toLowerCase(),
490
+ RELEASE_SHA,
491
+ RELEASE_TAG,
492
+ RELEASE_VERSION,
493
+ },
607
494
  run: [
608
- ...publications.map(
609
- (publication) =>
610
- `test "$(find dist/${publication.directory} -maxdepth 1 -type f \\( -name '*.whl' -o -name '*.tar.gz' \\) | wc -l | tr -d ' ')" -eq 2`,
611
- ),
612
- ...uniffiPublications.map(
613
- (publication) =>
614
- `find dist/${publication.directory} -maxdepth 1 -name '*.whl' -print -quit | grep -q .`,
615
- ),
616
- "find dist -type f \\( -name '*.whl' -o -name '*.tar.gz' \\) -print0 | xargs -0 uvx twine check",
495
+ "bun node_modules/@dbx-tools/projen/tasks/release-manifest.ts verify \\",
496
+ " --directory dist/release-download \\",
497
+ ' --tag "$RELEASE_TAG" \\',
498
+ ' --sha "$RELEASE_SHA" \\',
499
+ ' --version "$RELEASE_VERSION" \\',
500
+ " --kind pypi \\",
501
+ ' --package "$PACKAGE_NAME" \\',
502
+ ` --output dist/${publication.directory}`,
617
503
  ].join("\n"),
504
+ })),
505
+ {
506
+ name: "Validate approved distributions",
507
+ run: `find ${allPublications.map((publication) => `dist/${publication.directory}`).join(" ")} -type f \\( -name '*.whl' -o -name '*.tar.gz' \\) -print0 | xargs -0 uvx twine check`,
618
508
  },
619
509
  {
620
510
  name: "Upload distributions",
621
511
  uses: "actions/upload-artifact@v7",
622
- with: { name: "python-distributions", path: "dist", "retention-days": 7 },
512
+ with: {
513
+ name: "python-distributions",
514
+ path: allPublications.map((publication) => `dist/${publication.directory}`).join("\n"),
515
+ "retention-days": 7,
516
+ },
623
517
  },
624
518
  ],
625
519
  });
626
520
  for (const publication of allPublications) {
521
+ const dependencyJobs = (publication.dependencies ?? []).map(
522
+ (dependency) => `publish-pypi-${dependency}`,
523
+ );
627
524
  workflow.addJob(`publish-pypi-${publication.directory}`, {
628
- if: releasePublishCondition("python"),
629
- needs: [
630
- "verify-context",
631
- "build-python",
632
- ...(publication.dependencies ?? []).map((dependency) => `publish-pypi-${dependency}`),
633
- ],
525
+ if: releasePublishCondition("python", [
526
+ "needs.verify-context.result == 'success'",
527
+ "needs.build-python.result == 'success'",
528
+ ...dependencyJobs.map((job) => `needs['${job}'].result == 'success'`),
529
+ ]),
530
+ needs: ["verify-context", "build-python", ...dependencyJobs],
634
531
  environment: {
635
532
  name: publication.environment,
636
533
  url:
@@ -710,15 +607,16 @@ export class DBXToolsPythonWorkspace extends Component {
710
607
  ): void {
711
608
  const repository = this.githubRepository();
712
609
  const publications = this.trustedPublisherPublications(options);
713
- const releaseBranch = isDBXToolsJavaScriptProject()(project)
714
- ? projectReleaseBranch(project)
715
- : "main";
610
+ const releaseTag =
611
+ isDBXToolsJavaScriptProject()(project) && tryReleaseWorkflow(project)
612
+ ? releaseTagPattern(project)
613
+ : "v*";
716
614
  const linesBeforeAuthentication = [
717
615
  "# PyPI Trusted Publisher Setup Instructions",
718
616
  "",
719
617
  "Use the system browser to audit and configure the PyPI trusted publishers below.",
720
618
  "Do not use an in-app browser or embedded webview.",
721
- "Do not visit GitHub or use the GitHub API or CLI. Every required GitHub owner, repository, workflow, environment, and branch value is provided below and is authoritative.",
619
+ "Do not visit GitHub or use the GitHub API or CLI. Every required GitHub owner, repository, workflow, environment, and tag value is provided below and is authoritative.",
722
620
  "Use only PyPI pages for this task.",
723
621
  "",
724
622
  "## Audit and confirmation",
@@ -736,7 +634,7 @@ export class DBXToolsPythonWorkspace extends Component {
736
634
  "",
737
635
  "## GitHub environment policy",
738
636
  "",
739
- `- The supplied branch policy value is ${releaseBranch}.`,
637
+ `- The supplied deployment tag policy value is ${releaseTag}.`,
740
638
  "- Do not inspect or configure GitHub environments during this task.",
741
639
  "- GitHub environment administration is a separate task; use the supplied values only when comparing PyPI publisher entries.",
742
640
  "",
@@ -777,7 +675,7 @@ export class DBXToolsPythonWorkspace extends Component {
777
675
  `- Repository name: ${repository.name}`,
778
676
  "- Workflow name: release.yml",
779
677
  `- Environment name: ${publication.environment}`,
780
- `- GitHub environment branch: ${releaseBranch}`,
678
+ `- GitHub environment tag: ${releaseTag}`,
781
679
  ...(publication.artifacts ? [`- Artifacts: ${publication.artifacts}`] : []),
782
680
  "",
783
681
  ];
package/src/project-rs.ts CHANGED
@@ -19,19 +19,10 @@ import {
19
19
  type RustOpenApiOptions as RustOpenApiConfiguration,
20
20
  } from "./_rust-project.ts";
21
21
  import {
22
- rustAssetsJob,
23
22
  configureRustReleaseTask,
24
- independentRustCargoPublishJob,
25
- independentRustGitHubReleaseJob,
26
- independentRustNativeNpmPublishJob,
27
- independentRustNodeFacadePublishJob,
28
23
  orderRustBindings,
29
24
  planRustRelease,
30
- rustBuildJob,
31
25
  rustCargoPublishJob,
32
- rustGitHubReleaseJob,
33
- rustNativeNpmPublishJob,
34
- rustNodeFacadePublishJob,
35
26
  } from "./_rust-release-workflow.ts";
36
27
  import { toPosix } from "./packages.ts";
37
28
  import {
@@ -43,12 +34,7 @@ import {
43
34
  import { isDBXToolsJavaScriptProject } from "./project-predicate.ts";
44
35
  import { pythonModuleName, type PythonPackageOptions } from "./project-py.ts";
45
36
  import { defaultReleaseUnitId, type ReleaseDependencyInput } from "./release-catalog.ts";
46
- import {
47
- hasNodeRelease,
48
- independentReleaseSetupSteps,
49
- registerIndependentPublicationJob,
50
- tryReleaseWorkflow,
51
- } from "./release.ts";
37
+ import { tryReleaseWorkflow } from "./release.ts";
52
38
  import { readWorkspaceVersion } from "./workspace-version.ts";
53
39
  export { discoverRustCrates, hasUniFFIBindings } from "./_rust-project.ts";
54
40
  export { orderRustBindings } from "./_rust-release-workflow.ts";
@@ -458,10 +444,7 @@ function planRustReleaseBinaries(
458
444
  component: defaultReleaseUnitId("rust", pkg.crateName),
459
445
  version: readWorkspaceVersion(project.outdir),
460
446
  };
461
- const tagPrefix =
462
- isDBXToolsJavaScriptProject()(project) && project.releaseCatalog.mode === "independent"
463
- ? `${identity.component}-v`
464
- : "v";
447
+ const tagPrefix = "v";
465
448
  return {
466
449
  command,
467
450
  description,
@@ -670,14 +653,6 @@ function createRustPackages(
670
653
  ...resolved.packageOptions[directory],
671
654
  };
672
655
  const name = `${resolved.scope}-${directory.toLowerCase().replace(/[^a-z0-9-]+/g, "-")}`;
673
- const version =
674
- isDBXToolsJavaScriptProject()(project) && project.releaseCatalog.mode === "independent"
675
- ? project.releaseCatalog.versionForRegistration(
676
- "rust",
677
- name,
678
- `${resolved.root}/${directory}`,
679
- )
680
- : undefined;
681
656
  return new DBXToolsRustProject({
682
657
  parent: project,
683
658
  outdir: `${resolved.root}/${directory}`,
@@ -686,7 +661,6 @@ function createRustPackages(
686
661
  workspaceRoot: resolved.root,
687
662
  workspace: true,
688
663
  scope: resolved.scope,
689
- ...(version ? { version } : {}),
690
664
  ...configured,
691
665
  });
692
666
  });
@@ -832,9 +806,7 @@ function configureRustWorkspaceFiles(
832
806
  .map((pkg) => `${resolved.root}/${pkg.packageOptions.directory}`),
833
807
  resolver: "2",
834
808
  package: {
835
- ...(!isDBXToolsJavaScriptProject()(project) || project.releaseCatalog.mode === "fixed"
836
- ? { version: readWorkspaceVersion(project.outdir) }
837
- : {}),
809
+ version: readWorkspaceVersion(project.outdir),
838
810
  edition: options.edition ?? "2021",
839
811
  "rust-version": options.rustVersion ?? "1.82",
840
812
  license: options.license ?? DBX_TOOLS_LICENSE,
@@ -919,29 +891,6 @@ function rustReleaseDependencies(
919
891
  });
920
892
  }
921
893
 
922
- function configureIndependentRustVersions(
923
- project: DBXToolsJavaScriptProject,
924
- packages: readonly DBXToolsRustProject[],
925
- ): void {
926
- for (const pkg of packages) {
927
- for (const [section, dependencies] of [
928
- ["dependencies", pkg.packageOptions.dependencies],
929
- ["dev-dependencies", pkg.packageOptions.devDependencies],
930
- ] as const) {
931
- for (const [name, dependency] of Object.entries(dependencies ?? {})) {
932
- if (typeof dependency === "string" || !dependency.path) continue;
933
- const dependencyPath = resolve(pkg.outdir, dependency.path);
934
- const target = packages.find((candidate) => resolve(candidate.outdir) === dependencyPath);
935
- if (!target) continue;
936
- pkg.manifestFile.addOverride(
937
- `${section}.${name}.version`,
938
- project.releaseCatalog.versionFor(target),
939
- );
940
- }
941
- }
942
- }
943
- }
944
-
945
894
  /** Generated Rust workspace plus convention-derived UniFFI binding packages. */
946
895
  export class DBXToolsRustWorkspace extends Component {
947
896
  readonly packages: readonly DBXToolsRustProject[];
@@ -989,9 +938,6 @@ export class DBXToolsRustWorkspace extends Component {
989
938
  ],
990
939
  });
991
940
  }
992
- if (project.releaseCatalog.mode === "independent") {
993
- configureIndependentRustVersions(project, this.packages);
994
- }
995
941
  }
996
942
  this.releaseBinaries = planRustReleaseBinaries(project, this.packages, resolved);
997
943
  configureRustCliRegistry(project, options.cliRegistryPath, this.releaseBinaries);
@@ -1101,50 +1047,8 @@ export class DBXToolsRustWorkspace extends Component {
1101
1047
  throw new Error("Rust release requires the root dbx-tools release mode");
1102
1048
  }
1103
1049
  configureRustReleaseTask(project, plan);
1104
- if (project.releaseCatalog.mode === "independent") {
1105
- if (plan.targets.length) {
1106
- workflow.addJob("rust-build", rustBuildJob(plan, independentReleaseSetupSteps(project)));
1107
- }
1108
- if (plan.publicCrates.length) {
1109
- workflow.addJob("publish-cargo", independentRustCargoPublishJob(project, plan));
1110
- registerIndependentPublicationJob(workflow, "publish-cargo");
1111
- }
1112
- if (plan.releaseBinaries.length) {
1113
- workflow.addJob("publish-github-release", independentRustGitHubReleaseJob(project, plan));
1114
- registerIndependentPublicationJob(workflow, "publish-github-release");
1115
- }
1116
- if (plan.nodeBindings.length && hasNodeRelease(project)) {
1117
- workflow.addJob("publish-native-npm", independentRustNativeNpmPublishJob(project));
1118
- registerIndependentPublicationJob(workflow, "publish-native-npm");
1119
- workflow.addJob(
1120
- "publish-node-facades",
1121
- independentRustNodeFacadePublishJob(project, plan.nodeBindings),
1122
- );
1123
- registerIndependentPublicationJob(workflow, "publish-node-facades");
1124
- }
1125
- return;
1126
- }
1127
- if (plan.targets.length) {
1128
- workflow.addJob("rust-build", rustBuildJob(plan));
1129
- workflow.addJob("rust-assets", rustAssetsJob());
1130
- }
1131
1050
  if (plan.publicCrates.length) {
1132
- workflow.addJob("publish-cargo", rustCargoPublishJob(plan, false));
1133
- workflow.addJob("publish-local-cargo", rustCargoPublishJob(plan, true));
1134
- }
1135
- if (plan.hasTargetOutputs) {
1136
- workflow.addJob("publish-github-release", rustGitHubReleaseJob(plan));
1137
- }
1138
- if (plan.nodeBindings.length && hasNodeRelease(project)) {
1139
- workflow.addJob("publish-native-npm", rustNativeNpmPublishJob(project));
1140
- const nodeJob = workflow.getJob("publish-node");
1141
- if ("uses" in nodeJob) throw new Error("publish-node must be a workflow job");
1142
- workflow.updateJob("publish-node", {
1143
- ...nodeJob,
1144
- if: "${{ always() && needs.verify-context.result == 'success' && needs.publish-native-npm.result == 'success' && (github.event_name == 'push' || inputs.stage == 'all' || inputs.stage == 'node') }}",
1145
- needs: ["verify-context", "publish-native-npm"],
1146
- });
1147
- workflow.addJob("publish-node-facades", rustNodeFacadePublishJob(project, plan.nodeBindings));
1051
+ workflow.addJob("publish-cargo", rustCargoPublishJob(plan));
1148
1052
  }
1149
1053
  }
1150
1054
  }