@dbx-tools/projen 0.9.19 → 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 (104) 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 +67 -83
  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 +4 -391
  46. package/src/_task-command.ts +75 -0
  47. package/src/project-js.ts +2 -42
  48. package/src/project-py.ts +5 -96
  49. package/src/project-rs.ts +3 -76
  50. package/src/release-catalog.ts +37 -203
  51. package/src/release.ts +12 -634
  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 +62 -30
  82. package/tasks/publish-python.ts +38 -140
  83. package/tasks/publish-uniffi-local.ts +129 -84
  84. package/tasks/publish.ts +25 -139
  85. package/tasks/python-registry.ts +2 -9
  86. package/tasks/release-assets.ts +56 -65
  87. package/tasks/release-candidate.ts +123 -90
  88. package/tasks/release-pr.ts +266 -240
  89. package/tasks/release-summary.ts +8 -10
  90. package/tasks/release-version.ts +23 -2
  91. package/tasks/rust.ts +8 -13
  92. package/tasks/uniffi-python.d.ts +2 -3
  93. package/tasks/uniffi-python.js +7 -20
  94. package/tasks/uniffi-release.mjs +19 -13
  95. package/tasks/uniffi.ts +15 -24
  96. package/tasks/version-check.ts +24 -56
  97. package/src/release-plan.ts +0 -269
  98. package/src/release-please.ts +0 -242
  99. package/tasks/release-bootstrap.ts +0 -59
  100. package/tasks/release-plan.ts +0 -89
  101. package/tasks/release-please.ts +0 -162
  102. package/tasks/release-request.ts +0 -166
  103. package/tasks/release-summary-units.ts +0 -75
  104. package/tasks/stamp-python.ts +0 -13
@@ -1,46 +1,23 @@
1
- /** Rust release planning and generated GitHub workflow jobs. */
1
+ /** Rust release planning, candidate configuration, and Cargo promotion. */
2
2
  import { existsSync, readFileSync } from "node:fs";
3
3
  import { createRequire } from "node:module";
4
4
  import { dirname, join, resolve } from "node:path";
5
5
  import { fileURLToPath } from "node:url";
6
- import { stringUtils } from "@dbx-tools/shared-core";
7
6
  import { TextFile, javascript } from "projen";
8
- import { JobPermission, type Job, type JobStep } from "projen/lib/github/workflows-model";
9
- import { releaseBinaryAssetName } from "./_release-platform.ts";
7
+ import { JobPermission, type Job } from "projen/lib/github/workflows-model";
10
8
  import { UNIFFI_BINDGEN_FEATURE, type RustProject } from "./_rust-project.ts";
11
- import { BUN_VERSION } from "./bun-workflow.ts";
12
- import type { DBXToolsJavaScriptProject } from "./project-js.ts";
13
9
  import type {
14
10
  DBXToolsRustWorkspaceOptions,
15
11
  RustBindingMapping,
16
12
  RustReleaseOs,
17
13
  UniFFIReleaseTarget,
18
14
  } from "./project-rs.ts";
19
- import { defaultReleaseUnitId } from "./release-catalog.ts";
20
15
  import { RELEASE_VERSION, releaseSourceSteps } from "./release-dispatch.ts";
21
- import {
22
- independentReleaseSetupSteps,
23
- npmPublishEnvironment,
24
- releasePublishCondition,
25
- } from "./release.ts";
16
+ import { releasePublishCondition } from "./release.ts";
26
17
 
27
18
  const require = createRequire(import.meta.url);
28
-
29
19
  type RustPackageDependencyResolver = (pkg: RustProject) => RustProject[];
30
20
 
31
- const RUST_BUILD_ENV = {
32
- CARGO_INCREMENTAL: "0",
33
- CARGO_TERM_COLOR: "always",
34
- } as const;
35
-
36
- function timedBash(phase: string, command: string): string {
37
- return [
38
- "SECONDS=0",
39
- `trap 'status=$?; echo "phase=${phase} duration_seconds=$SECONDS status=$status"; exit "$status"' EXIT`,
40
- command,
41
- ].join("\n");
42
- }
43
-
44
21
  function taskSource(name: string): string {
45
22
  const sourceDirectory = dirname(fileURLToPath(import.meta.url));
46
23
  const candidates = [
@@ -257,17 +234,6 @@ export function planRustRelease(
257
234
  hasTargetOutputs,
258
235
  };
259
236
  }
260
-
261
- function rustBuildJobIds(plan: RustReleasePlan): string[] {
262
- return plan.targets.length ? ["rust-build"] : [];
263
- }
264
-
265
- function rustBuildResultCondition(plan: RustReleasePlan): string {
266
- return rustBuildJobIds(plan)
267
- .map((job) => `needs.${job}.result != 'failure' && needs.${job}.result != 'cancelled'`)
268
- .join(" && ");
269
- }
270
-
271
237
  export function configureRustReleaseTask(
272
238
  project: javascript.NodeProject,
273
239
  plan: RustReleasePlan,
@@ -310,214 +276,6 @@ export function configureRustReleaseTask(
310
276
  project.tryRemoveFile(".projen/rust-release.mjs");
311
277
  }
312
278
 
313
- function rustBindingCommands(plan: RustReleasePlan, independent: boolean): string[] {
314
- return plan.bindings.map((binding) => {
315
- const unit = defaultReleaseUnitId("rust", binding.crate);
316
- const command = [
317
- `node ${plan.releaseTask} build`,
318
- `--crate "${binding.crate}"`,
319
- `--node "${binding.node}"`,
320
- `--python "${binding.python}"`,
321
- `--node-package "${binding.nodePackage}"`,
322
- `--python-package "${binding.pythonPackage}"`,
323
- `--python-module "${binding.pythonModule}"`,
324
- '--cargo-target "${{ matrix.cargo }}"',
325
- '--node-triple "${{ matrix.node }}"',
326
- '--python-tag "${{ matrix.python }}"',
327
- '--os "${{ matrix.os }}"',
328
- '--cpu "${{ matrix.cpu }}"',
329
- '--libc "${{ matrix.libc }}"',
330
- independent
331
- ? `--version "$(jq -r --arg unit "${unit}" '.units[] | select(.id == $unit) | .newVersion' dist/release-plan.json)"`
332
- : '--version "$VERSION"',
333
- `--output "dist/release/${binding.crate}/\${{ matrix.node }}"`,
334
- "--skip-build",
335
- ].join(" \\\n ");
336
- return independent
337
- ? [
338
- `if jq -e --arg unit "${unit}" '.units[] | select(.id == $unit)' dist/release-plan.json >/dev/null; then`,
339
- ` ${command.replaceAll("\n", "\n ")}`,
340
- "fi",
341
- ].join("\n")
342
- : command;
343
- });
344
- }
345
-
346
- function rustReleaseBinaryCondition(excludedOs: readonly RustReleaseOs[]): string | undefined {
347
- return excludedOs.length
348
- ? `\${{ ${excludedOs.map((os) => `matrix.os != '${os}'`).join(" && ")} }}`
349
- : undefined;
350
- }
351
-
352
- function rustBinaryCommands(plan: RustReleasePlan, independent: boolean): string[] {
353
- return plan.releaseBinaries.flatMap((pkg) => {
354
- const unit = defaultReleaseUnitId("rust", pkg.crate);
355
- const windowsAsset = releaseBinaryAssetName(pkg.binary, "${{ matrix.node }}", "win32");
356
- const unixAsset = releaseBinaryAssetName(pkg.binary, "${{ matrix.node }}", "linux");
357
- const commands = [
358
- `mkdir -p "dist/release/${pkg.crate}/\${{ matrix.node }}/binary/stage"`,
359
- `SOURCE="target/\${{ matrix.cargo }}/release/${pkg.binary}\${{ matrix.os == 'win32' && '.exe' || '' }}"`,
360
- `DESTINATION="dist/release/${pkg.crate}/\${{ matrix.node }}/binary/stage/${pkg.binary}\${{ matrix.os == 'win32' && '.exe' || '' }}"`,
361
- 'cp "$SOURCE" "$DESTINATION"',
362
- 'if [ "${{ matrix.os }}" = "win32" ]; then',
363
- ` 7z a "dist/release/${pkg.crate}/\${{ matrix.node }}/binary/${windowsAsset}" "$DESTINATION"`,
364
- "else",
365
- ` tar -C "dist/release/${pkg.crate}/\${{ matrix.node }}/binary/stage" -czf "dist/release/${pkg.crate}/\${{ matrix.node }}/binary/${unixAsset}" "${pkg.binary}"`,
366
- "fi",
367
- `rm -rf "dist/release/${pkg.crate}/\${{ matrix.node }}/binary/stage"`,
368
- ];
369
- const excludedCondition = pkg.excludedOs
370
- .map((os) => `[ "\${{ matrix.os }}" != "${os}" ]`)
371
- .join(" && ");
372
- const platformCommands = excludedCondition
373
- ? [`if ${excludedCondition}; then`, ...commands.map((command) => ` ${command}`), "fi"]
374
- : commands;
375
- return independent
376
- ? [
377
- `if jq -e --arg unit "${unit}" '.units[] | select(.id == $unit)' dist/release-plan.json >/dev/null; then`,
378
- ...platformCommands.map((command) => ` ${command}`),
379
- "fi",
380
- ]
381
- : platformCommands;
382
- });
383
- }
384
-
385
- function rustArtifactSteps(plan: RustReleasePlan): JobStep[] {
386
- const binaryCrates = [...new Set(plan.releaseBinaries.map((binary) => binary.crate))];
387
- return [
388
- ...plan.bindings.flatMap((binding) => [
389
- ...(binding.node
390
- ? [
391
- {
392
- name: `Upload ${binding.crate} native npm package`,
393
- uses: "actions/upload-artifact@v7",
394
- with: {
395
- name: `${binding.crate}-\${{ matrix.node }}-npm`,
396
- path: `dist/release/${binding.crate}/\${{ matrix.node }}/npm/*.tgz`,
397
- "retention-days": 7,
398
- },
399
- },
400
- ]
401
- : []),
402
- ...(binding.python
403
- ? [
404
- {
405
- name: `Upload ${binding.crate} Python wheel`,
406
- uses: "actions/upload-artifact@v7",
407
- with: {
408
- name: `${binding.pythonPackage}--\${{ matrix.python }}--python-wheel`,
409
- path: `dist/release/${binding.crate}/\${{ matrix.node }}/python/*.whl`,
410
- "retention-days": 7,
411
- },
412
- },
413
- ]
414
- : []),
415
- ]),
416
- ...binaryCrates.map((crate) => {
417
- const binaries = plan.releaseBinaries.filter((binary) => binary.crate === crate);
418
- const excludedOs =
419
- binaries[0]?.excludedOs.filter((os) =>
420
- binaries.every((binary) => binary.excludedOs.includes(os)),
421
- ) ?? [];
422
- return {
423
- name: `Upload ${crate} release binary`,
424
- uses: "actions/upload-artifact@v7",
425
- ...(rustReleaseBinaryCondition(excludedOs)
426
- ? { if: rustReleaseBinaryCondition(excludedOs) }
427
- : {}),
428
- with: {
429
- name: `${crate}-\${{ matrix.node }}-binary`,
430
- path: `dist/release/${crate}/\${{ matrix.node }}/binary/*`,
431
- "retention-days": 7,
432
- },
433
- };
434
- }),
435
- ];
436
- }
437
-
438
- /** Build a Bash loop that preserves matrix values across LF and CRLF runners. */
439
- function matrixArgumentLoop(variable: string, field: string, flag: string): string {
440
- return `while IFS= read -r ${variable}; do ${variable}="\${${variable}%$'\\r'}"; ${variable}_ARGS+=(${flag} "$${variable}"); done < <(jq -r '.[]' <<<'\${{ toJSON(matrix.${field}) }}')`;
441
- }
442
-
443
- function rustCargoBuildCommand(plan: RustReleasePlan): string {
444
- return [
445
- "PACKAGE_ARGS=()",
446
- "FEATURE_ARGS=()",
447
- matrixArgumentLoop("PACKAGE", "packages", "--package"),
448
- matrixArgumentLoop("FEATURE", "features", "--features"),
449
- `cargo build --release --timings "\${PACKAGE_ARGS[@]}" "\${FEATURE_ARGS[@]}"${
450
- plan.usesCargoLock ? " --locked" : ""
451
- } --target "\${{ matrix.cargo }}"`,
452
- ].join("\n");
453
- }
454
-
455
- export function rustBuildJob(plan: RustReleasePlan, setup: readonly JobStep[]): Job {
456
- const bindingCommands = rustBindingCommands(plan, true);
457
- const binaryCommands = rustBinaryCommands(plan, true);
458
- return {
459
- if: "${{ needs.release-plan.outputs.rust_targets != '[]' && (github.event_name == 'push' || inputs.stage != 'docs') }}",
460
- name: "Rust / ${{ matrix.node }}",
461
- needs: ["release-plan"],
462
- runsOn: ["${{ matrix.runner }}"],
463
- permissions: { contents: JobPermission.READ },
464
- env: { ...RUST_BUILD_ENV, BUN_VERSION },
465
- strategy: {
466
- failFast: false,
467
- matrix: {
468
- include: "${{ fromJSON(needs.release-plan.outputs.rust_targets) }}" as never,
469
- },
470
- },
471
- steps: [
472
- ...setup,
473
- ...(plan.hasPythonBindings ? [{ name: "Setup uv", uses: "astral-sh/setup-uv@v7" }] : []),
474
- {
475
- name: "Setup Rust",
476
- uses: `dtolnay/rust-toolchain@${plan.releaseRustVersion}`,
477
- with: { targets: "${{ matrix.cargo }}" },
478
- },
479
- {
480
- name: "Install Linux native dependencies",
481
- if: "${{ matrix.os == 'linux' }}",
482
- // prettier-ignore
483
- run: stringUtils.dedent(
484
- // ============================================================================
485
- /*bash*/`
486
- sudo rm -f /etc/apt/sources.list.d/google-chrome.list
487
- sudo apt-get update
488
- sudo apt-get install --yes libdbus-1-dev pkg-config
489
- `
490
- // ============================================================================
491
- ),
492
- },
493
- {
494
- name: "Build Rust release outputs",
495
- shell: "bash",
496
- run: timedBash("rust", rustCargoBuildCommand(plan)),
497
- },
498
- ...(bindingCommands.length
499
- ? [
500
- {
501
- name: "Package UniFFI outputs",
502
- shell: "bash",
503
- run: timedBash("uniffi_packaging", bindingCommands.join("\n")),
504
- },
505
- ]
506
- : []),
507
- ...(binaryCommands.length
508
- ? [
509
- {
510
- name: "Package release binaries",
511
- shell: "bash",
512
- run: timedBash("binary_packaging", binaryCommands.join("\n")),
513
- },
514
- ]
515
- : []),
516
- ...rustArtifactSteps(plan),
517
- ],
518
- };
519
- }
520
-
521
279
  export function rustCargoPublishJob(plan: RustReleasePlan): Job {
522
280
  return {
523
281
  if: releasePublishCondition("cargo", ["needs.verify-context.result == 'success'"]),
@@ -542,7 +300,7 @@ export function rustCargoPublishJob(plan: RustReleasePlan): Job {
542
300
  `if cargo info "${crate}@$RELEASE_VERSION" >/dev/null 2>&1; then`,
543
301
  ` echo "skip published ${crate}@$RELEASE_VERSION"`,
544
302
  "else",
545
- ` cargo publish --package "${crate}" --registry crates-io --no-verify`,
303
+ ` cargo publish --package "${crate}" --registry crates-io --locked`,
546
304
  "fi",
547
305
  ].join("\n"),
548
306
  )
@@ -551,148 +309,3 @@ export function rustCargoPublishJob(plan: RustReleasePlan): Job {
551
309
  ],
552
310
  };
553
311
  }
554
-
555
- export function independentRustCargoPublishJob(
556
- project: DBXToolsJavaScriptProject,
557
- plan: RustReleasePlan,
558
- ): Job {
559
- const commands = plan.publicCrates.map((crate) =>
560
- [
561
- `VERSION="$(jq -r --arg package "${crate}" '.rustPackages[] | select(.identity == $package) | .version' dist/release-plan.json)"`,
562
- 'if [ -n "$VERSION" ]; then',
563
- ` if cargo info "${crate}@$VERSION" >/dev/null 2>&1; then`,
564
- ` echo "skip published ${crate}@$VERSION"`,
565
- " else",
566
- ` cargo publish --package "${crate}" --registry crates-io --no-verify`,
567
- " fi",
568
- "fi",
569
- ].join("\n"),
570
- );
571
- const buildCondition = rustBuildResultCondition(plan);
572
- return {
573
- if: plan.hasTargetOutputs
574
- ? `\${{ always() && needs.release-plan.outputs.rust == 'true' && ${buildCondition} && (github.event_name == 'push' || inputs.stage == 'all' || inputs.stage == 'rust') }}`
575
- : "${{ needs.release-plan.outputs.rust == 'true' && (github.event_name == 'push' || inputs.stage == 'all' || inputs.stage == 'rust') }}",
576
- needs: ["release-plan", ...rustBuildJobIds(plan)],
577
- runsOn: ["ubuntu-latest"],
578
- permissions: { contents: JobPermission.READ },
579
- env: { BUN_VERSION },
580
- steps: [
581
- ...independentReleaseSetupSteps(project),
582
- {
583
- name: "Setup Rust",
584
- uses: `dtolnay/rust-toolchain@${plan.releaseRustVersion}`,
585
- },
586
- {
587
- name: "Publish affected Cargo crates",
588
- env: { CARGO_REGISTRY_TOKEN: "${{ secrets.CARGO_REGISTRY_TOKEN }}" },
589
- run: commands.join("\n"),
590
- },
591
- ],
592
- };
593
- }
594
-
595
- export function independentRustGitHubReleaseJob(
596
- project: DBXToolsJavaScriptProject,
597
- plan: RustReleasePlan,
598
- ): Job {
599
- return {
600
- if: "${{ needs.release-plan.outputs.github == 'true' && (github.event_name == 'push' || inputs.stage == 'all' || inputs.stage == 'github') }}",
601
- needs: ["release-plan", ...rustBuildJobIds(plan)],
602
- runsOn: ["ubuntu-latest"],
603
- permissions: { contents: JobPermission.WRITE },
604
- env: { BUN_VERSION },
605
- steps: [
606
- ...independentReleaseSetupSteps(project),
607
- {
608
- name: "Download release binaries",
609
- uses: "actions/download-artifact@v8",
610
- with: {
611
- pattern: "*-binary",
612
- path: "dist/rust-release",
613
- "merge-multiple": true,
614
- },
615
- },
616
- {
617
- name: "Upload affected GitHub release assets",
618
- env: { GH_TOKEN: "${{ github.token }}" },
619
- shell: "bash",
620
- run: plan.releaseBinaries
621
- .map((binary) => {
622
- const unit = defaultReleaseUnitId("rust", binary.crate);
623
- return [
624
- `VERSION="$(jq -r --arg unit "${unit}" '.units[] | select(.id == $unit) | .newVersion' dist/release-plan.json)"`,
625
- 'if [ -n "$VERSION" ]; then',
626
- ` gh release upload "${unit}-v$VERSION" dist/rust-release/${binary.binary}-* --clobber`,
627
- "fi",
628
- ].join("\n");
629
- })
630
- .join("\n"),
631
- },
632
- ],
633
- };
634
- }
635
-
636
- export function independentRustNativeNpmPublishJob(project: DBXToolsJavaScriptProject): Job {
637
- return {
638
- if: "${{ needs.release-plan.outputs.node == 'true' && (github.event_name == 'push' || inputs.stage == 'all' || inputs.stage == 'node') }}",
639
- needs: ["release-plan", "rust-build"],
640
- runsOn: ["ubuntu-latest"],
641
- permissions: { contents: JobPermission.READ, idToken: JobPermission.WRITE },
642
- timeoutMinutes: 15,
643
- env: { BUN_VERSION, CI: "true" },
644
- steps: [
645
- ...independentReleaseSetupSteps(project),
646
- {
647
- name: "Download native npm packages",
648
- uses: "actions/download-artifact@v8",
649
- with: {
650
- pattern: "*-npm",
651
- path: "dist/uniffi/native",
652
- "merge-multiple": true,
653
- },
654
- },
655
- {
656
- name: "Publish affected native npm packages",
657
- env: npmPublishEnvironment(),
658
- run: "bun node_modules/@dbx-tools/projen/tasks/publish-npm.ts --directory dist/uniffi/native",
659
- },
660
- ],
661
- };
662
- }
663
-
664
- export function independentRustNodeFacadePublishJob(
665
- project: DBXToolsJavaScriptProject,
666
- bindings: readonly RustBindingMapping[],
667
- ): Job {
668
- return {
669
- if: "${{ needs.release-plan.outputs.node == 'true' && (github.event_name == 'push' || inputs.stage == 'all' || inputs.stage == 'node') }}",
670
- needs: ["release-plan", "publish-node", "publish-native-npm"],
671
- runsOn: ["ubuntu-latest"],
672
- permissions: { contents: JobPermission.READ, idToken: JobPermission.WRITE },
673
- timeoutMinutes: 30,
674
- env: { BUN_VERSION, CI: "true" },
675
- steps: [
676
- ...independentReleaseSetupSteps(project),
677
- {
678
- name: "Build and publish affected UniFFI npm facades",
679
- env: npmPublishEnvironment(),
680
- shell: "bash",
681
- run: bindings
682
- .flatMap((binding) => {
683
- if (!binding.node || !binding.nodePackage) return [];
684
- const unit = defaultReleaseUnitId("rust", binding.crate);
685
- const output = `dist/uniffi/facades/${binding.crate}`;
686
- return [
687
- `VERSION="$(jq -r --arg unit "${unit}" '.units[] | select(.id == $unit) | .newVersion' dist/release-plan.json)"`,
688
- 'if [ -n "$VERSION" ]; then',
689
- ` node .projen/uniffi-release.mjs facade --node "${binding.node}" --node-package "${binding.nodePackage}" --node-triple "linux-x64-gnu" --version "$VERSION" --output "${output}"`,
690
- ` bun node_modules/@dbx-tools/projen/tasks/publish-npm.ts --directory "${output}/npm-facade" --version "$VERSION"`,
691
- "fi",
692
- ];
693
- })
694
- .join("\n"),
695
- },
696
- ],
697
- };
698
- }
@@ -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
  }