@zjex/git-workflow 0.3.3 → 0.3.5
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 +1 -1
- package/dist/index.js +33 -26
- package/package.json +1 -1
- package/src/commands/commit.ts +11 -1
- package/src/commands/tag.ts +14 -12
- package/src/update-notifier.ts +26 -20
- package/tests/update-notifier.test.ts +27 -5
package/README.md
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
<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>
|
|
13
13
|
<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>
|
|
14
14
|
<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>
|
|
15
|
-
<a href="https://github.com/iamzjt-front-end/git-workflow/actions"><img src="https://img.shields.io/badge/tests-
|
|
15
|
+
<a href="https://github.com/iamzjt-front-end/git-workflow/actions"><img src="https://img.shields.io/badge/tests-267%20passed-success?style=flat&colorA=18181B" alt="tests"></a>
|
|
16
16
|
<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>
|
|
17
17
|
</p>
|
|
18
18
|
|
package/dist/index.js
CHANGED
|
@@ -100,12 +100,9 @@ async function checkForUpdates(currentVersion, packageName = "@zjex/git-workflow
|
|
|
100
100
|
try {
|
|
101
101
|
const cache = readCache();
|
|
102
102
|
const now = Date.now();
|
|
103
|
-
if (cache?.latestVersion && cache.
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
return;
|
|
107
|
-
}
|
|
108
|
-
if (semver.gt(cache.latestVersion, currentVersion)) {
|
|
103
|
+
if (cache?.latestVersion && semver.gt(cache.latestVersion, currentVersion)) {
|
|
104
|
+
const isDismissed = cache.lastDismiss && now - cache.lastDismiss < DISMISS_INTERVAL;
|
|
105
|
+
if (!isDismissed) {
|
|
109
106
|
if (interactive) {
|
|
110
107
|
const action = await showUpdateMessage(
|
|
111
108
|
currentVersion,
|
|
@@ -132,16 +129,19 @@ async function checkForUpdates(currentVersion, packageName = "@zjex/git-workflow
|
|
|
132
129
|
function backgroundCheck(currentVersion, packageName) {
|
|
133
130
|
const cache = readCache();
|
|
134
131
|
const now = Date.now();
|
|
135
|
-
|
|
132
|
+
const isUpToDate = cache?.latestVersion && !semver.gt(cache.latestVersion, currentVersion);
|
|
133
|
+
const recentlyChecked = cache?.lastCheck && now - cache.lastCheck < CHECK_INTERVAL;
|
|
134
|
+
if (isUpToDate && recentlyChecked) {
|
|
136
135
|
return;
|
|
137
136
|
}
|
|
138
|
-
|
|
137
|
+
setImmediate(async () => {
|
|
139
138
|
try {
|
|
140
139
|
const latestVersion = await getLatestVersion(packageName);
|
|
141
140
|
if (latestVersion) {
|
|
141
|
+
const cache2 = readCache() || {};
|
|
142
142
|
writeCache({
|
|
143
|
-
...
|
|
144
|
-
lastCheck: now,
|
|
143
|
+
...cache2,
|
|
144
|
+
lastCheck: Date.now(),
|
|
145
145
|
latestVersion,
|
|
146
146
|
checkedVersion: currentVersion
|
|
147
147
|
});
|
|
@@ -307,13 +307,13 @@ function writeCache(cache) {
|
|
|
307
307
|
} catch {
|
|
308
308
|
}
|
|
309
309
|
}
|
|
310
|
-
var
|
|
310
|
+
var DISMISS_INTERVAL, CHECK_INTERVAL, CACHE_FILE;
|
|
311
311
|
var init_update_notifier = __esm({
|
|
312
312
|
"src/update-notifier.ts"() {
|
|
313
313
|
"use strict";
|
|
314
314
|
init_utils();
|
|
315
|
-
CHECK_INTERVAL = 1e3 * 60 * 60 * 4;
|
|
316
315
|
DISMISS_INTERVAL = 1e3 * 60 * 60 * 24;
|
|
316
|
+
CHECK_INTERVAL = 1e3 * 60 * 60 * 1;
|
|
317
317
|
CACHE_FILE = ".gw-update-check";
|
|
318
318
|
}
|
|
319
319
|
});
|
|
@@ -669,13 +669,14 @@ async function listTags(prefix) {
|
|
|
669
669
|
return;
|
|
670
670
|
}
|
|
671
671
|
if (prefix) {
|
|
672
|
-
console.log(
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
if (tags.length >
|
|
676
|
-
console.log(colors.
|
|
677
|
-
\u5171 ${tags.length} \u4E2A\uFF0C\u4EC5\u663E\u793A\u6700\u65B0 20 \u4E2A`));
|
|
672
|
+
console.log(
|
|
673
|
+
colors.green(`\u4EE5 '${prefix}' \u5F00\u5934\u7684 tags (\u5171 ${tags.length} \u4E2A):`)
|
|
674
|
+
);
|
|
675
|
+
if (tags.length > 5) {
|
|
676
|
+
console.log(colors.dim(" ..."));
|
|
678
677
|
}
|
|
678
|
+
const displayTags = tags.slice(-5);
|
|
679
|
+
displayTags.forEach((tag) => console.log(` ${tag}`));
|
|
679
680
|
return;
|
|
680
681
|
}
|
|
681
682
|
const grouped = /* @__PURE__ */ new Map();
|
|
@@ -687,13 +688,12 @@ async function listTags(prefix) {
|
|
|
687
688
|
grouped.get(prefix2).push(tag);
|
|
688
689
|
});
|
|
689
690
|
if (grouped.size === 1) {
|
|
690
|
-
console.log(colors.green(
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
if (tags.length > 20) {
|
|
694
|
-
console.log(colors.yellow(`
|
|
695
|
-
\u5171 ${tags.length} \u4E2A\uFF0C\u4EC5\u663E\u793A\u6700\u65B0 20 \u4E2A`));
|
|
691
|
+
console.log(colors.green(`\u6240\u6709 tags (\u5171 ${tags.length} \u4E2A):`));
|
|
692
|
+
if (tags.length > 5) {
|
|
693
|
+
console.log(colors.dim(" ..."));
|
|
696
694
|
}
|
|
695
|
+
const displayTags = tags.slice(-5);
|
|
696
|
+
displayTags.forEach((tag) => console.log(` ${tag}`));
|
|
697
697
|
return;
|
|
698
698
|
}
|
|
699
699
|
console.log(colors.green("\u6240\u6709 tags (\u6309\u524D\u7F00\u5206\u7EC4):"));
|
|
@@ -2320,7 +2320,14 @@ async function commit() {
|
|
|
2320
2320
|
}
|
|
2321
2321
|
const spinner = ora4("\u6B63\u5728\u63D0\u4EA4...").start();
|
|
2322
2322
|
try {
|
|
2323
|
-
|
|
2323
|
+
let finalStatus = parseGitStatus();
|
|
2324
|
+
if (finalStatus.staged.length === 0 && finalStatus.unstaged.length > 0) {
|
|
2325
|
+
const autoStage = config2.autoStage ?? true;
|
|
2326
|
+
if (autoStage) {
|
|
2327
|
+
execSync5("git add -A", { stdio: "pipe" });
|
|
2328
|
+
finalStatus = parseGitStatus();
|
|
2329
|
+
}
|
|
2330
|
+
}
|
|
2324
2331
|
if (finalStatus.staged.length === 0) {
|
|
2325
2332
|
spinner.fail("\u6CA1\u6709\u6682\u5B58\u7684\u6587\u4EF6\u53EF\u4EE5\u63D0\u4EA4");
|
|
2326
2333
|
console.log("");
|
|
@@ -2909,7 +2916,7 @@ process.on("SIGTERM", () => {
|
|
|
2909
2916
|
console.log("");
|
|
2910
2917
|
process.exit(0);
|
|
2911
2918
|
});
|
|
2912
|
-
var version = true ? "0.3.
|
|
2919
|
+
var version = true ? "0.3.5" : "0.0.0-dev";
|
|
2913
2920
|
async function mainMenu() {
|
|
2914
2921
|
console.log(
|
|
2915
2922
|
colors.green(`
|
package/package.json
CHANGED
package/src/commands/commit.ts
CHANGED
|
@@ -265,7 +265,17 @@ export async function commit(): Promise<void> {
|
|
|
265
265
|
|
|
266
266
|
try {
|
|
267
267
|
// 提交前再次检查是否有暂存的文件
|
|
268
|
-
|
|
268
|
+
let finalStatus = parseGitStatus();
|
|
269
|
+
|
|
270
|
+
// 如果暂存区为空,但有未暂存的更改,且开启了自动暂存,则重新暂存
|
|
271
|
+
if (finalStatus.staged.length === 0 && finalStatus.unstaged.length > 0) {
|
|
272
|
+
const autoStage = config.autoStage ?? true;
|
|
273
|
+
if (autoStage) {
|
|
274
|
+
execSync("git add -A", { stdio: "pipe" });
|
|
275
|
+
finalStatus = parseGitStatus();
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
269
279
|
if (finalStatus.staged.length === 0) {
|
|
270
280
|
spinner.fail("没有暂存的文件可以提交");
|
|
271
281
|
console.log("");
|
package/src/commands/tag.ts
CHANGED
|
@@ -32,14 +32,16 @@ export async function listTags(prefix?: string): Promise<void> {
|
|
|
32
32
|
return;
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
//
|
|
35
|
+
// 5. 如果指定了前缀,直接显示单列(最多 5 个)
|
|
36
36
|
if (prefix) {
|
|
37
|
-
console.log(
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
if (tags.length >
|
|
41
|
-
console.log(colors.
|
|
37
|
+
console.log(
|
|
38
|
+
colors.green(`以 '${prefix}' 开头的 tags (共 ${tags.length} 个):`)
|
|
39
|
+
);
|
|
40
|
+
if (tags.length > 5) {
|
|
41
|
+
console.log(colors.dim(" ..."));
|
|
42
42
|
}
|
|
43
|
+
const displayTags = tags.slice(-5);
|
|
44
|
+
displayTags.forEach((tag) => console.log(` ${tag}`));
|
|
43
45
|
return;
|
|
44
46
|
}
|
|
45
47
|
|
|
@@ -55,14 +57,14 @@ export async function listTags(prefix?: string): Promise<void> {
|
|
|
55
57
|
grouped.get(prefix)!.push(tag);
|
|
56
58
|
});
|
|
57
59
|
|
|
58
|
-
//
|
|
60
|
+
// 7. 如果只有一个前缀,使用单列显示(最多 5 个)
|
|
59
61
|
if (grouped.size === 1) {
|
|
60
|
-
console.log(colors.green(
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
if (tags.length > 20) {
|
|
64
|
-
console.log(colors.yellow(`\n共 ${tags.length} 个,仅显示最新 20 个`));
|
|
62
|
+
console.log(colors.green(`所有 tags (共 ${tags.length} 个):`));
|
|
63
|
+
if (tags.length > 5) {
|
|
64
|
+
console.log(colors.dim(" ..."));
|
|
65
65
|
}
|
|
66
|
+
const displayTags = tags.slice(-5);
|
|
67
|
+
displayTags.forEach((tag) => console.log(` ${tag}`));
|
|
66
68
|
return;
|
|
67
69
|
}
|
|
68
70
|
|
package/src/update-notifier.ts
CHANGED
|
@@ -8,8 +8,8 @@ import ora from "ora";
|
|
|
8
8
|
import semver from "semver";
|
|
9
9
|
import { colors } from "./utils.js";
|
|
10
10
|
|
|
11
|
-
const CHECK_INTERVAL = 1000 * 60 * 60 * 4; // 4 小时检查一次
|
|
12
11
|
const DISMISS_INTERVAL = 1000 * 60 * 60 * 24; // 24 小时后再次提示
|
|
12
|
+
const CHECK_INTERVAL = 1000 * 60 * 60 * 1; // 已是最新版本时,1 小时检查一次
|
|
13
13
|
const CACHE_FILE = ".gw-update-check";
|
|
14
14
|
|
|
15
15
|
interface UpdateCache {
|
|
@@ -20,7 +20,8 @@ interface UpdateCache {
|
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
/**
|
|
23
|
-
*
|
|
23
|
+
* 检查是否有新版本
|
|
24
|
+
* 策略:后台异步检查,下次运行时提示
|
|
24
25
|
* @param currentVersion 当前版本
|
|
25
26
|
* @param packageName 包名
|
|
26
27
|
* @param interactive 是否交互式(true: 显示完整提示并可选择更新,false: 只显示简单提示)
|
|
@@ -34,19 +35,17 @@ export async function checkForUpdates(
|
|
|
34
35
|
const cache = readCache();
|
|
35
36
|
const now = Date.now();
|
|
36
37
|
|
|
37
|
-
// 1.
|
|
38
|
-
if (
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
38
|
+
// 1. 先用缓存的结果提示用户(如果有新版本)
|
|
39
|
+
if (
|
|
40
|
+
cache?.latestVersion &&
|
|
41
|
+
semver.gt(cache.latestVersion, currentVersion)
|
|
42
|
+
) {
|
|
43
|
+
// 检查用户是否在 24 小时内关闭过提示
|
|
44
|
+
const isDismissed =
|
|
45
|
+
cache.lastDismiss && now - cache.lastDismiss < DISMISS_INTERVAL;
|
|
45
46
|
|
|
46
|
-
|
|
47
|
-
if (semver.gt(cache.latestVersion, currentVersion)) {
|
|
47
|
+
if (!isDismissed) {
|
|
48
48
|
if (interactive) {
|
|
49
|
-
// 交互式模式:显示完整提示,可选择更新
|
|
50
49
|
const action = await showUpdateMessage(
|
|
51
50
|
currentVersion,
|
|
52
51
|
cache.latestVersion,
|
|
@@ -59,13 +58,12 @@ export async function checkForUpdates(
|
|
|
59
58
|
writeCache({ ...cache, lastDismiss: now });
|
|
60
59
|
}
|
|
61
60
|
} else {
|
|
62
|
-
// 非交互式模式:只显示简单提示
|
|
63
61
|
showSimpleNotification(currentVersion, cache.latestVersion);
|
|
64
62
|
}
|
|
65
63
|
}
|
|
66
64
|
}
|
|
67
65
|
|
|
68
|
-
// 2.
|
|
66
|
+
// 2. 后台异步检查更新(每次都检查,不阻塞)
|
|
69
67
|
backgroundCheck(currentVersion, packageName);
|
|
70
68
|
} catch (error) {
|
|
71
69
|
// 如果是用户按 Ctrl+C,重新抛出让全局处理
|
|
@@ -78,25 +76,33 @@ export async function checkForUpdates(
|
|
|
78
76
|
|
|
79
77
|
/**
|
|
80
78
|
* 后台异步检查更新(不阻塞)
|
|
79
|
+
* - 有新版本时:每次都检查
|
|
80
|
+
* - 已是最新版本时:1 小时检查一次
|
|
81
81
|
*/
|
|
82
82
|
function backgroundCheck(currentVersion: string, packageName: string): void {
|
|
83
83
|
const cache = readCache();
|
|
84
84
|
const now = Date.now();
|
|
85
85
|
|
|
86
|
-
//
|
|
87
|
-
|
|
86
|
+
// 如果已是最新版本,且距离上次检查不到 1 小时,跳过
|
|
87
|
+
const isUpToDate =
|
|
88
|
+
cache?.latestVersion && !semver.gt(cache.latestVersion, currentVersion);
|
|
89
|
+
const recentlyChecked =
|
|
90
|
+
cache?.lastCheck && now - cache.lastCheck < CHECK_INTERVAL;
|
|
91
|
+
|
|
92
|
+
if (isUpToDate && recentlyChecked) {
|
|
88
93
|
return;
|
|
89
94
|
}
|
|
90
95
|
|
|
91
|
-
// 使用
|
|
92
|
-
|
|
96
|
+
// 使用 setImmediate 确保不阻塞主流程
|
|
97
|
+
setImmediate(async () => {
|
|
93
98
|
try {
|
|
94
99
|
const latestVersion = await getLatestVersion(packageName);
|
|
95
100
|
|
|
96
101
|
if (latestVersion) {
|
|
102
|
+
const cache = readCache() || {};
|
|
97
103
|
writeCache({
|
|
98
104
|
...cache,
|
|
99
|
-
lastCheck: now,
|
|
105
|
+
lastCheck: Date.now(),
|
|
100
106
|
latestVersion,
|
|
101
107
|
checkedVersion: currentVersion,
|
|
102
108
|
});
|
|
@@ -153,7 +153,7 @@ describe("Update Notifier 模块测试", () => {
|
|
|
153
153
|
consoleSpy.mockRestore();
|
|
154
154
|
});
|
|
155
155
|
|
|
156
|
-
it("
|
|
156
|
+
it("已是最新版本且1小时内不应该重复检查", async () => {
|
|
157
157
|
const mockCache = {
|
|
158
158
|
lastCheck: Date.now(),
|
|
159
159
|
latestVersion: "1.0.0",
|
|
@@ -167,14 +167,14 @@ describe("Update Notifier 模块测试", () => {
|
|
|
167
167
|
await checkForUpdates("1.0.0");
|
|
168
168
|
await vi.runAllTimersAsync();
|
|
169
169
|
|
|
170
|
-
// writeFileSync
|
|
170
|
+
// writeFileSync 不应该被调用(因为已是最新版本且在1小时内)
|
|
171
171
|
expect(writeFileSync).not.toHaveBeenCalled();
|
|
172
172
|
});
|
|
173
173
|
|
|
174
|
-
it("
|
|
175
|
-
const
|
|
174
|
+
it("已是最新版本但超过1小时应该重新检查", async () => {
|
|
175
|
+
const oneHourAgo = Date.now() - 2 * 60 * 60 * 1000; // 2小时前
|
|
176
176
|
const mockCache = {
|
|
177
|
-
lastCheck:
|
|
177
|
+
lastCheck: oneHourAgo,
|
|
178
178
|
latestVersion: "1.0.0",
|
|
179
179
|
checkedVersion: "1.0.0",
|
|
180
180
|
};
|
|
@@ -189,6 +189,28 @@ describe("Update Notifier 模块测试", () => {
|
|
|
189
189
|
expect(writeFileSync).toHaveBeenCalled();
|
|
190
190
|
});
|
|
191
191
|
|
|
192
|
+
it("有新版本时每次都应该后台检查", async () => {
|
|
193
|
+
const mockCache = {
|
|
194
|
+
lastCheck: Date.now(), // 刚刚检查过
|
|
195
|
+
latestVersion: "1.0.1", // 有新版本
|
|
196
|
+
checkedVersion: "1.0.0",
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
vi.mocked(existsSync).mockReturnValue(true);
|
|
200
|
+
vi.mocked(readFileSync).mockReturnValue(JSON.stringify(mockCache));
|
|
201
|
+
vi.mocked(execSync).mockReturnValue("1.0.2" as any);
|
|
202
|
+
|
|
203
|
+
const consoleSpy = vi.spyOn(console, "log").mockImplementation(() => {});
|
|
204
|
+
|
|
205
|
+
await checkForUpdates("1.0.0");
|
|
206
|
+
await vi.runAllTimersAsync();
|
|
207
|
+
|
|
208
|
+
// 即使刚检查过,有新版本时也应该继续检查
|
|
209
|
+
expect(writeFileSync).toHaveBeenCalled();
|
|
210
|
+
|
|
211
|
+
consoleSpy.mockRestore();
|
|
212
|
+
});
|
|
213
|
+
|
|
192
214
|
it("缓存文件损坏时应该静默处理", async () => {
|
|
193
215
|
vi.mocked(existsSync).mockReturnValue(true);
|
|
194
216
|
vi.mocked(readFileSync).mockReturnValue("invalid json");
|