@dbx-tools/projen 0.8.0 → 0.9.0

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
@@ -454,16 +454,20 @@ a new package is covered without a re-synth. Work from the root:
454
454
  before notes, AI, commits, pushes, or PR creation. It commits dirty source,
455
455
  writes custom `--notes` / `--notes-file` content or a bounded Cursor, Codex,
456
456
  Claude, then Git fallback to `.release-notes/requests/<branch>.md`, adds one
457
- request commit, and pushes. The generated request workflow creates or updates
458
- the source PR. `--approve` enables auto-merge after checks.
457
+ request commit, and pushes. The generated workflow creates or updates the source
458
+ PR, merges it directly with `GITHUB_TOKEN`, and dispatches release planning, so
459
+ no UI approval or merge is required. GitHub may briefly record
460
+ `action_required` for the bot-authored PR validation; it is not a required gate.
459
461
 
460
462
  After the source PR merges, Release Please retains direct conventional semantic
461
463
  increments and applies required dependent patches. Reconciliation copies the
462
464
  readable request Markdown into one temporary
463
465
  `.release-notes/final/<component>-v<version>.md` per affected component and
464
- deletes the consumed request file in the release PR. Publication applies each
465
- file to its matching GitHub Release, then removes the temporary notes tree in a
466
- cleanup commit. Request and final notes never count as source changes.
466
+ deletes the consumed request file in the release PR, then merges that generated
467
+ PR and explicitly dispatches publication. A source merge with no affected
468
+ release units creates no release PR or publication work. Publication applies
469
+ each file to its matching GitHub Release, then removes the temporary notes tree
470
+ in a cleanup commit. Request and final notes never count as source changes.
467
471
 
468
472
  The GitHub PR workflow runs the explicit `pr:validate` task through Projen's
469
473
  public `BuildWorkflow` `buildTask` option. That task runs synth plus the
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.8.0";
6
+ export const PACKAGE_VERSION = "0.9.0";
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
@@ -50,7 +50,7 @@
50
50
  },
51
51
  "main": "index.ts",
52
52
  "license": "Apache-2.0",
53
- "version": "0.8.0",
53
+ "version": "0.9.0",
54
54
  "types": "index.ts",
55
55
  "type": "module",
56
56
  "exports": {
package/src/codegen.ts CHANGED
@@ -67,6 +67,16 @@ interface CodegenInput {
67
67
  name: string;
68
68
  }
69
69
 
70
+ function packageName(dir: string): string {
71
+ const name = readPackageManifest(dir)?.name;
72
+ return typeof name === "string" && name.length > 0 ? name : basename(dir);
73
+ }
74
+
75
+ function sourceLabel(source: string): string {
76
+ const nodeModulesPrefix = "node_modules/";
77
+ return source.startsWith(nodeModulesPrefix) ? source.slice(nodeModulesPrefix.length) : source;
78
+ }
79
+
70
80
  /** Read a package's `package.json` `codegen.inputs`, or `undefined` if absent. */
71
81
  function codegenInputs(dir: string): string[] | undefined {
72
82
  const codegen = readPackageManifest(dir)?.codegen;
@@ -282,6 +292,7 @@ function generatePackage(
282
292
  ): string {
283
293
  const parsed = inputs.map(parseInputArg);
284
294
  const srcDir = resolve(dir, "src");
295
+ const destinationPackage = packageName(dir);
285
296
  mkdirSync(srcDir, { recursive: true });
286
297
 
287
298
  // Remove prior generated modules (read-only + our header) so a dropped input
@@ -295,12 +306,13 @@ function generatePackage(
295
306
  }
296
307
  }
297
308
 
298
- let warnings = 0;
299
309
  for (const input of parsed) {
300
310
  const sourcePath = resolveInputSource(input.source, dir, projectRoot);
301
311
  if (!existsSync(sourcePath)) {
302
312
  throw new Error(`codegen input not found: ${input.source}`);
303
313
  }
314
+ const destination = `${destinationPackage}/src/${input.name}.ts`;
315
+ const source = sourceLabel(input.source);
304
316
 
305
317
  const sourceText = preprocess(stripImports(tsRuntime, sourcePath));
306
318
  const { getZodSchemasFile, getInferredTypes, errors } = generate({
@@ -310,8 +322,7 @@ function generatePackage(
310
322
  keepComments: true,
311
323
  });
312
324
  if (errors.length) {
313
- warnings += errors.length;
314
- for (const err of errors) logger.warn(` ! ${err}`);
325
+ for (const err of errors) logger.warn(`${destination} from ${source}: ${err}`);
315
326
  }
316
327
 
317
328
  // ts-to-zod adds an import line only when the source references external
@@ -324,11 +335,12 @@ function generatePackage(
324
335
  makeWritable(outPath);
325
336
  writeFileSync(outPath, content);
326
337
  makeReadonly(outPath);
338
+ logger.success(
339
+ `generated ${destination} from ${source}` +
340
+ (errors.length ? ` (${errors.length} warning(s))` : ""),
341
+ );
327
342
  }
328
343
 
329
- logger.success(
330
- `${basename(dir)}: ${parsed.length} module(s)` + (warnings ? ` (${warnings} warning(s))` : ""),
331
- );
332
344
  return dir;
333
345
  }
334
346
 
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 } from "projen/lib/github/workflows-model";
18
+ import { JobPermission, type JobStep, type Triggers } 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";
@@ -943,7 +943,35 @@ function configureBuildWorkflow(
943
943
  });
944
944
  validation.exec("bunx projen default");
945
945
  validation.exec("bun run compile");
946
- const configured = options.buildWorkflowOptions ?? {};
946
+ const configured = {
947
+ ...options.buildWorkflowOptions,
948
+ ...(!options.buildWorkflowOptions?.workflowTriggers &&
949
+ project.releaseCatalog.mode === "independent"
950
+ ? {
951
+ workflowTriggers: {
952
+ pullRequest: {
953
+ types: ["opened", "synchronize", "reopened", "closed"],
954
+ paths: [
955
+ "**",
956
+ "!.projen/release-units.json",
957
+ "!.release-please-manifest.json",
958
+ "!.release-units/**",
959
+ "!.release-notes/**",
960
+ "!docs/releases/**",
961
+ "!Cargo.lock",
962
+ "!**/Cargo.toml",
963
+ "!**/package.json",
964
+ "!**/pyproject.toml",
965
+ "!**/index.ts",
966
+ "!packages/js/node/rust-binary/src/_rust-release-binaries.ts",
967
+ "!packages/js/node/appkit-graphiti/src/_python-runtime.ts",
968
+ ],
969
+ },
970
+ workflowDispatch: {},
971
+ } satisfies Triggers,
972
+ }
973
+ : {}),
974
+ };
947
975
  const compatibility = nodeWorkflowCompatibility(project);
948
976
  const configuredRunner = configured.runsOn !== undefined || configured.runsOnGroup !== undefined;
949
977
  const workflow = new BuildWorkflow(project, {
@@ -800,12 +800,26 @@ function gitReleaseFiles(root: string, paths: readonly string[]): string[] | und
800
800
  .filter(Boolean)
801
801
  .map((path) => resolve(root, path))
802
802
  .filter((path) => existsSync(path))
803
+ .filter((path) => paths.some((sourcePath) => trackedReleaseFile(sourcePath, path)))
803
804
  .sort();
804
805
  } catch {
805
806
  return undefined;
806
807
  }
807
808
  }
808
809
 
810
+ function trackedReleaseFile(sourcePath: string, path: string): boolean {
811
+ const sourceRelative = toPosix(relative(sourcePath, path));
812
+ if (sourceRelative.startsWith("../") || sourceRelative === "..") return false;
813
+ const parts = sourceRelative.split("/");
814
+ const file = parts.pop();
815
+ if (!file) return false;
816
+ if (parts.some((part) => IGNORED_DIRECTORIES.has(part))) return false;
817
+ if (IGNORED_FILES.has(file) || file.endsWith(".lock") || file.startsWith("tsconfig")) {
818
+ return false;
819
+ }
820
+ return sourceRelative !== "index.ts";
821
+ }
822
+
809
823
  function releaseFiles(path: string): string[] {
810
824
  if (!existsSync(path)) return [];
811
825
  const entries = readdirSync(path, { withFileTypes: true });
package/src/release.ts CHANGED
@@ -352,9 +352,10 @@ function addDocsJobs(
352
352
 
353
353
  function independentReleasePleaseJob(project: DBXToolsJavaScriptProject, branch: string): Job {
354
354
  return {
355
- if: "${{ github.event_name == 'push' }}",
355
+ if: "${{ github.event_name == 'push' || inputs.automatic == true }}",
356
356
  runsOn: ["ubuntu-latest"],
357
357
  permissions: {
358
+ actions: JobPermission.WRITE,
358
359
  contents: JobPermission.WRITE,
359
360
  pullRequests: JobPermission.WRITE,
360
361
  },
@@ -419,7 +420,11 @@ function independentReleasePleaseJob(project: DBXToolsJavaScriptProject, branch:
419
420
  {
420
421
  name: "Reconcile generated release PR files",
421
422
  if: "${{ steps.release.outputs.prs_created == 'true' }}",
422
- env: { RELEASE_PRS: "${{ steps.release.outputs.prs }}" },
423
+ env: {
424
+ GH_TOKEN: "${{ github.token }}",
425
+ RELEASE_BRANCH: branch,
426
+ RELEASE_PRS: "${{ steps.release.outputs.prs }}",
427
+ },
423
428
  shell: "bash",
424
429
  run: [
425
430
  'BRANCH="$(jq -r \'.[0].headBranchName\' <<<"$RELEASE_PRS")"',
@@ -438,6 +443,26 @@ function independentReleasePleaseJob(project: DBXToolsJavaScriptProject, branch:
438
443
  ' git commit -m "chore: reconcile release metadata"',
439
444
  ' git push origin "HEAD:$BRANCH"',
440
445
  "fi",
446
+ 'MERGED="false"',
447
+ "for ATTEMPT in $(seq 1 15); do",
448
+ ' STATE="$(gh pr view "$BRANCH" --json state,mergeable --jq \'[.state,.mergeable] | join(" ")\')"',
449
+ ' if [[ "$STATE" == MERGED* ]]; then MERGED="true"; break; fi',
450
+ ' if [ "$STATE" = "OPEN CONFLICTING" ]; then',
451
+ ' echo "::error::release pull request has merge conflicts"',
452
+ " exit 1",
453
+ " fi",
454
+ ' if [ "$STATE" = "OPEN MERGEABLE" ] && gh pr merge "$BRANCH" --merge; then',
455
+ ' MERGED="true"',
456
+ " break",
457
+ " fi",
458
+ ' echo "release pull request is not mergeable yet (attempt $ATTEMPT/15)"',
459
+ " sleep 2",
460
+ "done",
461
+ 'if [ "$MERGED" != "true" ]; then',
462
+ ' echo "::error::release pull request did not become mergeable"',
463
+ " exit 1",
464
+ "fi",
465
+ 'gh workflow run release.yml --ref "$RELEASE_BRANCH" -f automatic=true',
441
466
  ].join("\n"),
442
467
  },
443
468
  ],
@@ -464,7 +489,8 @@ function configureReleaseRequestWorkflow(
464
489
  if: `\${{ github.ref_name != '${baseBranch}' }}`,
465
490
  runsOn: ["ubuntu-latest"],
466
491
  permissions: {
467
- contents: JobPermission.READ,
492
+ actions: JobPermission.WRITE,
493
+ contents: JobPermission.WRITE,
468
494
  pullRequests: JobPermission.WRITE,
469
495
  },
470
496
  steps: [
@@ -489,14 +515,13 @@ function configureReleaseRequestWorkflow(
489
515
  " exit 0",
490
516
  "fi",
491
517
  'echo "requested=true" >> "$GITHUB_OUTPUT"',
492
- 'echo "branch=$(git branch --show-current)" >> "$GITHUB_OUTPUT"',
493
- 'echo "approve=$(sed -n \'s/^Release-Approve: //p\' <<<"$MESSAGE" | tail -1)" >> "$GITHUB_OUTPUT"',
518
+ 'echo "branch=$GITHUB_REF_NAME" >> "$GITHUB_OUTPUT"',
494
519
  'echo "notes_path=$(sed -n \'s/^Release-Notes-Path: //p\' <<<"$MESSAGE" | tail -1)" >> "$GITHUB_OUTPUT"',
495
520
  'echo "title=$(git log -1 --skip=1 --format=%s)" >> "$GITHUB_OUTPUT"',
496
521
  ].join("\n"),
497
522
  },
498
523
  {
499
- name: "Create or update source pull request",
524
+ name: "Create, merge, and release source pull request",
500
525
  if: "${{ steps.request.outputs.requested == 'true' }}",
501
526
  env: {
502
527
  GH_TOKEN: "${{ github.token }}",
@@ -504,7 +529,6 @@ function configureReleaseRequestWorkflow(
504
529
  BASE_BRANCH: baseBranch,
505
530
  TITLE: "${{ steps.request.outputs.title }}",
506
531
  NOTES_PATH: "${{ steps.request.outputs.notes_path }}",
507
- APPROVE: "${{ steps.request.outputs.approve }}",
508
532
  },
509
533
  shell: "bash",
510
534
  run: [
@@ -515,9 +539,11 @@ function configureReleaseRequestWorkflow(
515
539
  'if [ -n "$PR" ]; then',
516
540
  ' gh pr edit "$PR" --title "$TITLE" --body "$BODY"',
517
541
  "else",
518
- ' PR="$(gh pr create --head "$SOURCE_BRANCH" --base "$BASE_BRANCH" --title "$TITLE" --body "$BODY" --json number --jq .number)"',
542
+ ' gh pr create --head "$SOURCE_BRANCH" --base "$BASE_BRANCH" --title "$TITLE" --body "$BODY"',
543
+ ' PR="$(gh pr list --head "$SOURCE_BRANCH" --base "$BASE_BRANCH" --state open --json number --jq \'.[0].number\')"',
519
544
  "fi",
520
- 'if [ "$APPROVE" = "true" ]; then gh pr merge "$PR" --auto --merge; fi',
545
+ 'gh pr merge "$PR" --merge',
546
+ 'gh workflow run release.yml --repo "$GITHUB_REPOSITORY" --ref "$BASE_BRANCH" -f automatic=true',
521
547
  ].join("\n"),
522
548
  },
523
549
  ],
@@ -526,7 +552,7 @@ function configureReleaseRequestWorkflow(
526
552
 
527
553
  function independentReleasePlanJob(project: DBXToolsJavaScriptProject): Job {
528
554
  return {
529
- if: "${{ always() && (github.event_name == 'workflow_dispatch' || needs.release-please.outputs.releases_created == 'true' || needs.release-please.outputs.docs_changed == 'true' || needs.release-please.outputs.recovery_requested == 'true') }}",
555
+ 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') }}",
530
556
  needs: ["release-please"],
531
557
  runsOn: ["ubuntu-latest"],
532
558
  permissions: { contents: JobPermission.READ },
@@ -721,41 +747,67 @@ function addIndependentDocsJobs(
721
747
  });
722
748
  }
723
749
 
724
- function addIndependentBranchSyncJob(workflow: GithubWorkflow, branch: string): void {
750
+ function addIndependentBranchSyncJob(
751
+ workflow: GithubWorkflow,
752
+ project: DBXToolsJavaScriptProject,
753
+ branch: string,
754
+ ): void {
755
+ const releaseBranch = projectReleaseBranch(project);
725
756
  workflow.addJob("sync-release-branch", {
726
- if: "${{ always() && github.event_name == 'push' && needs['publication-complete'].result == 'success' }}",
757
+ if: "${{ always() && (github.event_name == 'push' || inputs.automatic == true) && needs['publication-complete'].result == 'success' }}",
727
758
  needs: ["publication-complete"],
728
759
  runsOn: ["ubuntu-latest"],
729
760
  permissions: { contents: JobPermission.WRITE },
761
+ env: { BUN_VERSION },
730
762
  steps: [
731
763
  {
732
764
  name: "Checkout released main",
733
765
  uses: "actions/checkout@v6",
734
766
  with: { "fetch-depth": 0 },
735
767
  },
768
+ ...bunCacheRestoreSteps(project, {
769
+ ignorePaths: project.workflowCacheIgnorePaths,
770
+ }),
771
+ { name: "Install dependencies", run: "bun install" },
736
772
  {
737
773
  name: `Safely sync ${branch}`,
738
774
  shell: "bash",
739
- env: { SOURCE_BRANCH: branch },
775
+ env: { SOURCE_BRANCH: branch, RELEASE_BRANCH: releaseBranch },
740
776
  run: [
741
777
  'if ! git ls-remote --exit-code --heads origin "refs/heads/$SOURCE_BRANCH" >/dev/null 2>&1; then',
742
778
  ' echo "source branch $SOURCE_BRANCH no longer exists; nothing to sync"',
743
779
  " exit 0",
744
780
  "fi",
745
781
  'git fetch origin "refs/heads/$SOURCE_BRANCH:refs/remotes/origin/$SOURCE_BRANCH"',
746
- 'if git merge-base --is-ancestor "origin/$SOURCE_BRANCH" HEAD; then',
747
- ' git push origin "HEAD:refs/heads/$SOURCE_BRANCH"',
748
- 'elif git merge-base --is-ancestor HEAD "origin/$SOURCE_BRANCH"; then',
782
+ 'git fetch origin "refs/heads/$RELEASE_BRANCH:refs/remotes/origin/$RELEASE_BRANCH"',
783
+ 'if git merge-base --is-ancestor "origin/$SOURCE_BRANCH" "origin/$RELEASE_BRANCH"; then',
784
+ ' git push origin "refs/remotes/origin/$RELEASE_BRANCH:refs/heads/$SOURCE_BRANCH"',
785
+ 'elif git merge-base --is-ancestor "origin/$RELEASE_BRANCH" "origin/$SOURCE_BRANCH"; then',
749
786
  ' echo "source branch $SOURCE_BRANCH already contains released main"',
750
787
  "else",
751
788
  ' git config user.name "github-actions[bot]"',
752
789
  ' git config user.email "41898282+github-actions[bot]@users.noreply.github.com"',
753
790
  ' git switch --force-create "$SOURCE_BRANCH" "origin/$SOURCE_BRANCH"',
754
- ' if git merge --no-ff --no-edit "$GITHUB_SHA"; then',
791
+ ' if git merge --no-ff --no-edit "origin/$RELEASE_BRANCH"; then',
755
792
  ' git push origin "HEAD:refs/heads/$SOURCE_BRANCH"',
756
793
  " else",
794
+ ' SAFE_GENERATED="true"',
795
+ " while IFS= read -r FILE; do",
796
+ ' case "$FILE" in',
797
+ " .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) ;;",
798
+ ' *) SAFE_GENERATED="false" ;;',
799
+ " esac",
800
+ " done < <(git diff --name-only --diff-filter=U)",
801
+ ' if [ "$SAFE_GENERATED" = "true" ] && bunx projen; then',
802
+ " git add -A",
803
+ ' if [ -z "$(git diff --name-only --diff-filter=U)" ]; then',
804
+ " git commit --no-edit",
805
+ ' git push origin "HEAD:refs/heads/$SOURCE_BRANCH"',
806
+ " exit 0",
807
+ " fi",
808
+ " fi",
757
809
  " git merge --abort",
758
- ' echo "::warning::source branch $SOURCE_BRANCH has merge conflicts with released main; leaving it untouched"',
810
+ ' echo "::warning::source branch $SOURCE_BRANCH has handwritten or unresolved conflicts with released main; leaving it untouched"',
759
811
  " fi",
760
812
  "fi",
761
813
  ].join("\n"),
@@ -794,7 +846,11 @@ class IndependentReleaseFinalizer extends Component {
794
846
  addIndependentDocsJobs(this.workflow, this.project as DBXToolsJavaScriptProject, this.docs);
795
847
  }
796
848
  if (this.syncBranch) {
797
- addIndependentBranchSyncJob(this.workflow, this.syncBranch);
849
+ addIndependentBranchSyncJob(
850
+ this.workflow,
851
+ this.project as DBXToolsJavaScriptProject,
852
+ this.syncBranch,
853
+ );
798
854
  }
799
855
  }
800
856
  }
@@ -833,11 +889,18 @@ function configureIndependentRelease(
833
889
  concurrencyOptions: { group: "release", cancelInProgress: false },
834
890
  });
835
891
  workflow.runName =
836
- "release units " + "${{ github.event_name == 'push' && github.sha || inputs.component }}";
892
+ "release units " +
893
+ "${{ github.event_name == 'push' && github.sha || inputs.automatic && 'automatic' || inputs.component }}";
837
894
  workflow.on({
838
895
  push: { branches: [branch] },
839
896
  workflowDispatch: {
840
897
  inputs: {
898
+ automatic: {
899
+ description: "Continue an automatically merged Release Please pull request",
900
+ type: "boolean",
901
+ default: "false",
902
+ required: false,
903
+ },
841
904
  component: {
842
905
  description: "Component to recover",
843
906
  type: "string",
@@ -860,6 +923,7 @@ function configureIndependentRelease(
860
923
  },
861
924
  },
862
925
  });
926
+ workflow.file?.addOverride("on.workflow_dispatch.inputs.automatic.default", false);
863
927
  workflow.file?.addOverride("permissions.contents", "read");
864
928
  workflow.addJob("release-please", independentReleasePleaseJob(project, branch));
865
929
  workflow.addJob("release-plan", independentReleasePlanJob(project));
@@ -112,7 +112,6 @@ if (import.meta.main) {
112
112
  "Release Please version manifest",
113
113
  ".release-please-manifest.json",
114
114
  )
115
- .option("--approve", "compatibility alias for the Release Please release pass")
116
115
  .action(
117
116
  async (options: {
118
117
  owner?: string;
@@ -13,7 +13,6 @@ const logger = log.logger("projen:release-request");
13
13
  export interface PrepareReleaseRequestOptions {
14
14
  readonly root: string;
15
15
  readonly baseBranch: string;
16
- readonly approve?: boolean;
17
16
  readonly message?: string;
18
17
  readonly notes?: string;
19
18
  readonly notesFile?: string;
@@ -94,12 +93,11 @@ export async function prepareReleaseRequest(
94
93
  [
95
94
  "Release-Request: true",
96
95
  `Release-Source-Branch: ${branch}`,
97
- `Release-Approve: ${options.approve === true}`,
98
96
  `Release-Notes-Path: ${notesPath}`,
99
97
  ].join("\n"),
100
98
  ]);
101
99
  if (options.push !== false) run(root, ["push", "--set-upstream", "origin", branch]);
102
- logger.success("release request pushed", { branch, approve: options.approve === true });
100
+ logger.success("release request pushed", { branch });
103
101
  return true;
104
102
  }
105
103
 
@@ -155,19 +153,22 @@ function run(root: string, args: string[]): void {
155
153
  });
156
154
  }
157
155
 
156
+ /** Restore option text after Projen forwards task arguments without shell quoting. */
157
+ function optionText(values?: readonly string[]): string | undefined {
158
+ return values?.join(" ");
159
+ }
160
+
158
161
  if (import.meta.main) {
159
162
  await new Command()
160
163
  .option("--base <branch>", "release target branch", "main")
161
- .option("--approve", "enable merge-queue or auto-merge for the source PR")
162
- .option("--message <message>", "commit message for uncommitted source changes")
163
- .option("--notes <notes>", "custom release notes")
164
+ .option("--message <message...>", "commit message for uncommitted source changes")
165
+ .option("--notes <notes...>", "custom release notes")
164
166
  .option("--notes-file <path>", "read custom release notes from a file")
165
167
  .action(
166
168
  async (options: {
167
169
  base: string;
168
- approve?: boolean;
169
- message?: string;
170
- notes?: string;
170
+ message?: string[];
171
+ notes?: string[];
171
172
  notesFile?: string;
172
173
  }) => {
173
174
  if (options.notes && options.notesFile) {
@@ -176,9 +177,8 @@ if (import.meta.main) {
176
177
  await prepareReleaseRequest({
177
178
  root: project.root() ?? process.cwd(),
178
179
  baseBranch: options.base,
179
- approve: options.approve,
180
- message: options.message,
181
- notes: options.notes,
180
+ message: optionText(options.message),
181
+ notes: optionText(options.notes),
182
182
  notesFile: options.notesFile,
183
183
  });
184
184
  },