@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 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.svg?style=flat&colorA=18181B&colorB=28CF8D" alt="npm version"></a>
5
- <a href="https://www.npmjs.com/package/@zjex/git-workflow"><img src="https://img.shields.io/npm/dm/@zjex/git-workflow.svg?style=flat&colorA=18181B&colorB=28CF8D" alt="npm downloads"></a>
6
- <a href="https://www.npmjs.com/package/@zjex/git-workflow"><img src="https://img.shields.io/npm/l/@zjex/git-workflow.svg?style=flat&colorA=18181B&colorB=28CF8D" alt="license"></a>
7
- <img src="https://img.shields.io/badge/node-%3E%3D18-28CF8D?style=flat&colorA=18181B" alt="node version">
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.9" : "0.0.0-dev";
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{1F4CB} \u5217\u51FA tags ${colors.dim("gw ts")}`,
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: `[7] \u{1F4E6} \u53D1\u5E03\u7248\u672C ${colors.dim("gw r")}`,
2148
+ name: `[9] \u{1F4E6} \u53D1\u5E03\u7248\u672C ${colors.dim("gw r")}`,
2010
2149
  value: "release"
2011
2150
  },
2012
2151
  {
2013
- name: `[8] \u{1F4BE} \u7BA1\u7406 stash ${colors.dim("gw s")}`,
2152
+ name: `[a] \u{1F4BE} \u7BA1\u7406 stash ${colors.dim("gw s")}`,
2014
2153
  value: "stash"
2015
2154
  },
2016
2155
  {
2017
- name: `[9] \u2699\uFE0F \u521D\u59CB\u5316\u914D\u7F6E ${colors.dim("gw init")}`,
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("release", "\u4EA4\u4E92\u5F0F\u9009\u62E9\u7248\u672C\u53F7\u5E76\u66F4\u65B0 package.json").alias("r").action(() => {
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.9",
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": "./scripts/publish.sh",
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
+ });
@@ -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
- if output=$(eval "$command" 2>&1); then
38
- echo -e "${GREEN}✅${NC}"
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
- echo -e "${RED}❌${NC}"
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
- run_step_interactive "5" "选择新版本号" "npm run version"
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
- echo -e "${GREEN} ✅${NC} ${DIM}(${CURRENT_VERSION} → ${NEW_VERSION})${NC}"
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
- if ! run_step "11" "发布到 npm" "npm publish"; then
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
 
@@ -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 交互式提交代码
@@ -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] 📋 列出 tags ${colors.dim("gw ts")}`,
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: `[7] 📦 发布版本 ${colors.dim("gw r")}`,
107
+ name: `[9] 📦 发布版本 ${colors.dim("gw r")}`,
103
108
  value: "release",
104
109
  },
105
110
  {
106
- name: `[8] 💾 管理 stash ${colors.dim("gw s")}`,
111
+ name: `[a] 💾 管理 stash ${colors.dim("gw s")}`,
107
112
  value: "stash",
108
113
  },
109
114
  {
110
- name: `[9] ⚙️ 初始化配置 ${colors.dim("gw init")}`,
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 ""
@@ -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}"