@zjex/git-workflow 0.2.9 → 0.2.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -4
- package/dist/index.js +183 -16
- package/package.json +3 -3
- package/scripts/publish.js +274 -0
- package/scripts/publish.sh +55 -9
- package/src/commands/help.ts +8 -0
- package/src/commands/tag.ts +164 -0
- package/src/index.ts +59 -18
- package/scripts/README.md +0 -57
- package/test-box.sh +0 -18
- package/test-full-publish.sh +0 -30
package/README.md
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
# @zjex/git-workflow
|
|
2
2
|
|
|
3
3
|
<p align="center">
|
|
4
|
-
<a href="https://www.npmjs.com/package/@zjex/git-workflow"><img src="https://img.shields.io/npm/v/@zjex/git-workflow
|
|
5
|
-
<a href="https://www.npmjs.com/package/@zjex/git-workflow"><img src="https://img.shields.io/npm/
|
|
6
|
-
<a href="https://
|
|
7
|
-
<img src="https://img.shields.io/
|
|
4
|
+
<a href="https://www.npmjs.com/package/@zjex/git-workflow"><img src="https://img.shields.io/npm/v/@zjex/git-workflow?style=flat&colorA=18181B&colorB=F0DB4F" alt="npm version"></a>
|
|
5
|
+
<a href="https://www.npmjs.com/package/@zjex/git-workflow"><img src="https://img.shields.io/npm/dt/@zjex/git-workflow?style=flat&colorA=18181B&colorB=3178C6" alt="npm downloads"></a>
|
|
6
|
+
<a href="https://github.com/iamzjt-front-end/git-workflow"><img src="https://img.shields.io/github/stars/iamzjt-front-end/git-workflow?style=flat&colorA=18181B&colorB=F59E0B" alt="github stars"></a>
|
|
7
|
+
<a href="https://github.com/iamzjt-front-end/git-workflow/blob/main/LICENSE"><img src="https://img.shields.io/npm/l/@zjex/git-workflow?style=flat&colorA=18181B&colorB=10B981" alt="license"></a>
|
|
8
|
+
<a href="https://nodejs.org"><img src="https://img.shields.io/badge/node-%3E%3D18-339933?style=flat&logo=node.js&logoColor=white&colorA=18181B" alt="node version"></a>
|
|
9
|
+
<a href="https://github.com/iamzjt-front-end/git-workflow/issues"><img src="https://img.shields.io/github/issues/iamzjt-front-end/git-workflow?style=flat&colorA=18181B&colorB=EC4899" alt="issues"></a>
|
|
8
10
|
</p>
|
|
9
11
|
|
|
10
12
|
<p align="center">
|
package/dist/index.js
CHANGED
|
@@ -635,6 +635,130 @@ function doCreateTag(tagName) {
|
|
|
635
635
|
);
|
|
636
636
|
}
|
|
637
637
|
}
|
|
638
|
+
async function deleteTag() {
|
|
639
|
+
const fetchSpinner = ora2("\u6B63\u5728\u83B7\u53D6 tags...").start();
|
|
640
|
+
exec("git fetch --tags", true);
|
|
641
|
+
fetchSpinner.stop();
|
|
642
|
+
const tags = execOutput("git tag -l --sort=-v:refname").split("\n").filter(Boolean);
|
|
643
|
+
if (tags.length === 0) {
|
|
644
|
+
console.log(colors.yellow("\u6CA1\u6709\u53EF\u5220\u9664\u7684 tag"));
|
|
645
|
+
return;
|
|
646
|
+
}
|
|
647
|
+
divider();
|
|
648
|
+
const choices = tags.map((tag) => ({ name: tag, value: tag }));
|
|
649
|
+
choices.push({ name: "\u53D6\u6D88", value: "__cancel__" });
|
|
650
|
+
const tagToDelete = await select2({
|
|
651
|
+
message: "\u9009\u62E9\u8981\u5220\u9664\u7684 tag:",
|
|
652
|
+
choices,
|
|
653
|
+
theme
|
|
654
|
+
});
|
|
655
|
+
if (tagToDelete === "__cancel__") {
|
|
656
|
+
console.log(colors.yellow("\u5DF2\u53D6\u6D88"));
|
|
657
|
+
return;
|
|
658
|
+
}
|
|
659
|
+
const confirm = await select2({
|
|
660
|
+
message: `\u786E\u8BA4\u5220\u9664 tag: ${colors.red(tagToDelete)}?`,
|
|
661
|
+
choices: [
|
|
662
|
+
{ name: "\u662F", value: true },
|
|
663
|
+
{ name: "\u5426", value: false }
|
|
664
|
+
],
|
|
665
|
+
theme
|
|
666
|
+
});
|
|
667
|
+
if (!confirm) {
|
|
668
|
+
console.log(colors.yellow("\u5DF2\u53D6\u6D88"));
|
|
669
|
+
return;
|
|
670
|
+
}
|
|
671
|
+
divider();
|
|
672
|
+
const spinner = ora2(`\u6B63\u5728\u5220\u9664\u672C\u5730 tag: ${tagToDelete}`).start();
|
|
673
|
+
try {
|
|
674
|
+
execSync3(`git tag -d "${tagToDelete}"`, { stdio: "pipe" });
|
|
675
|
+
spinner.succeed(`\u672C\u5730 tag \u5DF2\u5220\u9664: ${tagToDelete}`);
|
|
676
|
+
} catch {
|
|
677
|
+
spinner.fail("\u672C\u5730 tag \u5220\u9664\u5931\u8D25");
|
|
678
|
+
return;
|
|
679
|
+
}
|
|
680
|
+
const deleteRemote = await select2({
|
|
681
|
+
message: "\u662F\u5426\u540C\u65F6\u5220\u9664\u8FDC\u7A0B tag?",
|
|
682
|
+
choices: [
|
|
683
|
+
{ name: "\u662F", value: true },
|
|
684
|
+
{ name: "\u5426", value: false }
|
|
685
|
+
],
|
|
686
|
+
theme
|
|
687
|
+
});
|
|
688
|
+
if (deleteRemote) {
|
|
689
|
+
const pushSpinner = ora2("\u6B63\u5728\u5220\u9664\u8FDC\u7A0B tag...").start();
|
|
690
|
+
try {
|
|
691
|
+
execSync3(`git push origin --delete "${tagToDelete}"`, { stdio: "pipe" });
|
|
692
|
+
pushSpinner.succeed(`\u8FDC\u7A0B tag \u5DF2\u5220\u9664: ${tagToDelete}`);
|
|
693
|
+
} catch {
|
|
694
|
+
pushSpinner.warn(
|
|
695
|
+
`\u8FDC\u7A0B\u5220\u9664\u5931\u8D25\uFF0C\u53EF\u7A0D\u540E\u624B\u52A8\u6267\u884C: git push origin --delete ${tagToDelete}`
|
|
696
|
+
);
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
async function updateTag() {
|
|
701
|
+
const fetchSpinner = ora2("\u6B63\u5728\u83B7\u53D6 tags...").start();
|
|
702
|
+
exec("git fetch --tags", true);
|
|
703
|
+
fetchSpinner.stop();
|
|
704
|
+
const tags = execOutput("git tag -l --sort=-v:refname").split("\n").filter(Boolean);
|
|
705
|
+
if (tags.length === 0) {
|
|
706
|
+
console.log(colors.yellow("\u6CA1\u6709\u53EF\u4FEE\u6539\u7684 tag"));
|
|
707
|
+
return;
|
|
708
|
+
}
|
|
709
|
+
divider();
|
|
710
|
+
const choices = tags.map((tag) => ({ name: tag, value: tag }));
|
|
711
|
+
choices.push({ name: "\u53D6\u6D88", value: "__cancel__" });
|
|
712
|
+
const tagToUpdate = await select2({
|
|
713
|
+
message: "\u9009\u62E9\u8981\u4FEE\u6539\u7684 tag:",
|
|
714
|
+
choices,
|
|
715
|
+
theme
|
|
716
|
+
});
|
|
717
|
+
if (tagToUpdate === "__cancel__") {
|
|
718
|
+
console.log(colors.yellow("\u5DF2\u53D6\u6D88"));
|
|
719
|
+
return;
|
|
720
|
+
}
|
|
721
|
+
const newMessage = await input2({
|
|
722
|
+
message: "\u8F93\u5165\u65B0\u7684 tag \u6D88\u606F:",
|
|
723
|
+
default: `Release ${tagToUpdate}`,
|
|
724
|
+
theme
|
|
725
|
+
});
|
|
726
|
+
if (!newMessage) {
|
|
727
|
+
console.log(colors.yellow("\u5DF2\u53D6\u6D88"));
|
|
728
|
+
return;
|
|
729
|
+
}
|
|
730
|
+
divider();
|
|
731
|
+
const spinner = ora2(`\u6B63\u5728\u66F4\u65B0 tag: ${tagToUpdate}`).start();
|
|
732
|
+
try {
|
|
733
|
+
execSync3(`git tag -d "${tagToUpdate}"`, { stdio: "pipe" });
|
|
734
|
+
execSync3(`git tag -a "${tagToUpdate}" -m "${newMessage}"`, {
|
|
735
|
+
stdio: "pipe"
|
|
736
|
+
});
|
|
737
|
+
spinner.succeed(`Tag \u5DF2\u66F4\u65B0: ${tagToUpdate}`);
|
|
738
|
+
} catch {
|
|
739
|
+
spinner.fail("tag \u66F4\u65B0\u5931\u8D25");
|
|
740
|
+
return;
|
|
741
|
+
}
|
|
742
|
+
const pushRemote = await select2({
|
|
743
|
+
message: "\u662F\u5426\u63A8\u9001\u5230\u8FDC\u7A0B\uFF08\u4F1A\u5F3A\u5236\u8986\u76D6\uFF09?",
|
|
744
|
+
choices: [
|
|
745
|
+
{ name: "\u662F", value: true },
|
|
746
|
+
{ name: "\u5426", value: false }
|
|
747
|
+
],
|
|
748
|
+
theme
|
|
749
|
+
});
|
|
750
|
+
if (pushRemote) {
|
|
751
|
+
const pushSpinner = ora2("\u6B63\u5728\u63A8\u9001\u5230\u8FDC\u7A0B...").start();
|
|
752
|
+
try {
|
|
753
|
+
execSync3(`git push origin "${tagToUpdate}" --force`, { stdio: "pipe" });
|
|
754
|
+
pushSpinner.succeed(`Tag \u5DF2\u63A8\u9001: ${tagToUpdate}`);
|
|
755
|
+
} catch {
|
|
756
|
+
pushSpinner.warn(
|
|
757
|
+
`\u8FDC\u7A0B\u63A8\u9001\u5931\u8D25\uFF0C\u53EF\u7A0D\u540E\u624B\u52A8\u6267\u884C: git push origin ${tagToUpdate} --force`
|
|
758
|
+
);
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
}
|
|
638
762
|
|
|
639
763
|
// src/commands/release.ts
|
|
640
764
|
import { readFileSync as readFileSync2, writeFileSync } from "fs";
|
|
@@ -1741,6 +1865,12 @@ Tag \u547D\u4EE4:
|
|
|
1741
1865
|
gw tag [prefix] \u4EA4\u4E92\u5F0F\u9009\u62E9\u7248\u672C\u7C7B\u578B\u5E76\u521B\u5EFA tag
|
|
1742
1866
|
gw t [prefix] \u540C\u4E0A (\u522B\u540D)
|
|
1743
1867
|
|
|
1868
|
+
gw tag:delete \u5220\u9664 tag
|
|
1869
|
+
gw td \u540C\u4E0A (\u522B\u540D)
|
|
1870
|
+
|
|
1871
|
+
gw tag:update \u4FEE\u6539 tag \u6D88\u606F
|
|
1872
|
+
gw tu \u540C\u4E0A (\u522B\u540D)
|
|
1873
|
+
|
|
1744
1874
|
\u53D1\u5E03\u547D\u4EE4:
|
|
1745
1875
|
gw release \u4EA4\u4E92\u5F0F\u9009\u62E9\u7248\u672C\u53F7\u5E76\u66F4\u65B0 package.json
|
|
1746
1876
|
gw r \u540C\u4E0A (\u522B\u540D)
|
|
@@ -1766,6 +1896,8 @@ Commit \u547D\u4EE4:
|
|
|
1766
1896
|
gw d feature/xxx \u76F4\u63A5\u5220\u9664\u6307\u5B9A\u5206\u652F
|
|
1767
1897
|
gw ts v \u5217\u51FA\u6240\u6709 v \u5F00\u5934\u7684 tag
|
|
1768
1898
|
gw t \u4EA4\u4E92\u5F0F\u521B\u5EFA tag
|
|
1899
|
+
gw td \u4EA4\u4E92\u5F0F\u5220\u9664 tag
|
|
1900
|
+
gw tu \u4EA4\u4E92\u5F0F\u4FEE\u6539 tag
|
|
1769
1901
|
gw r \u4EA4\u4E92\u5F0F\u53D1\u5E03\u7248\u672C
|
|
1770
1902
|
gw s \u4EA4\u4E92\u5F0F\u7BA1\u7406 stash
|
|
1771
1903
|
gw c \u4EA4\u4E92\u5F0F\u63D0\u4EA4\u4EE3\u7801
|
|
@@ -1963,9 +2095,8 @@ process.on("SIGTERM", () => {
|
|
|
1963
2095
|
console.log("");
|
|
1964
2096
|
process.exit(0);
|
|
1965
2097
|
});
|
|
1966
|
-
var version = true ? "0.2.
|
|
2098
|
+
var version = true ? "0.2.11" : "0.0.0-dev";
|
|
1967
2099
|
async function mainMenu() {
|
|
1968
|
-
await checkForUpdates(version, "@zjex/git-workflow");
|
|
1969
2100
|
console.log(
|
|
1970
2101
|
colors.green(`
|
|
1971
2102
|
\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2557 \u2588\u2588\u2557
|
|
@@ -2002,19 +2133,27 @@ async function mainMenu() {
|
|
|
2002
2133
|
value: "tag"
|
|
2003
2134
|
},
|
|
2004
2135
|
{
|
|
2005
|
-
name: `[6] \u{
|
|
2136
|
+
name: `[6] \u{1F5D1}\uFE0F \u5220\u9664 tag ${colors.dim("gw td")}`,
|
|
2137
|
+
value: "tag-delete"
|
|
2138
|
+
},
|
|
2139
|
+
{
|
|
2140
|
+
name: `[7] \u270F\uFE0F \u4FEE\u6539 tag ${colors.dim("gw tu")}`,
|
|
2141
|
+
value: "tag-update"
|
|
2142
|
+
},
|
|
2143
|
+
{
|
|
2144
|
+
name: `[8] \u{1F4CB} \u5217\u51FA tags ${colors.dim("gw ts")}`,
|
|
2006
2145
|
value: "tags"
|
|
2007
2146
|
},
|
|
2008
2147
|
{
|
|
2009
|
-
name: `[
|
|
2148
|
+
name: `[9] \u{1F4E6} \u53D1\u5E03\u7248\u672C ${colors.dim("gw r")}`,
|
|
2010
2149
|
value: "release"
|
|
2011
2150
|
},
|
|
2012
2151
|
{
|
|
2013
|
-
name: `[
|
|
2152
|
+
name: `[a] \u{1F4BE} \u7BA1\u7406 stash ${colors.dim("gw s")}`,
|
|
2014
2153
|
value: "stash"
|
|
2015
2154
|
},
|
|
2016
2155
|
{
|
|
2017
|
-
name: `[
|
|
2156
|
+
name: `[b] \u2699\uFE0F \u521D\u59CB\u5316\u914D\u7F6E ${colors.dim("gw init")}`,
|
|
2018
2157
|
value: "init"
|
|
2019
2158
|
},
|
|
2020
2159
|
{ name: "[0] \u2753 \u5E2E\u52A9", value: "help" },
|
|
@@ -2040,6 +2179,14 @@ async function mainMenu() {
|
|
|
2040
2179
|
checkGitRepo();
|
|
2041
2180
|
await createTag();
|
|
2042
2181
|
break;
|
|
2182
|
+
case "tag-delete":
|
|
2183
|
+
checkGitRepo();
|
|
2184
|
+
await deleteTag();
|
|
2185
|
+
break;
|
|
2186
|
+
case "tag-update":
|
|
2187
|
+
checkGitRepo();
|
|
2188
|
+
await updateTag();
|
|
2189
|
+
break;
|
|
2043
2190
|
case "tags":
|
|
2044
2191
|
checkGitRepo();
|
|
2045
2192
|
await listTags();
|
|
@@ -2066,40 +2213,60 @@ async function mainMenu() {
|
|
|
2066
2213
|
}
|
|
2067
2214
|
}
|
|
2068
2215
|
var cli = cac("gw");
|
|
2069
|
-
cli.command("", "\u663E\u793A\u4EA4\u4E92\u5F0F\u83DC\u5355").action(() => {
|
|
2216
|
+
cli.command("", "\u663E\u793A\u4EA4\u4E92\u5F0F\u83DC\u5355").action(async () => {
|
|
2217
|
+
await checkForUpdates(version, "@zjex/git-workflow");
|
|
2070
2218
|
return mainMenu();
|
|
2071
2219
|
});
|
|
2072
|
-
cli.command("feature", "\u521B\u5EFA feature \u5206\u652F").alias("feat").alias("f").option("--base <branch>", "\u6307\u5B9A\u57FA\u7840\u5206\u652F").action((options) => {
|
|
2220
|
+
cli.command("feature", "\u521B\u5EFA feature \u5206\u652F").alias("feat").alias("f").option("--base <branch>", "\u6307\u5B9A\u57FA\u7840\u5206\u652F").action(async (options) => {
|
|
2221
|
+
await checkForUpdates(version, "@zjex/git-workflow");
|
|
2073
2222
|
checkGitRepo();
|
|
2074
2223
|
return createBranch("feature", options.base);
|
|
2075
2224
|
});
|
|
2076
|
-
cli.command("hotfix", "\u521B\u5EFA hotfix \u5206\u652F").alias("fix").alias("h").option("--base <branch>", "\u6307\u5B9A\u57FA\u7840\u5206\u652F").action((options) => {
|
|
2225
|
+
cli.command("hotfix", "\u521B\u5EFA hotfix \u5206\u652F").alias("fix").alias("h").option("--base <branch>", "\u6307\u5B9A\u57FA\u7840\u5206\u652F").action(async (options) => {
|
|
2226
|
+
await checkForUpdates(version, "@zjex/git-workflow");
|
|
2077
2227
|
checkGitRepo();
|
|
2078
2228
|
return createBranch("hotfix", options.base);
|
|
2079
2229
|
});
|
|
2080
|
-
cli.command("delete [branch]", "\u5220\u9664\u672C\u5730/\u8FDC\u7A0B\u5206\u652F").alias("del").alias("d").action((branch) => {
|
|
2230
|
+
cli.command("delete [branch]", "\u5220\u9664\u672C\u5730/\u8FDC\u7A0B\u5206\u652F").alias("del").alias("d").action(async (branch) => {
|
|
2231
|
+
await checkForUpdates(version, "@zjex/git-workflow");
|
|
2081
2232
|
checkGitRepo();
|
|
2082
2233
|
return deleteBranch(branch);
|
|
2083
2234
|
});
|
|
2084
|
-
cli.command("tags [prefix]", "\u5217\u51FA\u6240\u6709 tag\uFF0C\u53EF\u6309\u524D\u7F00\u8FC7\u6EE4").alias("ts").action((prefix) => {
|
|
2235
|
+
cli.command("tags [prefix]", "\u5217\u51FA\u6240\u6709 tag\uFF0C\u53EF\u6309\u524D\u7F00\u8FC7\u6EE4").alias("ts").action(async (prefix) => {
|
|
2236
|
+
await checkForUpdates(version, "@zjex/git-workflow");
|
|
2085
2237
|
checkGitRepo();
|
|
2086
2238
|
return listTags(prefix);
|
|
2087
2239
|
});
|
|
2088
|
-
cli.command("tag [prefix]", "\u4EA4\u4E92\u5F0F\u9009\u62E9\u7248\u672C\u7C7B\u578B\u5E76\u521B\u5EFA tag").alias("t").action((prefix) => {
|
|
2240
|
+
cli.command("tag [prefix]", "\u4EA4\u4E92\u5F0F\u9009\u62E9\u7248\u672C\u7C7B\u578B\u5E76\u521B\u5EFA tag").alias("t").action(async (prefix) => {
|
|
2241
|
+
await checkForUpdates(version, "@zjex/git-workflow");
|
|
2089
2242
|
checkGitRepo();
|
|
2090
2243
|
return createTag(prefix);
|
|
2091
2244
|
});
|
|
2092
|
-
cli.command("
|
|
2245
|
+
cli.command("tag:delete", "\u5220\u9664 tag").alias("td").action(async () => {
|
|
2246
|
+
await checkForUpdates(version, "@zjex/git-workflow");
|
|
2247
|
+
checkGitRepo();
|
|
2248
|
+
return deleteTag();
|
|
2249
|
+
});
|
|
2250
|
+
cli.command("tag:update", "\u4FEE\u6539 tag \u6D88\u606F").alias("tu").action(async () => {
|
|
2251
|
+
await checkForUpdates(version, "@zjex/git-workflow");
|
|
2252
|
+
checkGitRepo();
|
|
2253
|
+
return updateTag();
|
|
2254
|
+
});
|
|
2255
|
+
cli.command("release", "\u4EA4\u4E92\u5F0F\u9009\u62E9\u7248\u672C\u53F7\u5E76\u66F4\u65B0 package.json").alias("r").action(async () => {
|
|
2256
|
+
await checkForUpdates(version, "@zjex/git-workflow");
|
|
2093
2257
|
return release();
|
|
2094
2258
|
});
|
|
2095
|
-
cli.command("init", "\u521D\u59CB\u5316\u914D\u7F6E\u6587\u4EF6 .gwrc.json").action(() => {
|
|
2259
|
+
cli.command("init", "\u521D\u59CB\u5316\u914D\u7F6E\u6587\u4EF6 .gwrc.json").action(async () => {
|
|
2260
|
+
await checkForUpdates(version, "@zjex/git-workflow");
|
|
2096
2261
|
return init();
|
|
2097
2262
|
});
|
|
2098
|
-
cli.command("stash", "\u4EA4\u4E92\u5F0F\u7BA1\u7406 stash").alias("s").alias("st").action(() => {
|
|
2263
|
+
cli.command("stash", "\u4EA4\u4E92\u5F0F\u7BA1\u7406 stash").alias("s").alias("st").action(async () => {
|
|
2264
|
+
await checkForUpdates(version, "@zjex/git-workflow");
|
|
2099
2265
|
checkGitRepo();
|
|
2100
2266
|
return stash();
|
|
2101
2267
|
});
|
|
2102
|
-
cli.command("commit", "\u4EA4\u4E92\u5F0F\u63D0\u4EA4 (Conventional Commits + Gitmoji)").alias("c").alias("cm").action(() => {
|
|
2268
|
+
cli.command("commit", "\u4EA4\u4E92\u5F0F\u63D0\u4EA4 (Conventional Commits + Gitmoji)").alias("c").alias("cm").action(async () => {
|
|
2269
|
+
await checkForUpdates(version, "@zjex/git-workflow");
|
|
2103
2270
|
checkGitRepo();
|
|
2104
2271
|
return commit();
|
|
2105
2272
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zjex/git-workflow",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.11",
|
|
4
4
|
"description": "🚀 极简的 Git 工作流 CLI 工具,让分支管理和版本发布变得轻松愉快",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"version": "node scripts/version.js",
|
|
18
18
|
"release": "./scripts/release.sh",
|
|
19
19
|
"release:dry": "./scripts/release.sh --dry-run",
|
|
20
|
-
"publish:npm": "
|
|
20
|
+
"publish:npm": "node scripts/publish.js",
|
|
21
21
|
"prepublishOnly": "npm run build",
|
|
22
22
|
"prepare": "husky"
|
|
23
23
|
},
|
|
@@ -52,4 +52,4 @@
|
|
|
52
52
|
"tsx": "^4.21.0",
|
|
53
53
|
"typescript": "^5.9.3"
|
|
54
54
|
}
|
|
55
|
-
}
|
|
55
|
+
}
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { execSync } from "child_process";
|
|
4
|
+
import { readFileSync } from "fs";
|
|
5
|
+
import ora from "ora";
|
|
6
|
+
import boxen from "boxen";
|
|
7
|
+
|
|
8
|
+
const colors = {
|
|
9
|
+
blue: (str) => `\x1b[34m${str}\x1b[0m`,
|
|
10
|
+
green: (str) => `\x1b[32m${str}\x1b[0m`,
|
|
11
|
+
red: (str) => `\x1b[31m${str}\x1b[0m`,
|
|
12
|
+
yellow: (str) => `\x1b[33m${str}\x1b[0m`,
|
|
13
|
+
cyan: (str) => `\x1b[36m${str}\x1b[0m`,
|
|
14
|
+
dim: (str) => `\x1b[2m${str}\x1b[0m`,
|
|
15
|
+
bold: (str) => `\x1b[1m${str}\x1b[0m`,
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const TOTAL_STEPS = 11;
|
|
19
|
+
|
|
20
|
+
function exec(command, silent = false) {
|
|
21
|
+
try {
|
|
22
|
+
return execSync(command, {
|
|
23
|
+
encoding: "utf-8",
|
|
24
|
+
stdio: silent ? "pipe" : "inherit",
|
|
25
|
+
});
|
|
26
|
+
} catch (error) {
|
|
27
|
+
if (!silent) throw error;
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function runStep(stepNum, stepName, command) {
|
|
33
|
+
const spinner = ora({
|
|
34
|
+
text: `${colors.blue(`[${stepNum}/${TOTAL_STEPS}]`)} ${stepName}...`,
|
|
35
|
+
spinner: "dots",
|
|
36
|
+
}).start();
|
|
37
|
+
|
|
38
|
+
try {
|
|
39
|
+
execSync(command, { encoding: "utf-8", stdio: "pipe" });
|
|
40
|
+
spinner.succeed(
|
|
41
|
+
`${colors.blue(`[${stepNum}/${TOTAL_STEPS}]`)} ${stepName}`
|
|
42
|
+
);
|
|
43
|
+
return true;
|
|
44
|
+
} catch (error) {
|
|
45
|
+
spinner.fail(`${colors.blue(`[${stepNum}/${TOTAL_STEPS}]`)} ${stepName}`);
|
|
46
|
+
console.log("");
|
|
47
|
+
console.log(colors.red("错误详情:"));
|
|
48
|
+
console.log(error.stdout || error.message);
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async function main() {
|
|
54
|
+
console.log("");
|
|
55
|
+
console.log(colors.bold("🚀 开始发布流程"));
|
|
56
|
+
console.log("");
|
|
57
|
+
|
|
58
|
+
// [1] 检查 Git 仓库
|
|
59
|
+
if (!runStep(1, "检查 Git 仓库", "git rev-parse --git-dir")) {
|
|
60
|
+
console.log(colors.red("✖ 当前目录不是 git 仓库"));
|
|
61
|
+
process.exit(1);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// [2] 检查工作区状态
|
|
65
|
+
const spinner2 = ora({
|
|
66
|
+
text: `${colors.blue("[2/11]")} 检查工作区状态...`,
|
|
67
|
+
spinner: "dots",
|
|
68
|
+
}).start();
|
|
69
|
+
|
|
70
|
+
const status = exec("git status --porcelain", true);
|
|
71
|
+
if (status && status.trim()) {
|
|
72
|
+
spinner2.fail(`${colors.blue("[2/11]")} 检查工作区状态`);
|
|
73
|
+
console.log("");
|
|
74
|
+
console.log(colors.red("✖ 检测到未提交的更改,请先提交后再发布"));
|
|
75
|
+
console.log("");
|
|
76
|
+
console.log(status);
|
|
77
|
+
console.log("");
|
|
78
|
+
console.log(colors.cyan("💡 提示: 可以使用 'gw c' 提交更改"));
|
|
79
|
+
process.exit(1);
|
|
80
|
+
}
|
|
81
|
+
spinner2.succeed(`${colors.blue("[2/11]")} 检查工作区状态`);
|
|
82
|
+
|
|
83
|
+
// [3] 检查 npm 登录状态
|
|
84
|
+
const spinner3 = ora({
|
|
85
|
+
text: `${colors.blue("[3/11]")} 检查 npm 登录状态...`,
|
|
86
|
+
spinner: "dots",
|
|
87
|
+
}).start();
|
|
88
|
+
|
|
89
|
+
const npmUser = exec("npm whoami", true);
|
|
90
|
+
if (!npmUser) {
|
|
91
|
+
spinner3.fail(`${colors.blue("[3/11]")} 检查 npm 登录状态`);
|
|
92
|
+
console.log(colors.red("✖ 未登录 npm,请先执行: npm login"));
|
|
93
|
+
process.exit(1);
|
|
94
|
+
}
|
|
95
|
+
spinner3.succeed(
|
|
96
|
+
`${colors.blue("[3/11]")} 检查 npm 登录状态 ${colors.dim(
|
|
97
|
+
`(${npmUser.trim()})`
|
|
98
|
+
)}`
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
// 获取当前分支
|
|
102
|
+
const currentBranch = exec("git branch --show-current", true).trim();
|
|
103
|
+
|
|
104
|
+
// [4] 拉取最新代码
|
|
105
|
+
if (!runStep(4, "拉取最新代码", `git pull origin ${currentBranch}`)) {
|
|
106
|
+
process.exit(1);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// 获取当前版本
|
|
110
|
+
const pkg = JSON.parse(readFileSync("./package.json", "utf-8"));
|
|
111
|
+
const currentVersion = pkg.version;
|
|
112
|
+
|
|
113
|
+
// [5] 选择新版本号
|
|
114
|
+
console.log(`${colors.blue("[5/11]")} 选择新版本号...`);
|
|
115
|
+
console.log("");
|
|
116
|
+
|
|
117
|
+
try {
|
|
118
|
+
execSync("npm run version", { stdio: "inherit" });
|
|
119
|
+
} catch (error) {
|
|
120
|
+
console.log("");
|
|
121
|
+
console.log(colors.yellow("已取消发布"));
|
|
122
|
+
process.exit(0);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// 获取新版本
|
|
126
|
+
const newPkg = JSON.parse(readFileSync("./package.json", "utf-8"));
|
|
127
|
+
const newVersion = newPkg.version;
|
|
128
|
+
|
|
129
|
+
if (newVersion === currentVersion) {
|
|
130
|
+
console.log(colors.cyan("版本号未更改,已取消发布"));
|
|
131
|
+
process.exit(0);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// 清除上面的输出,重新显示步骤5
|
|
135
|
+
process.stdout.write("\x1b[2A"); // 向上移动2行
|
|
136
|
+
process.stdout.write("\x1b[0J"); // 清除从光标到屏幕底部
|
|
137
|
+
|
|
138
|
+
console.log(
|
|
139
|
+
`${colors.blue("[5/11]")} 选择新版本号... ${colors.green(
|
|
140
|
+
"✅"
|
|
141
|
+
)} ${colors.dim(`(${currentVersion} → ${newVersion})`)}`
|
|
142
|
+
);
|
|
143
|
+
|
|
144
|
+
// [6] 构建项目
|
|
145
|
+
if (!runStep(6, "构建项目", "npm run build")) {
|
|
146
|
+
process.exit(1);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// [7] 生成 CHANGELOG
|
|
150
|
+
if (!runStep(7, "生成 CHANGELOG", "npm run changelog")) {
|
|
151
|
+
process.exit(1);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// [8] 提交版本更新
|
|
155
|
+
const spinner8 = ora({
|
|
156
|
+
text: `${colors.blue("[8/11]")} 提交版本更新...`,
|
|
157
|
+
spinner: "dots",
|
|
158
|
+
}).start();
|
|
159
|
+
|
|
160
|
+
try {
|
|
161
|
+
execSync("git add package.json CHANGELOG.md", { stdio: "pipe" });
|
|
162
|
+
execSync(`git commit -m "🔖 chore(release): 发布 v${newVersion}"`, {
|
|
163
|
+
stdio: "pipe",
|
|
164
|
+
});
|
|
165
|
+
spinner8.succeed(
|
|
166
|
+
`${colors.blue("[8/11]")} 提交版本更新 ${colors.dim(
|
|
167
|
+
`(🔖 chore(release): 发布 v${newVersion})`
|
|
168
|
+
)}`
|
|
169
|
+
);
|
|
170
|
+
} catch (error) {
|
|
171
|
+
spinner8.fail(`${colors.blue("[8/11]")} 提交版本更新`);
|
|
172
|
+
console.log("");
|
|
173
|
+
console.log(colors.red("错误详情:"));
|
|
174
|
+
console.log(error.message);
|
|
175
|
+
process.exit(1);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// [9] 创建 Git Tag
|
|
179
|
+
const spinner9 = ora({
|
|
180
|
+
text: `${colors.blue("[9/11]")} 创建 Git Tag...`,
|
|
181
|
+
spinner: "dots",
|
|
182
|
+
}).start();
|
|
183
|
+
|
|
184
|
+
try {
|
|
185
|
+
execSync(`git tag -a "v${newVersion}" -m "Release v${newVersion}"`, {
|
|
186
|
+
stdio: "pipe",
|
|
187
|
+
});
|
|
188
|
+
spinner9.succeed(
|
|
189
|
+
`${colors.blue("[9/11]")} 创建 Git Tag ${colors.dim(`(v${newVersion})`)}`
|
|
190
|
+
);
|
|
191
|
+
} catch (error) {
|
|
192
|
+
spinner9.fail(`${colors.blue("[9/11]")} 创建 Git Tag`);
|
|
193
|
+
console.log("");
|
|
194
|
+
console.log(colors.red("错误详情:"));
|
|
195
|
+
console.log(error.message);
|
|
196
|
+
process.exit(1);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// [10] 推送到远程仓库
|
|
200
|
+
const spinner10 = ora({
|
|
201
|
+
text: `${colors.blue("[10/11]")} 推送到远程仓库...`,
|
|
202
|
+
spinner: "dots",
|
|
203
|
+
}).start();
|
|
204
|
+
|
|
205
|
+
try {
|
|
206
|
+
execSync(`git push origin ${currentBranch}`, { stdio: "pipe" });
|
|
207
|
+
execSync(`git push origin v${newVersion}`, { stdio: "pipe" });
|
|
208
|
+
spinner10.succeed(
|
|
209
|
+
`${colors.blue("[10/11]")} 推送到远程仓库 ${colors.dim(
|
|
210
|
+
`(${currentBranch}, v${newVersion})`
|
|
211
|
+
)}`
|
|
212
|
+
);
|
|
213
|
+
} catch (error) {
|
|
214
|
+
spinner10.fail(`${colors.blue("[10/11]")} 推送到远程仓库`);
|
|
215
|
+
console.log("");
|
|
216
|
+
console.log(colors.red("错误详情:"));
|
|
217
|
+
console.log(error.message);
|
|
218
|
+
process.exit(1);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// [11] 发布到 npm
|
|
222
|
+
console.log(`${colors.blue("[11/11]")} 发布到 npm...`);
|
|
223
|
+
console.log("");
|
|
224
|
+
|
|
225
|
+
try {
|
|
226
|
+
execSync("npm publish", { stdio: "inherit" });
|
|
227
|
+
console.log("");
|
|
228
|
+
console.log(
|
|
229
|
+
`${colors.blue("[11/11]")} 发布到 npm... ${colors.green("✅")}`
|
|
230
|
+
);
|
|
231
|
+
} catch (error) {
|
|
232
|
+
console.log("");
|
|
233
|
+
console.log(`${colors.blue("[11/11]")} 发布到 npm... ${colors.red("❌")}`);
|
|
234
|
+
process.exit(1);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// 成功总结
|
|
238
|
+
console.log("");
|
|
239
|
+
console.log(
|
|
240
|
+
boxen(
|
|
241
|
+
[
|
|
242
|
+
colors.bold("🎉 发布成功!"),
|
|
243
|
+
"",
|
|
244
|
+
`${colors.cyan("版本:")} ${colors.bold(`v${newVersion}`)}`,
|
|
245
|
+
].join("\n"),
|
|
246
|
+
{
|
|
247
|
+
padding: { top: 1, bottom: 1, left: 8, right: 8 },
|
|
248
|
+
margin: { top: 0, bottom: 1, left: 0, right: 0 },
|
|
249
|
+
borderStyle: "round",
|
|
250
|
+
borderColor: "green",
|
|
251
|
+
align: "center",
|
|
252
|
+
}
|
|
253
|
+
)
|
|
254
|
+
);
|
|
255
|
+
|
|
256
|
+
console.log(
|
|
257
|
+
` ${colors.dim("🔗")} ${colors.cyan("GitHub:")} ${colors.dim(
|
|
258
|
+
"\x1b[4mhttps://github.com/iamzjt-front-end/git-workflow/releases/tag/v" +
|
|
259
|
+
newVersion +
|
|
260
|
+
"\x1b[0m"
|
|
261
|
+
)}`
|
|
262
|
+
);
|
|
263
|
+
console.log(
|
|
264
|
+
` ${colors.dim("📦")} ${colors.cyan("npm:")} ${colors.dim(
|
|
265
|
+
"\x1b[4mhttps://www.npmjs.com/package/@zjex/git-workflow\x1b[0m"
|
|
266
|
+
)}`
|
|
267
|
+
);
|
|
268
|
+
console.log("");
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
main().catch((error) => {
|
|
272
|
+
console.error(error);
|
|
273
|
+
process.exit(1);
|
|
274
|
+
});
|
package/scripts/publish.sh
CHANGED
|
@@ -25,20 +25,46 @@ print_dim() {
|
|
|
25
25
|
echo -e "${DIM} ${1}${NC}"
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
#
|
|
28
|
+
# 执行步骤(带 loading 动画和完成标记,折叠输出)
|
|
29
29
|
run_step() {
|
|
30
30
|
local step_num=$1
|
|
31
31
|
local step_name=$2
|
|
32
32
|
local command=$3
|
|
33
33
|
|
|
34
|
+
# 显示初始状态
|
|
34
35
|
echo -ne "${BLUE}[${step_num}/${TOTAL_STEPS}]${NC} ${step_name}... "
|
|
35
36
|
|
|
36
|
-
#
|
|
37
|
-
|
|
38
|
-
|
|
37
|
+
# 创建临时文件存储输出
|
|
38
|
+
local tmp_output=$(mktemp)
|
|
39
|
+
local tmp_status=$(mktemp)
|
|
40
|
+
|
|
41
|
+
# 在后台执行命令
|
|
42
|
+
(eval "$command" > "$tmp_output" 2>&1; echo $? > "$tmp_status") &
|
|
43
|
+
local pid=$!
|
|
44
|
+
|
|
45
|
+
# 显示 loading 动画
|
|
46
|
+
local spin='⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏'
|
|
47
|
+
local i=0
|
|
48
|
+
|
|
49
|
+
while kill -0 $pid 2>/dev/null; do
|
|
50
|
+
i=$(( (i+1) % 10 ))
|
|
51
|
+
printf "\r${BLUE}[${step_num}/${TOTAL_STEPS}]${NC} ${step_name}... ${spin:$i:1} "
|
|
52
|
+
sleep 0.1
|
|
53
|
+
done
|
|
54
|
+
|
|
55
|
+
wait $pid
|
|
56
|
+
local exit_code=$(cat "$tmp_status")
|
|
57
|
+
local output=$(cat "$tmp_output")
|
|
58
|
+
|
|
59
|
+
# 清理临时文件
|
|
60
|
+
rm -f "$tmp_output" "$tmp_status"
|
|
61
|
+
|
|
62
|
+
# 显示结果
|
|
63
|
+
if [ "$exit_code" -eq 0 ]; then
|
|
64
|
+
printf "\r${BLUE}[${step_num}/${TOTAL_STEPS}]${NC} ${step_name}... ${GREEN}✅${NC}\n"
|
|
39
65
|
return 0
|
|
40
66
|
else
|
|
41
|
-
|
|
67
|
+
printf "\r${BLUE}[${step_num}/${TOTAL_STEPS}]${NC} ${step_name}... ${RED}❌${NC}\n"
|
|
42
68
|
echo ""
|
|
43
69
|
echo -e "${RED}错误详情:${NC}"
|
|
44
70
|
echo "$output"
|
|
@@ -115,7 +141,14 @@ fi
|
|
|
115
141
|
CURRENT_VERSION=$(node -p "require('./package.json').version")
|
|
116
142
|
|
|
117
143
|
# [5] 交互式选择版本号
|
|
118
|
-
|
|
144
|
+
echo -e "${BLUE}[5/${TOTAL_STEPS}]${NC} 选择新版本号..."
|
|
145
|
+
echo ""
|
|
146
|
+
|
|
147
|
+
# 记录当前光标位置
|
|
148
|
+
tput sc
|
|
149
|
+
|
|
150
|
+
# 执行版本选择
|
|
151
|
+
npm run version
|
|
119
152
|
|
|
120
153
|
# 获取新版本
|
|
121
154
|
NEW_VERSION=$(node -p "require('./package.json').version")
|
|
@@ -125,8 +158,12 @@ if [[ "$NEW_VERSION" == "$CURRENT_VERSION" ]]; then
|
|
|
125
158
|
exit 0
|
|
126
159
|
fi
|
|
127
160
|
|
|
128
|
-
#
|
|
129
|
-
|
|
161
|
+
# 恢复光标位置并清除后续内容
|
|
162
|
+
tput rc
|
|
163
|
+
tput ed
|
|
164
|
+
|
|
165
|
+
# 显示步骤5完成,带版本信息
|
|
166
|
+
echo -e "${BLUE}[5/${TOTAL_STEPS}]${NC} 选择新版本号... ${GREEN}✅${NC} ${DIM}(${CURRENT_VERSION} → ${NEW_VERSION})${NC}"
|
|
130
167
|
|
|
131
168
|
# [6] 构建项目
|
|
132
169
|
if ! run_step "6" "构建项目" "npm run build"; then
|
|
@@ -175,7 +212,16 @@ else
|
|
|
175
212
|
fi
|
|
176
213
|
|
|
177
214
|
# [11] 发布到 npm
|
|
178
|
-
|
|
215
|
+
echo -e "${BLUE}[11/${TOTAL_STEPS}]${NC} 发布到 npm..."
|
|
216
|
+
echo ""
|
|
217
|
+
|
|
218
|
+
# 执行发布(保持交互式,允许 OTP 输入)
|
|
219
|
+
if npm publish; then
|
|
220
|
+
echo ""
|
|
221
|
+
echo -e "${BLUE}[11/${TOTAL_STEPS}]${NC} 发布到 npm... ${GREEN}✅${NC}"
|
|
222
|
+
else
|
|
223
|
+
echo ""
|
|
224
|
+
echo -e "${BLUE}[11/${TOTAL_STEPS}]${NC} 发布到 npm... ${RED}❌${NC}"
|
|
179
225
|
exit 1
|
|
180
226
|
fi
|
|
181
227
|
|
package/src/commands/help.ts
CHANGED
|
@@ -22,6 +22,12 @@ Tag 命令:
|
|
|
22
22
|
gw tag [prefix] 交互式选择版本类型并创建 tag
|
|
23
23
|
gw t [prefix] 同上 (别名)
|
|
24
24
|
|
|
25
|
+
gw tag:delete 删除 tag
|
|
26
|
+
gw td 同上 (别名)
|
|
27
|
+
|
|
28
|
+
gw tag:update 修改 tag 消息
|
|
29
|
+
gw tu 同上 (别名)
|
|
30
|
+
|
|
25
31
|
发布命令:
|
|
26
32
|
gw release 交互式选择版本号并更新 package.json
|
|
27
33
|
gw r 同上 (别名)
|
|
@@ -47,6 +53,8 @@ Commit 命令:
|
|
|
47
53
|
gw d feature/xxx 直接删除指定分支
|
|
48
54
|
gw ts v 列出所有 v 开头的 tag
|
|
49
55
|
gw t 交互式创建 tag
|
|
56
|
+
gw td 交互式删除 tag
|
|
57
|
+
gw tu 交互式修改 tag
|
|
50
58
|
gw r 交互式发布版本
|
|
51
59
|
gw s 交互式管理 stash
|
|
52
60
|
gw c 交互式提交代码
|
package/src/commands/tag.ts
CHANGED
|
@@ -334,3 +334,167 @@ function doCreateTag(tagName: string): void {
|
|
|
334
334
|
);
|
|
335
335
|
}
|
|
336
336
|
}
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* 删除 tag
|
|
340
|
+
*/
|
|
341
|
+
export async function deleteTag(): Promise<void> {
|
|
342
|
+
const fetchSpinner = ora("正在获取 tags...").start();
|
|
343
|
+
exec("git fetch --tags", true);
|
|
344
|
+
fetchSpinner.stop();
|
|
345
|
+
|
|
346
|
+
const tags = execOutput("git tag -l --sort=-v:refname")
|
|
347
|
+
.split("\n")
|
|
348
|
+
.filter(Boolean);
|
|
349
|
+
|
|
350
|
+
if (tags.length === 0) {
|
|
351
|
+
console.log(colors.yellow("没有可删除的 tag"));
|
|
352
|
+
return;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
divider();
|
|
356
|
+
|
|
357
|
+
const choices = tags.map((tag) => ({ name: tag, value: tag }));
|
|
358
|
+
choices.push({ name: "取消", value: "__cancel__" });
|
|
359
|
+
|
|
360
|
+
const tagToDelete = await select({
|
|
361
|
+
message: "选择要删除的 tag:",
|
|
362
|
+
choices,
|
|
363
|
+
theme,
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
if (tagToDelete === "__cancel__") {
|
|
367
|
+
console.log(colors.yellow("已取消"));
|
|
368
|
+
return;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
const confirm = await select({
|
|
372
|
+
message: `确认删除 tag: ${colors.red(tagToDelete)}?`,
|
|
373
|
+
choices: [
|
|
374
|
+
{ name: "是", value: true },
|
|
375
|
+
{ name: "否", value: false },
|
|
376
|
+
],
|
|
377
|
+
theme,
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
if (!confirm) {
|
|
381
|
+
console.log(colors.yellow("已取消"));
|
|
382
|
+
return;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
divider();
|
|
386
|
+
|
|
387
|
+
const spinner = ora(`正在删除本地 tag: ${tagToDelete}`).start();
|
|
388
|
+
|
|
389
|
+
try {
|
|
390
|
+
execSync(`git tag -d "${tagToDelete}"`, { stdio: "pipe" });
|
|
391
|
+
spinner.succeed(`本地 tag 已删除: ${tagToDelete}`);
|
|
392
|
+
} catch {
|
|
393
|
+
spinner.fail("本地 tag 删除失败");
|
|
394
|
+
return;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
const deleteRemote = await select({
|
|
398
|
+
message: "是否同时删除远程 tag?",
|
|
399
|
+
choices: [
|
|
400
|
+
{ name: "是", value: true },
|
|
401
|
+
{ name: "否", value: false },
|
|
402
|
+
],
|
|
403
|
+
theme,
|
|
404
|
+
});
|
|
405
|
+
|
|
406
|
+
if (deleteRemote) {
|
|
407
|
+
const pushSpinner = ora("正在删除远程 tag...").start();
|
|
408
|
+
try {
|
|
409
|
+
execSync(`git push origin --delete "${tagToDelete}"`, { stdio: "pipe" });
|
|
410
|
+
pushSpinner.succeed(`远程 tag 已删除: ${tagToDelete}`);
|
|
411
|
+
} catch {
|
|
412
|
+
pushSpinner.warn(
|
|
413
|
+
`远程删除失败,可稍后手动执行: git push origin --delete ${tagToDelete}`
|
|
414
|
+
);
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
/**
|
|
420
|
+
* 修改 tag(重新打标签)
|
|
421
|
+
*/
|
|
422
|
+
export async function updateTag(): Promise<void> {
|
|
423
|
+
const fetchSpinner = ora("正在获取 tags...").start();
|
|
424
|
+
exec("git fetch --tags", true);
|
|
425
|
+
fetchSpinner.stop();
|
|
426
|
+
|
|
427
|
+
const tags = execOutput("git tag -l --sort=-v:refname")
|
|
428
|
+
.split("\n")
|
|
429
|
+
.filter(Boolean);
|
|
430
|
+
|
|
431
|
+
if (tags.length === 0) {
|
|
432
|
+
console.log(colors.yellow("没有可修改的 tag"));
|
|
433
|
+
return;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
divider();
|
|
437
|
+
|
|
438
|
+
const choices = tags.map((tag) => ({ name: tag, value: tag }));
|
|
439
|
+
choices.push({ name: "取消", value: "__cancel__" });
|
|
440
|
+
|
|
441
|
+
const tagToUpdate = await select({
|
|
442
|
+
message: "选择要修改的 tag:",
|
|
443
|
+
choices,
|
|
444
|
+
theme,
|
|
445
|
+
});
|
|
446
|
+
|
|
447
|
+
if (tagToUpdate === "__cancel__") {
|
|
448
|
+
console.log(colors.yellow("已取消"));
|
|
449
|
+
return;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
const newMessage = await input({
|
|
453
|
+
message: "输入新的 tag 消息:",
|
|
454
|
+
default: `Release ${tagToUpdate}`,
|
|
455
|
+
theme,
|
|
456
|
+
});
|
|
457
|
+
|
|
458
|
+
if (!newMessage) {
|
|
459
|
+
console.log(colors.yellow("已取消"));
|
|
460
|
+
return;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
divider();
|
|
464
|
+
|
|
465
|
+
const spinner = ora(`正在更新 tag: ${tagToUpdate}`).start();
|
|
466
|
+
|
|
467
|
+
try {
|
|
468
|
+
// 删除旧 tag
|
|
469
|
+
execSync(`git tag -d "${tagToUpdate}"`, { stdio: "pipe" });
|
|
470
|
+
// 创建新 tag(在同一个 commit 上)
|
|
471
|
+
execSync(`git tag -a "${tagToUpdate}" -m "${newMessage}"`, {
|
|
472
|
+
stdio: "pipe",
|
|
473
|
+
});
|
|
474
|
+
spinner.succeed(`Tag 已更新: ${tagToUpdate}`);
|
|
475
|
+
} catch {
|
|
476
|
+
spinner.fail("tag 更新失败");
|
|
477
|
+
return;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
const pushRemote = await select({
|
|
481
|
+
message: "是否推送到远程(会强制覆盖)?",
|
|
482
|
+
choices: [
|
|
483
|
+
{ name: "是", value: true },
|
|
484
|
+
{ name: "否", value: false },
|
|
485
|
+
],
|
|
486
|
+
theme,
|
|
487
|
+
});
|
|
488
|
+
|
|
489
|
+
if (pushRemote) {
|
|
490
|
+
const pushSpinner = ora("正在推送到远程...").start();
|
|
491
|
+
try {
|
|
492
|
+
execSync(`git push origin "${tagToUpdate}" --force`, { stdio: "pipe" });
|
|
493
|
+
pushSpinner.succeed(`Tag 已推送: ${tagToUpdate}`);
|
|
494
|
+
} catch {
|
|
495
|
+
pushSpinner.warn(
|
|
496
|
+
`远程推送失败,可稍后手动执行: git push origin ${tagToUpdate} --force`
|
|
497
|
+
);
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -5,7 +5,7 @@ import { select } from "@inquirer/prompts";
|
|
|
5
5
|
import { ExitPromptError } from "@inquirer/core";
|
|
6
6
|
import { checkGitRepo, theme, colors } from "./utils.js";
|
|
7
7
|
import { createBranch, deleteBranch } from "./commands/branch.js";
|
|
8
|
-
import { listTags, createTag } from "./commands/tag.js";
|
|
8
|
+
import { listTags, createTag, deleteTag, updateTag } from "./commands/tag.js";
|
|
9
9
|
import { release } from "./commands/release.js";
|
|
10
10
|
import { init } from "./commands/init.js";
|
|
11
11
|
import { stash } from "./commands/stash.js";
|
|
@@ -55,9 +55,6 @@ const version: string =
|
|
|
55
55
|
|
|
56
56
|
// 交互式主菜单
|
|
57
57
|
async function mainMenu(): Promise<void> {
|
|
58
|
-
// 先检查更新,等待完成后再显示主菜单
|
|
59
|
-
await checkForUpdates(version, "@zjex/git-workflow");
|
|
60
|
-
|
|
61
58
|
// ASCII Art Logo
|
|
62
59
|
console.log(
|
|
63
60
|
colors.green(`
|
|
@@ -95,19 +92,27 @@ async function mainMenu(): Promise<void> {
|
|
|
95
92
|
value: "tag",
|
|
96
93
|
},
|
|
97
94
|
{
|
|
98
|
-
name: `[6]
|
|
95
|
+
name: `[6] 🗑️ 删除 tag ${colors.dim("gw td")}`,
|
|
96
|
+
value: "tag-delete",
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
name: `[7] ✏️ 修改 tag ${colors.dim("gw tu")}`,
|
|
100
|
+
value: "tag-update",
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
name: `[8] 📋 列出 tags ${colors.dim("gw ts")}`,
|
|
99
104
|
value: "tags",
|
|
100
105
|
},
|
|
101
106
|
{
|
|
102
|
-
name: `[
|
|
107
|
+
name: `[9] 📦 发布版本 ${colors.dim("gw r")}`,
|
|
103
108
|
value: "release",
|
|
104
109
|
},
|
|
105
110
|
{
|
|
106
|
-
name: `[
|
|
111
|
+
name: `[a] 💾 管理 stash ${colors.dim("gw s")}`,
|
|
107
112
|
value: "stash",
|
|
108
113
|
},
|
|
109
114
|
{
|
|
110
|
-
name: `[
|
|
115
|
+
name: `[b] ⚙️ 初始化配置 ${colors.dim("gw init")}`,
|
|
111
116
|
value: "init",
|
|
112
117
|
},
|
|
113
118
|
{ name: "[0] ❓ 帮助", value: "help" },
|
|
@@ -134,6 +139,14 @@ async function mainMenu(): Promise<void> {
|
|
|
134
139
|
checkGitRepo();
|
|
135
140
|
await createTag();
|
|
136
141
|
break;
|
|
142
|
+
case "tag-delete":
|
|
143
|
+
checkGitRepo();
|
|
144
|
+
await deleteTag();
|
|
145
|
+
break;
|
|
146
|
+
case "tag-update":
|
|
147
|
+
checkGitRepo();
|
|
148
|
+
await updateTag();
|
|
149
|
+
break;
|
|
137
150
|
case "tags":
|
|
138
151
|
checkGitRepo();
|
|
139
152
|
await listTags();
|
|
@@ -163,7 +176,8 @@ async function mainMenu(): Promise<void> {
|
|
|
163
176
|
const cli = cac("gw");
|
|
164
177
|
|
|
165
178
|
// 默认命令 - 显示交互式菜单
|
|
166
|
-
cli.command("", "显示交互式菜单").action(() => {
|
|
179
|
+
cli.command("", "显示交互式菜单").action(async () => {
|
|
180
|
+
await checkForUpdates(version, "@zjex/git-workflow");
|
|
167
181
|
return mainMenu();
|
|
168
182
|
});
|
|
169
183
|
|
|
@@ -172,7 +186,8 @@ cli
|
|
|
172
186
|
.alias("feat")
|
|
173
187
|
.alias("f")
|
|
174
188
|
.option("--base <branch>", "指定基础分支")
|
|
175
|
-
.action((options: { base?: string }) => {
|
|
189
|
+
.action(async (options: { base?: string }) => {
|
|
190
|
+
await checkForUpdates(version, "@zjex/git-workflow");
|
|
176
191
|
checkGitRepo();
|
|
177
192
|
return createBranch("feature", options.base);
|
|
178
193
|
});
|
|
@@ -182,7 +197,8 @@ cli
|
|
|
182
197
|
.alias("fix")
|
|
183
198
|
.alias("h")
|
|
184
199
|
.option("--base <branch>", "指定基础分支")
|
|
185
|
-
.action((options: { base?: string }) => {
|
|
200
|
+
.action(async (options: { base?: string }) => {
|
|
201
|
+
await checkForUpdates(version, "@zjex/git-workflow");
|
|
186
202
|
checkGitRepo();
|
|
187
203
|
return createBranch("hotfix", options.base);
|
|
188
204
|
});
|
|
@@ -191,7 +207,8 @@ cli
|
|
|
191
207
|
.command("delete [branch]", "删除本地/远程分支")
|
|
192
208
|
.alias("del")
|
|
193
209
|
.alias("d")
|
|
194
|
-
.action((branch?: string) => {
|
|
210
|
+
.action(async (branch?: string) => {
|
|
211
|
+
await checkForUpdates(version, "@zjex/git-workflow");
|
|
195
212
|
checkGitRepo();
|
|
196
213
|
return deleteBranch(branch);
|
|
197
214
|
});
|
|
@@ -199,7 +216,8 @@ cli
|
|
|
199
216
|
cli
|
|
200
217
|
.command("tags [prefix]", "列出所有 tag,可按前缀过滤")
|
|
201
218
|
.alias("ts")
|
|
202
|
-
.action((prefix?: string) => {
|
|
219
|
+
.action(async (prefix?: string) => {
|
|
220
|
+
await checkForUpdates(version, "@zjex/git-workflow");
|
|
203
221
|
checkGitRepo();
|
|
204
222
|
return listTags(prefix);
|
|
205
223
|
});
|
|
@@ -207,19 +225,40 @@ cli
|
|
|
207
225
|
cli
|
|
208
226
|
.command("tag [prefix]", "交互式选择版本类型并创建 tag")
|
|
209
227
|
.alias("t")
|
|
210
|
-
.action((prefix?: string) => {
|
|
228
|
+
.action(async (prefix?: string) => {
|
|
229
|
+
await checkForUpdates(version, "@zjex/git-workflow");
|
|
211
230
|
checkGitRepo();
|
|
212
231
|
return createTag(prefix);
|
|
213
232
|
});
|
|
214
233
|
|
|
234
|
+
cli
|
|
235
|
+
.command("tag:delete", "删除 tag")
|
|
236
|
+
.alias("td")
|
|
237
|
+
.action(async () => {
|
|
238
|
+
await checkForUpdates(version, "@zjex/git-workflow");
|
|
239
|
+
checkGitRepo();
|
|
240
|
+
return deleteTag();
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
cli
|
|
244
|
+
.command("tag:update", "修改 tag 消息")
|
|
245
|
+
.alias("tu")
|
|
246
|
+
.action(async () => {
|
|
247
|
+
await checkForUpdates(version, "@zjex/git-workflow");
|
|
248
|
+
checkGitRepo();
|
|
249
|
+
return updateTag();
|
|
250
|
+
});
|
|
251
|
+
|
|
215
252
|
cli
|
|
216
253
|
.command("release", "交互式选择版本号并更新 package.json")
|
|
217
254
|
.alias("r")
|
|
218
|
-
.action(() => {
|
|
255
|
+
.action(async () => {
|
|
256
|
+
await checkForUpdates(version, "@zjex/git-workflow");
|
|
219
257
|
return release();
|
|
220
258
|
});
|
|
221
259
|
|
|
222
|
-
cli.command("init", "初始化配置文件 .gwrc.json").action(() => {
|
|
260
|
+
cli.command("init", "初始化配置文件 .gwrc.json").action(async () => {
|
|
261
|
+
await checkForUpdates(version, "@zjex/git-workflow");
|
|
223
262
|
return init();
|
|
224
263
|
});
|
|
225
264
|
|
|
@@ -227,7 +266,8 @@ cli
|
|
|
227
266
|
.command("stash", "交互式管理 stash")
|
|
228
267
|
.alias("s")
|
|
229
268
|
.alias("st")
|
|
230
|
-
.action(() => {
|
|
269
|
+
.action(async () => {
|
|
270
|
+
await checkForUpdates(version, "@zjex/git-workflow");
|
|
231
271
|
checkGitRepo();
|
|
232
272
|
return stash();
|
|
233
273
|
});
|
|
@@ -236,7 +276,8 @@ cli
|
|
|
236
276
|
.command("commit", "交互式提交 (Conventional Commits + Gitmoji)")
|
|
237
277
|
.alias("c")
|
|
238
278
|
.alias("cm")
|
|
239
|
-
.action(() => {
|
|
279
|
+
.action(async () => {
|
|
280
|
+
await checkForUpdates(version, "@zjex/git-workflow");
|
|
240
281
|
checkGitRepo();
|
|
241
282
|
return commit();
|
|
242
283
|
});
|
package/scripts/README.md
DELETED
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
# 发布脚本
|
|
2
|
-
|
|
3
|
-
## 使用方法
|
|
4
|
-
|
|
5
|
-
```bash
|
|
6
|
-
# 预览发布流程(推荐先执行)
|
|
7
|
-
npm run release:dry
|
|
8
|
-
|
|
9
|
-
# 实际发布
|
|
10
|
-
npm run release
|
|
11
|
-
```
|
|
12
|
-
|
|
13
|
-
## 优化特性
|
|
14
|
-
|
|
15
|
-
### ✅ 已实现的优化
|
|
16
|
-
|
|
17
|
-
1. **Dry-run 模式** - 预览发布流程,不实际执行
|
|
18
|
-
2. **自动回滚** - 出错时自动恢复 package.json、删除 tag、回退 commit
|
|
19
|
-
3. **多重确认** - 版本选择、CHANGELOG 预览、最终发布三次确认
|
|
20
|
-
4. **完整验证**
|
|
21
|
-
- npm 登录状态检查
|
|
22
|
-
- 版本号重复检查
|
|
23
|
-
- 构建产物验证
|
|
24
|
-
- 测试运行(如果有)
|
|
25
|
-
5. **CHANGELOG 预览** - 生成后显示最新内容并确认
|
|
26
|
-
6. **错误处理** - 完善的错误捕获和提示
|
|
27
|
-
7. **友好输出** - 带颜色的步骤提示和进度显示
|
|
28
|
-
|
|
29
|
-
## 前置条件
|
|
30
|
-
|
|
31
|
-
- 确保已登录 npm:`npm login`
|
|
32
|
-
- 确保有 GitHub 推送权限
|
|
33
|
-
- 确保在 main/master 分支(或确认在其他分支发布)
|
|
34
|
-
- 确保没有未提交的更改
|
|
35
|
-
|
|
36
|
-
## 发布流程
|
|
37
|
-
|
|
38
|
-
1. 检查必要命令(git, node, npm)
|
|
39
|
-
2. 检查 npm 登录状态
|
|
40
|
-
3. 检查 git 仓库状态
|
|
41
|
-
4. 拉取最新代码
|
|
42
|
-
5. 选择版本号
|
|
43
|
-
6. 检查版本是否已存在
|
|
44
|
-
7. 运行测试(如果有)
|
|
45
|
-
8. 构建项目
|
|
46
|
-
9. 验证构建产物
|
|
47
|
-
10. 生成 CHANGELOG
|
|
48
|
-
11. 预览并确认
|
|
49
|
-
12. 最终确认
|
|
50
|
-
13. 提交更改
|
|
51
|
-
14. 创建 tag
|
|
52
|
-
15. 推送到 GitHub
|
|
53
|
-
16. 发布到 npm
|
|
54
|
-
|
|
55
|
-
## 使用示例
|
|
56
|
-
|
|
57
|
-
详见主 README 的发布部分。
|
package/test-box.sh
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
|
|
3
|
-
GREEN='\033[0;32m'
|
|
4
|
-
CYAN='\033[0;36m'
|
|
5
|
-
BOLD='\033[1m'
|
|
6
|
-
NC='\033[0m'
|
|
7
|
-
|
|
8
|
-
NEW_VERSION="0.2.8"
|
|
9
|
-
|
|
10
|
-
echo ""
|
|
11
|
-
echo -e "${GREEN}╭──────────────────────────────╮${NC}"
|
|
12
|
-
echo -e "${GREEN}│ │${NC}"
|
|
13
|
-
echo -e "${GREEN}│${NC} ${BOLD}🎉 发布成功!${NC} ${GREEN}│${NC}"
|
|
14
|
-
echo -e "${GREEN}│ │${NC}"
|
|
15
|
-
echo -e "${GREEN}│${NC} ${CYAN}版本:${NC} ${BOLD}v${NEW_VERSION}${NC} ${GREEN}│${NC}"
|
|
16
|
-
echo -e "${GREEN}│ │${NC}"
|
|
17
|
-
echo -e "${GREEN}╰──────────────────────────────╯${NC}"
|
|
18
|
-
echo ""
|
package/test-full-publish.sh
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
|
|
3
|
-
BLUE='\033[0;34m'
|
|
4
|
-
GREEN='\033[0;32m'
|
|
5
|
-
CYAN='\033[0;36m'
|
|
6
|
-
DIM='\033[2m'
|
|
7
|
-
BOLD='\033[1m'
|
|
8
|
-
NC='\033[0m'
|
|
9
|
-
|
|
10
|
-
TOTAL_STEPS=11
|
|
11
|
-
NEW_VERSION="0.2.8"
|
|
12
|
-
CURRENT_BRANCH="main"
|
|
13
|
-
|
|
14
|
-
echo ""
|
|
15
|
-
echo -e "${BOLD}🚀 开始发布流程${NC}"
|
|
16
|
-
echo ""
|
|
17
|
-
|
|
18
|
-
echo -e "${BLUE}[1/${TOTAL_STEPS}]${NC} 检查 Git 仓库... ${GREEN}✅${NC}"
|
|
19
|
-
echo -e "${BLUE}[2/${TOTAL_STEPS}]${NC} 检查工作区状态... ${GREEN}✅${NC}"
|
|
20
|
-
echo -e "${BLUE}[3/${TOTAL_STEPS}]${NC} 检查 npm 登录状态... ${GREEN}✅${NC} ${DIM}(zjex)${NC}"
|
|
21
|
-
echo -e "${BLUE}[4/${TOTAL_STEPS}]${NC} 拉取最新代码... ${GREEN}✅${NC}"
|
|
22
|
-
echo -e "${BLUE}[5/${TOTAL_STEPS}]${NC} 选择新版本号... ${GREEN}✅${NC} ${DIM}(0.2.7 → 0.2.8)${NC}"
|
|
23
|
-
echo -e "${BLUE}[6/${TOTAL_STEPS}]${NC} 构建项目... ${GREEN}✅${NC}"
|
|
24
|
-
echo -e "${BLUE}[7/${TOTAL_STEPS}]${NC} 生成 CHANGELOG... ${GREEN}✅${NC}"
|
|
25
|
-
echo -e "${BLUE}[8/${TOTAL_STEPS}]${NC} 提交版本更新... ${GREEN}✅${NC} ${DIM}(🔖 chore(release): 发布 v${NEW_VERSION})${NC}"
|
|
26
|
-
echo -e "${BLUE}[9/${TOTAL_STEPS}]${NC} 创建 Git Tag... ${GREEN}✅${NC} ${DIM}(v${NEW_VERSION})${NC}"
|
|
27
|
-
echo -e "${BLUE}[10/${TOTAL_STEPS}]${NC} 推送到远程仓库... ${GREEN}✅${NC} ${DIM}(${CURRENT_BRANCH}, v${NEW_VERSION})${NC}"
|
|
28
|
-
echo -e "${BLUE}[11/${TOTAL_STEPS}]${NC} 发布到 npm... ${GREEN}✅${NC}"
|
|
29
|
-
|
|
30
|
-
node scripts/publish-success.js "${NEW_VERSION}"
|