@zjex/git-workflow 0.2.24 → 0.3.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.
Files changed (92) hide show
  1. package/.github/workflows/deploy-docs.yml +68 -0
  2. package/.github/workflows/test.yml +24 -4
  3. package/.husky/pre-commit +14 -0
  4. package/README.md +72 -1066
  5. package/dist/index.js +103 -13
  6. package/docs/.vitepress/cache/deps/_metadata.json +52 -0
  7. package/docs/.vitepress/cache/deps/chunk-2CLQ7TTZ.js +9719 -0
  8. package/docs/.vitepress/cache/deps/chunk-2CLQ7TTZ.js.map +7 -0
  9. package/docs/.vitepress/cache/deps/chunk-LE5NDSFD.js +12824 -0
  10. package/docs/.vitepress/cache/deps/chunk-LE5NDSFD.js.map +7 -0
  11. package/docs/.vitepress/cache/deps/package.json +3 -0
  12. package/docs/.vitepress/cache/deps/vitepress___@vue_devtools-api.js +4505 -0
  13. package/docs/.vitepress/cache/deps/vitepress___@vue_devtools-api.js.map +7 -0
  14. package/docs/.vitepress/cache/deps/vitepress___@vueuse_core.js +583 -0
  15. package/docs/.vitepress/cache/deps/vitepress___@vueuse_core.js.map +7 -0
  16. package/docs/.vitepress/cache/deps/vitepress___@vueuse_integrations_useFocusTrap.js +1352 -0
  17. package/docs/.vitepress/cache/deps/vitepress___@vueuse_integrations_useFocusTrap.js.map +7 -0
  18. package/docs/.vitepress/cache/deps/vitepress___mark__js_src_vanilla__js.js +1665 -0
  19. package/docs/.vitepress/cache/deps/vitepress___mark__js_src_vanilla__js.js.map +7 -0
  20. package/docs/.vitepress/cache/deps/vitepress___minisearch.js +1813 -0
  21. package/docs/.vitepress/cache/deps/vitepress___minisearch.js.map +7 -0
  22. package/docs/.vitepress/cache/deps/vue.js +347 -0
  23. package/docs/.vitepress/cache/deps/vue.js.map +7 -0
  24. package/docs/.vitepress/cache/deps_temp_44e2fb0f/chunk-2CLQ7TTZ.js +9719 -0
  25. package/docs/.vitepress/cache/deps_temp_44e2fb0f/chunk-2CLQ7TTZ.js.map +7 -0
  26. package/docs/.vitepress/cache/deps_temp_44e2fb0f/chunk-LE5NDSFD.js +12824 -0
  27. package/docs/.vitepress/cache/deps_temp_44e2fb0f/chunk-LE5NDSFD.js.map +7 -0
  28. package/docs/.vitepress/cache/deps_temp_44e2fb0f/package.json +3 -0
  29. package/docs/.vitepress/cache/deps_temp_44e2fb0f/vitepress___@vue_devtools-api.js +4505 -0
  30. package/docs/.vitepress/cache/deps_temp_44e2fb0f/vitepress___@vue_devtools-api.js.map +7 -0
  31. package/docs/.vitepress/cache/deps_temp_44e2fb0f/vitepress___@vueuse_core.js +583 -0
  32. package/docs/.vitepress/cache/deps_temp_44e2fb0f/vitepress___@vueuse_core.js.map +7 -0
  33. package/docs/.vitepress/cache/deps_temp_44e2fb0f/vitepress___@vueuse_integrations_useFocusTrap.js +1352 -0
  34. package/docs/.vitepress/cache/deps_temp_44e2fb0f/vitepress___@vueuse_integrations_useFocusTrap.js.map +7 -0
  35. package/docs/.vitepress/cache/deps_temp_44e2fb0f/vitepress___mark__js_src_vanilla__js.js +1665 -0
  36. package/docs/.vitepress/cache/deps_temp_44e2fb0f/vitepress___mark__js_src_vanilla__js.js.map +7 -0
  37. package/docs/.vitepress/cache/deps_temp_44e2fb0f/vitepress___minisearch.js +1813 -0
  38. package/docs/.vitepress/cache/deps_temp_44e2fb0f/vitepress___minisearch.js.map +7 -0
  39. package/docs/.vitepress/cache/deps_temp_44e2fb0f/vue.js +347 -0
  40. package/docs/.vitepress/cache/deps_temp_44e2fb0f/vue.js.map +7 -0
  41. package/docs/.vitepress/config.ts +167 -0
  42. package/docs/.vitepress/theme/custom.css +39 -0
  43. package/docs/.vitepress/theme/index.ts +4 -0
  44. package/docs/README.md +82 -0
  45. package/docs/commands/branch.md +468 -0
  46. package/docs/commands/commit.md +554 -0
  47. package/docs/commands/config.md +346 -0
  48. package/docs/commands/index.md +312 -0
  49. package/docs/commands/interactive.md +384 -0
  50. package/docs/commands/release.md +300 -0
  51. package/docs/commands/stash.md +309 -0
  52. package/docs/commands/tag.md +278 -0
  53. package/docs/commands/update.md +347 -0
  54. package/docs/config/ai-config.md +160 -0
  55. package/docs/config/branch-config.md +133 -0
  56. package/docs/config/commit-config.md +185 -0
  57. package/docs/config/config-file.md +776 -0
  58. package/docs/config/examples.md +279 -0
  59. package/docs/config/index.md +478 -0
  60. package/docs/guide/ai-commit.md +576 -0
  61. package/docs/guide/basic-usage.md +522 -0
  62. package/docs/guide/best-practices.md +426 -0
  63. package/docs/guide/branch-management.md +712 -0
  64. package/docs/guide/getting-started.md +294 -0
  65. package/docs/guide/index.md +168 -0
  66. package/docs/guide/installation.md +449 -0
  67. package/docs/guide/release-management.md +744 -0
  68. package/docs/guide/stash-management.md +608 -0
  69. package/docs/guide/tag-management.md +614 -0
  70. package/docs/index.md +205 -0
  71. package/docs/public/favicon.svg +21 -0
  72. package/docs/public/hero-logo.svg +43 -0
  73. package/docs/public/logo.svg +20 -0
  74. package/package.json +11 -2
  75. package/scripts/publish.js +55 -8
  76. package/scripts/publish.sh +20 -2
  77. package/scripts/release.sh +20 -2
  78. package/scripts/update-test-count.js +55 -0
  79. package/src/ai-service.ts +101 -15
  80. package/src/commands/init.ts +18 -0
  81. package/src/config.ts +1 -0
  82. package/tests/ai-service.test.ts +237 -2
  83. package/tests/help.test.ts +134 -0
  84. package/tests/init.test.ts +582 -0
  85. package/tests/release.test.ts +333 -0
  86. package/tests/setup.ts +21 -0
  87. package/tests/stash.test.ts +376 -0
  88. package/tests/update.test.ts +402 -0
  89. package/vitest.config.ts +3 -0
  90. package/zjex-logo.svg +22 -0
  91. package/zjex-optimized.svg +34 -0
  92. package/zjex.svg +1 -0
@@ -0,0 +1,333 @@
1
+ import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
2
+ import { readFileSync, writeFileSync } from "fs";
3
+
4
+ // Mock fs 模块
5
+ vi.mock("fs", () => ({
6
+ readFileSync: vi.fn(),
7
+ writeFileSync: vi.fn(),
8
+ }));
9
+
10
+ // Mock inquirer
11
+ vi.mock("@inquirer/prompts", () => ({
12
+ select: vi.fn(),
13
+ }));
14
+
15
+ // Mock utils
16
+ vi.mock("../src/utils.js", () => ({
17
+ colors: {
18
+ yellow: (text: string) => text,
19
+ green: (text: string) => text,
20
+ },
21
+ theme: {},
22
+ divider: vi.fn(),
23
+ }));
24
+
25
+ describe("Release 模块测试", () => {
26
+ const mockReadFileSync = vi.mocked(readFileSync);
27
+ const mockWriteFileSync = vi.mocked(writeFileSync);
28
+
29
+ beforeEach(() => {
30
+ vi.clearAllMocks();
31
+ // 重置控制台输出 mock
32
+ vi.spyOn(console, "log").mockImplementation(() => {});
33
+ });
34
+
35
+ afterEach(() => {
36
+ vi.restoreAllMocks();
37
+ });
38
+
39
+ describe("版本号解析和生成", () => {
40
+ it("应该正确解析标准版本号", async () => {
41
+ mockReadFileSync.mockReturnValue(JSON.stringify({ version: "1.2.3" }));
42
+
43
+ const { release } = await import("../src/commands/release.js");
44
+
45
+ // 模拟用户选择取消
46
+ const { select } = await import("@inquirer/prompts");
47
+ vi.mocked(select).mockResolvedValue("__cancel__");
48
+
49
+ await release();
50
+
51
+ expect(mockReadFileSync).toHaveBeenCalledWith("package.json", "utf-8");
52
+ });
53
+
54
+ it("应该正确解析预发布版本号", async () => {
55
+ mockReadFileSync.mockReturnValue(JSON.stringify({ version: "1.2.3-alpha.1" }));
56
+
57
+ const { release } = await import("../src/commands/release.js");
58
+
59
+ // 模拟用户选择取消
60
+ const { select } = await import("@inquirer/prompts");
61
+ vi.mocked(select).mockResolvedValue("__cancel__");
62
+
63
+ await release();
64
+
65
+ expect(mockReadFileSync).toHaveBeenCalledWith("package.json", "utf-8");
66
+ });
67
+
68
+ it("应该处理缺失版本号的情况", async () => {
69
+ mockReadFileSync.mockReturnValue(JSON.stringify({}));
70
+
71
+ const { release } = await import("../src/commands/release.js");
72
+
73
+ // 模拟用户选择取消
74
+ const { select } = await import("@inquirer/prompts");
75
+ vi.mocked(select).mockResolvedValue("__cancel__");
76
+
77
+ await release();
78
+
79
+ expect(mockReadFileSync).toHaveBeenCalledWith("package.json", "utf-8");
80
+ });
81
+ });
82
+
83
+ describe("版本号更新", () => {
84
+ it("应该正确更新 patch 版本", async () => {
85
+ mockReadFileSync.mockReturnValue(JSON.stringify({
86
+ name: "test-package",
87
+ version: "1.2.3"
88
+ }));
89
+
90
+ const { release } = await import("../src/commands/release.js");
91
+
92
+ // 模拟用户选择 patch 版本
93
+ const { select } = await import("@inquirer/prompts");
94
+ vi.mocked(select).mockResolvedValue("1.2.4");
95
+
96
+ await release();
97
+
98
+ expect(mockWriteFileSync).toHaveBeenCalledWith(
99
+ "package.json",
100
+ JSON.stringify({ name: "test-package", version: "1.2.4" }, null, "\t") + "\n"
101
+ );
102
+ });
103
+
104
+ it("应该正确更新 minor 版本", async () => {
105
+ mockReadFileSync.mockReturnValue(JSON.stringify({
106
+ name: "test-package",
107
+ version: "1.2.3"
108
+ }));
109
+
110
+ const { release } = await import("../src/commands/release.js");
111
+
112
+ // 模拟用户选择 minor 版本
113
+ const { select } = await import("@inquirer/prompts");
114
+ vi.mocked(select).mockResolvedValue("1.3.0");
115
+
116
+ await release();
117
+
118
+ expect(mockWriteFileSync).toHaveBeenCalledWith(
119
+ "package.json",
120
+ JSON.stringify({ name: "test-package", version: "1.3.0" }, null, "\t") + "\n"
121
+ );
122
+ });
123
+
124
+ it("应该正确更新 major 版本", async () => {
125
+ mockReadFileSync.mockReturnValue(JSON.stringify({
126
+ name: "test-package",
127
+ version: "1.2.3"
128
+ }));
129
+
130
+ const { release } = await import("../src/commands/release.js");
131
+
132
+ // 模拟用户选择 major 版本
133
+ const { select } = await import("@inquirer/prompts");
134
+ vi.mocked(select).mockResolvedValue("2.0.0");
135
+
136
+ await release();
137
+
138
+ expect(mockWriteFileSync).toHaveBeenCalledWith(
139
+ "package.json",
140
+ JSON.stringify({ name: "test-package", version: "2.0.0" }, null, "\t") + "\n"
141
+ );
142
+ });
143
+
144
+ it("应该正确创建预发布版本", async () => {
145
+ mockReadFileSync.mockReturnValue(JSON.stringify({
146
+ name: "test-package",
147
+ version: "1.2.3"
148
+ }));
149
+
150
+ const { release } = await import("../src/commands/release.js");
151
+
152
+ // 模拟用户选择 alpha 版本
153
+ const { select } = await import("@inquirer/prompts");
154
+ vi.mocked(select).mockResolvedValue("1.2.4-alpha.1");
155
+
156
+ await release();
157
+
158
+ expect(mockWriteFileSync).toHaveBeenCalledWith(
159
+ "package.json",
160
+ JSON.stringify({ name: "test-package", version: "1.2.4-alpha.1" }, null, "\t") + "\n"
161
+ );
162
+ });
163
+
164
+ it("应该正确升级预发布版本", async () => {
165
+ mockReadFileSync.mockReturnValue(JSON.stringify({
166
+ name: "test-package",
167
+ version: "1.2.3-alpha.1"
168
+ }));
169
+
170
+ const { release } = await import("../src/commands/release.js");
171
+
172
+ // 模拟用户选择升级预发布版本
173
+ const { select } = await import("@inquirer/prompts");
174
+ vi.mocked(select).mockResolvedValue("1.2.3-alpha.2");
175
+
176
+ await release();
177
+
178
+ expect(mockWriteFileSync).toHaveBeenCalledWith(
179
+ "package.json",
180
+ JSON.stringify({ name: "test-package", version: "1.2.3-alpha.2" }, null, "\t") + "\n"
181
+ );
182
+ });
183
+
184
+ it("应该正确将预发布版本转为正式版本", async () => {
185
+ mockReadFileSync.mockReturnValue(JSON.stringify({
186
+ name: "test-package",
187
+ version: "1.2.3-beta.2"
188
+ }));
189
+
190
+ const { release } = await import("../src/commands/release.js");
191
+
192
+ // 模拟用户选择转为正式版本
193
+ const { select } = await import("@inquirer/prompts");
194
+ vi.mocked(select).mockResolvedValue("1.2.3");
195
+
196
+ await release();
197
+
198
+ expect(mockWriteFileSync).toHaveBeenCalledWith(
199
+ "package.json",
200
+ JSON.stringify({ name: "test-package", version: "1.2.3" }, null, "\t") + "\n"
201
+ );
202
+ });
203
+ });
204
+
205
+ describe("用户交互", () => {
206
+ it("应该在用户取消时不更新版本", async () => {
207
+ mockReadFileSync.mockReturnValue(JSON.stringify({
208
+ name: "test-package",
209
+ version: "1.2.3"
210
+ }));
211
+
212
+ const { release } = await import("../src/commands/release.js");
213
+
214
+ // 模拟用户选择取消
215
+ const { select } = await import("@inquirer/prompts");
216
+ vi.mocked(select).mockResolvedValue("__cancel__");
217
+
218
+ await release();
219
+
220
+ expect(mockWriteFileSync).not.toHaveBeenCalled();
221
+ });
222
+
223
+ it("应该显示当前版本号", async () => {
224
+ mockReadFileSync.mockReturnValue(JSON.stringify({ version: "1.2.3" }));
225
+
226
+ const consoleSpy = vi.spyOn(console, "log");
227
+
228
+ const { release } = await import("../src/commands/release.js");
229
+
230
+ // 模拟用户选择取消
231
+ const { select } = await import("@inquirer/prompts");
232
+ vi.mocked(select).mockResolvedValue("__cancel__");
233
+
234
+ await release();
235
+
236
+ expect(consoleSpy).toHaveBeenCalledWith("当前版本: 1.2.3");
237
+ });
238
+
239
+ it("应该显示版本更新成功信息", async () => {
240
+ mockReadFileSync.mockReturnValue(JSON.stringify({
241
+ name: "test-package",
242
+ version: "1.2.3"
243
+ }));
244
+
245
+ const consoleSpy = vi.spyOn(console, "log");
246
+
247
+ const { release } = await import("../src/commands/release.js");
248
+
249
+ // 模拟用户选择 patch 版本
250
+ const { select } = await import("@inquirer/prompts");
251
+ vi.mocked(select).mockResolvedValue("1.2.4");
252
+
253
+ await release();
254
+
255
+ expect(consoleSpy).toHaveBeenCalledWith("✓ 版本号已更新: 1.2.3 → 1.2.4");
256
+ });
257
+
258
+ it("应该显示取消信息", async () => {
259
+ mockReadFileSync.mockReturnValue(JSON.stringify({ version: "1.2.3" }));
260
+
261
+ const consoleSpy = vi.spyOn(console, "log");
262
+
263
+ const { release } = await import("../src/commands/release.js");
264
+
265
+ // 模拟用户选择取消
266
+ const { select } = await import("@inquirer/prompts");
267
+ vi.mocked(select).mockResolvedValue("__cancel__");
268
+
269
+ await release();
270
+
271
+ expect(consoleSpy).toHaveBeenCalledWith("已取消");
272
+ });
273
+ });
274
+
275
+ describe("边界情况", () => {
276
+ it("应该处理无效的版本号格式", async () => {
277
+ mockReadFileSync.mockReturnValue(JSON.stringify({ version: "invalid" }));
278
+
279
+ const { release } = await import("../src/commands/release.js");
280
+
281
+ // 模拟用户选择取消
282
+ const { select } = await import("@inquirer/prompts");
283
+ vi.mocked(select).mockResolvedValue("__cancel__");
284
+
285
+ // 应该不抛出错误
286
+ await expect(release()).resolves.toBeUndefined();
287
+ });
288
+
289
+ it("应该处理空的 package.json", async () => {
290
+ mockReadFileSync.mockReturnValue("{}");
291
+
292
+ const { release } = await import("../src/commands/release.js");
293
+
294
+ // 模拟用户选择取消
295
+ const { select } = await import("@inquirer/prompts");
296
+ vi.mocked(select).mockResolvedValue("__cancel__");
297
+
298
+ // 应该不抛出错误
299
+ await expect(release()).resolves.toBeUndefined();
300
+ });
301
+
302
+ it("应该保持 package.json 的其他字段不变", async () => {
303
+ const originalPackage = {
304
+ name: "test-package",
305
+ version: "1.2.3",
306
+ description: "Test package",
307
+ dependencies: {
308
+ "some-dep": "^1.0.0"
309
+ }
310
+ };
311
+
312
+ mockReadFileSync.mockReturnValue(JSON.stringify(originalPackage));
313
+
314
+ const { release } = await import("../src/commands/release.js");
315
+
316
+ // 模拟用户选择 patch 版本
317
+ const { select } = await import("@inquirer/prompts");
318
+ vi.mocked(select).mockResolvedValue("1.2.4");
319
+
320
+ await release();
321
+
322
+ const expectedPackage = {
323
+ ...originalPackage,
324
+ version: "1.2.4"
325
+ };
326
+
327
+ expect(mockWriteFileSync).toHaveBeenCalledWith(
328
+ "package.json",
329
+ JSON.stringify(expectedPackage, null, "\t") + "\n"
330
+ );
331
+ });
332
+ });
333
+ });
package/tests/setup.ts ADDED
@@ -0,0 +1,21 @@
1
+ import { vi } from "vitest";
2
+
3
+ // 在测试环境中抑制console输出,保持测试输出清洁
4
+ // 但仍然允许测试验证console.log的调用
5
+
6
+ // 保存原始的console方法
7
+ const originalConsole = {
8
+ log: console.log,
9
+ error: console.error,
10
+ warn: console.warn,
11
+ info: console.info,
12
+ };
13
+
14
+ // Mock console方法,但不输出到终端
15
+ console.log = vi.fn();
16
+ console.error = vi.fn();
17
+ console.warn = vi.fn();
18
+ console.info = vi.fn();
19
+
20
+ // 如果需要在测试中实际输出(调试用),可以使用这些方法
21
+ (global as any).originalConsole = originalConsole;