@qlover/fe-release 5.0.5 → 5.0.6

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.5";
31
+ var version = "5.0.6";
32
32
 
33
33
  // src/cli.ts
34
34
  var import_semver = __toESM(require("semver"), 1);
@@ -592,6 +592,9 @@ function workspaceVersionSummary(workspace) {
592
592
  function shouldProcessWorkspace(workspace, ignoreNonUpdatedPackages) {
593
593
  return !(ignoreNonUpdatedPackages && workspace.dependencyRelease);
594
594
  }
595
+ function isPrivateWorkspace(workspace) {
596
+ return workspace.packageJson?.private === true;
597
+ }
595
598
  function readJson(path) {
596
599
  const packageJsonContent = (0, import_fs.readFileSync)(path, "utf-8");
597
600
  return JSON.parse(packageJsonContent);
@@ -1733,9 +1736,18 @@ ${workspaces?.map((workspace) => workspace.toString()).join("\n")}`
1733
1736
  this.logDryRun("Would run: changeset publish, publish packages and tags");
1734
1737
  const preview = this.mergeWorkspaces(workspaces);
1735
1738
  this.context.setWorkspaces(preview);
1736
- const previewTags = preview.filter(
1737
- (workspace) => this.shouldProcessWorkspace(workspace) && !workspace.dependencyRelease && workspace.tagName
1739
+ const previewTags = this.collectPushableReleaseTags(preview);
1740
+ const privateTags = preview.filter(
1741
+ (workspace) => this.shouldProcessWorkspace(workspace) && !workspace.dependencyRelease && isPrivateWorkspace(workspace) && !!workspace.tagName
1738
1742
  ).map((workspace) => workspace.tagName);
1743
+ if (privateTags.length > 0) {
1744
+ this.logDryRun(
1745
+ `Would create local tag(s) for private package(s):
1746
+ ${[
1747
+ ...new Set(privateTags)
1748
+ ].map((tag) => ` ${tag}`).join("\n")}`
1749
+ );
1750
+ }
1739
1751
  this.logDryRun(
1740
1752
  previewTags.length === 0 ? "Would push release tag(s) from workspace.tagName: none" : `Would push release tag(s) from workspace.tagName:
1741
1753
  ${previewTags.map((tag) => ` ${tag}`).join("\n")}`
@@ -1749,37 +1761,106 @@ ${previewTags.map((tag) => ` ${tag}`).join("\n")}`
1749
1761
  if (workspaces.length > 0) {
1750
1762
  this.syncWorkspaces(workspaces);
1751
1763
  }
1764
+ const syncedWorkspaces = this.context.workspaces ?? workspaces;
1765
+ await this.step({
1766
+ label: "Create Private Package Tags",
1767
+ task: () => this.ensurePrivateReleaseTags(syncedWorkspaces)
1768
+ });
1752
1769
  await this.step({
1753
1770
  label: "Push Release Tags",
1754
- task: () => this.pushWorkspaceReleaseTags(this.context.workspaces ?? workspaces)
1771
+ task: () => this.pushWorkspaceReleaseTags(syncedWorkspaces)
1755
1772
  });
1756
1773
  }
1757
1774
  /**
1758
- * Push release tags from synced workspaces' `tagName` fields.
1775
+ * Collect tag names that should be pushed after `changeset publish`.
1759
1776
  *
1760
- * `changeset publish` only creates tags locally. After {@link syncWorkspaces},
1761
- * each publishable workspace should already have `tagName` (e.g. `pkg@1.2.3`).
1777
+ * Includes public packages (tagged by changesets) and private packages
1778
+ * (tagged by {@link ensurePrivateReleaseTags}). Skips dependency-release
1779
+ * workspaces.
1762
1780
  */
1763
- async pushWorkspaceReleaseTags(workspaces) {
1781
+ collectPushableReleaseTags(workspaces) {
1764
1782
  const tags = workspaces.filter(
1765
1783
  (workspace) => this.shouldProcessWorkspace(workspace) && !workspace.dependencyRelease && !!workspace.tagName
1766
1784
  ).map((workspace) => workspace.tagName);
1767
- const uniqueTags = [...new Set(tags)];
1785
+ return [...new Set(tags)];
1786
+ }
1787
+ /**
1788
+ * List local git tag names (short refs under `refs/tags/`).
1789
+ */
1790
+ async listLocalTags() {
1791
+ const output = await this.shell.exec(
1792
+ 'git for-each-ref --format="%(refname:short)" refs/tags/',
1793
+ { dryRun: false, silent: true }
1794
+ );
1795
+ const tags = output.split("\n").map((line) => line.trim()).filter(Boolean);
1796
+ return new Set(tags);
1797
+ }
1798
+ /**
1799
+ * Create local git tags for private packages after `changeset publish`.
1800
+ *
1801
+ * Changesets skips npm publish and tagging for `private: true` packages.
1802
+ * Release still needs those tags as changelog baselines, so create them here.
1803
+ */
1804
+ async ensurePrivateReleaseTags(workspaces) {
1805
+ const privateWorkspaces = workspaces.filter(
1806
+ (workspace) => this.shouldProcessWorkspace(workspace) && !workspace.dependencyRelease && isPrivateWorkspace(workspace) && !!workspace.tagName
1807
+ );
1808
+ if (privateWorkspaces.length === 0) {
1809
+ return;
1810
+ }
1811
+ const localTags = await this.listLocalTags();
1812
+ for (const workspace of privateWorkspaces) {
1813
+ const tagName = workspace.tagName;
1814
+ if (localTags.has(tagName)) {
1815
+ this.logger.debug(`Private package tag already exists: ${tagName}`);
1816
+ continue;
1817
+ }
1818
+ this.logger.info(
1819
+ `Create local release tag for private package: ${tagName}`
1820
+ );
1821
+ await this.shell.exec(["git", "tag", tagName]);
1822
+ localTags.add(tagName);
1823
+ }
1824
+ }
1825
+ /**
1826
+ * Push release tags from synced workspaces' `tagName` fields.
1827
+ *
1828
+ * After {@link syncWorkspaces} and {@link ensurePrivateReleaseTags}, push
1829
+ * tags that exist as local refs. Missing public-package tags are skipped
1830
+ * (changeset may have skipped publish); they are not invented here.
1831
+ */
1832
+ async pushWorkspaceReleaseTags(workspaces) {
1833
+ const uniqueTags = this.collectPushableReleaseTags(workspaces);
1768
1834
  if (uniqueTags.length === 0) {
1769
1835
  this.logger.warn(
1770
1836
  "No workspace.tagName found after publish sync; skip tag push"
1771
1837
  );
1772
1838
  return;
1773
1839
  }
1840
+ const localTags = await this.listLocalTags();
1841
+ const existingTags = uniqueTags.filter((tag) => localTags.has(tag));
1842
+ const missingTags = uniqueTags.filter((tag) => !localTags.has(tag));
1843
+ if (missingTags.length > 0) {
1844
+ this.logger.warn(
1845
+ `Skip push for missing local tag(s):
1846
+ ${missingTags.map((tag) => ` ${tag}`).join("\n")}`
1847
+ );
1848
+ }
1849
+ if (existingTags.length === 0) {
1850
+ this.logger.warn(
1851
+ "No local release tags found to push after publish; skip tag push"
1852
+ );
1853
+ return;
1854
+ }
1774
1855
  this.logger.info(
1775
- `Pushing ${uniqueTags.length} release tag(s) to origin:
1776
- ${uniqueTags.map((tag) => ` ${tag}`).join("\n")}`
1856
+ `Pushing ${existingTags.length} release tag(s) to origin:
1857
+ ${existingTags.map((tag) => ` ${tag}`).join("\n")}`
1777
1858
  );
1778
1859
  await this.shell.exec([
1779
1860
  "git",
1780
1861
  "push",
1781
1862
  "origin",
1782
- ...uniqueTags.map((tag) => `"refs/tags/${tag}"`)
1863
+ ...existingTags.map((tag) => `"refs/tags/${tag}"`)
1783
1864
  ]);
1784
1865
  }
1785
1866
  syncWorkspaces(workspaces) {
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.5";
8
+ var version = "5.0.6";
9
9
 
10
10
  // src/cli.ts
11
11
  import semver from "semver";
@@ -572,6 +572,9 @@ function workspaceVersionSummary(workspace) {
572
572
  function shouldProcessWorkspace(workspace, ignoreNonUpdatedPackages) {
573
573
  return !(ignoreNonUpdatedPackages && workspace.dependencyRelease);
574
574
  }
575
+ function isPrivateWorkspace(workspace) {
576
+ return workspace.packageJson?.private === true;
577
+ }
575
578
  function readJson(path) {
576
579
  const packageJsonContent = readFileSync(path, "utf-8");
577
580
  return JSON.parse(packageJsonContent);
@@ -1713,9 +1716,18 @@ ${workspaces?.map((workspace) => workspace.toString()).join("\n")}`
1713
1716
  this.logDryRun("Would run: changeset publish, publish packages and tags");
1714
1717
  const preview = this.mergeWorkspaces(workspaces);
1715
1718
  this.context.setWorkspaces(preview);
1716
- const previewTags = preview.filter(
1717
- (workspace) => this.shouldProcessWorkspace(workspace) && !workspace.dependencyRelease && workspace.tagName
1719
+ const previewTags = this.collectPushableReleaseTags(preview);
1720
+ const privateTags = preview.filter(
1721
+ (workspace) => this.shouldProcessWorkspace(workspace) && !workspace.dependencyRelease && isPrivateWorkspace(workspace) && !!workspace.tagName
1718
1722
  ).map((workspace) => workspace.tagName);
1723
+ if (privateTags.length > 0) {
1724
+ this.logDryRun(
1725
+ `Would create local tag(s) for private package(s):
1726
+ ${[
1727
+ ...new Set(privateTags)
1728
+ ].map((tag) => ` ${tag}`).join("\n")}`
1729
+ );
1730
+ }
1719
1731
  this.logDryRun(
1720
1732
  previewTags.length === 0 ? "Would push release tag(s) from workspace.tagName: none" : `Would push release tag(s) from workspace.tagName:
1721
1733
  ${previewTags.map((tag) => ` ${tag}`).join("\n")}`
@@ -1729,37 +1741,106 @@ ${previewTags.map((tag) => ` ${tag}`).join("\n")}`
1729
1741
  if (workspaces.length > 0) {
1730
1742
  this.syncWorkspaces(workspaces);
1731
1743
  }
1744
+ const syncedWorkspaces = this.context.workspaces ?? workspaces;
1745
+ await this.step({
1746
+ label: "Create Private Package Tags",
1747
+ task: () => this.ensurePrivateReleaseTags(syncedWorkspaces)
1748
+ });
1732
1749
  await this.step({
1733
1750
  label: "Push Release Tags",
1734
- task: () => this.pushWorkspaceReleaseTags(this.context.workspaces ?? workspaces)
1751
+ task: () => this.pushWorkspaceReleaseTags(syncedWorkspaces)
1735
1752
  });
1736
1753
  }
1737
1754
  /**
1738
- * Push release tags from synced workspaces' `tagName` fields.
1755
+ * Collect tag names that should be pushed after `changeset publish`.
1739
1756
  *
1740
- * `changeset publish` only creates tags locally. After {@link syncWorkspaces},
1741
- * each publishable workspace should already have `tagName` (e.g. `pkg@1.2.3`).
1757
+ * Includes public packages (tagged by changesets) and private packages
1758
+ * (tagged by {@link ensurePrivateReleaseTags}). Skips dependency-release
1759
+ * workspaces.
1742
1760
  */
1743
- async pushWorkspaceReleaseTags(workspaces) {
1761
+ collectPushableReleaseTags(workspaces) {
1744
1762
  const tags = workspaces.filter(
1745
1763
  (workspace) => this.shouldProcessWorkspace(workspace) && !workspace.dependencyRelease && !!workspace.tagName
1746
1764
  ).map((workspace) => workspace.tagName);
1747
- const uniqueTags = [...new Set(tags)];
1765
+ return [...new Set(tags)];
1766
+ }
1767
+ /**
1768
+ * List local git tag names (short refs under `refs/tags/`).
1769
+ */
1770
+ async listLocalTags() {
1771
+ const output = await this.shell.exec(
1772
+ 'git for-each-ref --format="%(refname:short)" refs/tags/',
1773
+ { dryRun: false, silent: true }
1774
+ );
1775
+ const tags = output.split("\n").map((line) => line.trim()).filter(Boolean);
1776
+ return new Set(tags);
1777
+ }
1778
+ /**
1779
+ * Create local git tags for private packages after `changeset publish`.
1780
+ *
1781
+ * Changesets skips npm publish and tagging for `private: true` packages.
1782
+ * Release still needs those tags as changelog baselines, so create them here.
1783
+ */
1784
+ async ensurePrivateReleaseTags(workspaces) {
1785
+ const privateWorkspaces = workspaces.filter(
1786
+ (workspace) => this.shouldProcessWorkspace(workspace) && !workspace.dependencyRelease && isPrivateWorkspace(workspace) && !!workspace.tagName
1787
+ );
1788
+ if (privateWorkspaces.length === 0) {
1789
+ return;
1790
+ }
1791
+ const localTags = await this.listLocalTags();
1792
+ for (const workspace of privateWorkspaces) {
1793
+ const tagName = workspace.tagName;
1794
+ if (localTags.has(tagName)) {
1795
+ this.logger.debug(`Private package tag already exists: ${tagName}`);
1796
+ continue;
1797
+ }
1798
+ this.logger.info(
1799
+ `Create local release tag for private package: ${tagName}`
1800
+ );
1801
+ await this.shell.exec(["git", "tag", tagName]);
1802
+ localTags.add(tagName);
1803
+ }
1804
+ }
1805
+ /**
1806
+ * Push release tags from synced workspaces' `tagName` fields.
1807
+ *
1808
+ * After {@link syncWorkspaces} and {@link ensurePrivateReleaseTags}, push
1809
+ * tags that exist as local refs. Missing public-package tags are skipped
1810
+ * (changeset may have skipped publish); they are not invented here.
1811
+ */
1812
+ async pushWorkspaceReleaseTags(workspaces) {
1813
+ const uniqueTags = this.collectPushableReleaseTags(workspaces);
1748
1814
  if (uniqueTags.length === 0) {
1749
1815
  this.logger.warn(
1750
1816
  "No workspace.tagName found after publish sync; skip tag push"
1751
1817
  );
1752
1818
  return;
1753
1819
  }
1820
+ const localTags = await this.listLocalTags();
1821
+ const existingTags = uniqueTags.filter((tag) => localTags.has(tag));
1822
+ const missingTags = uniqueTags.filter((tag) => !localTags.has(tag));
1823
+ if (missingTags.length > 0) {
1824
+ this.logger.warn(
1825
+ `Skip push for missing local tag(s):
1826
+ ${missingTags.map((tag) => ` ${tag}`).join("\n")}`
1827
+ );
1828
+ }
1829
+ if (existingTags.length === 0) {
1830
+ this.logger.warn(
1831
+ "No local release tags found to push after publish; skip tag push"
1832
+ );
1833
+ return;
1834
+ }
1754
1835
  this.logger.info(
1755
- `Pushing ${uniqueTags.length} release tag(s) to origin:
1756
- ${uniqueTags.map((tag) => ` ${tag}`).join("\n")}`
1836
+ `Pushing ${existingTags.length} release tag(s) to origin:
1837
+ ${existingTags.map((tag) => ` ${tag}`).join("\n")}`
1757
1838
  );
1758
1839
  await this.shell.exec([
1759
1840
  "git",
1760
1841
  "push",
1761
1842
  "origin",
1762
- ...uniqueTags.map((tag) => `"refs/tags/${tag}"`)
1843
+ ...existingTags.map((tag) => `"refs/tags/${tag}"`)
1763
1844
  ]);
1764
1845
  }
1765
1846
  syncWorkspaces(workspaces) {
package/dist/index.cjs CHANGED
@@ -597,6 +597,9 @@ function workspaceVersionSummary(workspace) {
597
597
  function shouldProcessWorkspace(workspace, ignoreNonUpdatedPackages) {
598
598
  return !(ignoreNonUpdatedPackages && workspace.dependencyRelease);
599
599
  }
600
+ function isPrivateWorkspace(workspace) {
601
+ return workspace.packageJson?.private === true;
602
+ }
600
603
  function readJson(path) {
601
604
  const packageJsonContent = (0, import_fs.readFileSync)(path, "utf-8");
602
605
  return JSON.parse(packageJsonContent);
@@ -1738,9 +1741,18 @@ ${workspaces?.map((workspace) => workspace.toString()).join("\n")}`
1738
1741
  this.logDryRun("Would run: changeset publish, publish packages and tags");
1739
1742
  const preview = this.mergeWorkspaces(workspaces);
1740
1743
  this.context.setWorkspaces(preview);
1741
- const previewTags = preview.filter(
1742
- (workspace) => this.shouldProcessWorkspace(workspace) && !workspace.dependencyRelease && workspace.tagName
1744
+ const previewTags = this.collectPushableReleaseTags(preview);
1745
+ const privateTags = preview.filter(
1746
+ (workspace) => this.shouldProcessWorkspace(workspace) && !workspace.dependencyRelease && isPrivateWorkspace(workspace) && !!workspace.tagName
1743
1747
  ).map((workspace) => workspace.tagName);
1748
+ if (privateTags.length > 0) {
1749
+ this.logDryRun(
1750
+ `Would create local tag(s) for private package(s):
1751
+ ${[
1752
+ ...new Set(privateTags)
1753
+ ].map((tag) => ` ${tag}`).join("\n")}`
1754
+ );
1755
+ }
1744
1756
  this.logDryRun(
1745
1757
  previewTags.length === 0 ? "Would push release tag(s) from workspace.tagName: none" : `Would push release tag(s) from workspace.tagName:
1746
1758
  ${previewTags.map((tag) => ` ${tag}`).join("\n")}`
@@ -1754,37 +1766,106 @@ ${previewTags.map((tag) => ` ${tag}`).join("\n")}`
1754
1766
  if (workspaces.length > 0) {
1755
1767
  this.syncWorkspaces(workspaces);
1756
1768
  }
1769
+ const syncedWorkspaces = this.context.workspaces ?? workspaces;
1770
+ await this.step({
1771
+ label: "Create Private Package Tags",
1772
+ task: () => this.ensurePrivateReleaseTags(syncedWorkspaces)
1773
+ });
1757
1774
  await this.step({
1758
1775
  label: "Push Release Tags",
1759
- task: () => this.pushWorkspaceReleaseTags(this.context.workspaces ?? workspaces)
1776
+ task: () => this.pushWorkspaceReleaseTags(syncedWorkspaces)
1760
1777
  });
1761
1778
  }
1762
1779
  /**
1763
- * Push release tags from synced workspaces' `tagName` fields.
1780
+ * Collect tag names that should be pushed after `changeset publish`.
1764
1781
  *
1765
- * `changeset publish` only creates tags locally. After {@link syncWorkspaces},
1766
- * each publishable workspace should already have `tagName` (e.g. `pkg@1.2.3`).
1782
+ * Includes public packages (tagged by changesets) and private packages
1783
+ * (tagged by {@link ensurePrivateReleaseTags}). Skips dependency-release
1784
+ * workspaces.
1767
1785
  */
1768
- async pushWorkspaceReleaseTags(workspaces) {
1786
+ collectPushableReleaseTags(workspaces) {
1769
1787
  const tags = workspaces.filter(
1770
1788
  (workspace) => this.shouldProcessWorkspace(workspace) && !workspace.dependencyRelease && !!workspace.tagName
1771
1789
  ).map((workspace) => workspace.tagName);
1772
- const uniqueTags = [...new Set(tags)];
1790
+ return [...new Set(tags)];
1791
+ }
1792
+ /**
1793
+ * List local git tag names (short refs under `refs/tags/`).
1794
+ */
1795
+ async listLocalTags() {
1796
+ const output = await this.shell.exec(
1797
+ 'git for-each-ref --format="%(refname:short)" refs/tags/',
1798
+ { dryRun: false, silent: true }
1799
+ );
1800
+ const tags = output.split("\n").map((line) => line.trim()).filter(Boolean);
1801
+ return new Set(tags);
1802
+ }
1803
+ /**
1804
+ * Create local git tags for private packages after `changeset publish`.
1805
+ *
1806
+ * Changesets skips npm publish and tagging for `private: true` packages.
1807
+ * Release still needs those tags as changelog baselines, so create them here.
1808
+ */
1809
+ async ensurePrivateReleaseTags(workspaces) {
1810
+ const privateWorkspaces = workspaces.filter(
1811
+ (workspace) => this.shouldProcessWorkspace(workspace) && !workspace.dependencyRelease && isPrivateWorkspace(workspace) && !!workspace.tagName
1812
+ );
1813
+ if (privateWorkspaces.length === 0) {
1814
+ return;
1815
+ }
1816
+ const localTags = await this.listLocalTags();
1817
+ for (const workspace of privateWorkspaces) {
1818
+ const tagName = workspace.tagName;
1819
+ if (localTags.has(tagName)) {
1820
+ this.logger.debug(`Private package tag already exists: ${tagName}`);
1821
+ continue;
1822
+ }
1823
+ this.logger.info(
1824
+ `Create local release tag for private package: ${tagName}`
1825
+ );
1826
+ await this.shell.exec(["git", "tag", tagName]);
1827
+ localTags.add(tagName);
1828
+ }
1829
+ }
1830
+ /**
1831
+ * Push release tags from synced workspaces' `tagName` fields.
1832
+ *
1833
+ * After {@link syncWorkspaces} and {@link ensurePrivateReleaseTags}, push
1834
+ * tags that exist as local refs. Missing public-package tags are skipped
1835
+ * (changeset may have skipped publish); they are not invented here.
1836
+ */
1837
+ async pushWorkspaceReleaseTags(workspaces) {
1838
+ const uniqueTags = this.collectPushableReleaseTags(workspaces);
1773
1839
  if (uniqueTags.length === 0) {
1774
1840
  this.logger.warn(
1775
1841
  "No workspace.tagName found after publish sync; skip tag push"
1776
1842
  );
1777
1843
  return;
1778
1844
  }
1845
+ const localTags = await this.listLocalTags();
1846
+ const existingTags = uniqueTags.filter((tag) => localTags.has(tag));
1847
+ const missingTags = uniqueTags.filter((tag) => !localTags.has(tag));
1848
+ if (missingTags.length > 0) {
1849
+ this.logger.warn(
1850
+ `Skip push for missing local tag(s):
1851
+ ${missingTags.map((tag) => ` ${tag}`).join("\n")}`
1852
+ );
1853
+ }
1854
+ if (existingTags.length === 0) {
1855
+ this.logger.warn(
1856
+ "No local release tags found to push after publish; skip tag push"
1857
+ );
1858
+ return;
1859
+ }
1779
1860
  this.logger.info(
1780
- `Pushing ${uniqueTags.length} release tag(s) to origin:
1781
- ${uniqueTags.map((tag) => ` ${tag}`).join("\n")}`
1861
+ `Pushing ${existingTags.length} release tag(s) to origin:
1862
+ ${existingTags.map((tag) => ` ${tag}`).join("\n")}`
1782
1863
  );
1783
1864
  await this.shell.exec([
1784
1865
  "git",
1785
1866
  "push",
1786
1867
  "origin",
1787
- ...uniqueTags.map((tag) => `"refs/tags/${tag}"`)
1868
+ ...existingTags.map((tag) => `"refs/tags/${tag}"`)
1788
1869
  ]);
1789
1870
  }
1790
1871
  syncWorkspaces(workspaces) {
package/dist/index.d.ts CHANGED
@@ -856,9 +856,10 @@ interface ChangeLogInterface {
856
856
  * - **onSuccess**: run version and/or publish flow based on `mode`
857
857
  *
858
858
  * Publish flow (`mode: 'publish'` or second half of `'both'`):
859
- * 1. Run `changeset publish` (npm publish + local `git tag` only)
859
+ * 1. Run `changeset publish` (npm publish + local `git tag` for public packages)
860
860
  * 2. Sync workspaces from disk so `tagName` is set from the release version
861
- * 3. Push `workspace.tagName` refs to `origin`
861
+ * 3. Create local tags for `private` packages (changesets skips tagging them)
862
+ * 4. Push `workspace.tagName` refs that exist locally to `origin`
862
863
  *
863
864
  * Version flow (`mode: 'version'` or first half of `'both'`):
864
865
  * 1. Write `.changeset/*.md` files for directly changed packages only
@@ -992,11 +993,31 @@ declare class ChangesetVersion extends ScriptPlugin<ReleaseContext, ChangesetVer
992
993
  protected logDryRun(message: string): void;
993
994
  protected runVersionFlow(): Promise<void>;
994
995
  protected runChangesetPublish(): Promise<void>;
996
+ /**
997
+ * Collect tag names that should be pushed after `changeset publish`.
998
+ *
999
+ * Includes public packages (tagged by changesets) and private packages
1000
+ * (tagged by {@link ensurePrivateReleaseTags}). Skips dependency-release
1001
+ * workspaces.
1002
+ */
1003
+ protected collectPushableReleaseTags(workspaces: WorkspaceInterface[]): string[];
1004
+ /**
1005
+ * List local git tag names (short refs under `refs/tags/`).
1006
+ */
1007
+ protected listLocalTags(): Promise<Set<string>>;
1008
+ /**
1009
+ * Create local git tags for private packages after `changeset publish`.
1010
+ *
1011
+ * Changesets skips npm publish and tagging for `private: true` packages.
1012
+ * Release still needs those tags as changelog baselines, so create them here.
1013
+ */
1014
+ protected ensurePrivateReleaseTags(workspaces: WorkspaceInterface[]): Promise<void>;
995
1015
  /**
996
1016
  * Push release tags from synced workspaces' `tagName` fields.
997
1017
  *
998
- * `changeset publish` only creates tags locally. After {@link syncWorkspaces},
999
- * each publishable workspace should already have `tagName` (e.g. `pkg@1.2.3`).
1018
+ * After {@link syncWorkspaces} and {@link ensurePrivateReleaseTags}, push
1019
+ * tags that exist as local refs. Missing public-package tags are skipped
1020
+ * (changeset may have skipped publish); they are not invented here.
1000
1021
  */
1001
1022
  protected pushWorkspaceReleaseTags(workspaces: WorkspaceInterface[]): Promise<void>;
1002
1023
  protected syncWorkspaces(workspaces: WorkspaceInterface[]): void;
package/dist/index.js CHANGED
@@ -548,6 +548,9 @@ function workspaceVersionSummary(workspace) {
548
548
  function shouldProcessWorkspace(workspace, ignoreNonUpdatedPackages) {
549
549
  return !(ignoreNonUpdatedPackages && workspace.dependencyRelease);
550
550
  }
551
+ function isPrivateWorkspace(workspace) {
552
+ return workspace.packageJson?.private === true;
553
+ }
551
554
  function readJson(path) {
552
555
  const packageJsonContent = readFileSync(path, "utf-8");
553
556
  return JSON.parse(packageJsonContent);
@@ -1689,9 +1692,18 @@ ${workspaces?.map((workspace) => workspace.toString()).join("\n")}`
1689
1692
  this.logDryRun("Would run: changeset publish, publish packages and tags");
1690
1693
  const preview = this.mergeWorkspaces(workspaces);
1691
1694
  this.context.setWorkspaces(preview);
1692
- const previewTags = preview.filter(
1693
- (workspace) => this.shouldProcessWorkspace(workspace) && !workspace.dependencyRelease && workspace.tagName
1695
+ const previewTags = this.collectPushableReleaseTags(preview);
1696
+ const privateTags = preview.filter(
1697
+ (workspace) => this.shouldProcessWorkspace(workspace) && !workspace.dependencyRelease && isPrivateWorkspace(workspace) && !!workspace.tagName
1694
1698
  ).map((workspace) => workspace.tagName);
1699
+ if (privateTags.length > 0) {
1700
+ this.logDryRun(
1701
+ `Would create local tag(s) for private package(s):
1702
+ ${[
1703
+ ...new Set(privateTags)
1704
+ ].map((tag) => ` ${tag}`).join("\n")}`
1705
+ );
1706
+ }
1695
1707
  this.logDryRun(
1696
1708
  previewTags.length === 0 ? "Would push release tag(s) from workspace.tagName: none" : `Would push release tag(s) from workspace.tagName:
1697
1709
  ${previewTags.map((tag) => ` ${tag}`).join("\n")}`
@@ -1705,37 +1717,106 @@ ${previewTags.map((tag) => ` ${tag}`).join("\n")}`
1705
1717
  if (workspaces.length > 0) {
1706
1718
  this.syncWorkspaces(workspaces);
1707
1719
  }
1720
+ const syncedWorkspaces = this.context.workspaces ?? workspaces;
1721
+ await this.step({
1722
+ label: "Create Private Package Tags",
1723
+ task: () => this.ensurePrivateReleaseTags(syncedWorkspaces)
1724
+ });
1708
1725
  await this.step({
1709
1726
  label: "Push Release Tags",
1710
- task: () => this.pushWorkspaceReleaseTags(this.context.workspaces ?? workspaces)
1727
+ task: () => this.pushWorkspaceReleaseTags(syncedWorkspaces)
1711
1728
  });
1712
1729
  }
1713
1730
  /**
1714
- * Push release tags from synced workspaces' `tagName` fields.
1731
+ * Collect tag names that should be pushed after `changeset publish`.
1715
1732
  *
1716
- * `changeset publish` only creates tags locally. After {@link syncWorkspaces},
1717
- * each publishable workspace should already have `tagName` (e.g. `pkg@1.2.3`).
1733
+ * Includes public packages (tagged by changesets) and private packages
1734
+ * (tagged by {@link ensurePrivateReleaseTags}). Skips dependency-release
1735
+ * workspaces.
1718
1736
  */
1719
- async pushWorkspaceReleaseTags(workspaces) {
1737
+ collectPushableReleaseTags(workspaces) {
1720
1738
  const tags = workspaces.filter(
1721
1739
  (workspace) => this.shouldProcessWorkspace(workspace) && !workspace.dependencyRelease && !!workspace.tagName
1722
1740
  ).map((workspace) => workspace.tagName);
1723
- const uniqueTags = [...new Set(tags)];
1741
+ return [...new Set(tags)];
1742
+ }
1743
+ /**
1744
+ * List local git tag names (short refs under `refs/tags/`).
1745
+ */
1746
+ async listLocalTags() {
1747
+ const output = await this.shell.exec(
1748
+ 'git for-each-ref --format="%(refname:short)" refs/tags/',
1749
+ { dryRun: false, silent: true }
1750
+ );
1751
+ const tags = output.split("\n").map((line) => line.trim()).filter(Boolean);
1752
+ return new Set(tags);
1753
+ }
1754
+ /**
1755
+ * Create local git tags for private packages after `changeset publish`.
1756
+ *
1757
+ * Changesets skips npm publish and tagging for `private: true` packages.
1758
+ * Release still needs those tags as changelog baselines, so create them here.
1759
+ */
1760
+ async ensurePrivateReleaseTags(workspaces) {
1761
+ const privateWorkspaces = workspaces.filter(
1762
+ (workspace) => this.shouldProcessWorkspace(workspace) && !workspace.dependencyRelease && isPrivateWorkspace(workspace) && !!workspace.tagName
1763
+ );
1764
+ if (privateWorkspaces.length === 0) {
1765
+ return;
1766
+ }
1767
+ const localTags = await this.listLocalTags();
1768
+ for (const workspace of privateWorkspaces) {
1769
+ const tagName = workspace.tagName;
1770
+ if (localTags.has(tagName)) {
1771
+ this.logger.debug(`Private package tag already exists: ${tagName}`);
1772
+ continue;
1773
+ }
1774
+ this.logger.info(
1775
+ `Create local release tag for private package: ${tagName}`
1776
+ );
1777
+ await this.shell.exec(["git", "tag", tagName]);
1778
+ localTags.add(tagName);
1779
+ }
1780
+ }
1781
+ /**
1782
+ * Push release tags from synced workspaces' `tagName` fields.
1783
+ *
1784
+ * After {@link syncWorkspaces} and {@link ensurePrivateReleaseTags}, push
1785
+ * tags that exist as local refs. Missing public-package tags are skipped
1786
+ * (changeset may have skipped publish); they are not invented here.
1787
+ */
1788
+ async pushWorkspaceReleaseTags(workspaces) {
1789
+ const uniqueTags = this.collectPushableReleaseTags(workspaces);
1724
1790
  if (uniqueTags.length === 0) {
1725
1791
  this.logger.warn(
1726
1792
  "No workspace.tagName found after publish sync; skip tag push"
1727
1793
  );
1728
1794
  return;
1729
1795
  }
1796
+ const localTags = await this.listLocalTags();
1797
+ const existingTags = uniqueTags.filter((tag) => localTags.has(tag));
1798
+ const missingTags = uniqueTags.filter((tag) => !localTags.has(tag));
1799
+ if (missingTags.length > 0) {
1800
+ this.logger.warn(
1801
+ `Skip push for missing local tag(s):
1802
+ ${missingTags.map((tag) => ` ${tag}`).join("\n")}`
1803
+ );
1804
+ }
1805
+ if (existingTags.length === 0) {
1806
+ this.logger.warn(
1807
+ "No local release tags found to push after publish; skip tag push"
1808
+ );
1809
+ return;
1810
+ }
1730
1811
  this.logger.info(
1731
- `Pushing ${uniqueTags.length} release tag(s) to origin:
1732
- ${uniqueTags.map((tag) => ` ${tag}`).join("\n")}`
1812
+ `Pushing ${existingTags.length} release tag(s) to origin:
1813
+ ${existingTags.map((tag) => ` ${tag}`).join("\n")}`
1733
1814
  );
1734
1815
  await this.shell.exec([
1735
1816
  "git",
1736
1817
  "push",
1737
1818
  "origin",
1738
- ...uniqueTags.map((tag) => `"refs/tags/${tag}"`)
1819
+ ...existingTags.map((tag) => `"refs/tags/${tag}"`)
1739
1820
  ]);
1740
1821
  }
1741
1822
  syncWorkspaces(workspaces) {
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.5",
4
+ "version": "5.0.6",
5
5
  "type": "module",
6
6
  "private": false,
7
7
  "homepage": "https://github.com/qlover/fe-base/tree/master/packages/fe-release",
@@ -60,7 +60,7 @@
60
60
  "p-limit": "^6.2.0",
61
61
  "semver": "^7.5.4",
62
62
  "@qlover/env-loader": "1.0.2",
63
- "@qlover/fe-corekit": "3.3.0",
63
+ "@qlover/fe-corekit": "3.4.4",
64
64
  "@qlover/logger": "1.2.1",
65
65
  "@qlover/scripts-context": "2.3.3"
66
66
  },