@dbx-tools/projen 0.9.18 → 0.9.19

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
@@ -274,28 +274,28 @@ architecture. Omit both filters to regenerate the complete maintained matrix.
274
274
  `DBX_TOOLS_RELEASE_PLATFORMS` can select the generated matrix without repeating
275
275
  environment parsing in a consumer.
276
276
 
277
- `bun run release --build auto` is the default. It selects a local build when
278
- the chosen GitHub CLI account has maintain/admin access, the host is macOS, and
279
- Cargo, Zig, cargo-zigbuild, cargo-xwin, LLVM, uv, and zip are available.
280
- Otherwise it selects the GitHub matrix. `--build local` and `--build remote`
281
- force the choice. Local mode builds the exact merged release SHA and uploads
282
- the archives plus a checksum manifest to the draft GitHub Release before
283
- registry publication continues. A manager can rerun an interrupted upload with
284
- `bun run release:assets --version <version> --tag <tag> --upload` from the
285
- matching release source.
277
+ `bun run release` always prepares the production candidate locally. After the
278
+ reviewed release pull request merges, the local process checks out the exact
279
+ merge commit, builds the complete npm, Python, native binding, and Rust binary
280
+ artifact set, then creates an annotated tag and draft GitHub Release. The draft
281
+ contains `release-manifest.json`, `SHA256SUMS`, and every registry or binary
282
+ artifact bound to that commit. A manager can rebuild an interrupted draft from
283
+ the exact source with
284
+ `bun run release:assets --version <version> --tag <tag> --sha <commit> --upload`.
285
+ Publishing the draft is the production promotion event; `release.yml` verifies
286
+ the tag, commit, version, manifest, checksums, and asset set before publishing
287
+ the approved npm archives, Python distributions, and Cargo crates. GitHub-hosted
288
+ runners never rebuild native release artifacts for singular-version releases.
286
289
 
287
290
  Public UniFFI facades are marked with `dbxToolsConfig.uniffi = true` in Node
288
291
  manifests and `[tool.dbx_tools.config] uniffi = true` in Python manifests. The
289
- Node jobs consume same-run artifacts, publish native archives first with npm
290
- provenance, compile and publish normal workspace packages including the Projen
291
- engine, then build facades from committed generated TypeScript without UBRN and
292
- publish them in binding dependency order. Local Verdaccio publication does not
293
- enable provenance. Set the `UNIFFI_FACADE_SMOKE` repository variable to `true`
294
- to run the optional nonblocking registry install and import check after facade
295
- publication. Python combines same-run platform wheels with standard wheel and
296
- source builds, then publishes each distribution through its own PyPI
297
- trusted-publisher environment. Binding publishers wait for their dependencies.
298
- Trusted-publisher instructions name `release.yml` and the release branch policy.
292
+ approved npm archives are dependency-ordered and published through npm OIDC
293
+ Trusted Publishing with provenance. Local Verdaccio publication does not enable
294
+ provenance. Python publishes the exact approved wheels and source distributions
295
+ through package-specific PyPI trusted-publisher environments; binding publishers
296
+ wait for their dependencies. Trusted-publisher instructions name `release.yml`
297
+ and the `v*` deployment tag policy. Cargo publication checks the approved source
298
+ commit and skips an already-published version during recovery.
299
299
  The docs jobs generate README and TypeScript API content and deploy GitHub Pages
300
300
  from the same workflow. When a conventional Node binding path already belongs
301
301
  to a root subproject, Rust mapping reuses that project and adds binding
package/index.ts CHANGED
@@ -3,7 +3,7 @@
3
3
  // Hand edits are overwritten on the next watch; this file is read-only.
4
4
 
5
5
  export const PACKAGE_IDENTIFIER = "@dbx-tools/projen";
6
- export const PACKAGE_VERSION = "0.9.18";
6
+ export const PACKAGE_VERSION = "0.9.19";
7
7
  export * as barrels from "./src/barrels.ts";
8
8
  export * as bunApp from "./src/bun-app.ts";
9
9
  export * as bunWorkflow from "./src/bun-workflow.ts";
package/package.json CHANGED
@@ -29,9 +29,9 @@
29
29
  },
30
30
  "dependencies": {
31
31
  "@clack/prompts": "^1.7.0",
32
- "@dbx-tools/core": "^0.9.18",
33
- "@dbx-tools/path": "^0.9.18",
34
- "@dbx-tools/shared-core": "^0.9.18",
32
+ "@dbx-tools/core": "^0.9.19",
33
+ "@dbx-tools/path": "^0.9.19",
34
+ "@dbx-tools/shared-core": "^0.9.19",
35
35
  "commander": "^15.0.0",
36
36
  "concurrently": "^10.0.3",
37
37
  "constructs": "^10.6.0",
@@ -52,7 +52,7 @@
52
52
  },
53
53
  "main": "index.ts",
54
54
  "license": "Apache-2.0",
55
- "version": "0.9.18",
55
+ "version": "0.9.19",
56
56
  "types": "index.ts",
57
57
  "type": "module",
58
58
  "exports": {
@@ -17,19 +17,11 @@ import type {
17
17
  UniFFIReleaseTarget,
18
18
  } from "./project-rs.ts";
19
19
  import { defaultReleaseUnitId } from "./release-catalog.ts";
20
- import {
21
- RELEASE_SHA,
22
- RELEASE_SUMMARY_FILE,
23
- RELEASE_TAG,
24
- RELEASE_VERSION,
25
- releaseSourceSteps,
26
- } from "./release-dispatch.ts";
20
+ import { RELEASE_VERSION, releaseSourceSteps } from "./release-dispatch.ts";
27
21
  import {
28
22
  independentReleaseSetupSteps,
29
23
  npmPublishEnvironment,
30
- nodeReleaseSetupSteps,
31
- releaseArtifactSteps,
32
- releaseStageCondition,
24
+ releasePublishCondition,
33
25
  } from "./release.ts";
34
26
 
35
27
  const require = createRequire(import.meta.url);
@@ -460,32 +452,24 @@ function rustCargoBuildCommand(plan: RustReleasePlan): string {
460
452
  ].join("\n");
461
453
  }
462
454
 
463
- export function rustBuildJob(plan: RustReleasePlan, independentSetup?: readonly JobStep[]): Job {
464
- const independent = Boolean(independentSetup);
465
- const bindingCommands = rustBindingCommands(plan, independent);
466
- const binaryCommands = rustBinaryCommands(plan, independent);
455
+ export function rustBuildJob(plan: RustReleasePlan, setup: readonly JobStep[]): Job {
456
+ const bindingCommands = rustBindingCommands(plan, true);
457
+ const binaryCommands = rustBinaryCommands(plan, true);
467
458
  return {
468
- if: independentSetup
469
- ? "${{ needs.release-plan.outputs.rust_targets != '[]' && (github.event_name == 'push' || inputs.stage != 'docs') }}"
470
- : "${{ needs.verify-context.outputs.build_mode == 'remote' && (github.event_name == 'push' || inputs.stage == 'all') }}",
459
+ if: "${{ needs.release-plan.outputs.rust_targets != '[]' && (github.event_name == 'push' || inputs.stage != 'docs') }}",
471
460
  name: "Rust / ${{ matrix.node }}",
472
- needs: [independentSetup ? "release-plan" : "verify-context"],
461
+ needs: ["release-plan"],
473
462
  runsOn: ["${{ matrix.runner }}"],
474
463
  permissions: { contents: JobPermission.READ },
475
- env: {
476
- ...RUST_BUILD_ENV,
477
- ...(independentSetup ? { BUN_VERSION } : {}),
478
- },
464
+ env: { ...RUST_BUILD_ENV, BUN_VERSION },
479
465
  strategy: {
480
466
  failFast: false,
481
467
  matrix: {
482
- include: independentSetup
483
- ? ("${{ fromJSON(needs.release-plan.outputs.rust_targets) }}" as never)
484
- : ([...plan.targets] as never),
468
+ include: "${{ fromJSON(needs.release-plan.outputs.rust_targets) }}" as never,
485
469
  },
486
470
  },
487
471
  steps: [
488
- ...(independentSetup ?? releaseSourceSteps()),
472
+ ...setup,
489
473
  ...(plan.hasPythonBindings ? [{ name: "Setup uv", uses: "astral-sh/setup-uv@v7" }] : []),
490
474
  {
491
475
  name: "Setup Rust",
@@ -516,7 +500,6 @@ export function rustBuildJob(plan: RustReleasePlan, independentSetup?: readonly
516
500
  {
517
501
  name: "Package UniFFI outputs",
518
502
  shell: "bash",
519
- ...(independentSetup ? {} : { env: { VERSION: RELEASE_VERSION } }),
520
503
  run: timedBash("uniffi_packaging", bindingCommands.join("\n")),
521
504
  },
522
505
  ]
@@ -535,50 +518,12 @@ export function rustBuildJob(plan: RustReleasePlan, independentSetup?: readonly
535
518
  };
536
519
  }
537
520
 
538
- /** Wait for the selected local or GitHub-hosted native build source. */
539
- export function rustAssetsJob(): Job {
521
+ export function rustCargoPublishJob(plan: RustReleasePlan): Job {
540
522
  return {
541
- if: "${{ always() && needs.verify-context.result == 'success' && (needs.verify-context.outputs.build_mode == 'local' || needs.rust-build.result == 'success') }}",
542
- needs: ["verify-context", "rust-build"],
523
+ if: releasePublishCondition("cargo", ["needs.verify-context.result == 'success'"]),
524
+ needs: ["verify-context"],
543
525
  runsOn: ["ubuntu-latest"],
544
526
  permissions: { contents: JobPermission.READ },
545
- steps: [
546
- {
547
- name: "Wait for locally uploaded release assets",
548
- if: "${{ needs.verify-context.outputs.build_mode == 'local' }}",
549
- env: {
550
- GH_TOKEN: "${{ github.token }}",
551
- RELEASE_TAG: RELEASE_TAG,
552
- },
553
- shell: "bash",
554
- run: [
555
- "mkdir -p dist/local-release",
556
- "for ATTEMPT in $(seq 1 360); do",
557
- ' if gh release download "$RELEASE_TAG" --pattern rust-assets.json --dir dist/local-release --clobber; then exit 0; fi',
558
- ' echo "waiting for local release assets ($ATTEMPT/360)"',
559
- " sleep 5",
560
- "done",
561
- 'echo "::error::local release assets were not uploaded"',
562
- "exit 1",
563
- ].join("\n"),
564
- },
565
- {
566
- name: "Confirm native release assets",
567
- run: "true",
568
- },
569
- ],
570
- };
571
- }
572
-
573
- export function rustCargoPublishJob(plan: RustReleasePlan, local: boolean): Job {
574
- const registry = local ? '"${{ vars.LOCAL_CARGO_REGISTRY }}"' : "crates-io";
575
- return {
576
- if: local
577
- ? "${{ github.event_name == 'push' && vars.LOCAL_REPOSITORIES == 'true' }}"
578
- : "${{ github.event_name == 'push' }}",
579
- needs: ["verify-context", ...(plan.hasTargetOutputs ? ["rust-assets"] : [])],
580
- runsOn: [local ? "self-hosted" : "ubuntu-latest"],
581
- permissions: { contents: JobPermission.READ },
582
527
  steps: [
583
528
  ...releaseSourceSteps(),
584
529
  {
@@ -586,14 +531,21 @@ export function rustCargoPublishJob(plan: RustReleasePlan, local: boolean): Job
586
531
  uses: `dtolnay/rust-toolchain@${plan.releaseRustVersion}`,
587
532
  },
588
533
  {
589
- name: local ? "Publish Cargo crates locally" : "Publish public crates",
534
+ name: "Publish public crates",
590
535
  env: {
591
- CARGO_REGISTRY_TOKEN: local
592
- ? "${{ secrets.LOCAL_CARGO_TOKEN }}"
593
- : "${{ secrets.CARGO_REGISTRY_TOKEN }}",
536
+ RELEASE_VERSION,
537
+ CARGO_REGISTRY_TOKEN: "${{ secrets.CARGO_REGISTRY_TOKEN }}",
594
538
  },
595
539
  run: plan.publicCrates
596
- .map((crate) => `cargo publish --package "${crate}" --registry ${registry} --no-verify`)
540
+ .map((crate) =>
541
+ [
542
+ `if cargo info "${crate}@$RELEASE_VERSION" >/dev/null 2>&1; then`,
543
+ ` echo "skip published ${crate}@$RELEASE_VERSION"`,
544
+ "else",
545
+ ` cargo publish --package "${crate}" --registry crates-io --no-verify`,
546
+ "fi",
547
+ ].join("\n"),
548
+ )
597
549
  .join("\n"),
598
550
  },
599
551
  ],
@@ -681,50 +633,6 @@ export function independentRustGitHubReleaseJob(
681
633
  };
682
634
  }
683
635
 
684
- export function rustGitHubReleaseJob(plan: RustReleasePlan): Job {
685
- return {
686
- if: "${{ github.event_name == 'push' }}",
687
- needs: ["verify-context", ...(plan.hasTargetOutputs ? ["rust-assets"] : [])],
688
- runsOn: ["ubuntu-latest"],
689
- permissions: { contents: JobPermission.WRITE },
690
- steps: [
691
- ...releaseSourceSteps(),
692
- {
693
- name: "Download release binaries",
694
- if: "${{ needs.verify-context.outputs.build_mode == 'remote' }}",
695
- uses: "actions/download-artifact@v8",
696
- with: {
697
- pattern: "*-binary",
698
- path: "dist/rust-release",
699
- "merge-multiple": true,
700
- },
701
- },
702
- {
703
- name: "Publish GitHub release assets",
704
- if: "${{ needs.verify-context.outputs.build_mode == 'remote' }}",
705
- uses: "softprops/action-gh-release@v2",
706
- with: {
707
- files: "dist/rust-release/*",
708
- body_path: RELEASE_SUMMARY_FILE,
709
- generate_release_notes: true,
710
- tag_name: RELEASE_TAG,
711
- target_commitish: RELEASE_SHA,
712
- },
713
- },
714
- {
715
- name: "Publish locally built GitHub release",
716
- if: "${{ needs.verify-context.outputs.build_mode == 'local' }}",
717
- env: {
718
- GH_TOKEN: "${{ github.token }}",
719
- RELEASE_NOTES: RELEASE_SUMMARY_FILE,
720
- RELEASE_TAG,
721
- },
722
- run: 'gh release edit "$RELEASE_TAG" --draft=false --latest --notes-file "$RELEASE_NOTES"',
723
- },
724
- ],
725
- };
726
- }
727
-
728
636
  export function independentRustNativeNpmPublishJob(project: DBXToolsJavaScriptProject): Job {
729
637
  return {
730
638
  if: "${{ needs.release-plan.outputs.node == 'true' && (github.event_name == 'push' || inputs.stage == 'all' || inputs.stage == 'node') }}",
@@ -788,88 +696,3 @@ export function independentRustNodeFacadePublishJob(
788
696
  ],
789
697
  };
790
698
  }
791
-
792
- export function rustNativeNpmPublishJob(project: DBXToolsJavaScriptProject): Job {
793
- return {
794
- if: "${{ always() && needs.verify-context.result == 'success' && needs.rust-assets.result == 'success' && (github.event_name == 'push' || inputs.stage == 'all' || inputs.stage == 'node') }}",
795
- needs: ["verify-context", "rust-assets"],
796
- runsOn: ["ubuntu-latest"],
797
- permissions: {
798
- actions: JobPermission.READ,
799
- contents: JobPermission.READ,
800
- idToken: JobPermission.WRITE,
801
- },
802
- timeoutMinutes: 15,
803
- env: { BUN_VERSION, CI: "true" },
804
- steps: [
805
- ...nodeReleaseSetupSteps(project),
806
- ...releaseArtifactSteps({
807
- currentName: "Download native npm packages",
808
- recoveredName: "Download recovered native npm packages",
809
- pattern: "*-npm",
810
- path: "dist/uniffi/native",
811
- condition: "needs.verify-context.outputs.build_mode == 'remote'",
812
- }),
813
- {
814
- name: "Download locally built native npm packages",
815
- if: "${{ needs.verify-context.outputs.build_mode == 'local' }}",
816
- env: { GH_TOKEN: "${{ github.token }}", RELEASE_TAG },
817
- run: [
818
- "mkdir -p dist/uniffi/native",
819
- 'gh release download "$RELEASE_TAG" --pattern "*.tgz" --dir dist/uniffi/native',
820
- ].join("\n"),
821
- },
822
- {
823
- name: "Publish native npm packages",
824
- env: { RELEASE_VERSION, ...npmPublishEnvironment() },
825
- run: 'bun node_modules/@dbx-tools/projen/tasks/publish-npm.ts --directory dist/uniffi/native --version "$RELEASE_VERSION" $DRY_RUN',
826
- },
827
- ],
828
- };
829
- }
830
-
831
- export function rustNodeFacadePublishJob(
832
- project: DBXToolsJavaScriptProject,
833
- bindings: readonly RustReleaseBinding[],
834
- ): Job {
835
- return {
836
- if: releaseStageCondition("node"),
837
- needs: ["verify-context", "publish-node"],
838
- runsOn: ["ubuntu-latest"],
839
- permissions: { contents: JobPermission.READ, idToken: JobPermission.WRITE },
840
- timeoutMinutes: 30,
841
- env: { BUN_VERSION, CI: "true" },
842
- steps: [
843
- ...nodeReleaseSetupSteps(project),
844
- {
845
- name: "Build and publish UniFFI npm facades",
846
- env: { RELEASE_VERSION, ...npmPublishEnvironment() },
847
- run: bindings
848
- .flatMap((binding) => {
849
- const output = `dist/uniffi/facades/${binding.crate}`;
850
- return [
851
- `node .projen/uniffi-release.mjs facade --node "${binding.node}" --node-package "${binding.nodePackage}" --node-triple "linux-x64-gnu" --version "$RELEASE_VERSION" --output "${output}"`,
852
- `bun node_modules/@dbx-tools/projen/tasks/publish-npm.ts --directory "${output}/npm-facade" --version "$RELEASE_VERSION" $DRY_RUN`,
853
- ];
854
- })
855
- .join("\n"),
856
- },
857
- {
858
- name: "Smoke test published UniFFI npm facades",
859
- if: "${{ github.event_name == 'push' && vars.UNIFFI_FACADE_SMOKE == 'true' }}",
860
- continueOnError: true,
861
- env: { RELEASE_VERSION },
862
- run: [
863
- 'SMOKE_DIR="$(mktemp -d)"',
864
- "trap 'rm -rf \"$SMOKE_DIR\"' EXIT",
865
- 'cd "$SMOKE_DIR"',
866
- "npm init --yes >/dev/null",
867
- ...bindings.flatMap((binding) => [
868
- `npm install --ignore-scripts --no-audit --no-fund --package-lock=false "${binding.nodePackage}@$RELEASE_VERSION"`,
869
- `node -e 'import("${binding.nodePackage}")'`,
870
- ]),
871
- ].join("\n"),
872
- },
873
- ],
874
- };
875
- }
package/src/project-py.ts CHANGED
@@ -4,14 +4,19 @@ 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";
10
+ import {
11
+ RELEASE_SHA,
12
+ RELEASE_TAG,
13
+ RELEASE_VERSION,
14
+ releaseSourceSteps,
15
+ } from "./release-dispatch.ts";
11
16
  import {
12
17
  independentReleaseSetupSteps,
13
18
  registerIndependentPublicationJob,
14
- releaseArtifactSteps,
19
+ releaseTagPattern,
15
20
  releasePublishCondition,
16
21
  releaseStageCondition,
17
22
  tryReleaseWorkflow,
@@ -545,12 +550,10 @@ export class DBXToolsPythonWorkspace extends Component {
545
550
  return;
546
551
  }
547
552
  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"] : [])],
553
+ if: releaseStageCondition("python", ["needs.verify-context.result == 'success'"]),
554
+ needs: ["verify-context"],
552
555
  runsOn: ["ubuntu-latest"],
553
- permissions: { actions: JobPermission.READ, contents: JobPermission.READ },
556
+ permissions: { contents: JobPermission.READ },
554
557
  timeoutMinutes: 20,
555
558
  env: { BUN_VERSION },
556
559
  steps: [
@@ -561,76 +564,60 @@ export class DBXToolsPythonWorkspace extends Component {
561
564
  { name: "Setup uv", uses: "astral-sh/setup-uv@v7" },
562
565
  { name: "Install release helpers", run: "bun install" },
563
566
  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
567
  {
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
- {
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"),
568
+ name: "Download approved Python distributions",
569
+ env: { GH_TOKEN: "${{ github.token }}", RELEASE_TAG },
570
+ run: [
571
+ "rm -rf dist/release-download",
572
+ "mkdir -p dist/release-download",
573
+ 'gh release download "$RELEASE_TAG" --pattern release-manifest.json --pattern SHA256SUMS --pattern "*.whl" --pattern "*.tar.gz" --dir dist/release-download',
574
+ ].join("\n"),
604
575
  },
605
- {
606
- name: "Validate distributions",
576
+ ...allPublications.map((publication) => ({
577
+ name: `Select ${publication.distribution} distributions`,
578
+ env: {
579
+ PACKAGE_NAME: publication.distribution.replaceAll("_", "-").toLowerCase(),
580
+ RELEASE_SHA,
581
+ RELEASE_TAG,
582
+ RELEASE_VERSION,
583
+ },
607
584
  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",
585
+ "bun node_modules/@dbx-tools/projen/tasks/release-manifest.ts verify \\",
586
+ " --directory dist/release-download \\",
587
+ ' --tag "$RELEASE_TAG" \\',
588
+ ' --sha "$RELEASE_SHA" \\',
589
+ ' --version "$RELEASE_VERSION" \\',
590
+ " --kind pypi \\",
591
+ ' --package "$PACKAGE_NAME" \\',
592
+ ` --output dist/${publication.directory}`,
617
593
  ].join("\n"),
594
+ })),
595
+ {
596
+ name: "Validate approved distributions",
597
+ run: `find ${allPublications.map((publication) => `dist/${publication.directory}`).join(" ")} -type f \\( -name '*.whl' -o -name '*.tar.gz' \\) -print0 | xargs -0 uvx twine check`,
618
598
  },
619
599
  {
620
600
  name: "Upload distributions",
621
601
  uses: "actions/upload-artifact@v7",
622
- with: { name: "python-distributions", path: "dist", "retention-days": 7 },
602
+ with: {
603
+ name: "python-distributions",
604
+ path: allPublications.map((publication) => `dist/${publication.directory}`).join("\n"),
605
+ "retention-days": 7,
606
+ },
623
607
  },
624
608
  ],
625
609
  });
626
610
  for (const publication of allPublications) {
611
+ const dependencyJobs = (publication.dependencies ?? []).map(
612
+ (dependency) => `publish-pypi-${dependency}`,
613
+ );
627
614
  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
- ],
615
+ if: releasePublishCondition("python", [
616
+ "needs.verify-context.result == 'success'",
617
+ "needs.build-python.result == 'success'",
618
+ ...dependencyJobs.map((job) => `needs['${job}'].result == 'success'`),
619
+ ]),
620
+ needs: ["verify-context", "build-python", ...dependencyJobs],
634
621
  environment: {
635
622
  name: publication.environment,
636
623
  url:
@@ -710,15 +697,17 @@ export class DBXToolsPythonWorkspace extends Component {
710
697
  ): void {
711
698
  const repository = this.githubRepository();
712
699
  const publications = this.trustedPublisherPublications(options);
713
- const releaseBranch = isDBXToolsJavaScriptProject()(project)
714
- ? projectReleaseBranch(project)
715
- : "main";
700
+ const releaseTag = isDBXToolsJavaScriptProject()(project)
701
+ ? project.releaseCatalog.mode === "independent"
702
+ ? "*-v*"
703
+ : releaseTagPattern(project)
704
+ : "v*";
716
705
  const linesBeforeAuthentication = [
717
706
  "# PyPI Trusted Publisher Setup Instructions",
718
707
  "",
719
708
  "Use the system browser to audit and configure the PyPI trusted publishers below.",
720
709
  "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.",
710
+ "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
711
  "Use only PyPI pages for this task.",
723
712
  "",
724
713
  "## Audit and confirmation",
@@ -736,7 +725,7 @@ export class DBXToolsPythonWorkspace extends Component {
736
725
  "",
737
726
  "## GitHub environment policy",
738
727
  "",
739
- `- The supplied branch policy value is ${releaseBranch}.`,
728
+ `- The supplied deployment tag policy value is ${releaseTag}.`,
740
729
  "- Do not inspect or configure GitHub environments during this task.",
741
730
  "- GitHub environment administration is a separate task; use the supplied values only when comparing PyPI publisher entries.",
742
731
  "",
@@ -777,7 +766,7 @@ export class DBXToolsPythonWorkspace extends Component {
777
766
  `- Repository name: ${repository.name}`,
778
767
  "- Workflow name: release.yml",
779
768
  `- Environment name: ${publication.environment}`,
780
- `- GitHub environment branch: ${releaseBranch}`,
769
+ `- GitHub environment tag: ${releaseTag}`,
781
770
  ...(publication.artifacts ? [`- Artifacts: ${publication.artifacts}`] : []),
782
771
  "",
783
772
  ];
package/src/project-rs.ts CHANGED
@@ -19,7 +19,6 @@ import {
19
19
  type RustOpenApiOptions as RustOpenApiConfiguration,
20
20
  } from "./_rust-project.ts";
21
21
  import {
22
- rustAssetsJob,
23
22
  configureRustReleaseTask,
24
23
  independentRustCargoPublishJob,
25
24
  independentRustGitHubReleaseJob,
@@ -29,9 +28,6 @@ import {
29
28
  planRustRelease,
30
29
  rustBuildJob,
31
30
  rustCargoPublishJob,
32
- rustGitHubReleaseJob,
33
- rustNativeNpmPublishJob,
34
- rustNodeFacadePublishJob,
35
31
  } from "./_rust-release-workflow.ts";
36
32
  import { toPosix } from "./packages.ts";
37
33
  import {
@@ -1124,27 +1120,8 @@ export class DBXToolsRustWorkspace extends Component {
1124
1120
  }
1125
1121
  return;
1126
1122
  }
1127
- if (plan.targets.length) {
1128
- workflow.addJob("rust-build", rustBuildJob(plan));
1129
- workflow.addJob("rust-assets", rustAssetsJob());
1130
- }
1131
1123
  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));
1124
+ workflow.addJob("publish-cargo", rustCargoPublishJob(plan));
1148
1125
  }
1149
1126
  }
1150
1127
  }