@dbx-tools/projen 0.6.148 → 0.6.150

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/index.ts CHANGED
@@ -45,8 +45,8 @@ export { PackageIdentifier, PROJEN_VERSION, DBXToolsNodeProject, ROOT_INSTALL_ON
45
45
  export type { DBXToolsJavaScriptProject, DBXToolsJavaScriptProjectOptions, DBXToolsTypeScriptProjectOptions } from "./src/project-js.ts";
46
46
  export { DBXToolsPythonProject, DBXToolsPythonWorkspace } from "./src/project-py.ts";
47
47
  export type { PythonRepositoryOptions, PythonPackageOptions, PythonTrustedPublisherOptions, DBXToolsPythonProjectOptions, PythonReleaseOptions, DBXToolsPythonWorkspaceOptions } from "./src/project-py.ts";
48
- export { UNIFFI_RELEASE_TARGETS, DBXToolsRustProject, DBXToolsRustWorkspace } from "./src/project-rs.ts";
49
- export type { CargoDependencyOptions, CargoDependency, RustPackageOptions, DBXToolsRustWorkspaceOptions, UniFFIReleaseTarget, RustBindingMapping, RustWorkspaceMapping } from "./src/project-rs.ts";
48
+ export { RustReleaseOs, RustReleaseCpu, UNIFFI_RELEASE_TARGETS, DBXToolsRustProject, DBXToolsRustWorkspace } from "./src/project-rs.ts";
49
+ export type { CargoDependencyOptions, CargoDependency, RustPackageOptions, DBXToolsRustWorkspaceOptions, RustReleasePlatform, UniFFIReleaseTarget, RustBindingMapping, RustWorkspaceMapping } from "./src/project-rs.ts";
50
50
  export { COMPILED_DIR, COMPILED_COMPILER_OPTIONS } from "./src/publish.ts";
51
51
  export { DBXToolsRelease } from "./src/release.ts";
52
52
  export type { StandaloneRelease, DBXToolsReleaseOptions } from "./src/release.ts";
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.148",
30
- "@dbx-tools/path": "0.6.148",
31
- "@dbx-tools/shared-core": "0.6.148",
29
+ "@dbx-tools/core": "0.6.150",
30
+ "@dbx-tools/path": "0.6.150",
31
+ "@dbx-tools/shared-core": "0.6.150",
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.148",
51
+ "version": "0.6.150",
52
52
  "types": "index.ts",
53
53
  "type": "module",
54
54
  "exports": {
package/src/project-rs.ts CHANGED
@@ -32,6 +32,7 @@ export interface RustPackageOptions {
32
32
  readonly devDependencies?: Readonly<Record<string, CargoDependency>>;
33
33
  readonly features?: Readonly<Record<string, readonly string[]>>;
34
34
  readonly defaultFeatures?: readonly string[];
35
+ /** Cargo and release executable name. Defaults to the generated package name. */
35
36
  readonly binaryName?: string;
36
37
  readonly bindings?: readonly ("node" | "python")[];
37
38
  readonly nodeDependencies?: readonly string[];
@@ -56,17 +57,35 @@ export interface DBXToolsRustWorkspaceOptions {
56
57
  readonly release?: boolean;
57
58
  /** Native release targets; defaults to the maintained GitHub-hosted matrix. */
58
59
  readonly releaseTargets?: readonly UniFFIReleaseTarget[];
60
+ /** Maintained OS/CPU combinations to release. Defaults to every supported target. */
61
+ readonly releasePlatforms?: readonly RustReleasePlatform[];
59
62
  /** Workflow name used by downstream release stages. Defaults to `rust-release`. */
60
63
  readonly releaseWorkflowName?: string;
61
64
  }
62
65
 
66
+ export enum RustReleaseOs {
67
+ DARWIN = "darwin",
68
+ LINUX = "linux",
69
+ WINDOWS = "win32",
70
+ }
71
+
72
+ export enum RustReleaseCpu {
73
+ ARM64 = "arm64",
74
+ X64 = "x64",
75
+ }
76
+
77
+ export interface RustReleasePlatform {
78
+ readonly os: RustReleaseOs;
79
+ readonly cpu: RustReleaseCpu;
80
+ }
81
+
63
82
  export interface UniFFIReleaseTarget {
64
83
  readonly runner: string;
65
84
  readonly cargo: string;
66
85
  readonly node: string;
67
86
  readonly python: string;
68
- readonly os: "darwin" | "linux" | "win32";
69
- readonly cpu: "arm64" | "x64";
87
+ readonly os: RustReleaseOs;
88
+ readonly cpu: RustReleaseCpu;
70
89
  readonly libc?: "glibc";
71
90
  }
72
91
 
@@ -77,8 +96,8 @@ export const UNIFFI_RELEASE_TARGETS: readonly UniFFIReleaseTarget[] = [
77
96
  cargo: "x86_64-unknown-linux-gnu",
78
97
  node: "linux-x64-gnu",
79
98
  python: "manylinux_2_35_x86_64",
80
- os: "linux",
81
- cpu: "x64",
99
+ os: RustReleaseOs.LINUX,
100
+ cpu: RustReleaseCpu.X64,
82
101
  libc: "glibc",
83
102
  },
84
103
  {
@@ -86,8 +105,8 @@ export const UNIFFI_RELEASE_TARGETS: readonly UniFFIReleaseTarget[] = [
86
105
  cargo: "aarch64-unknown-linux-gnu",
87
106
  node: "linux-arm64-gnu",
88
107
  python: "manylinux_2_39_aarch64",
89
- os: "linux",
90
- cpu: "arm64",
108
+ os: RustReleaseOs.LINUX,
109
+ cpu: RustReleaseCpu.ARM64,
91
110
  libc: "glibc",
92
111
  },
93
112
  {
@@ -95,27 +114,64 @@ export const UNIFFI_RELEASE_TARGETS: readonly UniFFIReleaseTarget[] = [
95
114
  cargo: "x86_64-apple-darwin",
96
115
  node: "darwin-x64",
97
116
  python: "macosx_13_0_x86_64",
98
- os: "darwin",
99
- cpu: "x64",
117
+ os: RustReleaseOs.DARWIN,
118
+ cpu: RustReleaseCpu.X64,
100
119
  },
101
120
  {
102
121
  runner: "macos-14",
103
122
  cargo: "aarch64-apple-darwin",
104
123
  node: "darwin-arm64",
105
124
  python: "macosx_11_0_arm64",
106
- os: "darwin",
107
- cpu: "arm64",
125
+ os: RustReleaseOs.DARWIN,
126
+ cpu: RustReleaseCpu.ARM64,
108
127
  },
109
128
  {
110
129
  runner: "windows-latest",
111
130
  cargo: "x86_64-pc-windows-msvc",
112
131
  node: "win32-x64-msvc",
113
132
  python: "win_amd64",
114
- os: "win32",
115
- cpu: "x64",
133
+ os: RustReleaseOs.WINDOWS,
134
+ cpu: RustReleaseCpu.X64,
116
135
  },
117
136
  ] as const;
118
137
 
138
+ const RUST_CACHE_ENV = {
139
+ RUSTC_WRAPPER: "sccache",
140
+ SCCACHE_GHA_ENABLED: "true",
141
+ } as const;
142
+
143
+ function rustCacheSteps(sharedKey: string): readonly Record<string, unknown>[] {
144
+ return [
145
+ { name: "Setup sccache", uses: "mozilla-actions/sccache-action@v0.0.11" },
146
+ {
147
+ name: "Cache Cargo registry",
148
+ uses: "Swatinem/rust-cache@v2.9.2",
149
+ with: {
150
+ "cache-targets": false,
151
+ "add-job-id-key": false,
152
+ "add-rust-environment-hash-key": false,
153
+ "shared-key": sharedKey,
154
+ },
155
+ },
156
+ ];
157
+ }
158
+
159
+ function releaseTargets(options: DBXToolsRustWorkspaceOptions): readonly UniFFIReleaseTarget[] {
160
+ if (options.releaseTargets && options.releasePlatforms) {
161
+ throw new Error("releaseTargets and releasePlatforms are mutually exclusive");
162
+ }
163
+ if (options.releaseTargets) return options.releaseTargets;
164
+ if (!options.releasePlatforms) return UNIFFI_RELEASE_TARGETS;
165
+ return options.releasePlatforms.map((platform) => {
166
+ const target = UNIFFI_RELEASE_TARGETS.find(
167
+ (candidate) => candidate.os === platform.os && candidate.cpu === platform.cpu,
168
+ );
169
+ if (!target)
170
+ throw new Error(`Unsupported Rust release platform: ${platform.os}-${platform.cpu}`);
171
+ return target;
172
+ });
173
+ }
174
+
119
175
  /** Persisted mapping consumed by the focused Rust source watcher. */
120
176
  export interface RustBindingMapping {
121
177
  readonly crate: string;
@@ -218,6 +274,8 @@ export class DBXToolsRustProject extends Project implements DBXToolsProject {
218
274
  this.packageOptions = options;
219
275
  this.uniffi = hasUniFFIBindings(this.outdir);
220
276
  const library = existsSync(join(this.outdir, "src/lib.rs"));
277
+ const binary = existsSync(join(this.outdir, "src/main.rs"));
278
+ const binaryName = options.binaryName ?? crateName;
221
279
  const manifest: Record<string, unknown> = {
222
280
  package: {
223
281
  name: crateName,
@@ -238,11 +296,11 @@ export class DBXToolsRustProject extends Project implements DBXToolsProject {
238
296
  },
239
297
  }
240
298
  : {}),
241
- ...(this.uniffi || options.binaryName
299
+ ...(this.uniffi || binary
242
300
  ? {
243
301
  bin: this.uniffi
244
302
  ? { name: "uniffi-bindgen", path: "uniffi-bindgen.rs" }
245
- : { name: options.binaryName, path: "src/main.rs" },
303
+ : { name: binaryName, path: "src/main.rs" },
246
304
  }
247
305
  : {}),
248
306
  ...(options.features || options.defaultFeatures
@@ -356,9 +414,7 @@ export class DBXToolsRustWorkspace {
356
414
  trustedPublisher: {
357
415
  workflowName: options.releaseWorkflowName ?? "rust-release",
358
416
  environment: `native-${pkg.crateName}`,
359
- artifacts: `platform-specific wheels for ${(
360
- options.releaseTargets ?? UNIFFI_RELEASE_TARGETS
361
- )
417
+ artifacts: `platform-specific wheels for ${releaseTargets(options)
362
418
  .map((target) => `${target.os}-${target.cpu}`)
363
419
  .join(", ")}; all architectures publish to this one PyPI project`,
364
420
  },
@@ -390,12 +446,12 @@ export class DBXToolsRustWorkspace {
390
446
  node.package.addField("private", true);
391
447
  node.package.file.addDeletionOverride("publishConfig");
392
448
  node.package.addField("description", `Node bindings for ${binding.crateName}`);
393
- const releaseTargets = options.releaseTargets ?? UNIFFI_RELEASE_TARGETS;
449
+ const nativeTargets = releaseTargets(options);
394
450
  if (options.release ?? true) {
395
451
  node.package.addField(
396
452
  "optionalDependencies",
397
453
  Object.fromEntries(
398
- releaseTargets.map((target) => [
454
+ nativeTargets.map((target) => [
399
455
  `@${string.toSlug(options.scope)}/${directory}-${target.node}`,
400
456
  readWorkspaceVersion(project.outdir),
401
457
  ]),
@@ -471,7 +527,7 @@ export class DBXToolsRustWorkspace {
471
527
  (options.release ?? true) &&
472
528
  (this.bindingMappings.length > 0 || this.packages.some((pkg) => pkg.packageOptions.release))
473
529
  ) {
474
- this.addReleaseWorkflow(project, options, options.releaseTargets ?? UNIFFI_RELEASE_TARGETS);
530
+ this.addReleaseWorkflow(project, options, releaseTargets(options));
475
531
  }
476
532
  }
477
533
 
@@ -506,6 +562,7 @@ export class DBXToolsRustWorkspace {
506
562
  const buildJob = {
507
563
  name: "${{ matrix.binding.crate }} / ${{ matrix.target.node }}",
508
564
  "runs-on": "${{ matrix.target.runner }}",
565
+ env: RUST_CACHE_ENV,
509
566
  strategy: {
510
567
  "fail-fast": false,
511
568
  matrix: { include: matrix },
@@ -523,6 +580,7 @@ export class DBXToolsRustWorkspace {
523
580
  uses: "dtolnay/rust-toolchain@stable",
524
581
  with: { targets: "${{ matrix.target.cargo }}" },
525
582
  },
583
+ ...rustCacheSteps("release-${{ matrix.target.cargo }}"),
526
584
  {
527
585
  name: "Install Linux native dependencies",
528
586
  if: "${{ matrix.target.os == 'linux' }}",
@@ -554,6 +612,7 @@ export class DBXToolsRustWorkspace {
554
612
  const binaryBuildJob = {
555
613
  name: "${{ matrix.package.crate }} / ${{ matrix.target.node }}",
556
614
  "runs-on": "${{ matrix.target.runner }}",
615
+ env: RUST_CACHE_ENV,
557
616
  strategy: { "fail-fast": false, matrix: { include: binaryMatrix } },
558
617
  steps: [
559
618
  { name: "Checkout", uses: "actions/checkout@v6" },
@@ -562,6 +621,7 @@ export class DBXToolsRustWorkspace {
562
621
  uses: "dtolnay/rust-toolchain@stable",
563
622
  with: { targets: "${{ matrix.target.cargo }}" },
564
623
  },
624
+ ...rustCacheSteps("release-${{ matrix.target.cargo }}"),
565
625
  {
566
626
  name: "Install Linux native dependencies",
567
627
  if: "${{ matrix.target.os == 'linux' }}",
@@ -572,10 +632,18 @@ export class DBXToolsRustWorkspace {
572
632
  shell: "bash",
573
633
  run: [
574
634
  'cargo build --release --package "${{ matrix.package.crate }}" --bin "${{ matrix.package.binary }}" --target "${{ matrix.target.cargo }}"',
575
- "mkdir -p dist/rust-release",
635
+ "mkdir -p dist/rust-release/stage",
576
636
  "SOURCE=\"target/${{ matrix.target.cargo }}/release/${{ matrix.package.binary }}${{ matrix.target.os == 'win32' && '.exe' || '' }}\"",
577
- "DESTINATION=\"dist/rust-release/${{ matrix.package.binary }}-${{ matrix.target.node }}${{ matrix.target.os == 'win32' && '.exe' || '' }}\"",
637
+ "DESTINATION=\"dist/rust-release/stage/${{ matrix.package.binary }}${{ matrix.target.os == 'win32' && '.exe' || '' }}\"",
578
638
  'cp "$SOURCE" "$DESTINATION"',
639
+ 'if [ "${{ matrix.target.os }}" = "win32" ]; then',
640
+ ' ARCHIVE="dist/rust-release/${{ matrix.package.binary }}-${{ matrix.target.node }}.zip"',
641
+ ' 7z a "$ARCHIVE" "$DESTINATION"',
642
+ "else",
643
+ ' ARCHIVE="dist/rust-release/${{ matrix.package.binary }}-${{ matrix.target.node }}.tar.gz"',
644
+ ' tar -C dist/rust-release/stage -czf "$ARCHIVE" "${{ matrix.package.binary }}"',
645
+ "fi",
646
+ "rm -rf dist/rust-release/stage",
579
647
  ].join("\n"),
580
648
  },
581
649
  {
@@ -665,9 +733,15 @@ export class DBXToolsRustWorkspace {
665
733
  needs: [matrix.length ? "build" : "build-binaries"],
666
734
  "runs-on": "ubuntu-latest",
667
735
  permissions: { contents: "read" },
736
+ env: RUST_CACHE_ENV,
668
737
  steps: [
669
738
  { name: "Checkout", uses: "actions/checkout@v6" },
670
739
  { name: "Setup Rust", uses: "dtolnay/rust-toolchain@stable" },
740
+ ...rustCacheSteps("cargo-publish"),
741
+ {
742
+ name: "Install Linux native dependencies",
743
+ run: "sudo apt-get update && sudo apt-get install --yes libdbus-1-dev pkg-config",
744
+ },
671
745
  {
672
746
  name: "Publish public crates",
673
747
  env: { CARGO_REGISTRY_TOKEN: "${{ secrets.CARGO_REGISTRY_TOKEN }}" },
@@ -679,6 +753,7 @@ export class DBXToolsRustWorkspace {
679
753
  if: "${{ github.event_name == 'push' && vars.LOCAL_REPOSITORIES == 'true' }}",
680
754
  "runs-on": ["self-hosted"],
681
755
  permissions: { contents: "read" },
756
+ env: RUST_CACHE_ENV,
682
757
  steps: [
683
758
  { name: "Checkout", uses: "actions/checkout@v6" },
684
759
  {
@@ -688,6 +763,7 @@ export class DBXToolsRustWorkspace {
688
763
  },
689
764
  { name: "Setup uv", uses: "astral-sh/setup-uv@v7" },
690
765
  { name: "Setup Rust", uses: "dtolnay/rust-toolchain@stable" },
766
+ ...rustCacheSteps("local-${{ runner.os }}-${{ runner.arch }}"),
691
767
  { name: "Install", run: "bun install" },
692
768
  {
693
769
  name: "Build and publish host-native packages",
package/tasks/bump.ts CHANGED
@@ -67,7 +67,11 @@ import {
67
67
 
68
68
  const logger = log.logger("projen:bump");
69
69
  const LEVELS = ["patch", "minor", "major"] as const;
70
+ const RELEASE_OSES = ["darwin", "linux", "win32"] as const;
71
+ const RELEASE_ARCHES = ["arm64", "x64"] as const;
70
72
  type Level = (typeof LEVELS)[number];
73
+ type ReleaseOs = (typeof RELEASE_OSES)[number];
74
+ type ReleaseArch = (typeof RELEASE_ARCHES)[number];
71
75
 
72
76
  /** A standalone in-repo project released alongside the root, on its own tag prefix. */
73
77
  interface Sibling {
@@ -89,6 +93,10 @@ function parseSibling(value: string, previous: Sibling[]): Sibling[] {
89
93
  return [...previous, { dir: value.slice(0, at), prefix: value.slice(at + 1) }];
90
94
  }
91
95
 
96
+ function collectValue<T extends string>(value: T, previous: T[]): T[] {
97
+ return [...previous, value];
98
+ }
99
+
92
100
  function increment(v: Semver, level: Level): Semver {
93
101
  if (level === "major") return [v[0] + 1, 0, 0];
94
102
  if (level === "minor") return [v[0], v[1] + 1, 0];
@@ -174,6 +182,18 @@ program
174
182
  parseSibling,
175
183
  [] as Sibling[],
176
184
  )
185
+ .addOption(
186
+ new Option("--os <os>", "release operating system, repeatable; crossed with every --arch")
187
+ .choices([...RELEASE_OSES])
188
+ .argParser((value, previous: ReleaseOs[]) => collectValue(value as ReleaseOs, previous))
189
+ .default([] as ReleaseOs[]),
190
+ )
191
+ .addOption(
192
+ new Option("--arch <arch>", "release CPU architecture, repeatable; crossed with every --os")
193
+ .choices([...RELEASE_ARCHES])
194
+ .argParser((value, previous: ReleaseArch[]) => collectValue(value as ReleaseArch, previous))
195
+ .default([] as ReleaseArch[]),
196
+ )
177
197
  // Declared in the `--no-` form so commander creates a boolean that defaults to
178
198
  // `true` and is turned off by `--no-synth` / `--no-version` / ... (the
179
199
  // positive `--synth` etc. also work and are no-ops on the default).
@@ -200,6 +220,8 @@ program
200
220
  level: Level;
201
221
  prefix: string;
202
222
  sibling: Sibling[];
223
+ os: ReleaseOs[];
224
+ arch: ReleaseArch[];
203
225
  synth: boolean;
204
226
  version: boolean;
205
227
  commit: boolean;
@@ -212,6 +234,9 @@ program
212
234
  }) => {
213
235
  const pkgPath = resolve(process.cwd(), "package.json");
214
236
  if (!existsSync(pkgPath)) throw new Error(`no package.json in ${process.cwd()}`);
237
+ if ((opts.os.length === 0) !== (opts.arch.length === 0)) {
238
+ throw new Error("--os and --arch must be used together");
239
+ }
215
240
 
216
241
  const siblings = opts.sibling.map((s) => ({ ...s, pkgPath: resolve(s.dir, "package.json") }));
217
242
  for (const s of siblings) {
@@ -260,12 +285,19 @@ program
260
285
 
261
286
  if (opts.synth) {
262
287
  logger.info("synthesizing (projen)");
288
+ const releasePlatforms = opts.os
289
+ .flatMap((os) => opts.arch.map((arch) => `${os}:${arch}`))
290
+ .join(",");
263
291
  exec.spawnSync("bun", [".projenrc.ts"], {
264
292
  cwd: process.cwd(),
265
293
  stdout: "inherit",
266
294
  stderr: "inherit",
267
295
  stdin: "ignore",
268
296
  check: true,
297
+ env: {
298
+ ...process.env,
299
+ ...(releasePlatforms ? { DBX_TOOLS_RELEASE_PLATFORMS: releasePlatforms } : {}),
300
+ },
269
301
  });
270
302
  }
271
303