@dbx-tools/projen 0.6.152 → 0.6.153

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -104,11 +104,26 @@ removing a crate or `setup_scaffolding!()` marker triggers a full synth. Repos
104
104
  without Rust crates start no Rust watcher. When Rust projects are detected,
105
105
  Cargo is required and the focused task fails immediately if it is unavailable.
106
106
 
107
- The workspace also generates a `rust-release` workflow from those discovered
108
- UniFFI crates. It builds Linux x64/arm64, macOS x64/arm64, and Windows x64.
109
- Node publishes a small facade plus optional OS/CPU packages, so npm installs
110
- only the matching native library. Python publishes one platform-tagged wheel
111
- per target, so pip follows the same thin-install model.
107
+ The workspace also generates a `rust-release` workflow from discovered crates,
108
+ UniFFI bindings, and release-enabled binaries. Its matrix has one row per
109
+ target. Each row installs native dependencies and restores Cargo/sccache once,
110
+ builds the Rust workspace once, then packages every discovered output from that
111
+ shared build. Bun and the workspace install are present only when a Node binding
112
+ needs TypeScript generation; uv is present only when a Python wheel is needed.
113
+ A binary-only workspace therefore installs neither.
114
+
115
+ The Node generator's Rust CLI is cached under one target directory keyed by its
116
+ pinned UBRN version and runner architecture, then prepared before the workspace
117
+ build. Python generation executes the already-built
118
+ `target/<triple>/release/uniffi-bindgen` directly. Artifact packaging therefore
119
+ does no Rust compilation after the main workspace build.
120
+
121
+ Artifacts identify their crate, target, and type. Download-only publication
122
+ jobs publish native npm platform archives before the facade and publish
123
+ platform-tagged Python wheels without checkout, Bun, or `bun install`.
124
+ Non-private Cargo crates publish from a source-only job with
125
+ `cargo publish --no-verify`; GitHub Release uploads likewise consume prebuilt
126
+ binary artifacts without reinstalling a toolchain.
112
127
 
113
128
  Set the repository variable `LOCAL_REPOSITORIES=true` to enable the generated
114
129
  self-hosted mirror job. Configure `LOCAL_NPM_REGISTRY` and
@@ -119,11 +134,13 @@ detects its OS, architecture, libc, Rust target, and Python wheel tag and builds
119
134
  only that native target. Public Cargo crates also publish directly to crates.io
120
135
  with `CARGO_REGISTRY_TOKEN`. Override `releaseTargets` only when a consumer has
121
136
  additional native runners; ordinary projects inherit the maintained matrix
122
- automatically.
137
+ automatically. `bun run bump` also accepts repeatable `--os` and `--arch`
138
+ selectors; every selected operating system is crossed with every selected
139
+ architecture. Omit both filters to regenerate the complete maintained matrix.
123
140
 
124
- Private Python projects are marked with `[tool.dbx-tools] private = true`. They
125
- are excluded from the uv workspace, documentation, and Python release workflow
126
- until their native artifact publishing matrix is enabled.
141
+ Private Python binding projects are marked with `[tool.dbx-tools] private =
142
+ true`. They stay out of the standard uv/Python release and docs surfaces;
143
+ `rust-release` publishes their prebuilt native wheels directly.
127
144
 
128
145
  ## Customize Packages With Mixins
129
146
 
@@ -318,10 +335,12 @@ Use `--local-registry false` or `--local-pypi false` to disable either local
318
335
  publish. An explicit `--local-pypi http://localhost:3141/user/index/` overrides
319
336
  auto-detection; `--python-root` defaults to `packages/py`.
320
337
 
321
- The pushed `v*` tag is also the public release boundary: it triggers npm
322
- publishing, stamps and publishes every Python distribution to PyPI, and rebuilds
323
- and deploys the documentation site. Ordinary pushes to `main` publish none of
324
- those surfaces.
338
+ The pushed `v*` tag is also the public release boundary. Available workflow
339
+ stages form the generated chain Rust -> Python -> Node -> docs: Rust builds and
340
+ publishes native artifacts and Cargo crates, Python publishes standard
341
+ distributions, Node publishes standard workspace packages, and docs deploys
342
+ after publication. A stage with no corresponding outputs is omitted. Ordinary
343
+ pushes to `main` publish none of those surfaces.
325
344
 
326
345
  Members intentionally keep only the tasks that something OTHER than a human
327
346
  invokes, so there is no second place to run the same thing:
package/package.json CHANGED
@@ -26,9 +26,9 @@
26
26
  },
27
27
  "dependencies": {
28
28
  "@clack/prompts": "^1.7.0",
29
- "@dbx-tools/core": "0.6.152",
30
- "@dbx-tools/path": "0.6.152",
31
- "@dbx-tools/shared-core": "0.6.152",
29
+ "@dbx-tools/core": "0.6.153",
30
+ "@dbx-tools/path": "0.6.153",
31
+ "@dbx-tools/shared-core": "0.6.153",
32
32
  "commander": "^15.0.0",
33
33
  "concurrently": "^10.0.3",
34
34
  "constructs": "^10.6.0",
@@ -48,7 +48,7 @@
48
48
  },
49
49
  "main": "index.ts",
50
50
  "license": "Apache-2.0",
51
- "version": "0.6.152",
51
+ "version": "0.6.153",
52
52
  "types": "index.ts",
53
53
  "type": "module",
54
54
  "exports": {
package/src/project-js.ts CHANGED
@@ -379,6 +379,7 @@ function defaultProjectOptions(
379
379
  // provider. See {@link DBXToolsRelease}.
380
380
  ...(isRoot ? {} : { npmAccess: javascript.NpmAccess.PUBLIC }),
381
381
  buildWorkflow: false,
382
+ workflowPackageCache: false,
382
383
  release: false,
383
384
  // The root build validates the whole workspace and must not also pack every
384
385
  // member into unused `dist/js` tarballs. Child projects keep projen's package
@@ -518,7 +519,7 @@ export interface DBXToolsJavaScriptProjectOptions
518
519
  /** Workflow that must finish successfully before the main Node release runs. */
519
520
  readonly releaseUpstreamWorkflow?: string;
520
521
  /** Main Node release workflow name. Defaults to `node-release`. */
521
- readonly releaseWorkflowName?: string | false;
522
+ readonly nodeReleaseWorkflowName?: string | false;
522
523
  /**
523
524
  * Extra workspace member paths (repo-relative, POSIX) to list in the workspace
524
525
  * config ALONGSIDE the discovered `packageRoots` members - for a package that
@@ -1188,7 +1189,7 @@ function initProject(
1188
1189
  tagPrefix: options.releaseTagPrefix,
1189
1190
  standaloneReleases: options.standaloneReleases,
1190
1191
  upstreamWorkflow: options.releaseUpstreamWorkflow,
1191
- workflowName: options.releaseWorkflowName,
1192
+ workflowName: options.nodeReleaseWorkflowName ?? options.releaseWorkflowName,
1192
1193
  });
1193
1194
  }
1194
1195
 
package/src/project-rs.ts CHANGED
@@ -139,6 +139,7 @@ const RUST_CACHE_ENV = {
139
139
  RUSTC_WRAPPER: "sccache",
140
140
  SCCACHE_GHA_ENABLED: "true",
141
141
  } as const;
142
+ const UBRN_VERSION = "0.31.0-5";
142
143
 
143
144
  function rustCacheSteps(sharedKey: string): readonly Record<string, unknown>[] {
144
145
  return [
@@ -473,7 +474,7 @@ export class DBXToolsRustWorkspace {
473
474
  if (binding.packageOptions.nodeDependencies?.length) {
474
475
  node.addDeps(...binding.packageOptions.nodeDependencies);
475
476
  }
476
- node.addDevDeps("uniffi-bindgen-react-native@0.31.0-5");
477
+ node.addDevDeps(`uniffi-bindgen-react-native@${UBRN_VERSION}`);
477
478
  if (binding.packageOptions.nodeDevDependencies?.length) {
478
479
  node.addDevDeps(...binding.packageOptions.nodeDevDependencies);
479
480
  }
@@ -537,9 +538,7 @@ export class DBXToolsRustWorkspace {
537
538
  if (
538
539
  (options.release ?? true) &&
539
540
  (this.bindingMappings.length > 0 ||
540
- this.packages.some(
541
- (pkg) => pkg.packageOptions.release || !pkg.packageOptions.private,
542
- ))
541
+ this.packages.some((pkg) => pkg.packageOptions.release || !pkg.packageOptions.private))
543
542
  ) {
544
543
  this.addReleaseWorkflow(project, options, releaseTargets(options));
545
544
  }
@@ -680,21 +679,42 @@ export class DBXToolsRustWorkspace {
680
679
  },
681
680
  ]
682
681
  : []),
683
- ...(hasPythonBindings
684
- ? [{ name: "Setup uv", uses: "astral-sh/setup-uv@v7" }]
685
- : []),
682
+ ...(hasPythonBindings ? [{ name: "Setup uv", uses: "astral-sh/setup-uv@v7" }] : []),
686
683
  {
687
684
  name: "Setup Rust",
688
685
  uses: "dtolnay/rust-toolchain@stable",
689
686
  with: { targets: "${{ matrix.target.cargo }}" },
690
687
  },
691
688
  ...rustCacheSteps("release-${{ matrix.target.cargo }}"),
689
+ ...(hasNodeBindings
690
+ ? [
691
+ {
692
+ name: "Cache UBRN generator",
693
+ uses: "actions/cache@v5",
694
+ with: {
695
+ path: "target/ubrn",
696
+ key: `ubrn-\${{ runner.os }}-\${{ runner.arch }}-${UBRN_VERSION}`,
697
+ },
698
+ },
699
+ ]
700
+ : []),
692
701
  {
693
702
  name: "Install Linux native dependencies",
694
703
  if: "${{ matrix.target.os == 'linux' }}",
695
704
  run: "sudo apt-get update && sudo apt-get install --yes libdbus-1-dev pkg-config",
696
705
  },
697
706
  ...(hasNodeBindings ? [{ name: "Install Node tooling", run: "bun install" }] : []),
707
+ ...(hasNodeBindings
708
+ ? [
709
+ {
710
+ name: "Prepare UBRN generator",
711
+ env: {
712
+ CARGO_TARGET_DIR: "${{ github.workspace }}/target/ubrn",
713
+ },
714
+ run: "cargo build --manifest-path node_modules/uniffi-bindgen-react-native/crates/ubrn_cli/Cargo.toml",
715
+ },
716
+ ]
717
+ : []),
698
718
  {
699
719
  name: "Build Rust outputs",
700
720
  run: 'cargo build --release --workspace --target "${{ matrix.target.cargo }}"',
@@ -705,7 +725,13 @@ export class DBXToolsRustWorkspace {
705
725
  name: "Package UniFFI outputs",
706
726
  shell: "bash",
707
727
  env: {
708
- VERSION: "${{ github.event_name == 'push' && github.ref_name || inputs.version }}",
728
+ VERSION:
729
+ "${{ github.event_name == 'push' && github.ref_name || inputs.version }}",
730
+ ...(hasNodeBindings
731
+ ? {
732
+ CARGO_TARGET_DIR: "${{ github.workspace }}/target/ubrn",
733
+ }
734
+ : {}),
709
735
  },
710
736
  run: bindingCommands.join("\n"),
711
737
  },
@@ -734,9 +760,7 @@ export class DBXToolsRustWorkspace {
734
760
  contents: "read",
735
761
  ...(binding.python ? { "id-token": "write" } : {}),
736
762
  },
737
- ...(binding.python
738
- ? { environment: { name: `pypi-${binding.crate}` } }
739
- : {}),
763
+ ...(binding.python ? { environment: { name: `pypi-${binding.crate}` } } : {}),
740
764
  steps: [
741
765
  ...(binding.node
742
766
  ? [
@@ -77,7 +77,7 @@ const packageNode = ({
77
77
  cpu,
78
78
  version,
79
79
  facade,
80
- nodeGenerator,
80
+ nodeGenerator,
81
81
  }) => {
82
82
  const libraryFile = basename(library);
83
83
  const nativePackage = resolve(output, "native-node");
@@ -119,9 +119,7 @@ const packageNode = ({
119
119
  manifest.dependencies = Object.fromEntries(
120
120
  Object.entries(manifest.dependencies ?? {}).map(([name, dependency]) => [
121
121
  name,
122
- typeof dependency === "string" && dependency.startsWith("workspace:")
123
- ? version
124
- : dependency,
122
+ typeof dependency === "string" && dependency.startsWith("workspace:") ? version : dependency,
125
123
  ]),
126
124
  );
127
125
  delete manifest.scripts;
@@ -142,11 +140,7 @@ const packageNode = ({
142
140
  "--skip-build",
143
141
  ]);
144
142
  mkdirSync(resolve(output, "npm-facade"), { recursive: true });
145
- run(
146
- "npm",
147
- ["pack", "--pack-destination", resolve(output, "npm-facade")],
148
- facadeDirectory,
149
- );
143
+ run("npm", ["pack", "--pack-destination", resolve(output, "npm-facade")], facadeDirectory);
150
144
  };
151
145
 
152
146
  const packagePython = ({
@@ -156,6 +150,8 @@ const packagePython = ({
156
150
  pythonDirectory,
157
151
  pythonTag,
158
152
  version,
153
+ cargoTarget,
154
+ os,
159
155
  }) => {
160
156
  const pythonRoot = resolve(output, "python-root");
161
157
  cpSync(resolve(root, pythonDirectory), pythonRoot, { recursive: true });
@@ -166,21 +162,15 @@ const packagePython = ({
166
162
  const packageName = crate.replace(/^dbx-tools-/, "").replaceAll("-", "_");
167
163
  const packageDirectory = resolve(pythonRoot, "src", "dbx_tools", packageName);
168
164
  const generatedDirectory = mkdtempSync(join(tmpdir(), `${packageName}-python-`));
169
- run("cargo", [
170
- "run",
171
- "--release",
172
- "--package",
173
- crate,
174
- "--bin",
175
- "uniffi-bindgen",
176
- "--",
177
- "generate",
178
- "--language",
179
- "python",
180
- "--out-dir",
181
- generatedDirectory,
182
- library,
183
- ]);
165
+ const generator = resolve(
166
+ root,
167
+ "target",
168
+ cargoTarget,
169
+ "release",
170
+ `uniffi-bindgen${os === "win32" ? ".exe" : ""}`,
171
+ );
172
+ if (!existsSync(generator)) throw new Error(`Missing UniFFI generator ${generator}`);
173
+ run(generator, ["generate", "--language", "python", "--out-dir", generatedDirectory, library]);
184
174
  const bindings = join(packageDirectory, "bindings.py");
185
175
  writable(bindings);
186
176
  const body = readFileSync(join(generatedDirectory, `${crate.replaceAll("-", "_")}.py`), "utf8");
@@ -259,6 +249,8 @@ const build = () => {
259
249
  pythonDirectory,
260
250
  pythonTag,
261
251
  version,
252
+ cargoTarget,
253
+ os,
262
254
  });
263
255
  }
264
256
  };
package/tasks/uniffi.ts CHANGED
@@ -64,7 +64,8 @@ const run = (command: string, args: string[]) => {
64
64
 
65
65
  const replaceGenerated = (source: string, destination: string): void => {
66
66
  const deadline = Date.now() + 5_000;
67
- while (!existsSync(source) && Date.now() < deadline) Bun.sleepSync(25);
67
+ const signal = new Int32Array(new SharedArrayBuffer(4));
68
+ while (!existsSync(source) && Date.now() < deadline) Atomics.wait(signal, 0, 0, 25);
68
69
  if (!existsSync(source)) throw new Error(`Missing generated binding: ${source}`);
69
70
  mkdirSync(dirname(destination), { recursive: true });
70
71
  makeWritable(destination);