@zjex/git-workflow 0.0.1 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +220 -4
- package/dist/index.js +68 -35
- package/package.json +6 -3
- package/scripts/README.md +57 -0
- package/scripts/release.sh +363 -0
- package/scripts/version.js +138 -0
- package/scripts/version.sh +133 -0
- package/src/commands/branch.ts +123 -28
- package/src/commands/commit.ts +263 -0
- package/src/commands/help.ts +26 -17
- package/src/commands/init.ts +40 -0
- package/src/commands/tag.ts +66 -7
- package/src/config.ts +20 -0
- package/src/index.ts +140 -3
package/src/commands/tag.ts
CHANGED
|
@@ -42,6 +42,14 @@ interface TagChoice {
|
|
|
42
42
|
value: string;
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
+
// 获取指定前缀的最新 tag(不依赖 shell 管道)
|
|
46
|
+
function getLatestTag(prefix: string): string {
|
|
47
|
+
const tags = execOutput(`git tag -l "${prefix}*" --sort=-v:refname`)
|
|
48
|
+
.split("\n")
|
|
49
|
+
.filter(Boolean);
|
|
50
|
+
return tags[0] || "";
|
|
51
|
+
}
|
|
52
|
+
|
|
45
53
|
export async function createTag(inputPrefix?: string): Promise<void> {
|
|
46
54
|
const config = getConfig();
|
|
47
55
|
const fetchSpinner = ora("正在获取 tags...").start();
|
|
@@ -60,13 +68,68 @@ export async function createTag(inputPrefix?: string): Promise<void> {
|
|
|
60
68
|
|
|
61
69
|
if (!prefix) {
|
|
62
70
|
const allTags = execOutput("git tag -l").split("\n").filter(Boolean);
|
|
71
|
+
|
|
72
|
+
// 仓库没有任何 tag 的情况
|
|
73
|
+
if (allTags.length === 0) {
|
|
74
|
+
prefix = await input({
|
|
75
|
+
message: "当前仓库没有 tag,请输入前缀 (如 v):",
|
|
76
|
+
default: "v",
|
|
77
|
+
theme,
|
|
78
|
+
});
|
|
79
|
+
if (!prefix) {
|
|
80
|
+
console.log(colors.yellow("已取消"));
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// 选择初始版本号
|
|
85
|
+
const initialVersion = await select({
|
|
86
|
+
message: "选择初始版本号:",
|
|
87
|
+
choices: [
|
|
88
|
+
{ name: `${prefix}0.0.1`, value: "0.0.1" },
|
|
89
|
+
{ name: `${prefix}0.1.0`, value: "0.1.0" },
|
|
90
|
+
{ name: `${prefix}1.0.0`, value: "1.0.0" },
|
|
91
|
+
{ name: "自定义...", value: "__custom__" },
|
|
92
|
+
],
|
|
93
|
+
theme,
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
let version = initialVersion;
|
|
97
|
+
if (initialVersion === "__custom__") {
|
|
98
|
+
version = await input({
|
|
99
|
+
message: "请输入版本号 (如 0.0.1):",
|
|
100
|
+
theme,
|
|
101
|
+
});
|
|
102
|
+
if (!version) {
|
|
103
|
+
console.log(colors.yellow("已取消"));
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const newTag = `${prefix}${version}`;
|
|
109
|
+
const ok = await select({
|
|
110
|
+
message: `确认创建 ${newTag}?`,
|
|
111
|
+
choices: [
|
|
112
|
+
{ name: "是", value: true },
|
|
113
|
+
{ name: "否", value: false },
|
|
114
|
+
],
|
|
115
|
+
theme,
|
|
116
|
+
});
|
|
117
|
+
if (ok) {
|
|
118
|
+
doCreateTag(newTag);
|
|
119
|
+
}
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// 从现有 tag 中提取前缀
|
|
63
124
|
const prefixes = [
|
|
64
125
|
...new Set(allTags.map((t) => t.replace(/[0-9].*/, "")).filter(Boolean)),
|
|
65
126
|
];
|
|
66
127
|
|
|
67
128
|
if (prefixes.length === 0) {
|
|
129
|
+
// 有 tag 但无法提取前缀(比如纯数字 tag)
|
|
68
130
|
prefix = await input({
|
|
69
|
-
message: "
|
|
131
|
+
message: "请输入 tag 前缀 (如 v):",
|
|
132
|
+
default: "v",
|
|
70
133
|
theme,
|
|
71
134
|
});
|
|
72
135
|
if (!prefix) {
|
|
@@ -75,9 +138,7 @@ export async function createTag(inputPrefix?: string): Promise<void> {
|
|
|
75
138
|
}
|
|
76
139
|
} else {
|
|
77
140
|
const prefixWithDate: PrefixInfo[] = prefixes.map((p) => {
|
|
78
|
-
const latest =
|
|
79
|
-
`git tag -l "${p}*" --sort=-v:refname | head -1`
|
|
80
|
-
);
|
|
141
|
+
const latest = getLatestTag(p);
|
|
81
142
|
const date = latest
|
|
82
143
|
? execOutput(`git log -1 --format=%ct "${latest}" 2>/dev/null`)
|
|
83
144
|
: "0";
|
|
@@ -108,9 +169,7 @@ export async function createTag(inputPrefix?: string): Promise<void> {
|
|
|
108
169
|
}
|
|
109
170
|
}
|
|
110
171
|
|
|
111
|
-
const latestTag =
|
|
112
|
-
`git tag -l "${prefix}*" --sort=-v:refname | head -1`
|
|
113
|
-
);
|
|
172
|
+
const latestTag = getLatestTag(prefix);
|
|
114
173
|
|
|
115
174
|
if (!latestTag) {
|
|
116
175
|
const newTag = `${prefix}1.0.0`;
|
package/src/config.ts
CHANGED
|
@@ -18,6 +18,24 @@ export interface GwConfig {
|
|
|
18
18
|
defaultTagPrefix?: string;
|
|
19
19
|
// 创建分支后是否自动推送,默认询问
|
|
20
20
|
autoPush?: boolean;
|
|
21
|
+
// commit 时是否自动暂存所有更改,默认 true
|
|
22
|
+
autoStage?: boolean;
|
|
23
|
+
// commit 时是否使用 emoji,默认 true
|
|
24
|
+
useEmoji?: boolean;
|
|
25
|
+
// 自定义 commit 类型的 emoji
|
|
26
|
+
commitEmojis?: {
|
|
27
|
+
feat?: string;
|
|
28
|
+
fix?: string;
|
|
29
|
+
docs?: string;
|
|
30
|
+
style?: string;
|
|
31
|
+
refactor?: string;
|
|
32
|
+
perf?: string;
|
|
33
|
+
test?: string;
|
|
34
|
+
build?: string;
|
|
35
|
+
ci?: string;
|
|
36
|
+
chore?: string;
|
|
37
|
+
revert?: string;
|
|
38
|
+
};
|
|
21
39
|
}
|
|
22
40
|
|
|
23
41
|
const defaultConfig: GwConfig = {
|
|
@@ -26,6 +44,8 @@ const defaultConfig: GwConfig = {
|
|
|
26
44
|
requireId: false,
|
|
27
45
|
featureIdLabel: "Story ID",
|
|
28
46
|
hotfixIdLabel: "Issue ID",
|
|
47
|
+
autoStage: true,
|
|
48
|
+
useEmoji: true,
|
|
29
49
|
};
|
|
30
50
|
|
|
31
51
|
function getGitRoot(): string {
|
package/src/index.ts
CHANGED
|
@@ -1,18 +1,146 @@
|
|
|
1
1
|
// @ts-nocheck shebang handled by tsup banner
|
|
2
2
|
|
|
3
3
|
import { cac } from "cac";
|
|
4
|
-
import {
|
|
4
|
+
import { select } from "@inquirer/prompts";
|
|
5
|
+
import { ExitPromptError } from "@inquirer/core";
|
|
6
|
+
import { checkGitRepo, theme, colors } from "./utils.js";
|
|
5
7
|
import { createBranch, deleteBranch } from "./commands/branch.js";
|
|
6
8
|
import { listTags, createTag } from "./commands/tag.js";
|
|
7
9
|
import { release } from "./commands/release.js";
|
|
8
10
|
import { init } from "./commands/init.js";
|
|
9
11
|
import { stash } from "./commands/stash.js";
|
|
12
|
+
import { commit } from "./commands/commit.js";
|
|
10
13
|
import { showHelp } from "./commands/help.js";
|
|
11
14
|
|
|
12
|
-
|
|
15
|
+
// 捕获 Ctrl+C 退出,静默处理
|
|
16
|
+
process.on("uncaughtException", (err) => {
|
|
17
|
+
if (err instanceof ExitPromptError) {
|
|
18
|
+
process.exit(0);
|
|
19
|
+
}
|
|
20
|
+
console.error(err);
|
|
21
|
+
process.exit(1);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
declare const __VERSION__: string | undefined;
|
|
25
|
+
|
|
26
|
+
// 开发环境下从 package.json 读取版本号
|
|
27
|
+
const version: string =
|
|
28
|
+
typeof __VERSION__ !== "undefined"
|
|
29
|
+
? __VERSION__
|
|
30
|
+
: (await import("../package.json", { with: { type: "json" } })).default
|
|
31
|
+
.version;
|
|
32
|
+
|
|
33
|
+
// 交互式主菜单
|
|
34
|
+
async function mainMenu(): Promise<void> {
|
|
35
|
+
// ASCII Art Logo
|
|
36
|
+
console.log(
|
|
37
|
+
colors.green(`
|
|
38
|
+
███████╗ ██╗███████╗██╗ ██╗
|
|
39
|
+
╚══███╔╝ ██║██╔════╝╚██╗██╔╝
|
|
40
|
+
███╔╝ ██║█████╗ ╚███╔╝
|
|
41
|
+
███╔╝ ██ ██║██╔══╝ ██╔██╗
|
|
42
|
+
███████╗╚█████╔╝███████╗██╔╝ ██╗
|
|
43
|
+
╚══════╝ ╚════╝ ╚══════╝╚═╝ ╚═╝
|
|
44
|
+
`)
|
|
45
|
+
);
|
|
46
|
+
console.log(colors.dim(` git-workflow v${version}\n`));
|
|
47
|
+
|
|
48
|
+
const action = await select({
|
|
49
|
+
message: "选择操作:",
|
|
50
|
+
choices: [
|
|
51
|
+
{
|
|
52
|
+
name: `[1] ✨ 创建 feature 分支 ${colors.dim("gw f")}`,
|
|
53
|
+
value: "feature",
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
name: `[2] 🐛 创建 hotfix 分支 ${colors.dim("gw h")}`,
|
|
57
|
+
value: "hotfix",
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
name: `[3] 🗑️ 删除分支 ${colors.dim("gw d")}`,
|
|
61
|
+
value: "delete",
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
name: `[4] 📝 提交代码 ${colors.dim("gw c")}`,
|
|
65
|
+
value: "commit",
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
name: `[5] 🏷️ 创建 tag ${colors.dim("gw t")}`,
|
|
69
|
+
value: "tag",
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
name: `[6] 📋 列出 tags ${colors.dim("gw ts")}`,
|
|
73
|
+
value: "tags",
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
name: `[7] 📦 发布版本 ${colors.dim("gw r")}`,
|
|
77
|
+
value: "release",
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
name: `[8] 💾 管理 stash ${colors.dim("gw s")}`,
|
|
81
|
+
value: "stash",
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
name: `[9] ⚙️ 初始化配置 ${colors.dim("gw init")}`,
|
|
85
|
+
value: "init",
|
|
86
|
+
},
|
|
87
|
+
{ name: "[0] ❓ 帮助", value: "help" },
|
|
88
|
+
{ name: "[q] 退出", value: "exit" },
|
|
89
|
+
],
|
|
90
|
+
loop: false,
|
|
91
|
+
theme,
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
switch (action) {
|
|
95
|
+
case "feature":
|
|
96
|
+
checkGitRepo();
|
|
97
|
+
await createBranch("feature");
|
|
98
|
+
break;
|
|
99
|
+
case "hotfix":
|
|
100
|
+
checkGitRepo();
|
|
101
|
+
await createBranch("hotfix");
|
|
102
|
+
break;
|
|
103
|
+
case "delete":
|
|
104
|
+
checkGitRepo();
|
|
105
|
+
await deleteBranch();
|
|
106
|
+
break;
|
|
107
|
+
case "tag":
|
|
108
|
+
checkGitRepo();
|
|
109
|
+
await createTag();
|
|
110
|
+
break;
|
|
111
|
+
case "tags":
|
|
112
|
+
checkGitRepo();
|
|
113
|
+
await listTags();
|
|
114
|
+
break;
|
|
115
|
+
case "commit":
|
|
116
|
+
checkGitRepo();
|
|
117
|
+
await commit();
|
|
118
|
+
break;
|
|
119
|
+
case "release":
|
|
120
|
+
await release();
|
|
121
|
+
break;
|
|
122
|
+
case "stash":
|
|
123
|
+
checkGitRepo();
|
|
124
|
+
await stash();
|
|
125
|
+
break;
|
|
126
|
+
case "init":
|
|
127
|
+
await init();
|
|
128
|
+
break;
|
|
129
|
+
case "help":
|
|
130
|
+
console.log(showHelp());
|
|
131
|
+
break;
|
|
132
|
+
case "exit":
|
|
133
|
+
break;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
13
136
|
|
|
14
137
|
const cli = cac("gw");
|
|
15
138
|
|
|
139
|
+
// 默认命令 - 显示交互式菜单
|
|
140
|
+
cli.command("", "显示交互式菜单").action(() => {
|
|
141
|
+
return mainMenu();
|
|
142
|
+
});
|
|
143
|
+
|
|
16
144
|
cli
|
|
17
145
|
.command("feature", "创建 feature 分支")
|
|
18
146
|
.alias("feat")
|
|
@@ -78,11 +206,20 @@ cli
|
|
|
78
206
|
return stash();
|
|
79
207
|
});
|
|
80
208
|
|
|
209
|
+
cli
|
|
210
|
+
.command("commit", "交互式提交 (Conventional Commits + Gitmoji)")
|
|
211
|
+
.alias("c")
|
|
212
|
+
.alias("cm")
|
|
213
|
+
.action(() => {
|
|
214
|
+
checkGitRepo();
|
|
215
|
+
return commit();
|
|
216
|
+
});
|
|
217
|
+
|
|
81
218
|
cli.help((sections) => {
|
|
82
219
|
sections.push({
|
|
83
220
|
body: showHelp(),
|
|
84
221
|
});
|
|
85
222
|
});
|
|
86
|
-
cli.version(
|
|
223
|
+
cli.version(version);
|
|
87
224
|
|
|
88
225
|
cli.parse();
|