@qlover/fe-release 5.0.6 → 5.1.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/dist/cli.cjs CHANGED
@@ -28,7 +28,7 @@ var import_commander = require("commander");
28
28
 
29
29
  // package.json
30
30
  var description = "A tool for releasing front-end projects, supporting multiple release modes and configurations, simplifying the release process and improving efficiency.";
31
- var version = "5.0.6";
31
+ var version = "5.1.0";
32
32
 
33
33
  // src/cli.ts
34
34
  var import_semver = __toESM(require("semver"), 1);
@@ -82,6 +82,10 @@ var releaseJson = {
82
82
  branchName: "release/${repoName}-${releaseId}",
83
83
  releaseTagName: "release-tag-${count}-patch-${releaseId}",
84
84
  commitMessage: "chore(release): ${spaces}",
85
+ /** GitHub release title template (createRelease mode) */
86
+ releaseName: "${name}@${version}",
87
+ /** Path prefixes skipped when creating GitHub releases */
88
+ ignoreReleasePaths: [],
85
89
  label: {
86
90
  name: "CI-Release",
87
91
  color: "1A7F37",
@@ -2745,7 +2749,7 @@ var GithubManager = class {
2745
2749
  */
2746
2750
  getOctokitReleaseOptions(options) {
2747
2751
  const {
2748
- releaseName,
2752
+ releaseName = releaseJson.github.releaseName,
2749
2753
  draft = false,
2750
2754
  preRelease = false,
2751
2755
  autoGenerate = false,
@@ -2754,7 +2758,7 @@ var GithubManager = class {
2754
2758
  discussionCategoryName = void 0
2755
2759
  } = this.context.getParameters("github");
2756
2760
  const name = releaseName;
2757
- const body = autoGenerate ? "" : this.truncateBody(String(releaseNotes));
2761
+ const body = autoGenerate ? "" : this.truncateBody(String(releaseNotes ?? ""));
2758
2762
  return {
2759
2763
  name,
2760
2764
  make_latest: makeLatest.toString(),
@@ -3518,6 +3522,7 @@ var ReleaseFormatter = class {
3518
3522
  var Github = class extends GitBase {
3519
3523
  releaseFormatter;
3520
3524
  githubManager;
3525
+ pather = new Pather();
3521
3526
  constructor(context, props = {}) {
3522
3527
  super(context, "github", props);
3523
3528
  this.releaseFormatter = new ReleaseFormatter(
@@ -3529,6 +3534,9 @@ var Github = class extends GitBase {
3529
3534
  get mode() {
3530
3535
  return this.config.mode || releaseJson.github.mode;
3531
3536
  }
3537
+ get ignoreReleasePaths() {
3538
+ return this.config.ignoreReleasePaths ?? [...releaseJson.github.ignoreReleasePaths];
3539
+ }
3532
3540
  async onBefore() {
3533
3541
  await super.onBefore();
3534
3542
  await this.githubManager.validateToken();
@@ -3554,6 +3562,9 @@ var Github = class extends GitBase {
3554
3562
  if (workspace.dependencyRelease) {
3555
3563
  return workspace;
3556
3564
  }
3565
+ if (this.mode === "createRelease" && this.isIgnoredReleasePath(workspace)) {
3566
+ return workspace;
3567
+ }
3557
3568
  return githubChangelog.enrichWorkspaceChangelog(workspace);
3558
3569
  })
3559
3570
  )
@@ -3562,6 +3573,13 @@ var Github = class extends GitBase {
3562
3573
  }
3563
3574
  async onSuccess() {
3564
3575
  const workspaces = this.context.requireWorkspaces();
3576
+ if (this.mode === "createRelease") {
3577
+ await this.step({
3578
+ label: "Create GitHub Releases",
3579
+ task: () => this.createGithubReleases(workspaces)
3580
+ });
3581
+ return;
3582
+ }
3565
3583
  const releaseBranchResult = await this.step({
3566
3584
  label: "Create Release Branch",
3567
3585
  task: () => this.createReleaseBranch(workspaces)
@@ -3578,6 +3596,63 @@ var Github = class extends GitBase {
3578
3596
  }
3579
3597
  await this.handleReleasePullRequest(workspaces, releaseBranchResult);
3580
3598
  }
3599
+ /**
3600
+ * Whether this workspace path is under an ignoreReleasePaths entry.
3601
+ */
3602
+ isIgnoredReleasePath(workspace) {
3603
+ const ignorePaths = this.ignoreReleasePaths;
3604
+ if (ignorePaths.length === 0) {
3605
+ return false;
3606
+ }
3607
+ return ignorePaths.some(
3608
+ (ignorePath) => this.pather.isSubPath(workspace.path, ignorePath)
3609
+ );
3610
+ }
3611
+ /**
3612
+ * Workspaces that should receive a GitHub Release in createRelease mode.
3613
+ *
3614
+ * Requires `tagName`. Skips dependency-release packages and paths matched
3615
+ * by {@link GithubProps.ignoreReleasePaths}. Private packages are included
3616
+ * when they have a tag.
3617
+ */
3618
+ getReleaseWorkspaces(workspaces) {
3619
+ return workspaces.filter((workspace) => {
3620
+ if (!workspace.tagName) {
3621
+ return false;
3622
+ }
3623
+ if (workspace.dependencyRelease) {
3624
+ return false;
3625
+ }
3626
+ if (this.isIgnoredReleasePath(workspace)) {
3627
+ this.logger.info(
3628
+ `Skip GitHub Release for ignored path: ${workspace.path} (${workspace.tagName})`
3629
+ );
3630
+ return false;
3631
+ }
3632
+ return true;
3633
+ });
3634
+ }
3635
+ /**
3636
+ * Create a GitHub Release for each eligible workspace.
3637
+ *
3638
+ * Release notes use the enriched workspace changelog from `onExec`.
3639
+ */
3640
+ async createGithubReleases(workspaces) {
3641
+ const releaseWorkspaces = this.getReleaseWorkspaces(workspaces);
3642
+ if (releaseWorkspaces.length === 0) {
3643
+ this.logger.warn("No workspaces eligible for GitHub Release creation");
3644
+ return;
3645
+ }
3646
+ this.logger.info(
3647
+ `Creating GitHub Releases (${releaseWorkspaces.length}):
3648
+ ${releaseWorkspaces.map((w) => ` ${w.tagName}`).join("\n")}`
3649
+ );
3650
+ await Promise.all(
3651
+ releaseWorkspaces.map(
3652
+ (workspace) => this.githubManager.createRelease(workspace)
3653
+ )
3654
+ );
3655
+ }
3581
3656
  /**
3582
3657
  * Create the release PR and optionally auto-merge it.
3583
3658
  *
@@ -3933,6 +4008,22 @@ function programArgs() {
3933
4008
  ).option(
3934
4009
  "--workspaces.skip",
3935
4010
  "Skip the Workspaces plugin"
4011
+ ).option(
4012
+ "--github.mode <mode>",
4013
+ "Github mode: createPR (default) or createRelease (fe-config: release.github.mode)",
4014
+ (value) => {
4015
+ if (value !== "createPR" && value !== "createRelease") {
4016
+ throw new Error(
4017
+ 'Invalid github.mode. Must be "createPR" or "createRelease"'
4018
+ );
4019
+ }
4020
+ return value;
4021
+ },
4022
+ "createPR"
4023
+ ).option(
4024
+ "--github.ignore-release-paths <paths>",
4025
+ "Path prefixes to skip when creating GitHub Releases, comma-separated (fe-config: release.github.ignoreReleasePaths)",
4026
+ splitWithComma
3936
4027
  ).option(
3937
4028
  "--github.skip [lifecycle]",
3938
4029
  "Skip the Github plugin (no value) or a lifecycle such as onSuccess"
package/dist/cli.js CHANGED
@@ -5,7 +5,7 @@ import { Command } from "commander";
5
5
 
6
6
  // package.json
7
7
  var description = "A tool for releasing front-end projects, supporting multiple release modes and configurations, simplifying the release process and improving efficiency.";
8
- var version = "5.0.6";
8
+ var version = "5.1.0";
9
9
 
10
10
  // src/cli.ts
11
11
  import semver from "semver";
@@ -59,6 +59,10 @@ var releaseJson = {
59
59
  branchName: "release/${repoName}-${releaseId}",
60
60
  releaseTagName: "release-tag-${count}-patch-${releaseId}",
61
61
  commitMessage: "chore(release): ${spaces}",
62
+ /** GitHub release title template (createRelease mode) */
63
+ releaseName: "${name}@${version}",
64
+ /** Path prefixes skipped when creating GitHub releases */
65
+ ignoreReleasePaths: [],
62
66
  label: {
63
67
  name: "CI-Release",
64
68
  color: "1A7F37",
@@ -2725,7 +2729,7 @@ var GithubManager = class {
2725
2729
  */
2726
2730
  getOctokitReleaseOptions(options) {
2727
2731
  const {
2728
- releaseName,
2732
+ releaseName = releaseJson.github.releaseName,
2729
2733
  draft = false,
2730
2734
  preRelease = false,
2731
2735
  autoGenerate = false,
@@ -2734,7 +2738,7 @@ var GithubManager = class {
2734
2738
  discussionCategoryName = void 0
2735
2739
  } = this.context.getParameters("github");
2736
2740
  const name = releaseName;
2737
- const body = autoGenerate ? "" : this.truncateBody(String(releaseNotes));
2741
+ const body = autoGenerate ? "" : this.truncateBody(String(releaseNotes ?? ""));
2738
2742
  return {
2739
2743
  name,
2740
2744
  make_latest: makeLatest.toString(),
@@ -3498,6 +3502,7 @@ var ReleaseFormatter = class {
3498
3502
  var Github = class extends GitBase {
3499
3503
  releaseFormatter;
3500
3504
  githubManager;
3505
+ pather = new Pather();
3501
3506
  constructor(context, props = {}) {
3502
3507
  super(context, "github", props);
3503
3508
  this.releaseFormatter = new ReleaseFormatter(
@@ -3509,6 +3514,9 @@ var Github = class extends GitBase {
3509
3514
  get mode() {
3510
3515
  return this.config.mode || releaseJson.github.mode;
3511
3516
  }
3517
+ get ignoreReleasePaths() {
3518
+ return this.config.ignoreReleasePaths ?? [...releaseJson.github.ignoreReleasePaths];
3519
+ }
3512
3520
  async onBefore() {
3513
3521
  await super.onBefore();
3514
3522
  await this.githubManager.validateToken();
@@ -3534,6 +3542,9 @@ var Github = class extends GitBase {
3534
3542
  if (workspace.dependencyRelease) {
3535
3543
  return workspace;
3536
3544
  }
3545
+ if (this.mode === "createRelease" && this.isIgnoredReleasePath(workspace)) {
3546
+ return workspace;
3547
+ }
3537
3548
  return githubChangelog.enrichWorkspaceChangelog(workspace);
3538
3549
  })
3539
3550
  )
@@ -3542,6 +3553,13 @@ var Github = class extends GitBase {
3542
3553
  }
3543
3554
  async onSuccess() {
3544
3555
  const workspaces = this.context.requireWorkspaces();
3556
+ if (this.mode === "createRelease") {
3557
+ await this.step({
3558
+ label: "Create GitHub Releases",
3559
+ task: () => this.createGithubReleases(workspaces)
3560
+ });
3561
+ return;
3562
+ }
3545
3563
  const releaseBranchResult = await this.step({
3546
3564
  label: "Create Release Branch",
3547
3565
  task: () => this.createReleaseBranch(workspaces)
@@ -3558,6 +3576,63 @@ var Github = class extends GitBase {
3558
3576
  }
3559
3577
  await this.handleReleasePullRequest(workspaces, releaseBranchResult);
3560
3578
  }
3579
+ /**
3580
+ * Whether this workspace path is under an ignoreReleasePaths entry.
3581
+ */
3582
+ isIgnoredReleasePath(workspace) {
3583
+ const ignorePaths = this.ignoreReleasePaths;
3584
+ if (ignorePaths.length === 0) {
3585
+ return false;
3586
+ }
3587
+ return ignorePaths.some(
3588
+ (ignorePath) => this.pather.isSubPath(workspace.path, ignorePath)
3589
+ );
3590
+ }
3591
+ /**
3592
+ * Workspaces that should receive a GitHub Release in createRelease mode.
3593
+ *
3594
+ * Requires `tagName`. Skips dependency-release packages and paths matched
3595
+ * by {@link GithubProps.ignoreReleasePaths}. Private packages are included
3596
+ * when they have a tag.
3597
+ */
3598
+ getReleaseWorkspaces(workspaces) {
3599
+ return workspaces.filter((workspace) => {
3600
+ if (!workspace.tagName) {
3601
+ return false;
3602
+ }
3603
+ if (workspace.dependencyRelease) {
3604
+ return false;
3605
+ }
3606
+ if (this.isIgnoredReleasePath(workspace)) {
3607
+ this.logger.info(
3608
+ `Skip GitHub Release for ignored path: ${workspace.path} (${workspace.tagName})`
3609
+ );
3610
+ return false;
3611
+ }
3612
+ return true;
3613
+ });
3614
+ }
3615
+ /**
3616
+ * Create a GitHub Release for each eligible workspace.
3617
+ *
3618
+ * Release notes use the enriched workspace changelog from `onExec`.
3619
+ */
3620
+ async createGithubReleases(workspaces) {
3621
+ const releaseWorkspaces = this.getReleaseWorkspaces(workspaces);
3622
+ if (releaseWorkspaces.length === 0) {
3623
+ this.logger.warn("No workspaces eligible for GitHub Release creation");
3624
+ return;
3625
+ }
3626
+ this.logger.info(
3627
+ `Creating GitHub Releases (${releaseWorkspaces.length}):
3628
+ ${releaseWorkspaces.map((w) => ` ${w.tagName}`).join("\n")}`
3629
+ );
3630
+ await Promise.all(
3631
+ releaseWorkspaces.map(
3632
+ (workspace) => this.githubManager.createRelease(workspace)
3633
+ )
3634
+ );
3635
+ }
3561
3636
  /**
3562
3637
  * Create the release PR and optionally auto-merge it.
3563
3638
  *
@@ -3913,6 +3988,22 @@ function programArgs() {
3913
3988
  ).option(
3914
3989
  "--workspaces.skip",
3915
3990
  "Skip the Workspaces plugin"
3991
+ ).option(
3992
+ "--github.mode <mode>",
3993
+ "Github mode: createPR (default) or createRelease (fe-config: release.github.mode)",
3994
+ (value) => {
3995
+ if (value !== "createPR" && value !== "createRelease") {
3996
+ throw new Error(
3997
+ 'Invalid github.mode. Must be "createPR" or "createRelease"'
3998
+ );
3999
+ }
4000
+ return value;
4001
+ },
4002
+ "createPR"
4003
+ ).option(
4004
+ "--github.ignore-release-paths <paths>",
4005
+ "Path prefixes to skip when creating GitHub Releases, comma-separated (fe-config: release.github.ignoreReleasePaths)",
4006
+ splitWithComma
3916
4007
  ).option(
3917
4008
  "--github.skip [lifecycle]",
3918
4009
  "Skip the Github plugin (no value) or a lifecycle such as onSuccess"
package/dist/index.cjs CHANGED
@@ -79,6 +79,10 @@ var releaseJson = {
79
79
  branchName: "release/${repoName}-${releaseId}",
80
80
  releaseTagName: "release-tag-${count}-patch-${releaseId}",
81
81
  commitMessage: "chore(release): ${spaces}",
82
+ /** GitHub release title template (createRelease mode) */
83
+ releaseName: "${name}@${version}",
84
+ /** Path prefixes skipped when creating GitHub releases */
85
+ ignoreReleasePaths: [],
82
86
  label: {
83
87
  name: "CI-Release",
84
88
  color: "1A7F37",
@@ -2750,7 +2754,7 @@ var GithubManager = class {
2750
2754
  */
2751
2755
  getOctokitReleaseOptions(options) {
2752
2756
  const {
2753
- releaseName,
2757
+ releaseName = releaseJson.github.releaseName,
2754
2758
  draft = false,
2755
2759
  preRelease = false,
2756
2760
  autoGenerate = false,
@@ -2759,7 +2763,7 @@ var GithubManager = class {
2759
2763
  discussionCategoryName = void 0
2760
2764
  } = this.context.getParameters("github");
2761
2765
  const name = releaseName;
2762
- const body = autoGenerate ? "" : this.truncateBody(String(releaseNotes));
2766
+ const body = autoGenerate ? "" : this.truncateBody(String(releaseNotes ?? ""));
2763
2767
  return {
2764
2768
  name,
2765
2769
  make_latest: makeLatest.toString(),
@@ -3523,6 +3527,7 @@ var ReleaseFormatter = class {
3523
3527
  var Github = class extends GitBase {
3524
3528
  releaseFormatter;
3525
3529
  githubManager;
3530
+ pather = new Pather();
3526
3531
  constructor(context, props = {}) {
3527
3532
  super(context, "github", props);
3528
3533
  this.releaseFormatter = new ReleaseFormatter(
@@ -3534,6 +3539,9 @@ var Github = class extends GitBase {
3534
3539
  get mode() {
3535
3540
  return this.config.mode || releaseJson.github.mode;
3536
3541
  }
3542
+ get ignoreReleasePaths() {
3543
+ return this.config.ignoreReleasePaths ?? [...releaseJson.github.ignoreReleasePaths];
3544
+ }
3537
3545
  async onBefore() {
3538
3546
  await super.onBefore();
3539
3547
  await this.githubManager.validateToken();
@@ -3559,6 +3567,9 @@ var Github = class extends GitBase {
3559
3567
  if (workspace.dependencyRelease) {
3560
3568
  return workspace;
3561
3569
  }
3570
+ if (this.mode === "createRelease" && this.isIgnoredReleasePath(workspace)) {
3571
+ return workspace;
3572
+ }
3562
3573
  return githubChangelog.enrichWorkspaceChangelog(workspace);
3563
3574
  })
3564
3575
  )
@@ -3567,6 +3578,13 @@ var Github = class extends GitBase {
3567
3578
  }
3568
3579
  async onSuccess() {
3569
3580
  const workspaces = this.context.requireWorkspaces();
3581
+ if (this.mode === "createRelease") {
3582
+ await this.step({
3583
+ label: "Create GitHub Releases",
3584
+ task: () => this.createGithubReleases(workspaces)
3585
+ });
3586
+ return;
3587
+ }
3570
3588
  const releaseBranchResult = await this.step({
3571
3589
  label: "Create Release Branch",
3572
3590
  task: () => this.createReleaseBranch(workspaces)
@@ -3583,6 +3601,63 @@ var Github = class extends GitBase {
3583
3601
  }
3584
3602
  await this.handleReleasePullRequest(workspaces, releaseBranchResult);
3585
3603
  }
3604
+ /**
3605
+ * Whether this workspace path is under an ignoreReleasePaths entry.
3606
+ */
3607
+ isIgnoredReleasePath(workspace) {
3608
+ const ignorePaths = this.ignoreReleasePaths;
3609
+ if (ignorePaths.length === 0) {
3610
+ return false;
3611
+ }
3612
+ return ignorePaths.some(
3613
+ (ignorePath) => this.pather.isSubPath(workspace.path, ignorePath)
3614
+ );
3615
+ }
3616
+ /**
3617
+ * Workspaces that should receive a GitHub Release in createRelease mode.
3618
+ *
3619
+ * Requires `tagName`. Skips dependency-release packages and paths matched
3620
+ * by {@link GithubProps.ignoreReleasePaths}. Private packages are included
3621
+ * when they have a tag.
3622
+ */
3623
+ getReleaseWorkspaces(workspaces) {
3624
+ return workspaces.filter((workspace) => {
3625
+ if (!workspace.tagName) {
3626
+ return false;
3627
+ }
3628
+ if (workspace.dependencyRelease) {
3629
+ return false;
3630
+ }
3631
+ if (this.isIgnoredReleasePath(workspace)) {
3632
+ this.logger.info(
3633
+ `Skip GitHub Release for ignored path: ${workspace.path} (${workspace.tagName})`
3634
+ );
3635
+ return false;
3636
+ }
3637
+ return true;
3638
+ });
3639
+ }
3640
+ /**
3641
+ * Create a GitHub Release for each eligible workspace.
3642
+ *
3643
+ * Release notes use the enriched workspace changelog from `onExec`.
3644
+ */
3645
+ async createGithubReleases(workspaces) {
3646
+ const releaseWorkspaces = this.getReleaseWorkspaces(workspaces);
3647
+ if (releaseWorkspaces.length === 0) {
3648
+ this.logger.warn("No workspaces eligible for GitHub Release creation");
3649
+ return;
3650
+ }
3651
+ this.logger.info(
3652
+ `Creating GitHub Releases (${releaseWorkspaces.length}):
3653
+ ${releaseWorkspaces.map((w) => ` ${w.tagName}`).join("\n")}`
3654
+ );
3655
+ await Promise.all(
3656
+ releaseWorkspaces.map(
3657
+ (workspace) => this.githubManager.createRelease(workspace)
3658
+ )
3659
+ );
3660
+ }
3586
3661
  /**
3587
3662
  * Create the release PR and optionally auto-merge it.
3588
3663
  *
package/dist/index.d.ts CHANGED
@@ -253,7 +253,7 @@ interface ReleaseFormatterConfig {
253
253
 
254
254
  /**
255
255
  * @module Github
256
- * @description GitHub changelog enrichment and release PR plugin
256
+ * @description GitHub changelog enrichment, release PR, and GitHub Release plugin
257
257
  *
258
258
  * Third plugin in the default release pipeline (after {@link Workspaces} and
259
259
  * {@link ChangesetVersion}). Extends {@link GitBase} for git operations and
@@ -262,33 +262,48 @@ interface ReleaseFormatterConfig {
262
262
  * Pipeline phases:
263
263
  * - **onBefore**: validate GitHub token; seed {@link ReleaseFormatter} context
264
264
  * - **onExec**: enrich workspace changelogs with PR/commit links via {@link GithubChangelog}
265
- * - **onSuccess**: create release branch, commit, push, and open PR (unless skipped)
265
+ * - **onSuccess**:
266
+ * - `createPR` — create release branch, commit, push, and open PR (unless skipped)
267
+ * - `createRelease` — create a GitHub Release per workspace (tag + changelog body)
266
268
  *
267
- * Release branch flow:
269
+ * Release branch flow (`createPR`):
268
270
  * 1. `ReleaseFormatter.getReleaseBranch()` — derive branch and tag names from templates
269
271
  * 2. Create branch from `sourceBranch`, commit version/changelog changes, push
270
272
  * 3. Open PR with formatted title/body and optional labels
271
273
  * 4. Auto-merge when `autoMergeReleasePr` is enabled
272
274
  *
275
+ * GitHub Release flow (`createRelease`):
276
+ * 1. Filter workspaces with `tagName`, skipping `dependencyRelease` and
277
+ * paths matched by {@link GithubProps.ignoreReleasePaths}
278
+ * 2. Call {@link GithubManager.createRelease} with workspace changelog as notes
279
+ *
273
280
  * @example Skip PR creation (local dry-run)
274
281
  * ```bash
275
282
  * fe-release --github.skip-create-release-pr --dry-run
276
283
  * ```
277
284
  *
285
+ * @example Publish + GitHub Releases (ignore examples)
286
+ * ```bash
287
+ * fe-release --changesetVersion.mode publish \
288
+ * --github.mode createRelease \
289
+ * --github.ignore-release-paths examples
290
+ * ```
291
+ *
278
292
  * @example fe-config label and merge settings
279
293
  * ```json
280
294
  * {
281
295
  * "release": {
282
296
  * "github": {
283
297
  * "autoMergeReleasePr": false,
284
- * "label": { "name": "CI-Release" }
298
+ * "label": { "name": "CI-Release" },
299
+ * "ignoreReleasePaths": ["examples"]
285
300
  * }
286
301
  * }
287
302
  * }
288
303
  * ```
289
304
  */
290
305
 
291
- type GithubMode = 'createPR';
306
+ type GithubMode = 'createPR' | 'createRelease';
292
307
  type GithubLabel = {
293
308
  /**
294
309
  * Hexadecimal color code for label appearance
@@ -345,8 +360,8 @@ interface GithubProps extends ReleaseFormatterConfig, GitBaseProps {
345
360
  /**
346
361
  * Plugin work mode
347
362
  *
348
- * Currently only `createPR` is supported: enrich changelogs in `onExec`,
349
- * then create release branch and PR in `onSuccess`.
363
+ * - `createPR`: enrich changelogs in `onExec`, then create release branch and PR in `onSuccess`
364
+ * - `createRelease`: enrich changelogs in `onExec`, then create GitHub Releases in `onSuccess`
350
365
  *
351
366
  * @default `'createPR'`
352
367
  */
@@ -423,6 +438,22 @@ interface GithubProps extends ReleaseFormatterConfig, GitBaseProps {
423
438
  * @default false
424
439
  */
425
440
  skipCreateReleasePr?: boolean;
441
+ /**
442
+ * Workspace path prefixes to skip when creating GitHub Releases.
443
+ *
444
+ * Matched with segment-aware path containment (e.g. `examples` skips
445
+ * `examples/react-seed`). Applies only in `createRelease` mode.
446
+ *
447
+ * CLI: `--github.ignore-release-paths`
448
+ * fe-config: `release.github.ignoreReleasePaths`
449
+ *
450
+ * @default `[]`
451
+ * @example
452
+ * ```json
453
+ * { "ignoreReleasePaths": ["examples", "apps/docs"] }
454
+ * ```
455
+ */
456
+ ignoreReleasePaths?: string[];
426
457
  /**
427
458
  * Configuration for release pull request labels
428
459
  *
@@ -1465,6 +1496,10 @@ declare const releaseJson: {
1465
1496
  readonly branchName: "release/${repoName}-${releaseId}";
1466
1497
  readonly releaseTagName: "release-tag-${count}-patch-${releaseId}";
1467
1498
  readonly commitMessage: "chore(release): ${spaces}";
1499
+ /** GitHub release title template (createRelease mode) */
1500
+ readonly releaseName: "${name}@${version}";
1501
+ /** Path prefixes skipped when creating GitHub releases */
1502
+ readonly ignoreReleasePaths: readonly string[];
1468
1503
  readonly label: {
1469
1504
  readonly name: "CI-Release";
1470
1505
  readonly color: "1A7F37";
package/dist/index.js CHANGED
@@ -27,6 +27,10 @@ var releaseJson = {
27
27
  branchName: "release/${repoName}-${releaseId}",
28
28
  releaseTagName: "release-tag-${count}-patch-${releaseId}",
29
29
  commitMessage: "chore(release): ${spaces}",
30
+ /** GitHub release title template (createRelease mode) */
31
+ releaseName: "${name}@${version}",
32
+ /** Path prefixes skipped when creating GitHub releases */
33
+ ignoreReleasePaths: [],
30
34
  label: {
31
35
  name: "CI-Release",
32
36
  color: "1A7F37",
@@ -2701,7 +2705,7 @@ var GithubManager = class {
2701
2705
  */
2702
2706
  getOctokitReleaseOptions(options) {
2703
2707
  const {
2704
- releaseName,
2708
+ releaseName = releaseJson.github.releaseName,
2705
2709
  draft = false,
2706
2710
  preRelease = false,
2707
2711
  autoGenerate = false,
@@ -2710,7 +2714,7 @@ var GithubManager = class {
2710
2714
  discussionCategoryName = void 0
2711
2715
  } = this.context.getParameters("github");
2712
2716
  const name = releaseName;
2713
- const body = autoGenerate ? "" : this.truncateBody(String(releaseNotes));
2717
+ const body = autoGenerate ? "" : this.truncateBody(String(releaseNotes ?? ""));
2714
2718
  return {
2715
2719
  name,
2716
2720
  make_latest: makeLatest.toString(),
@@ -3474,6 +3478,7 @@ var ReleaseFormatter = class {
3474
3478
  var Github = class extends GitBase {
3475
3479
  releaseFormatter;
3476
3480
  githubManager;
3481
+ pather = new Pather();
3477
3482
  constructor(context, props = {}) {
3478
3483
  super(context, "github", props);
3479
3484
  this.releaseFormatter = new ReleaseFormatter(
@@ -3485,6 +3490,9 @@ var Github = class extends GitBase {
3485
3490
  get mode() {
3486
3491
  return this.config.mode || releaseJson.github.mode;
3487
3492
  }
3493
+ get ignoreReleasePaths() {
3494
+ return this.config.ignoreReleasePaths ?? [...releaseJson.github.ignoreReleasePaths];
3495
+ }
3488
3496
  async onBefore() {
3489
3497
  await super.onBefore();
3490
3498
  await this.githubManager.validateToken();
@@ -3510,6 +3518,9 @@ var Github = class extends GitBase {
3510
3518
  if (workspace.dependencyRelease) {
3511
3519
  return workspace;
3512
3520
  }
3521
+ if (this.mode === "createRelease" && this.isIgnoredReleasePath(workspace)) {
3522
+ return workspace;
3523
+ }
3513
3524
  return githubChangelog.enrichWorkspaceChangelog(workspace);
3514
3525
  })
3515
3526
  )
@@ -3518,6 +3529,13 @@ var Github = class extends GitBase {
3518
3529
  }
3519
3530
  async onSuccess() {
3520
3531
  const workspaces = this.context.requireWorkspaces();
3532
+ if (this.mode === "createRelease") {
3533
+ await this.step({
3534
+ label: "Create GitHub Releases",
3535
+ task: () => this.createGithubReleases(workspaces)
3536
+ });
3537
+ return;
3538
+ }
3521
3539
  const releaseBranchResult = await this.step({
3522
3540
  label: "Create Release Branch",
3523
3541
  task: () => this.createReleaseBranch(workspaces)
@@ -3534,6 +3552,63 @@ var Github = class extends GitBase {
3534
3552
  }
3535
3553
  await this.handleReleasePullRequest(workspaces, releaseBranchResult);
3536
3554
  }
3555
+ /**
3556
+ * Whether this workspace path is under an ignoreReleasePaths entry.
3557
+ */
3558
+ isIgnoredReleasePath(workspace) {
3559
+ const ignorePaths = this.ignoreReleasePaths;
3560
+ if (ignorePaths.length === 0) {
3561
+ return false;
3562
+ }
3563
+ return ignorePaths.some(
3564
+ (ignorePath) => this.pather.isSubPath(workspace.path, ignorePath)
3565
+ );
3566
+ }
3567
+ /**
3568
+ * Workspaces that should receive a GitHub Release in createRelease mode.
3569
+ *
3570
+ * Requires `tagName`. Skips dependency-release packages and paths matched
3571
+ * by {@link GithubProps.ignoreReleasePaths}. Private packages are included
3572
+ * when they have a tag.
3573
+ */
3574
+ getReleaseWorkspaces(workspaces) {
3575
+ return workspaces.filter((workspace) => {
3576
+ if (!workspace.tagName) {
3577
+ return false;
3578
+ }
3579
+ if (workspace.dependencyRelease) {
3580
+ return false;
3581
+ }
3582
+ if (this.isIgnoredReleasePath(workspace)) {
3583
+ this.logger.info(
3584
+ `Skip GitHub Release for ignored path: ${workspace.path} (${workspace.tagName})`
3585
+ );
3586
+ return false;
3587
+ }
3588
+ return true;
3589
+ });
3590
+ }
3591
+ /**
3592
+ * Create a GitHub Release for each eligible workspace.
3593
+ *
3594
+ * Release notes use the enriched workspace changelog from `onExec`.
3595
+ */
3596
+ async createGithubReleases(workspaces) {
3597
+ const releaseWorkspaces = this.getReleaseWorkspaces(workspaces);
3598
+ if (releaseWorkspaces.length === 0) {
3599
+ this.logger.warn("No workspaces eligible for GitHub Release creation");
3600
+ return;
3601
+ }
3602
+ this.logger.info(
3603
+ `Creating GitHub Releases (${releaseWorkspaces.length}):
3604
+ ${releaseWorkspaces.map((w) => ` ${w.tagName}`).join("\n")}`
3605
+ );
3606
+ await Promise.all(
3607
+ releaseWorkspaces.map(
3608
+ (workspace) => this.githubManager.createRelease(workspace)
3609
+ )
3610
+ );
3611
+ }
3537
3612
  /**
3538
3613
  * Create the release PR and optionally auto-merge it.
3539
3614
  *
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@qlover/fe-release",
3
3
  "description": "A tool for releasing front-end projects, supporting multiple release modes and configurations, simplifying the release process and improving efficiency.",
4
- "version": "5.0.6",
4
+ "version": "5.1.0",
5
5
  "type": "module",
6
6
  "private": false,
7
7
  "homepage": "https://github.com/qlover/fe-base/tree/master/packages/fe-release",