@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
package/src/release.ts CHANGED
@@ -16,11 +16,11 @@ import {
16
16
  import { readWorkspaceVersion } from "./workspace-version.ts";
17
17
 
18
18
  const NODE_VERSION = "24";
19
+ const NPM_VERSION = "11.4.2";
19
20
  const NPM_REGISTRY_URL = "https://registry.npmjs.org";
20
21
  const nodeReleaseProjects = new WeakSet<DBXToolsJavaScriptProject>();
21
22
  const releaseTagPrefixes = new WeakMap<DBXToolsJavaScriptProject, string>();
22
23
  const releaseWorkflows = new WeakMap<DBXToolsJavaScriptProject, GithubWorkflow>();
23
- const independentPublicationJobs = new WeakMap<GithubWorkflow, Set<string>>();
24
24
 
25
25
  /** Independently recoverable portions of the release workflow. */
26
26
  export type ReleaseStage = "all" | "node" | "python" | "cargo" | "docs";
@@ -57,14 +57,8 @@ export interface DBXToolsReleaseOptions {
57
57
  readonly docs?: ReleaseDocsOptions;
58
58
  /** Python package root passed to local release preparation. */
59
59
  readonly pythonRoot?: string;
60
- /** Repository task names run in the release worktree before expensive validation and publication. */
60
+ /** Repository task names run after VERSION generation and before candidate construction. */
61
61
  readonly validationTasks?: readonly string[];
62
- /**
63
- * Existing source branch safely fast-forwarded after publication.
64
- *
65
- * Missing or diverged branches are left untouched.
66
- */
67
- readonly syncBranch?: string | false;
68
62
  /**
69
63
  * Generate a versioned release summary locally. Defaults to enabled with the
70
64
  * standard Cursor, Codex, Claude fallback order.
@@ -84,13 +78,6 @@ export function tryReleaseWorkflow(project: DBXToolsJavaScriptProject): GithubWo
84
78
  return releaseWorkflows.get(project);
85
79
  }
86
80
 
87
- /** Register one publication job for the independent release completion barrier. */
88
- export function registerIndependentPublicationJob(workflow: GithubWorkflow, jobId: string): void {
89
- const jobs = independentPublicationJobs.get(workflow) ?? new Set<string>();
90
- jobs.add(jobId);
91
- independentPublicationJobs.set(workflow, jobs);
92
- }
93
-
94
81
  /** Whether the unified workflow publishes the normal npm workspace. */
95
82
  export function hasNodeRelease(project: DBXToolsJavaScriptProject): boolean {
96
83
  return nodeReleaseProjects.has(project);
@@ -145,14 +132,16 @@ export function nodeReleaseSetupSteps(project: DBXToolsJavaScriptProject): reado
145
132
  "package-manager-cache": false,
146
133
  },
147
134
  },
135
+ { name: "Install npm CLI", run: `npm install --global npm@${NPM_VERSION}` },
148
136
  { name: "Install", run: "bun install" },
149
137
  bunCacheSaveStep(),
150
138
  ];
151
139
  }
152
140
 
153
- /** Authentication, provenance, and dry-run values shared by npm publishers. */
141
+ /** Token authentication, GitHub provenance, and dry-run values shared by npm publishers. */
154
142
  export function npmPublishEnvironment(): Record<string, string> {
155
143
  return {
144
+ NODE_AUTH_TOKEN: "${{ secrets.NPM_TOKEN }}",
156
145
  NPM_CONFIG_PROVENANCE:
157
146
  "${{ (github.event_name == 'release' || inputs.dry_run != true) && 'true' || 'false' }}",
158
147
  DRY_RUN:
@@ -316,627 +305,10 @@ function addDocsJobs(
316
305
  });
317
306
  }
318
307
 
319
- function independentReleasePleaseJob(project: DBXToolsJavaScriptProject, branch: string): Job {
320
- return {
321
- if: "${{ github.event_name == 'push' || inputs.automatic == true }}",
322
- runsOn: ["ubuntu-latest"],
323
- permissions: {
324
- actions: JobPermission.WRITE,
325
- contents: JobPermission.WRITE,
326
- pullRequests: JobPermission.WRITE,
327
- },
328
- timeoutMinutes: 30,
329
- env: { BUN_VERSION },
330
- outputs: {
331
- releases_created: { stepId: "release", outputName: "releases_created" },
332
- prs_created: { stepId: "release", outputName: "prs_created" },
333
- releases: { stepId: "release", outputName: "releases" },
334
- prs: { stepId: "release", outputName: "prs" },
335
- docs_changed: { stepId: "changes", outputName: "docs_changed" },
336
- recovery_requested: { stepId: "recovery", outputName: "requested" },
337
- recovery_component: { stepId: "recovery", outputName: "component" },
338
- recovery_version: { stepId: "recovery", outputName: "version" },
339
- },
340
- steps: [
341
- {
342
- name: "Checkout source",
343
- uses: "actions/checkout@v6",
344
- with: { "fetch-depth": 2 },
345
- },
346
- ...bunCacheRestoreSteps(project, {
347
- ignorePaths: project.workflowCacheIgnorePaths,
348
- }),
349
- { name: "Install dependencies", run: "bun install" },
350
- {
351
- name: "Refresh Release Please state",
352
- id: "release",
353
- env: { GITHUB_TOKEN: "${{ github.token }}" },
354
- run: [
355
- 'OWNER="${GITHUB_REPOSITORY%%/*}"',
356
- 'REPO="${GITHUB_REPOSITORY#*/}"',
357
- `bun node_modules/@dbx-tools/projen/tasks/release-please.ts --owner "$OWNER" --repo "$REPO" --target-branch ${JSON.stringify(branch)}`,
358
- ].join("\n"),
359
- },
360
- {
361
- name: "Detect documentation changes",
362
- id: "changes",
363
- shell: "bash",
364
- run: [
365
- "if git diff --quiet HEAD^ HEAD -- README.md AGENTS.md ':(glob)**/README.md' docs ':(exclude)docs/releases/**' ':(exclude).release-notes/**'; then",
366
- ' echo "docs_changed=false" >> "$GITHUB_OUTPUT"',
367
- "else",
368
- ' echo "docs_changed=true" >> "$GITHUB_OUTPUT"',
369
- "fi",
370
- ].join("\n"),
371
- },
372
- {
373
- name: "Detect committed recovery request",
374
- id: "recovery",
375
- shell: "bash",
376
- run: [
377
- "if [ -f .release-recovery.json ]; then",
378
- ' echo "requested=true" >> "$GITHUB_OUTPUT"',
379
- ' echo "component=$(jq -r .component .release-recovery.json)" >> "$GITHUB_OUTPUT"',
380
- ' echo "version=$(jq -r .version .release-recovery.json)" >> "$GITHUB_OUTPUT"',
381
- "else",
382
- ' echo "requested=false" >> "$GITHUB_OUTPUT"',
383
- "fi",
384
- ].join("\n"),
385
- },
386
- {
387
- name: "Reconcile generated release PR files",
388
- if: "${{ steps.release.outputs.prs_created == 'true' }}",
389
- env: {
390
- GH_TOKEN: "${{ github.token }}",
391
- RELEASE_BRANCH: branch,
392
- RELEASE_PRS: "${{ steps.release.outputs.prs }}",
393
- },
394
- shell: "bash",
395
- // prettier-ignore
396
- run: stringUtils.dedent(
397
- // ============================================================================
398
- /*bash*/`
399
- BRANCH="$(jq -r '.[0].headBranchName' <<<"$RELEASE_PRS")"
400
- test -n "$BRANCH" && test "$BRANCH" != "null"
401
- git fetch origin "$BRANCH"
402
- git switch --force-create "$BRANCH" "origin/$BRANCH"
403
- bun install
404
- bunx projen
405
- bun node_modules/@dbx-tools/projen/tasks/version-check.ts
406
- bun node_modules/@dbx-tools/projen/tasks/release-plan.ts --base-ref origin/${branch}
407
- bun node_modules/@dbx-tools/projen/tasks/release-summary-units.ts --from-ref origin/${branch}
408
- if ! git diff --quiet || test -n "$(git ls-files --others --exclude-standard)"; then
409
- git config user.name "github-actions[bot]"
410
- git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
411
- git add -A
412
- git commit -m "chore: reconcile release metadata"
413
- git push origin "HEAD:$BRANCH"
414
- fi
415
- MERGED="false"
416
- for ATTEMPT in $(seq 1 15); do
417
- STATE="$(gh pr view "$BRANCH" --json state,mergeable --jq '[.state,.mergeable] | join(" ")')"
418
- if [[ "$STATE" == MERGED* ]]; then MERGED="true"; break; fi
419
- if [ "$STATE" = "OPEN CONFLICTING" ]; then
420
- echo "::error::release pull request has merge conflicts"
421
- exit 1
422
- fi
423
- if [ "$STATE" = "OPEN MERGEABLE" ] && gh pr merge "$BRANCH" --merge; then
424
- MERGED="true"
425
- break
426
- fi
427
- echo "release pull request is not mergeable yet (attempt $ATTEMPT/15)"
428
- sleep 2
429
- done
430
- if [ "$MERGED" != "true" ]; then
431
- echo "::error::release pull request did not become mergeable"
432
- exit 1
433
- fi
434
- gh workflow run release.yml --ref "$RELEASE_BRANCH" -f automatic=true
435
- `
436
- // ============================================================================
437
- ),
438
- },
439
- ],
440
- };
441
- }
442
-
443
- function configureReleaseRequestWorkflow(
444
- project: DBXToolsJavaScriptProject,
445
- baseBranch: string,
446
- ): void {
447
- if (!project.github) return;
448
- const workflow = new GithubWorkflow(project.github, "release-request", {
449
- fileName: "release-request.yml",
450
- limitConcurrency: true,
451
- concurrencyOptions: {
452
- group: "release-request-${{ github.ref_name }}",
453
- cancelInProgress: false,
454
- },
455
- });
456
- workflow.runName = "release request ${{ github.ref_name }}";
457
- workflow.on({ push: {} });
458
- workflow.file?.addOverride("permissions.contents", "read");
459
- workflow.addJob("request", {
460
- if: `\${{ github.ref_name != '${baseBranch}' }}`,
461
- runsOn: ["ubuntu-latest"],
462
- permissions: {
463
- actions: JobPermission.WRITE,
464
- contents: JobPermission.WRITE,
465
- pullRequests: JobPermission.WRITE,
466
- },
467
- steps: [
468
- {
469
- name: "Checkout source branch",
470
- uses: "actions/checkout@v6",
471
- with: { "fetch-depth": 2 },
472
- },
473
- {
474
- name: "Read release request",
475
- id: "request",
476
- shell: "bash",
477
- // prettier-ignore
478
- run: stringUtils.dedent(
479
- // ============================================================================
480
- /*bash*/`
481
- MESSAGE="$(git log -1 --format=%B)"
482
- if ! grep -q "^Release-Request: true$" <<<"$MESSAGE"; then
483
- echo "requested=false" >> "$GITHUB_OUTPUT"
484
- exit 0
485
- fi
486
- git fetch origin ${JSON.stringify(baseBranch)}
487
- if git diff --quiet "origin/${baseBranch}...HEAD"; then
488
- echo "requested=false" >> "$GITHUB_OUTPUT"
489
- exit 0
490
- fi
491
- echo "requested=true" >> "$GITHUB_OUTPUT"
492
- echo "branch=$GITHUB_REF_NAME" >> "$GITHUB_OUTPUT"
493
- echo "notes_path=$(sed -n 's/^Release-Notes-Path: //p' <<<"$MESSAGE" | tail -1)" >> "$GITHUB_OUTPUT"
494
- echo "title=$(git log -1 --skip=1 --format=%s)" >> "$GITHUB_OUTPUT"
495
- `
496
- // ============================================================================
497
- ),
498
- },
499
- {
500
- name: "Create, merge, and release source pull request",
501
- if: "${{ steps.request.outputs.requested == 'true' }}",
502
- env: {
503
- GH_TOKEN: "${{ github.token }}",
504
- SOURCE_BRANCH: "${{ steps.request.outputs.branch }}",
505
- BASE_BRANCH: baseBranch,
506
- TITLE: "${{ steps.request.outputs.title }}",
507
- NOTES_PATH: "${{ steps.request.outputs.notes_path }}",
508
- },
509
- shell: "bash",
510
- // prettier-ignore
511
- run: stringUtils.dedent(
512
- // ============================================================================
513
- /*bash*/`
514
- test -f "$NOTES_PATH"
515
- NOTES="$(cat "$NOTES_PATH")"
516
- BODY="$(printf '## Release notes\\n\\n%s\\n' "$NOTES")"
517
- PR="$(gh pr list --head "$SOURCE_BRANCH" --base "$BASE_BRANCH" --state open --json number --jq '.[0].number // empty')"
518
- if [ -n "$PR" ]; then
519
- gh pr edit "$PR" --title "$TITLE" --body "$BODY"
520
- else
521
- gh pr create --head "$SOURCE_BRANCH" --base "$BASE_BRANCH" --title "$TITLE" --body "$BODY"
522
- PR="$(gh pr list --head "$SOURCE_BRANCH" --base "$BASE_BRANCH" --state open --json number --jq '.[0].number')"
523
- fi
524
- gh pr merge "$PR" --merge
525
- gh workflow run release.yml --repo "$GITHUB_REPOSITORY" --ref "$BASE_BRANCH" -f automatic=true
526
- `
527
- // ============================================================================
528
- ),
529
- },
530
- ],
531
- });
532
- }
533
-
534
- function independentReleasePlanJob(project: DBXToolsJavaScriptProject): Job {
535
- return {
536
- if: "${{ always() && ((github.event_name == 'workflow_dispatch' && inputs.automatic != true) || needs.release-please.outputs.releases_created == 'true' || (needs.release-please.outputs.docs_changed == 'true' && needs.release-please.outputs.prs_created != 'true') || needs.release-please.outputs.recovery_requested == 'true') }}",
537
- needs: ["release-please"],
538
- runsOn: ["ubuntu-latest"],
539
- permissions: { contents: JobPermission.READ },
540
- timeoutMinutes: 15,
541
- env: { BUN_VERSION },
542
- outputs: {
543
- release_sha: { stepId: "plan", outputName: "release_sha" },
544
- rust: { stepId: "plan", outputName: "rust" },
545
- python: { stepId: "plan", outputName: "python" },
546
- node: { stepId: "plan", outputName: "node" },
547
- github: { stepId: "plan", outputName: "github" },
548
- docs: { stepId: "plan", outputName: "docs" },
549
- units: { stepId: "plan", outputName: "units" },
550
- python_packages: { stepId: "plan", outputName: "python_packages" },
551
- rust_packages: { stepId: "plan", outputName: "rust_packages" },
552
- artifacts: { stepId: "plan", outputName: "artifacts" },
553
- rust_targets: { stepId: "plan", outputName: "rust_targets" },
554
- },
555
- steps: [
556
- {
557
- name: "Checkout release source",
558
- uses: "actions/checkout@v6",
559
- with: { "fetch-depth": 2 },
560
- },
561
- ...bunCacheRestoreSteps(project, {
562
- ignorePaths: project.workflowCacheIgnorePaths,
563
- }),
564
- { name: "Install dependencies", run: "bun install" },
565
- {
566
- name: "Build affected release plan",
567
- id: "plan",
568
- shell: "bash",
569
- env: {
570
- COMPONENT:
571
- "${{ inputs.component || needs.release-please.outputs.recovery_component || '' }}",
572
- VERSION: "${{ inputs.version || needs.release-please.outputs.recovery_version || '' }}",
573
- DOCS_CHANGED: "${{ needs.release-please.outputs.docs_changed || 'false' }}",
574
- },
575
- run: [
576
- "ARGS=()",
577
- 'if [ -n "$COMPONENT" ]; then ARGS+=(--component "$COMPONENT" --version "$VERSION"); fi',
578
- 'if [ "$DOCS_CHANGED" = "true" ]; then ARGS+=(--docs); fi',
579
- 'bun node_modules/@dbx-tools/projen/tasks/release-plan.ts "${ARGS[@]}"',
580
- ].join("\n"),
581
- },
582
- {
583
- name: "Upload release plan",
584
- uses: "actions/upload-artifact@v7",
585
- with: {
586
- name: "release-plan",
587
- path: "dist/release-plan.json",
588
- "if-no-files-found": "error",
589
- },
590
- },
591
- ],
592
- };
593
- }
594
-
595
- /** Checkout and install the immutable source selected by an independent release plan. */
596
- export function independentReleaseSetupSteps(
597
- project: DBXToolsJavaScriptProject,
598
- ): readonly JobStep[] {
599
- return [
600
- {
601
- name: "Checkout release source",
602
- uses: "actions/checkout@v6",
603
- with: { ref: "${{ needs.release-plan.outputs.release_sha }}", "fetch-depth": 1 },
604
- },
605
- ...bunCacheRestoreSteps(project, {
606
- ignorePaths: project.workflowCacheIgnorePaths,
607
- }),
608
- { name: "Install dependencies", run: "bun install" },
609
- {
610
- name: "Download release plan",
611
- uses: "actions/download-artifact@v8",
612
- with: { name: "release-plan", path: "dist" },
613
- },
614
- ];
615
- }
616
-
617
- function independentNodePublishJob(project: DBXToolsJavaScriptProject): Job {
618
- return {
619
- if: "${{ needs.release-plan.outputs.node == 'true' && (github.event_name == 'push' || inputs.stage == 'all' || inputs.stage == 'node') }}",
620
- needs: ["release-plan"],
621
- runsOn: ["ubuntu-latest"],
622
- permissions: { contents: JobPermission.READ, idToken: JobPermission.WRITE },
623
- timeoutMinutes: 60,
624
- env: { BUN_VERSION, CI: "true" },
625
- steps: [
626
- ...independentReleaseSetupSteps(project),
627
- {
628
- name: "Publish affected npm packages",
629
- env: npmPublishEnvironment(),
630
- run: "bun node_modules/@dbx-tools/projen/tasks/publish.ts --plan dist/release-plan.json",
631
- },
632
- ],
633
- };
634
- }
635
-
636
- function independentReleaseNotesJob(project: DBXToolsJavaScriptProject): Job {
637
- return {
638
- if: "${{ needs.release-plan.outputs.units != '[]' }}",
639
- needs: ["release-plan"],
640
- runsOn: ["ubuntu-latest"],
641
- permissions: { contents: JobPermission.WRITE },
642
- env: { BUN_VERSION },
643
- steps: [
644
- ...independentReleaseSetupSteps(project),
645
- {
646
- name: "Publish component release notes",
647
- env: { GH_TOKEN: "${{ github.token }}" },
648
- shell: "bash",
649
- run: [
650
- "jq -c '.units[]' dist/release-plan.json | while read -r UNIT; do",
651
- ' COMPONENT="$(jq -r .component <<<"$UNIT")"',
652
- ' VERSION="$(jq -r .newVersion <<<"$UNIT")"',
653
- ' gh release edit "$COMPONENT-v$VERSION" --notes-file ".release-notes/final/$COMPONENT-v$VERSION.md"',
654
- "done",
655
- ].join("\n"),
656
- },
657
- {
658
- name: "Remove consumed release notes",
659
- shell: "bash",
660
- // prettier-ignore
661
- run: stringUtils.dedent(
662
- // ============================================================================
663
- /*bash*/`
664
- git fetch origin ${JSON.stringify(projectReleaseBranch(project))}
665
- if [ "$(git rev-parse origin/${projectReleaseBranch(project)})" != "$(git rev-parse HEAD)" ]; then
666
- echo "::warning::main advanced before release-note cleanup; leaving temporary notes for the next release"
667
- exit 0
668
- fi
669
- git rm -r --ignore-unmatch .release-notes
670
- if ! git diff --cached --quiet; then
671
- git config user.name "github-actions[bot]"
672
- git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
673
- git commit -m "chore: remove consumed release notes"
674
- git push origin "HEAD:${projectReleaseBranch(project)}"
675
- fi
676
- `
677
- // ============================================================================
678
- ),
679
- },
680
- ],
681
- };
682
- }
683
-
684
- function addIndependentDocsJobs(
685
- workflow: GithubWorkflow,
686
- project: DBXToolsJavaScriptProject,
687
- options: ReleaseDocsOptions,
688
- ): void {
689
- workflow.addJob("build-docs", {
690
- if: "${{ always() && needs['release-plan'].outputs.docs == 'true' && needs['publication-complete'].result == 'success' && (github.event_name == 'push' || inputs.stage == 'all' || inputs.stage == 'docs') }}",
691
- needs: ["release-plan", "publication-complete"],
692
- runsOn: ["ubuntu-latest"],
693
- permissions: {
694
- contents: JobPermission.READ,
695
- pages: JobPermission.WRITE,
696
- idToken: JobPermission.WRITE,
697
- },
698
- timeoutMinutes: 30,
699
- env: {
700
- BUN_VERSION,
701
- DOCS_SITE_URL: options.siteUrl,
702
- DOCS_BASE: options.base ?? "/",
703
- },
704
- steps: [
705
- ...independentReleaseSetupSteps(project),
706
- ...options.prepareSteps,
707
- bunCacheSaveStep(),
708
- ...options.buildSteps,
709
- {
710
- name: "Upload Pages artifact",
711
- uses: "actions/upload-pages-artifact@v4",
712
- with: { path: options.artifactPath },
713
- },
714
- ],
715
- });
716
- workflow.addJob("deploy-docs", {
717
- if: "${{ always() && needs['build-docs'].result == 'success' }}",
718
- needs: ["build-docs"],
719
- environment: {
720
- name: "github-pages",
721
- url: "${{ steps.deployment.outputs.page_url }}",
722
- },
723
- runsOn: ["ubuntu-latest"],
724
- permissions: { pages: JobPermission.WRITE, idToken: JobPermission.WRITE },
725
- timeoutMinutes: 15,
726
- steps: [
727
- {
728
- name: "Deploy to GitHub Pages",
729
- id: "deployment",
730
- uses: "actions/deploy-pages@v4",
731
- },
732
- ],
733
- });
734
- }
735
-
736
- function addIndependentBranchSyncJob(
737
- workflow: GithubWorkflow,
738
- project: DBXToolsJavaScriptProject,
739
- branch: string,
740
- ): void {
741
- const releaseBranch = projectReleaseBranch(project);
742
- workflow.addJob("sync-release-branch", {
743
- if: "${{ always() && (github.event_name == 'push' || inputs.automatic == true) && needs['publication-complete'].result == 'success' }}",
744
- needs: ["publication-complete"],
745
- runsOn: ["ubuntu-latest"],
746
- permissions: { contents: JobPermission.WRITE },
747
- env: { BUN_VERSION },
748
- steps: [
749
- {
750
- name: "Checkout released main",
751
- uses: "actions/checkout@v6",
752
- with: { "fetch-depth": 0 },
753
- },
754
- ...bunCacheRestoreSteps(project, {
755
- ignorePaths: project.workflowCacheIgnorePaths,
756
- }),
757
- { name: "Install dependencies", run: "bun install" },
758
- {
759
- name: `Safely sync ${branch}`,
760
- shell: "bash",
761
- env: { SOURCE_BRANCH: branch, RELEASE_BRANCH: releaseBranch },
762
- // prettier-ignore
763
- run: stringUtils.dedent(
764
- // ============================================================================
765
- /*bash*/`
766
- if ! git ls-remote --exit-code --heads origin "refs/heads/$SOURCE_BRANCH" >/dev/null 2>&1; then
767
- echo "source branch $SOURCE_BRANCH no longer exists; nothing to sync"
768
- exit 0
769
- fi
770
- git fetch origin "refs/heads/$SOURCE_BRANCH:refs/remotes/origin/$SOURCE_BRANCH"
771
- git fetch origin "refs/heads/$RELEASE_BRANCH:refs/remotes/origin/$RELEASE_BRANCH"
772
- if git merge-base --is-ancestor "origin/$SOURCE_BRANCH" "origin/$RELEASE_BRANCH"; then
773
- git push origin "refs/remotes/origin/$RELEASE_BRANCH:refs/heads/$SOURCE_BRANCH"
774
- elif git merge-base --is-ancestor "origin/$RELEASE_BRANCH" "origin/$SOURCE_BRANCH"; then
775
- echo "source branch $SOURCE_BRANCH already contains released main"
776
- else
777
- git config user.name "github-actions[bot]"
778
- git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
779
- git switch --force-create "$SOURCE_BRANCH" "origin/$SOURCE_BRANCH"
780
- if git merge --no-ff --no-edit "origin/$RELEASE_BRANCH"; then
781
- git push origin "HEAD:refs/heads/$SOURCE_BRANCH"
782
- else
783
- SAFE_GENERATED="true"
784
- while IFS= read -r FILE; do
785
- case "$FILE" in
786
- .projen/release-units.json|.release-please-manifest.json|.release-units/*/source.json|.release-units/*/version.txt|.release-units/*/CHANGELOG.md|Cargo.lock|*/Cargo.toml|*/package.json|*/pyproject.toml|*/index.ts) ;;
787
- *) SAFE_GENERATED="false" ;;
788
- esac
789
- done < <(git diff --name-only --diff-filter=U)
790
- if [ "$SAFE_GENERATED" = "true" ] && bunx projen; then
791
- git add -A
792
- if [ -z "$(git diff --name-only --diff-filter=U)" ]; then
793
- git commit --no-edit
794
- git push origin "HEAD:refs/heads/$SOURCE_BRANCH"
795
- exit 0
796
- fi
797
- fi
798
- git merge --abort
799
- echo "::warning::source branch $SOURCE_BRANCH has handwritten or unresolved conflicts with released main; leaving it untouched"
800
- fi
801
- fi
802
- `
803
- // ============================================================================
804
- ),
805
- },
806
- ],
807
- });
808
- }
809
-
810
- class IndependentReleaseFinalizer extends Component {
811
- private finalized = false;
812
-
813
- constructor(
814
- project: DBXToolsJavaScriptProject,
815
- private readonly workflow: GithubWorkflow,
816
- private readonly docs: ReleaseDocsOptions | undefined,
817
- private readonly syncBranch: string | undefined,
818
- ) {
819
- super(project);
820
- }
821
-
822
- public override preSynthesize(): void {
823
- if (this.finalized) return;
824
- this.finalized = true;
825
- const jobs = [...(independentPublicationJobs.get(this.workflow) ?? [])].sort();
826
- const successful = jobs
827
- .map((job) => `needs.${job}.result != 'failure' && needs.${job}.result != 'cancelled'`)
828
- .join(" && ");
829
- this.workflow.addJob("publication-complete", {
830
- if: `\${{ always()${successful ? ` && ${successful}` : ""} }}`,
831
- needs: jobs,
832
- runsOn: ["ubuntu-latest"],
833
- permissions: { contents: JobPermission.READ },
834
- steps: [{ name: "Confirm publication stages", run: "true" }],
835
- });
836
- if (this.docs) {
837
- addIndependentDocsJobs(this.workflow, this.project as DBXToolsJavaScriptProject, this.docs);
838
- }
839
- if (this.syncBranch) {
840
- addIndependentBranchSyncJob(
841
- this.workflow,
842
- this.project as DBXToolsJavaScriptProject,
843
- this.syncBranch,
844
- );
845
- }
846
- }
847
- }
848
-
849
- function configureIndependentRelease(
850
- project: DBXToolsJavaScriptProject,
851
- options: DBXToolsReleaseOptions,
852
- ): void {
853
- const branch = projectReleaseBranch(project);
854
- if (options.nodeRelease !== false) nodeReleaseProjects.add(project);
855
- applyTasks(project, {
856
- "version:check": {
857
- exec: taskScript(project, "version-check.ts"),
858
- description: "Verify every package against its release unit",
859
- },
860
- "release:plan": {
861
- exec: taskScript(project, "release-plan.ts"),
862
- receiveArgs: true,
863
- description: "Build the affected release plan",
864
- },
865
- release: {
866
- exec: taskScript(project, "release-request.ts", `--base ${JSON.stringify(branch)}`),
867
- receiveArgs: true,
868
- description: "Commit, annotate, and push a source release request",
869
- },
870
- "release:refresh": {
871
- exec: taskScript(project, "release-please.ts", `--target-branch ${JSON.stringify(branch)}`),
872
- receiveArgs: true,
873
- description: "Refresh Release Please PRs, tags, and GitHub Releases",
874
- },
875
- });
876
- if (!project.github) return;
877
- const workflow = new GithubWorkflow(project.github, "release", {
878
- fileName: "release.yml",
879
- limitConcurrency: true,
880
- concurrencyOptions: { group: "release", cancelInProgress: false },
881
- });
882
- workflow.runName =
883
- "release units " +
884
- "${{ github.event_name == 'push' && github.sha || inputs.automatic && 'automatic' || inputs.component }}";
885
- workflow.on({
886
- push: { branches: [branch] },
887
- workflowDispatch: {
888
- inputs: {
889
- automatic: {
890
- description: "Continue an automatically merged Release Please pull request",
891
- type: "boolean",
892
- default: "false",
893
- required: false,
894
- },
895
- component: {
896
- description: "Component to recover",
897
- type: "string",
898
- default: "",
899
- required: false,
900
- },
901
- version: {
902
- description: "Component version to recover",
903
- type: "string",
904
- default: "",
905
- required: false,
906
- },
907
- stage: {
908
- description: "Publication target to recover",
909
- type: "choice",
910
- options: ["all", "node", "python", "rust", "github", "docs"],
911
- default: "all",
912
- required: true,
913
- },
914
- },
915
- },
916
- });
917
- workflow.file?.addOverride("on.workflow_dispatch.inputs.automatic.default", false);
918
- workflow.file?.addOverride("permissions.contents", "read");
919
- workflow.addJob("release-please", independentReleasePleaseJob(project, branch));
920
- workflow.addJob("release-plan", independentReleasePlanJob(project));
921
- workflow.addJob("publish-release-notes", independentReleaseNotesJob(project));
922
- registerIndependentPublicationJob(workflow, "publish-release-notes");
923
- if (options.nodeRelease !== false) {
924
- workflow.addJob("publish-node", independentNodePublishJob(project));
925
- registerIndependentPublicationJob(workflow, "publish-node");
926
- }
927
- new IndependentReleaseFinalizer(project, workflow, options.docs, options.syncBranch || undefined);
928
- configureReleaseRequestWorkflow(project, branch);
929
- releaseWorkflows.set(project, workflow);
930
- }
931
-
932
308
  /** Owns the single release workflow and local release preparation tasks. */
933
309
  export class DBXToolsRelease extends Component {
934
310
  constructor(project: DBXToolsJavaScriptProject, options: DBXToolsReleaseOptions = {}) {
935
311
  super(project);
936
- if (project.releaseCatalog.mode === "independent") {
937
- configureIndependentRelease(project, options);
938
- return;
939
- }
940
312
  const tagPrefix = options.tagPrefix ?? "v";
941
313
  const releaseBranch = projectReleaseBranch(project);
942
314
  const summary = options.summary ?? true;
@@ -981,7 +353,13 @@ export class DBXToolsRelease extends Component {
981
353
  "Prepare, validate, locally publish, and upload a draft release candidate",
982
354
  },
983
355
  "release:assets": {
984
- exec: taskScript(project, "release-candidate.ts"),
356
+ exec: taskScript(
357
+ project,
358
+ "release-candidate.ts",
359
+ options.pythonRoot
360
+ ? `--python-root ${JSON.stringify(options.pythonRoot)}`
361
+ : undefined,
362
+ ),
985
363
  receiveArgs: true,
986
364
  description: "Rebuild and upload a complete draft release candidate",
987
365
  },