@qlover/fe-release 5.0.4 → 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 +130 -30
- package/dist/cli.js +130 -30
- package/dist/index.cjs +129 -29
- package/dist/index.d.ts +54 -10
- package/dist/index.js +129 -29
- package/package.json +4 -4
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.
|
|
31
|
+
var version = "5.0.6";
|
|
32
32
|
|
|
33
33
|
// src/cli.ts
|
|
34
34
|
var import_semver = __toESM(require("semver"), 1);
|
|
@@ -69,16 +69,23 @@ var releaseJson = {
|
|
|
69
69
|
autoMergeReleasePR: false,
|
|
70
70
|
pushChangeLabels: false,
|
|
71
71
|
skipCreateReleasePR: false,
|
|
72
|
-
|
|
72
|
+
/** Used when releasing 1–2 packages */
|
|
73
|
+
PRTitle: "Release ${spaces}",
|
|
74
|
+
/** Used when releasing more than 2 packages */
|
|
75
|
+
PRTitleMany: "Release ${count} packages: ${spaces}",
|
|
73
76
|
PRBody: "## Changelog\n\n${changelog}",
|
|
74
|
-
|
|
77
|
+
/**
|
|
78
|
+
* Per-workspace section in the PR body (single and multi package).
|
|
79
|
+
* `${changeTypeSection}` is Patch/Minor/Major Changes from increment.
|
|
80
|
+
*/
|
|
81
|
+
batchPRBody: "\n### ${name}@${newVersion}\n\n#### ${changeTypeSection}\n\n${changelog}\n",
|
|
75
82
|
branchName: "release/${repoName}-${releaseId}",
|
|
76
83
|
releaseTagName: "release-tag-${count}-patch-${releaseId}",
|
|
77
84
|
commitMessage: "chore(release): ${spaces}",
|
|
78
85
|
label: {
|
|
79
86
|
name: "CI-Release",
|
|
80
87
|
color: "1A7F37",
|
|
81
|
-
description: "
|
|
88
|
+
description: "Automated release PR (merge to publish)"
|
|
82
89
|
}
|
|
83
90
|
},
|
|
84
91
|
changesetVersion: {
|
|
@@ -585,6 +592,9 @@ function workspaceVersionSummary(workspace) {
|
|
|
585
592
|
function shouldProcessWorkspace(workspace, ignoreNonUpdatedPackages) {
|
|
586
593
|
return !(ignoreNonUpdatedPackages && workspace.dependencyRelease);
|
|
587
594
|
}
|
|
595
|
+
function isPrivateWorkspace(workspace) {
|
|
596
|
+
return workspace.packageJson?.private === true;
|
|
597
|
+
}
|
|
588
598
|
function readJson(path) {
|
|
589
599
|
const packageJsonContent = (0, import_fs.readFileSync)(path, "utf-8");
|
|
590
600
|
return JSON.parse(packageJsonContent);
|
|
@@ -1726,9 +1736,18 @@ ${workspaces?.map((workspace) => workspace.toString()).join("\n")}`
|
|
|
1726
1736
|
this.logDryRun("Would run: changeset publish, publish packages and tags");
|
|
1727
1737
|
const preview = this.mergeWorkspaces(workspaces);
|
|
1728
1738
|
this.context.setWorkspaces(preview);
|
|
1729
|
-
const previewTags =
|
|
1730
|
-
|
|
1739
|
+
const previewTags = this.collectPushableReleaseTags(preview);
|
|
1740
|
+
const privateTags = preview.filter(
|
|
1741
|
+
(workspace) => this.shouldProcessWorkspace(workspace) && !workspace.dependencyRelease && isPrivateWorkspace(workspace) && !!workspace.tagName
|
|
1731
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
|
+
}
|
|
1732
1751
|
this.logDryRun(
|
|
1733
1752
|
previewTags.length === 0 ? "Would push release tag(s) from workspace.tagName: none" : `Would push release tag(s) from workspace.tagName:
|
|
1734
1753
|
${previewTags.map((tag) => ` ${tag}`).join("\n")}`
|
|
@@ -1742,37 +1761,106 @@ ${previewTags.map((tag) => ` ${tag}`).join("\n")}`
|
|
|
1742
1761
|
if (workspaces.length > 0) {
|
|
1743
1762
|
this.syncWorkspaces(workspaces);
|
|
1744
1763
|
}
|
|
1764
|
+
const syncedWorkspaces = this.context.workspaces ?? workspaces;
|
|
1765
|
+
await this.step({
|
|
1766
|
+
label: "Create Private Package Tags",
|
|
1767
|
+
task: () => this.ensurePrivateReleaseTags(syncedWorkspaces)
|
|
1768
|
+
});
|
|
1745
1769
|
await this.step({
|
|
1746
1770
|
label: "Push Release Tags",
|
|
1747
|
-
task: () => this.pushWorkspaceReleaseTags(
|
|
1771
|
+
task: () => this.pushWorkspaceReleaseTags(syncedWorkspaces)
|
|
1748
1772
|
});
|
|
1749
1773
|
}
|
|
1750
1774
|
/**
|
|
1751
|
-
*
|
|
1775
|
+
* Collect tag names that should be pushed after `changeset publish`.
|
|
1752
1776
|
*
|
|
1753
|
-
*
|
|
1754
|
-
*
|
|
1777
|
+
* Includes public packages (tagged by changesets) and private packages
|
|
1778
|
+
* (tagged by {@link ensurePrivateReleaseTags}). Skips dependency-release
|
|
1779
|
+
* workspaces.
|
|
1755
1780
|
*/
|
|
1756
|
-
|
|
1781
|
+
collectPushableReleaseTags(workspaces) {
|
|
1757
1782
|
const tags = workspaces.filter(
|
|
1758
1783
|
(workspace) => this.shouldProcessWorkspace(workspace) && !workspace.dependencyRelease && !!workspace.tagName
|
|
1759
1784
|
).map((workspace) => workspace.tagName);
|
|
1760
|
-
|
|
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);
|
|
1761
1834
|
if (uniqueTags.length === 0) {
|
|
1762
1835
|
this.logger.warn(
|
|
1763
1836
|
"No workspace.tagName found after publish sync; skip tag push"
|
|
1764
1837
|
);
|
|
1765
1838
|
return;
|
|
1766
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
|
+
}
|
|
1767
1855
|
this.logger.info(
|
|
1768
|
-
`Pushing ${
|
|
1769
|
-
${
|
|
1856
|
+
`Pushing ${existingTags.length} release tag(s) to origin:
|
|
1857
|
+
${existingTags.map((tag) => ` ${tag}`).join("\n")}`
|
|
1770
1858
|
);
|
|
1771
1859
|
await this.shell.exec([
|
|
1772
1860
|
"git",
|
|
1773
1861
|
"push",
|
|
1774
1862
|
"origin",
|
|
1775
|
-
...
|
|
1863
|
+
...existingTags.map((tag) => `"refs/tags/${tag}"`)
|
|
1776
1864
|
]);
|
|
1777
1865
|
}
|
|
1778
1866
|
syncWorkspaces(workspaces) {
|
|
@@ -3375,23 +3463,33 @@ var ReleaseFormatter = class {
|
|
|
3375
3463
|
if (workspaces.length === 0) {
|
|
3376
3464
|
return "";
|
|
3377
3465
|
}
|
|
3378
|
-
|
|
3379
|
-
|
|
3380
|
-
}
|
|
3466
|
+
const changeTypeSection = this.getChangeTypeSection();
|
|
3467
|
+
const batchTpl = this.config.batchPRBody || releaseJson.github.batchPRBody;
|
|
3381
3468
|
return workspaces.map(
|
|
3382
|
-
(workspace) => this.templateEngine.render(
|
|
3383
|
-
|
|
3384
|
-
|
|
3385
|
-
|
|
3386
|
-
|
|
3387
|
-
|
|
3388
|
-
|
|
3389
|
-
}
|
|
3390
|
-
)
|
|
3469
|
+
(workspace) => this.templateEngine.render(batchTpl, {
|
|
3470
|
+
...workspace,
|
|
3471
|
+
// Prefer bumped version in PR changelog section headers
|
|
3472
|
+
version: workspace.newVersion || workspace.version,
|
|
3473
|
+
newVersion: workspace.newVersion || workspace.version,
|
|
3474
|
+
changeTypeSection
|
|
3475
|
+
})
|
|
3391
3476
|
).join("\n");
|
|
3392
3477
|
}
|
|
3478
|
+
/**
|
|
3479
|
+
* Map semver increment to Changesets-style section title.
|
|
3480
|
+
*/
|
|
3481
|
+
getChangeTypeSection() {
|
|
3482
|
+
const increment = (this.config.increment || "patch").toLowerCase();
|
|
3483
|
+
if (increment === "major") {
|
|
3484
|
+
return "Major Changes";
|
|
3485
|
+
}
|
|
3486
|
+
if (increment === "minor") {
|
|
3487
|
+
return "Minor Changes";
|
|
3488
|
+
}
|
|
3489
|
+
return "Patch Changes";
|
|
3490
|
+
}
|
|
3393
3491
|
formatPRTagName(workspaces, releaseBranchResult) {
|
|
3394
|
-
if (workspaces.length
|
|
3492
|
+
if (workspaces.length === 0) {
|
|
3395
3493
|
return releaseBranchResult.releaseTagName;
|
|
3396
3494
|
}
|
|
3397
3495
|
return workspaces.map(
|
|
@@ -3399,7 +3497,8 @@ var ReleaseFormatter = class {
|
|
|
3399
3497
|
).join(" ");
|
|
3400
3498
|
}
|
|
3401
3499
|
getPRTitle(releaseBranchResult, context, workspaces = []) {
|
|
3402
|
-
const
|
|
3500
|
+
const count = workspaces.length;
|
|
3501
|
+
const prTitleTpl = count > 2 ? this.config.PRTitleMany || this.config.PRTitle || releaseJson.github.PRTitleMany : this.config.PRTitle || releaseJson.github.PRTitle;
|
|
3403
3502
|
return this.templateEngine.render(
|
|
3404
3503
|
prTitleTpl,
|
|
3405
3504
|
this.buildPRTemplateVars(workspaces, releaseBranchResult, context)
|
|
@@ -3437,7 +3536,8 @@ var Github = class extends GitBase {
|
|
|
3437
3536
|
repoName: this.context.options.repoName,
|
|
3438
3537
|
authorName: this.context.options.authorName,
|
|
3439
3538
|
env: this.context.options.releaseEnv,
|
|
3440
|
-
releaseId: this.context.releaseId
|
|
3539
|
+
releaseId: this.context.releaseId,
|
|
3540
|
+
increment: this.context.parameters.changesetVersion?.increment || releaseJson.changesetVersion.increment
|
|
3441
3541
|
});
|
|
3442
3542
|
this.logger.info(`Github mode: ${this.mode}`);
|
|
3443
3543
|
}
|
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.
|
|
8
|
+
var version = "5.0.6";
|
|
9
9
|
|
|
10
10
|
// src/cli.ts
|
|
11
11
|
import semver from "semver";
|
|
@@ -46,16 +46,23 @@ var releaseJson = {
|
|
|
46
46
|
autoMergeReleasePR: false,
|
|
47
47
|
pushChangeLabels: false,
|
|
48
48
|
skipCreateReleasePR: false,
|
|
49
|
-
|
|
49
|
+
/** Used when releasing 1–2 packages */
|
|
50
|
+
PRTitle: "Release ${spaces}",
|
|
51
|
+
/** Used when releasing more than 2 packages */
|
|
52
|
+
PRTitleMany: "Release ${count} packages: ${spaces}",
|
|
50
53
|
PRBody: "## Changelog\n\n${changelog}",
|
|
51
|
-
|
|
54
|
+
/**
|
|
55
|
+
* Per-workspace section in the PR body (single and multi package).
|
|
56
|
+
* `${changeTypeSection}` is Patch/Minor/Major Changes from increment.
|
|
57
|
+
*/
|
|
58
|
+
batchPRBody: "\n### ${name}@${newVersion}\n\n#### ${changeTypeSection}\n\n${changelog}\n",
|
|
52
59
|
branchName: "release/${repoName}-${releaseId}",
|
|
53
60
|
releaseTagName: "release-tag-${count}-patch-${releaseId}",
|
|
54
61
|
commitMessage: "chore(release): ${spaces}",
|
|
55
62
|
label: {
|
|
56
63
|
name: "CI-Release",
|
|
57
64
|
color: "1A7F37",
|
|
58
|
-
description: "
|
|
65
|
+
description: "Automated release PR (merge to publish)"
|
|
59
66
|
}
|
|
60
67
|
},
|
|
61
68
|
changesetVersion: {
|
|
@@ -565,6 +572,9 @@ function workspaceVersionSummary(workspace) {
|
|
|
565
572
|
function shouldProcessWorkspace(workspace, ignoreNonUpdatedPackages) {
|
|
566
573
|
return !(ignoreNonUpdatedPackages && workspace.dependencyRelease);
|
|
567
574
|
}
|
|
575
|
+
function isPrivateWorkspace(workspace) {
|
|
576
|
+
return workspace.packageJson?.private === true;
|
|
577
|
+
}
|
|
568
578
|
function readJson(path) {
|
|
569
579
|
const packageJsonContent = readFileSync(path, "utf-8");
|
|
570
580
|
return JSON.parse(packageJsonContent);
|
|
@@ -1706,9 +1716,18 @@ ${workspaces?.map((workspace) => workspace.toString()).join("\n")}`
|
|
|
1706
1716
|
this.logDryRun("Would run: changeset publish, publish packages and tags");
|
|
1707
1717
|
const preview = this.mergeWorkspaces(workspaces);
|
|
1708
1718
|
this.context.setWorkspaces(preview);
|
|
1709
|
-
const previewTags =
|
|
1710
|
-
|
|
1719
|
+
const previewTags = this.collectPushableReleaseTags(preview);
|
|
1720
|
+
const privateTags = preview.filter(
|
|
1721
|
+
(workspace) => this.shouldProcessWorkspace(workspace) && !workspace.dependencyRelease && isPrivateWorkspace(workspace) && !!workspace.tagName
|
|
1711
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
|
+
}
|
|
1712
1731
|
this.logDryRun(
|
|
1713
1732
|
previewTags.length === 0 ? "Would push release tag(s) from workspace.tagName: none" : `Would push release tag(s) from workspace.tagName:
|
|
1714
1733
|
${previewTags.map((tag) => ` ${tag}`).join("\n")}`
|
|
@@ -1722,37 +1741,106 @@ ${previewTags.map((tag) => ` ${tag}`).join("\n")}`
|
|
|
1722
1741
|
if (workspaces.length > 0) {
|
|
1723
1742
|
this.syncWorkspaces(workspaces);
|
|
1724
1743
|
}
|
|
1744
|
+
const syncedWorkspaces = this.context.workspaces ?? workspaces;
|
|
1745
|
+
await this.step({
|
|
1746
|
+
label: "Create Private Package Tags",
|
|
1747
|
+
task: () => this.ensurePrivateReleaseTags(syncedWorkspaces)
|
|
1748
|
+
});
|
|
1725
1749
|
await this.step({
|
|
1726
1750
|
label: "Push Release Tags",
|
|
1727
|
-
task: () => this.pushWorkspaceReleaseTags(
|
|
1751
|
+
task: () => this.pushWorkspaceReleaseTags(syncedWorkspaces)
|
|
1728
1752
|
});
|
|
1729
1753
|
}
|
|
1730
1754
|
/**
|
|
1731
|
-
*
|
|
1755
|
+
* Collect tag names that should be pushed after `changeset publish`.
|
|
1732
1756
|
*
|
|
1733
|
-
*
|
|
1734
|
-
*
|
|
1757
|
+
* Includes public packages (tagged by changesets) and private packages
|
|
1758
|
+
* (tagged by {@link ensurePrivateReleaseTags}). Skips dependency-release
|
|
1759
|
+
* workspaces.
|
|
1735
1760
|
*/
|
|
1736
|
-
|
|
1761
|
+
collectPushableReleaseTags(workspaces) {
|
|
1737
1762
|
const tags = workspaces.filter(
|
|
1738
1763
|
(workspace) => this.shouldProcessWorkspace(workspace) && !workspace.dependencyRelease && !!workspace.tagName
|
|
1739
1764
|
).map((workspace) => workspace.tagName);
|
|
1740
|
-
|
|
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);
|
|
1741
1814
|
if (uniqueTags.length === 0) {
|
|
1742
1815
|
this.logger.warn(
|
|
1743
1816
|
"No workspace.tagName found after publish sync; skip tag push"
|
|
1744
1817
|
);
|
|
1745
1818
|
return;
|
|
1746
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
|
+
}
|
|
1747
1835
|
this.logger.info(
|
|
1748
|
-
`Pushing ${
|
|
1749
|
-
${
|
|
1836
|
+
`Pushing ${existingTags.length} release tag(s) to origin:
|
|
1837
|
+
${existingTags.map((tag) => ` ${tag}`).join("\n")}`
|
|
1750
1838
|
);
|
|
1751
1839
|
await this.shell.exec([
|
|
1752
1840
|
"git",
|
|
1753
1841
|
"push",
|
|
1754
1842
|
"origin",
|
|
1755
|
-
...
|
|
1843
|
+
...existingTags.map((tag) => `"refs/tags/${tag}"`)
|
|
1756
1844
|
]);
|
|
1757
1845
|
}
|
|
1758
1846
|
syncWorkspaces(workspaces) {
|
|
@@ -3355,23 +3443,33 @@ var ReleaseFormatter = class {
|
|
|
3355
3443
|
if (workspaces.length === 0) {
|
|
3356
3444
|
return "";
|
|
3357
3445
|
}
|
|
3358
|
-
|
|
3359
|
-
|
|
3360
|
-
}
|
|
3446
|
+
const changeTypeSection = this.getChangeTypeSection();
|
|
3447
|
+
const batchTpl = this.config.batchPRBody || releaseJson.github.batchPRBody;
|
|
3361
3448
|
return workspaces.map(
|
|
3362
|
-
(workspace) => this.templateEngine.render(
|
|
3363
|
-
|
|
3364
|
-
|
|
3365
|
-
|
|
3366
|
-
|
|
3367
|
-
|
|
3368
|
-
|
|
3369
|
-
}
|
|
3370
|
-
)
|
|
3449
|
+
(workspace) => this.templateEngine.render(batchTpl, {
|
|
3450
|
+
...workspace,
|
|
3451
|
+
// Prefer bumped version in PR changelog section headers
|
|
3452
|
+
version: workspace.newVersion || workspace.version,
|
|
3453
|
+
newVersion: workspace.newVersion || workspace.version,
|
|
3454
|
+
changeTypeSection
|
|
3455
|
+
})
|
|
3371
3456
|
).join("\n");
|
|
3372
3457
|
}
|
|
3458
|
+
/**
|
|
3459
|
+
* Map semver increment to Changesets-style section title.
|
|
3460
|
+
*/
|
|
3461
|
+
getChangeTypeSection() {
|
|
3462
|
+
const increment = (this.config.increment || "patch").toLowerCase();
|
|
3463
|
+
if (increment === "major") {
|
|
3464
|
+
return "Major Changes";
|
|
3465
|
+
}
|
|
3466
|
+
if (increment === "minor") {
|
|
3467
|
+
return "Minor Changes";
|
|
3468
|
+
}
|
|
3469
|
+
return "Patch Changes";
|
|
3470
|
+
}
|
|
3373
3471
|
formatPRTagName(workspaces, releaseBranchResult) {
|
|
3374
|
-
if (workspaces.length
|
|
3472
|
+
if (workspaces.length === 0) {
|
|
3375
3473
|
return releaseBranchResult.releaseTagName;
|
|
3376
3474
|
}
|
|
3377
3475
|
return workspaces.map(
|
|
@@ -3379,7 +3477,8 @@ var ReleaseFormatter = class {
|
|
|
3379
3477
|
).join(" ");
|
|
3380
3478
|
}
|
|
3381
3479
|
getPRTitle(releaseBranchResult, context, workspaces = []) {
|
|
3382
|
-
const
|
|
3480
|
+
const count = workspaces.length;
|
|
3481
|
+
const prTitleTpl = count > 2 ? this.config.PRTitleMany || this.config.PRTitle || releaseJson.github.PRTitleMany : this.config.PRTitle || releaseJson.github.PRTitle;
|
|
3383
3482
|
return this.templateEngine.render(
|
|
3384
3483
|
prTitleTpl,
|
|
3385
3484
|
this.buildPRTemplateVars(workspaces, releaseBranchResult, context)
|
|
@@ -3417,7 +3516,8 @@ var Github = class extends GitBase {
|
|
|
3417
3516
|
repoName: this.context.options.repoName,
|
|
3418
3517
|
authorName: this.context.options.authorName,
|
|
3419
3518
|
env: this.context.options.releaseEnv,
|
|
3420
|
-
releaseId: this.context.releaseId
|
|
3519
|
+
releaseId: this.context.releaseId,
|
|
3520
|
+
increment: this.context.parameters.changesetVersion?.increment || releaseJson.changesetVersion.increment
|
|
3421
3521
|
});
|
|
3422
3522
|
this.logger.info(`Github mode: ${this.mode}`);
|
|
3423
3523
|
}
|
package/dist/index.cjs
CHANGED
|
@@ -66,16 +66,23 @@ var releaseJson = {
|
|
|
66
66
|
autoMergeReleasePR: false,
|
|
67
67
|
pushChangeLabels: false,
|
|
68
68
|
skipCreateReleasePR: false,
|
|
69
|
-
|
|
69
|
+
/** Used when releasing 1–2 packages */
|
|
70
|
+
PRTitle: "Release ${spaces}",
|
|
71
|
+
/** Used when releasing more than 2 packages */
|
|
72
|
+
PRTitleMany: "Release ${count} packages: ${spaces}",
|
|
70
73
|
PRBody: "## Changelog\n\n${changelog}",
|
|
71
|
-
|
|
74
|
+
/**
|
|
75
|
+
* Per-workspace section in the PR body (single and multi package).
|
|
76
|
+
* `${changeTypeSection}` is Patch/Minor/Major Changes from increment.
|
|
77
|
+
*/
|
|
78
|
+
batchPRBody: "\n### ${name}@${newVersion}\n\n#### ${changeTypeSection}\n\n${changelog}\n",
|
|
72
79
|
branchName: "release/${repoName}-${releaseId}",
|
|
73
80
|
releaseTagName: "release-tag-${count}-patch-${releaseId}",
|
|
74
81
|
commitMessage: "chore(release): ${spaces}",
|
|
75
82
|
label: {
|
|
76
83
|
name: "CI-Release",
|
|
77
84
|
color: "1A7F37",
|
|
78
|
-
description: "
|
|
85
|
+
description: "Automated release PR (merge to publish)"
|
|
79
86
|
}
|
|
80
87
|
},
|
|
81
88
|
changesetVersion: {
|
|
@@ -590,6 +597,9 @@ function workspaceVersionSummary(workspace) {
|
|
|
590
597
|
function shouldProcessWorkspace(workspace, ignoreNonUpdatedPackages) {
|
|
591
598
|
return !(ignoreNonUpdatedPackages && workspace.dependencyRelease);
|
|
592
599
|
}
|
|
600
|
+
function isPrivateWorkspace(workspace) {
|
|
601
|
+
return workspace.packageJson?.private === true;
|
|
602
|
+
}
|
|
593
603
|
function readJson(path) {
|
|
594
604
|
const packageJsonContent = (0, import_fs.readFileSync)(path, "utf-8");
|
|
595
605
|
return JSON.parse(packageJsonContent);
|
|
@@ -1731,9 +1741,18 @@ ${workspaces?.map((workspace) => workspace.toString()).join("\n")}`
|
|
|
1731
1741
|
this.logDryRun("Would run: changeset publish, publish packages and tags");
|
|
1732
1742
|
const preview = this.mergeWorkspaces(workspaces);
|
|
1733
1743
|
this.context.setWorkspaces(preview);
|
|
1734
|
-
const previewTags =
|
|
1735
|
-
|
|
1744
|
+
const previewTags = this.collectPushableReleaseTags(preview);
|
|
1745
|
+
const privateTags = preview.filter(
|
|
1746
|
+
(workspace) => this.shouldProcessWorkspace(workspace) && !workspace.dependencyRelease && isPrivateWorkspace(workspace) && !!workspace.tagName
|
|
1736
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
|
+
}
|
|
1737
1756
|
this.logDryRun(
|
|
1738
1757
|
previewTags.length === 0 ? "Would push release tag(s) from workspace.tagName: none" : `Would push release tag(s) from workspace.tagName:
|
|
1739
1758
|
${previewTags.map((tag) => ` ${tag}`).join("\n")}`
|
|
@@ -1747,37 +1766,106 @@ ${previewTags.map((tag) => ` ${tag}`).join("\n")}`
|
|
|
1747
1766
|
if (workspaces.length > 0) {
|
|
1748
1767
|
this.syncWorkspaces(workspaces);
|
|
1749
1768
|
}
|
|
1769
|
+
const syncedWorkspaces = this.context.workspaces ?? workspaces;
|
|
1770
|
+
await this.step({
|
|
1771
|
+
label: "Create Private Package Tags",
|
|
1772
|
+
task: () => this.ensurePrivateReleaseTags(syncedWorkspaces)
|
|
1773
|
+
});
|
|
1750
1774
|
await this.step({
|
|
1751
1775
|
label: "Push Release Tags",
|
|
1752
|
-
task: () => this.pushWorkspaceReleaseTags(
|
|
1776
|
+
task: () => this.pushWorkspaceReleaseTags(syncedWorkspaces)
|
|
1753
1777
|
});
|
|
1754
1778
|
}
|
|
1755
1779
|
/**
|
|
1756
|
-
*
|
|
1780
|
+
* Collect tag names that should be pushed after `changeset publish`.
|
|
1757
1781
|
*
|
|
1758
|
-
*
|
|
1759
|
-
*
|
|
1782
|
+
* Includes public packages (tagged by changesets) and private packages
|
|
1783
|
+
* (tagged by {@link ensurePrivateReleaseTags}). Skips dependency-release
|
|
1784
|
+
* workspaces.
|
|
1760
1785
|
*/
|
|
1761
|
-
|
|
1786
|
+
collectPushableReleaseTags(workspaces) {
|
|
1762
1787
|
const tags = workspaces.filter(
|
|
1763
1788
|
(workspace) => this.shouldProcessWorkspace(workspace) && !workspace.dependencyRelease && !!workspace.tagName
|
|
1764
1789
|
).map((workspace) => workspace.tagName);
|
|
1765
|
-
|
|
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);
|
|
1766
1839
|
if (uniqueTags.length === 0) {
|
|
1767
1840
|
this.logger.warn(
|
|
1768
1841
|
"No workspace.tagName found after publish sync; skip tag push"
|
|
1769
1842
|
);
|
|
1770
1843
|
return;
|
|
1771
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
|
+
}
|
|
1772
1860
|
this.logger.info(
|
|
1773
|
-
`Pushing ${
|
|
1774
|
-
${
|
|
1861
|
+
`Pushing ${existingTags.length} release tag(s) to origin:
|
|
1862
|
+
${existingTags.map((tag) => ` ${tag}`).join("\n")}`
|
|
1775
1863
|
);
|
|
1776
1864
|
await this.shell.exec([
|
|
1777
1865
|
"git",
|
|
1778
1866
|
"push",
|
|
1779
1867
|
"origin",
|
|
1780
|
-
...
|
|
1868
|
+
...existingTags.map((tag) => `"refs/tags/${tag}"`)
|
|
1781
1869
|
]);
|
|
1782
1870
|
}
|
|
1783
1871
|
syncWorkspaces(workspaces) {
|
|
@@ -3380,23 +3468,33 @@ var ReleaseFormatter = class {
|
|
|
3380
3468
|
if (workspaces.length === 0) {
|
|
3381
3469
|
return "";
|
|
3382
3470
|
}
|
|
3383
|
-
|
|
3384
|
-
|
|
3385
|
-
}
|
|
3471
|
+
const changeTypeSection = this.getChangeTypeSection();
|
|
3472
|
+
const batchTpl = this.config.batchPRBody || releaseJson.github.batchPRBody;
|
|
3386
3473
|
return workspaces.map(
|
|
3387
|
-
(workspace) => this.templateEngine.render(
|
|
3388
|
-
|
|
3389
|
-
|
|
3390
|
-
|
|
3391
|
-
|
|
3392
|
-
|
|
3393
|
-
|
|
3394
|
-
}
|
|
3395
|
-
)
|
|
3474
|
+
(workspace) => this.templateEngine.render(batchTpl, {
|
|
3475
|
+
...workspace,
|
|
3476
|
+
// Prefer bumped version in PR changelog section headers
|
|
3477
|
+
version: workspace.newVersion || workspace.version,
|
|
3478
|
+
newVersion: workspace.newVersion || workspace.version,
|
|
3479
|
+
changeTypeSection
|
|
3480
|
+
})
|
|
3396
3481
|
).join("\n");
|
|
3397
3482
|
}
|
|
3483
|
+
/**
|
|
3484
|
+
* Map semver increment to Changesets-style section title.
|
|
3485
|
+
*/
|
|
3486
|
+
getChangeTypeSection() {
|
|
3487
|
+
const increment = (this.config.increment || "patch").toLowerCase();
|
|
3488
|
+
if (increment === "major") {
|
|
3489
|
+
return "Major Changes";
|
|
3490
|
+
}
|
|
3491
|
+
if (increment === "minor") {
|
|
3492
|
+
return "Minor Changes";
|
|
3493
|
+
}
|
|
3494
|
+
return "Patch Changes";
|
|
3495
|
+
}
|
|
3398
3496
|
formatPRTagName(workspaces, releaseBranchResult) {
|
|
3399
|
-
if (workspaces.length
|
|
3497
|
+
if (workspaces.length === 0) {
|
|
3400
3498
|
return releaseBranchResult.releaseTagName;
|
|
3401
3499
|
}
|
|
3402
3500
|
return workspaces.map(
|
|
@@ -3404,7 +3502,8 @@ var ReleaseFormatter = class {
|
|
|
3404
3502
|
).join(" ");
|
|
3405
3503
|
}
|
|
3406
3504
|
getPRTitle(releaseBranchResult, context, workspaces = []) {
|
|
3407
|
-
const
|
|
3505
|
+
const count = workspaces.length;
|
|
3506
|
+
const prTitleTpl = count > 2 ? this.config.PRTitleMany || this.config.PRTitle || releaseJson.github.PRTitleMany : this.config.PRTitle || releaseJson.github.PRTitle;
|
|
3408
3507
|
return this.templateEngine.render(
|
|
3409
3508
|
prTitleTpl,
|
|
3410
3509
|
this.buildPRTemplateVars(workspaces, releaseBranchResult, context)
|
|
@@ -3442,7 +3541,8 @@ var Github = class extends GitBase {
|
|
|
3442
3541
|
repoName: this.context.options.repoName,
|
|
3443
3542
|
authorName: this.context.options.authorName,
|
|
3444
3543
|
env: this.context.options.releaseEnv,
|
|
3445
|
-
releaseId: this.context.releaseId
|
|
3544
|
+
releaseId: this.context.releaseId,
|
|
3545
|
+
increment: this.context.parameters.changesetVersion?.increment || releaseJson.changesetVersion.increment
|
|
3446
3546
|
});
|
|
3447
3547
|
this.logger.info(`Github mode: ${this.mode}`);
|
|
3448
3548
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -216,11 +216,19 @@ interface ReleaseFormatterConfig {
|
|
|
216
216
|
more: string;
|
|
217
217
|
};
|
|
218
218
|
/**
|
|
219
|
-
* Pull request title template
|
|
219
|
+
* Pull request title template (1–2 packages)
|
|
220
220
|
*
|
|
221
|
-
* @default {@link
|
|
221
|
+
* @default {@link releaseJson.github.PRTitle}
|
|
222
222
|
*/
|
|
223
223
|
PRTitle?: string;
|
|
224
|
+
/**
|
|
225
|
+
* Pull request title template when releasing more than 2 packages
|
|
226
|
+
*
|
|
227
|
+
* Falls back to {@link PRTitle} when unset.
|
|
228
|
+
*
|
|
229
|
+
* @default {@link releaseJson.github.PRTitleMany}
|
|
230
|
+
*/
|
|
231
|
+
PRTitleMany?: string;
|
|
224
232
|
/**
|
|
225
233
|
* Pull request body template
|
|
226
234
|
*
|
|
@@ -228,11 +236,19 @@ interface ReleaseFormatterConfig {
|
|
|
228
236
|
*/
|
|
229
237
|
PRBody?: string;
|
|
230
238
|
/**
|
|
231
|
-
* Template for each workspace section in
|
|
239
|
+
* Template for each workspace section in the PR body
|
|
240
|
+
*
|
|
241
|
+
* Used for both single- and multi-workspace releases.
|
|
242
|
+
* Supports `${changeTypeSection}` (Patch/Minor/Major Changes).
|
|
232
243
|
*
|
|
233
244
|
* @default from release.json
|
|
234
245
|
*/
|
|
235
246
|
batchPRBody?: string;
|
|
247
|
+
/**
|
|
248
|
+
* Semver increment used to title changelog sections (patch | minor | major)
|
|
249
|
+
* @default 'patch'
|
|
250
|
+
*/
|
|
251
|
+
increment?: string;
|
|
236
252
|
}
|
|
237
253
|
|
|
238
254
|
/**
|
|
@@ -840,9 +856,10 @@ interface ChangeLogInterface {
|
|
|
840
856
|
* - **onSuccess**: run version and/or publish flow based on `mode`
|
|
841
857
|
*
|
|
842
858
|
* Publish flow (`mode: 'publish'` or second half of `'both'`):
|
|
843
|
-
* 1. Run `changeset publish` (npm publish + local `git tag`
|
|
859
|
+
* 1. Run `changeset publish` (npm publish + local `git tag` for public packages)
|
|
844
860
|
* 2. Sync workspaces from disk so `tagName` is set from the release version
|
|
845
|
-
* 3.
|
|
861
|
+
* 3. Create local tags for `private` packages (changesets skips tagging them)
|
|
862
|
+
* 4. Push `workspace.tagName` refs that exist locally to `origin`
|
|
846
863
|
*
|
|
847
864
|
* Version flow (`mode: 'version'` or first half of `'both'`):
|
|
848
865
|
* 1. Write `.changeset/*.md` files for directly changed packages only
|
|
@@ -976,11 +993,31 @@ declare class ChangesetVersion extends ScriptPlugin<ReleaseContext, ChangesetVer
|
|
|
976
993
|
protected logDryRun(message: string): void;
|
|
977
994
|
protected runVersionFlow(): Promise<void>;
|
|
978
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>;
|
|
979
1015
|
/**
|
|
980
1016
|
* Push release tags from synced workspaces' `tagName` fields.
|
|
981
1017
|
*
|
|
982
|
-
*
|
|
983
|
-
*
|
|
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.
|
|
984
1021
|
*/
|
|
985
1022
|
protected pushWorkspaceReleaseTags(workspaces: WorkspaceInterface[]): Promise<void>;
|
|
986
1023
|
protected syncWorkspaces(workspaces: WorkspaceInterface[]): void;
|
|
@@ -1415,16 +1452,23 @@ declare const releaseJson: {
|
|
|
1415
1452
|
readonly autoMergeReleasePR: false;
|
|
1416
1453
|
readonly pushChangeLabels: false;
|
|
1417
1454
|
readonly skipCreateReleasePR: false;
|
|
1418
|
-
|
|
1455
|
+
/** Used when releasing 1–2 packages */
|
|
1456
|
+
readonly PRTitle: "Release ${spaces}";
|
|
1457
|
+
/** Used when releasing more than 2 packages */
|
|
1458
|
+
readonly PRTitleMany: "Release ${count} packages: ${spaces}";
|
|
1419
1459
|
readonly PRBody: "## Changelog\n\n${changelog}";
|
|
1420
|
-
|
|
1460
|
+
/**
|
|
1461
|
+
* Per-workspace section in the PR body (single and multi package).
|
|
1462
|
+
* `${changeTypeSection}` is Patch/Minor/Major Changes from increment.
|
|
1463
|
+
*/
|
|
1464
|
+
readonly batchPRBody: "\n### ${name}@${newVersion}\n\n#### ${changeTypeSection}\n\n${changelog}\n";
|
|
1421
1465
|
readonly branchName: "release/${repoName}-${releaseId}";
|
|
1422
1466
|
readonly releaseTagName: "release-tag-${count}-patch-${releaseId}";
|
|
1423
1467
|
readonly commitMessage: "chore(release): ${spaces}";
|
|
1424
1468
|
readonly label: {
|
|
1425
1469
|
readonly name: "CI-Release";
|
|
1426
1470
|
readonly color: "1A7F37";
|
|
1427
|
-
readonly description: "
|
|
1471
|
+
readonly description: "Automated release PR (merge to publish)";
|
|
1428
1472
|
};
|
|
1429
1473
|
};
|
|
1430
1474
|
readonly changesetVersion: {
|
package/dist/index.js
CHANGED
|
@@ -14,16 +14,23 @@ var releaseJson = {
|
|
|
14
14
|
autoMergeReleasePR: false,
|
|
15
15
|
pushChangeLabels: false,
|
|
16
16
|
skipCreateReleasePR: false,
|
|
17
|
-
|
|
17
|
+
/** Used when releasing 1–2 packages */
|
|
18
|
+
PRTitle: "Release ${spaces}",
|
|
19
|
+
/** Used when releasing more than 2 packages */
|
|
20
|
+
PRTitleMany: "Release ${count} packages: ${spaces}",
|
|
18
21
|
PRBody: "## Changelog\n\n${changelog}",
|
|
19
|
-
|
|
22
|
+
/**
|
|
23
|
+
* Per-workspace section in the PR body (single and multi package).
|
|
24
|
+
* `${changeTypeSection}` is Patch/Minor/Major Changes from increment.
|
|
25
|
+
*/
|
|
26
|
+
batchPRBody: "\n### ${name}@${newVersion}\n\n#### ${changeTypeSection}\n\n${changelog}\n",
|
|
20
27
|
branchName: "release/${repoName}-${releaseId}",
|
|
21
28
|
releaseTagName: "release-tag-${count}-patch-${releaseId}",
|
|
22
29
|
commitMessage: "chore(release): ${spaces}",
|
|
23
30
|
label: {
|
|
24
31
|
name: "CI-Release",
|
|
25
32
|
color: "1A7F37",
|
|
26
|
-
description: "
|
|
33
|
+
description: "Automated release PR (merge to publish)"
|
|
27
34
|
}
|
|
28
35
|
},
|
|
29
36
|
changesetVersion: {
|
|
@@ -541,6 +548,9 @@ function workspaceVersionSummary(workspace) {
|
|
|
541
548
|
function shouldProcessWorkspace(workspace, ignoreNonUpdatedPackages) {
|
|
542
549
|
return !(ignoreNonUpdatedPackages && workspace.dependencyRelease);
|
|
543
550
|
}
|
|
551
|
+
function isPrivateWorkspace(workspace) {
|
|
552
|
+
return workspace.packageJson?.private === true;
|
|
553
|
+
}
|
|
544
554
|
function readJson(path) {
|
|
545
555
|
const packageJsonContent = readFileSync(path, "utf-8");
|
|
546
556
|
return JSON.parse(packageJsonContent);
|
|
@@ -1682,9 +1692,18 @@ ${workspaces?.map((workspace) => workspace.toString()).join("\n")}`
|
|
|
1682
1692
|
this.logDryRun("Would run: changeset publish, publish packages and tags");
|
|
1683
1693
|
const preview = this.mergeWorkspaces(workspaces);
|
|
1684
1694
|
this.context.setWorkspaces(preview);
|
|
1685
|
-
const previewTags =
|
|
1686
|
-
|
|
1695
|
+
const previewTags = this.collectPushableReleaseTags(preview);
|
|
1696
|
+
const privateTags = preview.filter(
|
|
1697
|
+
(workspace) => this.shouldProcessWorkspace(workspace) && !workspace.dependencyRelease && isPrivateWorkspace(workspace) && !!workspace.tagName
|
|
1687
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
|
+
}
|
|
1688
1707
|
this.logDryRun(
|
|
1689
1708
|
previewTags.length === 0 ? "Would push release tag(s) from workspace.tagName: none" : `Would push release tag(s) from workspace.tagName:
|
|
1690
1709
|
${previewTags.map((tag) => ` ${tag}`).join("\n")}`
|
|
@@ -1698,37 +1717,106 @@ ${previewTags.map((tag) => ` ${tag}`).join("\n")}`
|
|
|
1698
1717
|
if (workspaces.length > 0) {
|
|
1699
1718
|
this.syncWorkspaces(workspaces);
|
|
1700
1719
|
}
|
|
1720
|
+
const syncedWorkspaces = this.context.workspaces ?? workspaces;
|
|
1721
|
+
await this.step({
|
|
1722
|
+
label: "Create Private Package Tags",
|
|
1723
|
+
task: () => this.ensurePrivateReleaseTags(syncedWorkspaces)
|
|
1724
|
+
});
|
|
1701
1725
|
await this.step({
|
|
1702
1726
|
label: "Push Release Tags",
|
|
1703
|
-
task: () => this.pushWorkspaceReleaseTags(
|
|
1727
|
+
task: () => this.pushWorkspaceReleaseTags(syncedWorkspaces)
|
|
1704
1728
|
});
|
|
1705
1729
|
}
|
|
1706
1730
|
/**
|
|
1707
|
-
*
|
|
1731
|
+
* Collect tag names that should be pushed after `changeset publish`.
|
|
1708
1732
|
*
|
|
1709
|
-
*
|
|
1710
|
-
*
|
|
1733
|
+
* Includes public packages (tagged by changesets) and private packages
|
|
1734
|
+
* (tagged by {@link ensurePrivateReleaseTags}). Skips dependency-release
|
|
1735
|
+
* workspaces.
|
|
1711
1736
|
*/
|
|
1712
|
-
|
|
1737
|
+
collectPushableReleaseTags(workspaces) {
|
|
1713
1738
|
const tags = workspaces.filter(
|
|
1714
1739
|
(workspace) => this.shouldProcessWorkspace(workspace) && !workspace.dependencyRelease && !!workspace.tagName
|
|
1715
1740
|
).map((workspace) => workspace.tagName);
|
|
1716
|
-
|
|
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);
|
|
1717
1790
|
if (uniqueTags.length === 0) {
|
|
1718
1791
|
this.logger.warn(
|
|
1719
1792
|
"No workspace.tagName found after publish sync; skip tag push"
|
|
1720
1793
|
);
|
|
1721
1794
|
return;
|
|
1722
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
|
+
}
|
|
1723
1811
|
this.logger.info(
|
|
1724
|
-
`Pushing ${
|
|
1725
|
-
${
|
|
1812
|
+
`Pushing ${existingTags.length} release tag(s) to origin:
|
|
1813
|
+
${existingTags.map((tag) => ` ${tag}`).join("\n")}`
|
|
1726
1814
|
);
|
|
1727
1815
|
await this.shell.exec([
|
|
1728
1816
|
"git",
|
|
1729
1817
|
"push",
|
|
1730
1818
|
"origin",
|
|
1731
|
-
...
|
|
1819
|
+
...existingTags.map((tag) => `"refs/tags/${tag}"`)
|
|
1732
1820
|
]);
|
|
1733
1821
|
}
|
|
1734
1822
|
syncWorkspaces(workspaces) {
|
|
@@ -3331,23 +3419,33 @@ var ReleaseFormatter = class {
|
|
|
3331
3419
|
if (workspaces.length === 0) {
|
|
3332
3420
|
return "";
|
|
3333
3421
|
}
|
|
3334
|
-
|
|
3335
|
-
|
|
3336
|
-
}
|
|
3422
|
+
const changeTypeSection = this.getChangeTypeSection();
|
|
3423
|
+
const batchTpl = this.config.batchPRBody || releaseJson.github.batchPRBody;
|
|
3337
3424
|
return workspaces.map(
|
|
3338
|
-
(workspace) => this.templateEngine.render(
|
|
3339
|
-
|
|
3340
|
-
|
|
3341
|
-
|
|
3342
|
-
|
|
3343
|
-
|
|
3344
|
-
|
|
3345
|
-
}
|
|
3346
|
-
)
|
|
3425
|
+
(workspace) => this.templateEngine.render(batchTpl, {
|
|
3426
|
+
...workspace,
|
|
3427
|
+
// Prefer bumped version in PR changelog section headers
|
|
3428
|
+
version: workspace.newVersion || workspace.version,
|
|
3429
|
+
newVersion: workspace.newVersion || workspace.version,
|
|
3430
|
+
changeTypeSection
|
|
3431
|
+
})
|
|
3347
3432
|
).join("\n");
|
|
3348
3433
|
}
|
|
3434
|
+
/**
|
|
3435
|
+
* Map semver increment to Changesets-style section title.
|
|
3436
|
+
*/
|
|
3437
|
+
getChangeTypeSection() {
|
|
3438
|
+
const increment = (this.config.increment || "patch").toLowerCase();
|
|
3439
|
+
if (increment === "major") {
|
|
3440
|
+
return "Major Changes";
|
|
3441
|
+
}
|
|
3442
|
+
if (increment === "minor") {
|
|
3443
|
+
return "Minor Changes";
|
|
3444
|
+
}
|
|
3445
|
+
return "Patch Changes";
|
|
3446
|
+
}
|
|
3349
3447
|
formatPRTagName(workspaces, releaseBranchResult) {
|
|
3350
|
-
if (workspaces.length
|
|
3448
|
+
if (workspaces.length === 0) {
|
|
3351
3449
|
return releaseBranchResult.releaseTagName;
|
|
3352
3450
|
}
|
|
3353
3451
|
return workspaces.map(
|
|
@@ -3355,7 +3453,8 @@ var ReleaseFormatter = class {
|
|
|
3355
3453
|
).join(" ");
|
|
3356
3454
|
}
|
|
3357
3455
|
getPRTitle(releaseBranchResult, context, workspaces = []) {
|
|
3358
|
-
const
|
|
3456
|
+
const count = workspaces.length;
|
|
3457
|
+
const prTitleTpl = count > 2 ? this.config.PRTitleMany || this.config.PRTitle || releaseJson.github.PRTitleMany : this.config.PRTitle || releaseJson.github.PRTitle;
|
|
3359
3458
|
return this.templateEngine.render(
|
|
3360
3459
|
prTitleTpl,
|
|
3361
3460
|
this.buildPRTemplateVars(workspaces, releaseBranchResult, context)
|
|
@@ -3393,7 +3492,8 @@ var Github = class extends GitBase {
|
|
|
3393
3492
|
repoName: this.context.options.repoName,
|
|
3394
3493
|
authorName: this.context.options.authorName,
|
|
3395
3494
|
env: this.context.options.releaseEnv,
|
|
3396
|
-
releaseId: this.context.releaseId
|
|
3495
|
+
releaseId: this.context.releaseId,
|
|
3496
|
+
increment: this.context.parameters.changesetVersion?.increment || releaseJson.changesetVersion.increment
|
|
3397
3497
|
});
|
|
3398
3498
|
this.logger.info(`Github mode: ${this.mode}`);
|
|
3399
3499
|
}
|
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.
|
|
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,9 +60,9 @@
|
|
|
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.
|
|
64
|
-
"@qlover/
|
|
65
|
-
"@qlover/
|
|
63
|
+
"@qlover/fe-corekit": "3.4.4",
|
|
64
|
+
"@qlover/logger": "1.2.1",
|
|
65
|
+
"@qlover/scripts-context": "2.3.3"
|
|
66
66
|
},
|
|
67
67
|
"scripts": {
|
|
68
68
|
"lint": "eslint src --fix",
|