git-push-time 1.0.2 → 1.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 +151 -47
- package/bin/index.js +231 -443
- package/lib/commands/edit-time.js +127 -0
- package/lib/commands/fix.js +105 -0
- package/lib/commands/github.js +436 -0
- package/lib/commands/pull.js +88 -0
- package/lib/commands/push.js +653 -0
- package/lib/config.js +210 -0
- package/lib/git.js +222 -13
- package/lib/github.js +304 -0
- package/lib/prompt-helpers.js +62 -0
- package/lib/ui.js +683 -248
- package/package.json +4 -4
- package/lib/npm.js +0 -82
package/lib/ui.js
CHANGED
|
@@ -1,409 +1,844 @@
|
|
|
1
|
-
import chalk from
|
|
2
|
-
import stringWidth from
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import stringWidth from "string-width";
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
/**
|
|
5
|
+
* Tự động tính toán chiều rộng card linh hoạt theo Terminal (68 - 80 ký tự)
|
|
6
|
+
*/
|
|
7
|
+
export function getW() {
|
|
8
|
+
const cols = process.stdout.columns || 72;
|
|
9
|
+
return Math.min(80, Math.max(66, cols - 4));
|
|
10
|
+
}
|
|
5
11
|
|
|
6
|
-
// Chuyển mã hex thành [r, g, b]
|
|
7
12
|
function hexToRgb(hex) {
|
|
8
|
-
const n = parseInt(hex.replace(
|
|
13
|
+
const n = parseInt(hex.replace("#", ""), 16);
|
|
9
14
|
return [(n >> 16) & 255, (n >> 8) & 255, n & 255];
|
|
10
15
|
}
|
|
11
16
|
|
|
12
17
|
/**
|
|
13
|
-
* Tạo gradient màu mượt mà
|
|
18
|
+
* Tạo gradient màu mượt mà theo chuẩn RGB Interpolation
|
|
14
19
|
*/
|
|
15
|
-
export function multiGradient(
|
|
20
|
+
export function multiGradient(
|
|
21
|
+
text,
|
|
22
|
+
colors = ["#C084FC", "#6366F1", "#38BDF8", "#34D399"],
|
|
23
|
+
) {
|
|
16
24
|
const chars = [...text];
|
|
17
25
|
const len = chars.length || 1;
|
|
18
26
|
const rgbColors = colors.map(hexToRgb);
|
|
19
27
|
|
|
20
|
-
return chars
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
28
|
+
return chars
|
|
29
|
+
.map((c, i) => {
|
|
30
|
+
const t = (i / (len - 1 || 1)) * (rgbColors.length - 1);
|
|
31
|
+
const idx = Math.min(Math.floor(t), rgbColors.length - 2);
|
|
32
|
+
const localT = t - idx;
|
|
33
|
+
const [r1, g1, b1] = rgbColors[idx];
|
|
34
|
+
const [r2, g2, b2] = rgbColors[idx + 1];
|
|
35
|
+
const r = Math.round(r1 + (r2 - r1) * localT);
|
|
36
|
+
const g = Math.round(g1 + (g2 - g1) * localT);
|
|
37
|
+
const b = Math.round(b1 + (b2 - b1) * localT);
|
|
38
|
+
return chalk.rgb(r, g, b)(c);
|
|
39
|
+
})
|
|
40
|
+
.join("");
|
|
31
41
|
}
|
|
32
42
|
|
|
33
|
-
|
|
34
|
-
* Căn lề chuẩn xác dựa trên chiều rộng hiển thị thực tế (hỗ trợ Emoji và Unicode)
|
|
35
|
-
*/
|
|
36
|
-
function pad(str, len) {
|
|
43
|
+
export function pad(str, len) {
|
|
37
44
|
const vLen = stringWidth(str);
|
|
38
45
|
if (vLen >= len) return str;
|
|
39
|
-
return str +
|
|
46
|
+
return str + " ".repeat(len - vLen);
|
|
40
47
|
}
|
|
41
48
|
|
|
42
|
-
|
|
43
|
-
* Cắt ngắn chuỗi nếu quá dài theo chiều rộng terminal thực tế
|
|
44
|
-
*/
|
|
45
|
-
function truncateVisual(str, maxLen) {
|
|
49
|
+
export function truncateVisual(str, maxLen) {
|
|
46
50
|
if (stringWidth(str) <= maxLen) return str;
|
|
47
|
-
let res =
|
|
51
|
+
let res = "";
|
|
48
52
|
for (const ch of str) {
|
|
49
|
-
if (stringWidth(res + ch +
|
|
50
|
-
return res +
|
|
53
|
+
if (stringWidth(res + ch + "…") > maxLen) {
|
|
54
|
+
return res + "…";
|
|
51
55
|
}
|
|
52
56
|
res += ch;
|
|
53
57
|
}
|
|
54
|
-
return res +
|
|
58
|
+
return res + "…";
|
|
55
59
|
}
|
|
56
60
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
+
/* ==========================================================================
|
|
62
|
+
BOX ENGINE CHUẨN (Bo góc mềm & Căn lề pixel-perfect)
|
|
63
|
+
========================================================================== */
|
|
64
|
+
|
|
65
|
+
export function boxTop(title = "", borderColor = chalk.hex("#334155")) {
|
|
66
|
+
const W = getW();
|
|
67
|
+
if (!title) return borderColor("╭" + "─".repeat(W - 2) + "╮");
|
|
68
|
+
|
|
61
69
|
const titleWidth = stringWidth(title);
|
|
62
|
-
const leftDash =
|
|
70
|
+
const leftDash = "─".repeat(3);
|
|
63
71
|
const rightDashLen = Math.max(0, W - 2 - 3 - titleWidth);
|
|
64
|
-
return
|
|
72
|
+
return (
|
|
73
|
+
borderColor("╭" + leftDash) +
|
|
74
|
+
title +
|
|
75
|
+
borderColor("─".repeat(rightDashLen) + "╮")
|
|
76
|
+
);
|
|
65
77
|
}
|
|
66
78
|
|
|
67
|
-
function boxBottom(borderColor = chalk.hex(
|
|
68
|
-
|
|
79
|
+
export function boxBottom(borderColor = chalk.hex("#334155")) {
|
|
80
|
+
const W = getW();
|
|
81
|
+
return borderColor("╰" + "─".repeat(W - 2) + "╯");
|
|
69
82
|
}
|
|
70
83
|
|
|
71
|
-
function
|
|
72
|
-
|
|
84
|
+
export function boxRow(content = "", borderColor = chalk.hex("#334155")) {
|
|
85
|
+
const W = getW();
|
|
86
|
+
return borderColor("│ ") + pad(content, W - 4) + borderColor(" │");
|
|
73
87
|
}
|
|
74
88
|
|
|
75
|
-
function
|
|
76
|
-
|
|
89
|
+
export function boxDivider(borderColor = chalk.hex("#334155")) {
|
|
90
|
+
const W = getW();
|
|
91
|
+
return borderColor("├" + "─".repeat(W - 2) + "┤");
|
|
77
92
|
}
|
|
78
93
|
|
|
79
94
|
/**
|
|
80
|
-
*
|
|
95
|
+
* Hàng Key-Value có đường nối bằng dấu chấm ․ ․ ․
|
|
81
96
|
*/
|
|
82
|
-
export function
|
|
97
|
+
export function boxKvDot(
|
|
98
|
+
label,
|
|
99
|
+
value,
|
|
100
|
+
labelWidth = 16,
|
|
101
|
+
borderColor = chalk.hex("#334155"),
|
|
102
|
+
) {
|
|
103
|
+
const W = getW();
|
|
104
|
+
const maxValWidth = Math.max(10, W - 6 - labelWidth);
|
|
105
|
+
const truncatedVal = truncateVisual(value, maxValWidth);
|
|
106
|
+
const valWidth = stringWidth(truncatedVal);
|
|
107
|
+
|
|
108
|
+
const dotsLen = Math.max(1, W - 6 - labelWidth - valWidth);
|
|
109
|
+
const dots = chalk.hex("#1E293B")("․".repeat(dotsLen));
|
|
110
|
+
const formattedLabel = chalk.hex("#94A3B8")(pad(label, labelWidth));
|
|
111
|
+
|
|
112
|
+
return boxRow(`${formattedLabel}${dots}${truncatedVal}`, borderColor);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/* ==========================================================================
|
|
116
|
+
PUBLIC INTERFACE & BANNER
|
|
117
|
+
========================================================================== */
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Banner DevTools sắc nét, hiện đại
|
|
121
|
+
*/
|
|
122
|
+
export function printBanner(version = "1.1.0") {
|
|
83
123
|
const ascii = [
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
124
|
+
" ____ _ _ ____ _ _____ _ ",
|
|
125
|
+
" / ___(_) |_ | _ \\ _ _ ___| |__ |_ _(_)_ __ ___ ___ ",
|
|
126
|
+
" | | _| | __| | |_) | | | / __| '_ \\ | | | | '_ ` _ \\ / _ \\ ",
|
|
127
|
+
" | |_| | | |_ | __/| |_| \\__ \\ | | | | | | | | | | | | __/ ",
|
|
128
|
+
" \\____|_|\\__| |_| \\__,_|___/_| |_| |_| |_|_| |_| |_|\\___| ",
|
|
89
129
|
];
|
|
90
130
|
|
|
91
|
-
console.log(
|
|
92
|
-
const colors = [
|
|
131
|
+
console.log("");
|
|
132
|
+
const colors = ["#818CF8", "#38BDF8", "#34D399", "#A7F3D0"];
|
|
93
133
|
ascii.forEach((l) => console.log(multiGradient(l, colors)));
|
|
94
134
|
|
|
95
|
-
const badgeVer = chalk.bgHex(
|
|
96
|
-
const
|
|
97
|
-
const
|
|
98
|
-
const
|
|
135
|
+
const badgeVer = chalk.bgHex("#1E1B4B").hex("#818CF8").bold(` v${version} `);
|
|
136
|
+
const badgeGit = chalk.bgHex("#064E3B").hex("#34D399").bold(" SMART GIT ");
|
|
137
|
+
const badgeHub = chalk.bgHex("#4338CA").hex("#C7D2FE").bold(" GITHUB SYNC ");
|
|
138
|
+
const badgeShield = chalk.bgHex("#0F172A").hex("#38BDF8").bold(" SECRET SHIELD ");
|
|
139
|
+
const badgeDate = chalk.bgHex("#78350F").hex("#FDE047").bold(" BACKDATE ");
|
|
99
140
|
|
|
100
|
-
console.log(`\n ${badgeVer} ${
|
|
141
|
+
console.log(`\n ${badgeVer} ${badgeGit} ${badgeHub} ${badgeShield} ${badgeDate}\n`);
|
|
101
142
|
}
|
|
102
143
|
|
|
103
144
|
/**
|
|
104
|
-
*
|
|
145
|
+
* Hiển thị Dashboard trạng thái ngắn gọn ở đầu menu
|
|
146
|
+
*/
|
|
147
|
+
export function printDashboardStatus({ targetDir = process.cwd(), currentBranch = 'main', githubUser = null }) {
|
|
148
|
+
const border = chalk.hex("#1E293B");
|
|
149
|
+
const W = getW();
|
|
150
|
+
|
|
151
|
+
const dirName = truncateVisual(targetDir, 32);
|
|
152
|
+
const branchText = chalk.hex("#38BDF8").bold(currentBranch);
|
|
153
|
+
|
|
154
|
+
let ghStatusText;
|
|
155
|
+
if (githubUser) {
|
|
156
|
+
ghStatusText = chalk.hex("#34D399").bold(`@${githubUser.login}`) + chalk.hex("#64748B")(' (Đã kết nối)');
|
|
157
|
+
} else {
|
|
158
|
+
ghStatusText = chalk.hex("#94A3B8")('Chưa kết nối') + chalk.hex("#64748B")(' [chọn mục GITHUB để login]');
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
console.log(boxTop(chalk.hex("#475569")(" [ TRẠNG THÁI ] "), border));
|
|
162
|
+
console.log(boxKvDot("Thư mục", chalk.hex("#F8FAFC")(dirName), 14, border));
|
|
163
|
+
console.log(boxKvDot("Nhánh (Branch)", branchText, 14, border));
|
|
164
|
+
console.log(boxKvDot("GitHub Account", ghStatusText, 14, border));
|
|
165
|
+
console.log(boxBottom(border));
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Tiêu đề phân đoạn với điểm nhấn màu sắc
|
|
105
170
|
*/
|
|
106
171
|
export function printSection(title) {
|
|
107
|
-
const
|
|
108
|
-
const
|
|
109
|
-
const
|
|
172
|
+
const W = getW();
|
|
173
|
+
const clean = String(title || "").toUpperCase();
|
|
174
|
+
const marker = chalk.hex("#38BDF8").bold("»");
|
|
175
|
+
const titleStyled = chalk.bold.hex("#F8FAFC")(` ${clean} `);
|
|
176
|
+
const prefix = chalk.hex("#334155")("── ") + marker + titleStyled;
|
|
110
177
|
const remaining = Math.max(0, W - stringWidth(prefix));
|
|
111
|
-
console.log(
|
|
112
|
-
console.log(prefix + chalk.hex(
|
|
178
|
+
console.log("");
|
|
179
|
+
console.log(prefix + chalk.hex("#334155")("─".repeat(remaining)));
|
|
113
180
|
}
|
|
114
181
|
|
|
115
182
|
/**
|
|
116
|
-
*
|
|
183
|
+
* Pill Step Badge (Ví dụ: 1/6)
|
|
117
184
|
*/
|
|
118
185
|
export function stepLabel(current, total, text) {
|
|
119
|
-
const pill = chalk
|
|
120
|
-
|
|
186
|
+
const pill = chalk
|
|
187
|
+
.bgHex("#2563EB")
|
|
188
|
+
.hex("#FFFFFF")
|
|
189
|
+
.bold(` ${current}/${total} `);
|
|
190
|
+
return `${pill} ` + chalk.hex("#F1F5F9").bold(text);
|
|
121
191
|
}
|
|
122
192
|
|
|
123
193
|
/**
|
|
124
|
-
* Bảng tóm tắt thông tin
|
|
194
|
+
* Bảng tóm tắt thông tin trước khi Push
|
|
125
195
|
*/
|
|
126
|
-
export function printSummary({
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
196
|
+
export function printSummary({
|
|
197
|
+
repoUrl,
|
|
198
|
+
commitMsg,
|
|
199
|
+
dateDisplay,
|
|
200
|
+
branch,
|
|
201
|
+
force,
|
|
202
|
+
sourceDir,
|
|
203
|
+
}) {
|
|
204
|
+
const border = chalk.hex("#475569");
|
|
205
|
+
const title = chalk.bold.bgHex("#4338CA").hex("#FFFFFF")(
|
|
206
|
+
" XÁC NHẬN THÔNG TIN ĐẨY DỰ ÁN ",
|
|
207
|
+
);
|
|
208
|
+
|
|
209
|
+
console.log("");
|
|
133
210
|
console.log(boxTop(title, border));
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
211
|
+
|
|
212
|
+
if (sourceDir) {
|
|
213
|
+
console.log(
|
|
214
|
+
boxKvDot("Thư mục nguồn", chalk.hex("#34D399")(sourceDir), 16, border),
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
console.log(
|
|
218
|
+
boxKvDot(
|
|
219
|
+
"Git Repository",
|
|
220
|
+
chalk.hex("#38BDF8").underline(repoUrl),
|
|
221
|
+
16,
|
|
222
|
+
border,
|
|
223
|
+
),
|
|
224
|
+
);
|
|
225
|
+
console.log(
|
|
226
|
+
boxKvDot("Commit Message", chalk.hex("#FBBF24").bold(commitMsg), 16, border),
|
|
227
|
+
);
|
|
228
|
+
console.log(
|
|
229
|
+
boxKvDot("Thời gian", chalk.hex("#C084FC")(dateDisplay), 16, border),
|
|
230
|
+
);
|
|
231
|
+
|
|
232
|
+
const branchPill = chalk
|
|
233
|
+
.bgHex("#065F46")
|
|
234
|
+
.hex("#6EE7B7")
|
|
235
|
+
.bold(` ${branch} `);
|
|
236
|
+
console.log(boxKvDot("Nhánh đẩy lên", branchPill, 16, border));
|
|
138
237
|
|
|
139
238
|
const forceText = force
|
|
140
|
-
? chalk.bgHex(
|
|
141
|
-
: chalk.hex(
|
|
142
|
-
console.log(
|
|
239
|
+
? chalk.bgHex("#991B1B").hex("#FCA5A5").bold(" BẬT (--force) [!] ")
|
|
240
|
+
: chalk.hex("#64748B")("Tắt (chế độ an toàn)");
|
|
241
|
+
console.log(boxKvDot("Ghi đè (Force)", forceText, 16, border));
|
|
242
|
+
|
|
243
|
+
console.log(boxBottom(border));
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
export function printTargetDir(dir = "") {
|
|
247
|
+
const W = getW();
|
|
248
|
+
console.log(
|
|
249
|
+
chalk.hex("#64748B")(
|
|
250
|
+
` » Folder: ${truncateVisual(String(dir || ""), W - 13)}`,
|
|
251
|
+
),
|
|
252
|
+
);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Hiển thị thẻ Profile thông tin tài khoản GitHub
|
|
257
|
+
*/
|
|
258
|
+
export function printGitHubProfile(user, source = '') {
|
|
259
|
+
const border = chalk.hex("#4F46E5");
|
|
260
|
+
const title = chalk.bold.bgHex("#4F46E5").hex("#FFFFFF")(
|
|
261
|
+
" TÀI KHOẢN GITHUB KẾT NỐI ",
|
|
262
|
+
);
|
|
263
|
+
|
|
264
|
+
console.log("");
|
|
265
|
+
console.log(boxTop(title, border));
|
|
266
|
+
|
|
267
|
+
console.log(
|
|
268
|
+
boxKvDot("Username", chalk.hex("#38BDF8").bold(`@${user.login}`), 16, border)
|
|
269
|
+
);
|
|
270
|
+
if (user.name) {
|
|
271
|
+
console.log(boxKvDot("Tên hiển thị", chalk.hex("#F8FAFC")(user.name), 16, border));
|
|
272
|
+
}
|
|
273
|
+
if (user.email) {
|
|
274
|
+
console.log(boxKvDot("Email", chalk.hex("#94A3B8")(user.email), 16, border));
|
|
275
|
+
}
|
|
276
|
+
if (user.bio) {
|
|
277
|
+
console.log(boxKvDot("Tiểu sử", chalk.hex("#CBD5E1")(truncateVisual(user.bio, 40)), 16, border));
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
const repoStats = `${user.public_repos || 0} public • ${user.total_private_repos || 0} private`;
|
|
281
|
+
console.log(boxKvDot("Repositories", chalk.hex("#34D399").bold(repoStats), 16, border));
|
|
282
|
+
|
|
283
|
+
if (source) {
|
|
284
|
+
const srcDesc = source === 'gh-cli' ? 'GitHub CLI (gh)' : source === 'env' ? 'Biến môi trường' : 'Lưu trữ cá nhân';
|
|
285
|
+
console.log(boxKvDot("Nguồn xác thực", chalk.hex("#A78BFA")(srcDesc), 16, border));
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
console.log(boxDivider(border));
|
|
289
|
+
console.log(boxRow(chalk.hex("#64748B")(`» Trang cá nhân: ${user.html_url}`), border));
|
|
290
|
+
console.log(boxBottom(border));
|
|
291
|
+
console.log("");
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* Hiển thị mã xác thực GitHub Device Flow cho người dùng
|
|
296
|
+
*/
|
|
297
|
+
export function printDeviceCodePrompt({ userCode, verificationUri }) {
|
|
298
|
+
const border = chalk.hex("#6366F1");
|
|
299
|
+
const title = chalk.bold.bgHex("#4F46E5").hex("#FFFFFF")(
|
|
300
|
+
" GITHUB OAUTH / DEVICE FLOW ",
|
|
301
|
+
);
|
|
302
|
+
|
|
303
|
+
console.log("");
|
|
304
|
+
console.log(boxTop(title, border));
|
|
305
|
+
console.log(boxRow("", border));
|
|
306
|
+
console.log(boxRow(chalk.hex("#F8FAFC").bold("Bước 1: Mở trình duyệt và truy cập liên kết:"), border));
|
|
307
|
+
console.log(boxRow(chalk.hex("#38BDF8").bold.underline(` ${verificationUri}`), border));
|
|
308
|
+
console.log(boxRow("", border));
|
|
309
|
+
console.log(boxRow(chalk.hex("#F8FAFC").bold("Bước 2: Nhập mã xác nhận (User Code):"), border));
|
|
310
|
+
|
|
311
|
+
const codeBox = chalk.bgHex("#1E1B4B").hex("#A5B4FC").bold(` >>> ${userCode} <<< `);
|
|
312
|
+
console.log(boxRow(` ${codeBox}`, border));
|
|
313
|
+
console.log(boxRow("", border));
|
|
314
|
+
console.log(boxRow(chalk.hex("#94A3B8")("» Hệ thống đang tự động mở trình duyệt cho bạn..."), border));
|
|
315
|
+
console.log(boxRow(chalk.hex("#64748B")("» Nhấn Authorize trên trình duyệt để hoàn tất."), border));
|
|
143
316
|
console.log(boxBottom(border));
|
|
317
|
+
console.log("");
|
|
144
318
|
}
|
|
145
319
|
|
|
146
320
|
/**
|
|
147
|
-
*
|
|
321
|
+
* Hiển thị thông báo sau khi tạo repository GitHub mới thành công
|
|
322
|
+
*/
|
|
323
|
+
export function printRepoCreatedBox(repo) {
|
|
324
|
+
const border = chalk.hex("#059669");
|
|
325
|
+
const title = chalk.bold.bgHex("#10B981").hex("#064E3B")(
|
|
326
|
+
" TẠO REPOSITORY GITHUB THÀNH CÔNG! ",
|
|
327
|
+
);
|
|
328
|
+
|
|
329
|
+
console.log("");
|
|
330
|
+
console.log(boxTop(title, border));
|
|
331
|
+
console.log(boxRow("", border));
|
|
332
|
+
|
|
333
|
+
const visibilityPill = repo.isPrivate
|
|
334
|
+
? chalk.bgHex("#7F1D1D").hex("#FCA5A5").bold(" PRIVATE ")
|
|
335
|
+
: chalk.bgHex("#065F46").hex("#6EE7B7").bold(" PUBLIC ");
|
|
336
|
+
|
|
337
|
+
console.log(boxKvDot("Tên Repo", chalk.hex("#F8FAFC").bold(repo.fullName || repo.name), 14, border));
|
|
338
|
+
console.log(boxKvDot("Quyền riêng tư", visibilityPill, 14, border));
|
|
339
|
+
if (repo.description) {
|
|
340
|
+
console.log(boxKvDot("Mô tả", chalk.hex("#CBD5E1")(truncateVisual(repo.description, 42)), 14, border));
|
|
341
|
+
}
|
|
342
|
+
console.log(boxKvDot("HTTPS Clone", chalk.hex("#38BDF8").underline(repo.cloneUrl), 14, border));
|
|
343
|
+
console.log(boxKvDot("SSH Clone", chalk.hex("#A78BFA")(repo.sshUrl), 14, border));
|
|
344
|
+
|
|
345
|
+
console.log(boxRow("", border));
|
|
346
|
+
console.log(boxDivider(border));
|
|
347
|
+
console.log(boxRow(chalk.hex("#6EE7B7")(`» Xem trên GitHub: ${repo.htmlUrl}`), border));
|
|
348
|
+
console.log(boxBottom(border));
|
|
349
|
+
console.log("");
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* Cảnh báo Folder lồng trong Repo cha
|
|
354
|
+
*/
|
|
355
|
+
export function printNestedRepoWarning({ targetDir = "", topLevel = "" } = {}) {
|
|
356
|
+
const border = chalk.hex("#D97706");
|
|
357
|
+
const title = chalk.bold.bgHex("#D97706").hex("#FFFFFF")(
|
|
358
|
+
" FOLDER NẰM TRONG REPO CHA ",
|
|
359
|
+
);
|
|
360
|
+
|
|
361
|
+
console.log("");
|
|
362
|
+
console.log(boxTop(title, border));
|
|
363
|
+
console.log(
|
|
364
|
+
boxRow(
|
|
365
|
+
chalk.hex("#FDE047")(
|
|
366
|
+
"Folder nguồn đang nằm trong Git repo của folder cha:",
|
|
367
|
+
),
|
|
368
|
+
border,
|
|
369
|
+
),
|
|
370
|
+
);
|
|
371
|
+
console.log(boxKvDot(" +- CHA", chalk.hex("#F8FAFC")(topLevel), 10, border));
|
|
372
|
+
console.log(
|
|
373
|
+
boxKvDot(" +- CON", chalk.hex("#F8FAFC")(targetDir), 10, border),
|
|
374
|
+
);
|
|
375
|
+
console.log(boxRow("", border));
|
|
376
|
+
console.log(
|
|
377
|
+
boxRow(
|
|
378
|
+
chalk.hex("#94A3B8")(
|
|
379
|
+
"Nếu push tiếp, lịch sử/file của folder cha có thể đi kèm.",
|
|
380
|
+
),
|
|
381
|
+
border,
|
|
382
|
+
),
|
|
383
|
+
);
|
|
384
|
+
console.log(
|
|
385
|
+
boxRow(
|
|
386
|
+
chalk.hex("#34D399")(
|
|
387
|
+
"Gợi ý: Tạo repo RIÊNG để chỉ push folder này.",
|
|
388
|
+
),
|
|
389
|
+
border,
|
|
390
|
+
),
|
|
391
|
+
);
|
|
392
|
+
console.log(boxBottom(border));
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
/**
|
|
396
|
+
* Cảnh báo Secret nhạy cảm
|
|
148
397
|
*/
|
|
149
398
|
export function printPrePushSecretWarning({ riskyFiles = [], findings = [] }) {
|
|
150
|
-
const border = chalk.hex(
|
|
151
|
-
const title = chalk.bold.hex(
|
|
399
|
+
const border = chalk.hex("#D97706");
|
|
400
|
+
const title = chalk.bold.bgHex("#D97706").hex("#FFFFFF")(
|
|
401
|
+
" PHÁT HIỆN DỮ LIỆU NHẠY CẢM ",
|
|
402
|
+
);
|
|
152
403
|
|
|
153
|
-
console.log(
|
|
404
|
+
console.log("");
|
|
154
405
|
console.log(boxTop(title, border));
|
|
155
|
-
console.log(
|
|
156
|
-
|
|
406
|
+
console.log(
|
|
407
|
+
boxRow(
|
|
408
|
+
chalk.hex("#FDE047")(
|
|
409
|
+
"GitHub có thể chặn push nếu phát hiện key bí mật (GH013)",
|
|
410
|
+
),
|
|
411
|
+
border,
|
|
412
|
+
),
|
|
413
|
+
);
|
|
414
|
+
console.log(boxRow("", border));
|
|
157
415
|
|
|
158
416
|
if (riskyFiles.length > 0) {
|
|
159
|
-
console.log(
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
417
|
+
console.log(
|
|
418
|
+
boxRow(chalk.bold.hex("#F8FAFC")("File cấu hình nguy cơ:"), border),
|
|
419
|
+
);
|
|
420
|
+
const showFiles = riskyFiles.slice(0, 4);
|
|
421
|
+
showFiles.forEach((f) => {
|
|
422
|
+
const tree = chalk.hex("#64748B")(" +- ");
|
|
423
|
+
console.log(
|
|
424
|
+
boxRow(tree + chalk.hex("#FCD34D")(truncateVisual(f, 46)), border),
|
|
425
|
+
);
|
|
164
426
|
});
|
|
165
|
-
if (riskyFiles.length >
|
|
166
|
-
console.log(
|
|
427
|
+
if (riskyFiles.length > 4) {
|
|
428
|
+
console.log(
|
|
429
|
+
boxRow(
|
|
430
|
+
chalk.hex("#64748B")(` +- ...và ${riskyFiles.length - 4} file khác`),
|
|
431
|
+
border,
|
|
432
|
+
),
|
|
433
|
+
);
|
|
167
434
|
}
|
|
168
435
|
}
|
|
169
436
|
|
|
170
437
|
if (findings.length > 0) {
|
|
171
|
-
if (riskyFiles.length > 0) console.log(boxRow(
|
|
172
|
-
console.log(
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
438
|
+
if (riskyFiles.length > 0) console.log(boxRow("", border));
|
|
439
|
+
console.log(
|
|
440
|
+
boxRow(
|
|
441
|
+
chalk.bold.hex("#F8FAFC")("Vị trí nghi vấn Secret trong code:"),
|
|
442
|
+
border,
|
|
443
|
+
),
|
|
444
|
+
);
|
|
445
|
+
const showFindings = findings.slice(0, 4);
|
|
446
|
+
showFindings.forEach((f, idx) => {
|
|
447
|
+
const isLast = idx === showFindings.length - 1;
|
|
448
|
+
const tree = chalk.hex("#64748B")(" +- ");
|
|
449
|
+
const loc = truncateVisual(`${f.file}:${f.line}`, 26);
|
|
450
|
+
console.log(
|
|
451
|
+
boxRow(
|
|
452
|
+
tree +
|
|
453
|
+
chalk.hex("#F8FAFC")(loc) +
|
|
454
|
+
chalk.hex("#94A3B8")(` [${f.type}]`),
|
|
455
|
+
border,
|
|
456
|
+
),
|
|
457
|
+
);
|
|
178
458
|
if (f.preview) {
|
|
179
|
-
const indent = chalk.hex(
|
|
180
|
-
console.log(
|
|
459
|
+
const indent = chalk.hex("#64748B")(isLast ? " " : " | ");
|
|
460
|
+
console.log(
|
|
461
|
+
boxRow(
|
|
462
|
+
indent + chalk.hex("#64748B")(`"${truncateVisual(f.preview, 40)}"`),
|
|
463
|
+
border,
|
|
464
|
+
),
|
|
465
|
+
);
|
|
181
466
|
}
|
|
182
467
|
});
|
|
183
|
-
if (findings.length >
|
|
184
|
-
console.log(
|
|
468
|
+
if (findings.length > 4) {
|
|
469
|
+
console.log(
|
|
470
|
+
boxRow(
|
|
471
|
+
chalk.hex("#64748B")(` +- ...và ${findings.length - 4} vị trí khác`),
|
|
472
|
+
border,
|
|
473
|
+
),
|
|
474
|
+
);
|
|
185
475
|
}
|
|
186
476
|
}
|
|
187
477
|
|
|
188
|
-
console.log(boxRow(
|
|
189
|
-
console.log(
|
|
478
|
+
console.log(boxRow("", border));
|
|
479
|
+
console.log(
|
|
480
|
+
boxRow(
|
|
481
|
+
chalk.hex("#34D399")(
|
|
482
|
+
'Gợi ý: Chọn "Tự động fix" để gỡ file + .gitignore an toàn',
|
|
483
|
+
),
|
|
484
|
+
border,
|
|
485
|
+
),
|
|
486
|
+
);
|
|
190
487
|
console.log(boxBottom(border));
|
|
191
488
|
}
|
|
192
489
|
|
|
193
490
|
/**
|
|
194
|
-
*
|
|
491
|
+
* Cảnh báo Push bị chặn bởi GitHub Push Protection (GH013)
|
|
195
492
|
*/
|
|
196
493
|
export function printSecretBlockedBox(rawMessage, details = {}) {
|
|
197
|
-
const border = chalk.hex(
|
|
494
|
+
const border = chalk.hex("#E11D48");
|
|
198
495
|
const files = details.files || [];
|
|
199
496
|
const types = details.secretTypes || [];
|
|
200
|
-
const title = chalk.bold.hex(
|
|
497
|
+
const title = chalk.bold.bgHex("#E11D48").hex("#FFFFFF")(
|
|
498
|
+
" PUSH BỊ CHẶN — PHÁT HIỆN SECRET (GH013) ",
|
|
499
|
+
);
|
|
201
500
|
|
|
202
|
-
console.log(
|
|
501
|
+
console.log("");
|
|
203
502
|
console.log(boxTop(title, border));
|
|
204
|
-
console.log(
|
|
205
|
-
|
|
503
|
+
console.log(
|
|
504
|
+
boxRow(
|
|
505
|
+
chalk.hex("#FDA4AF")(
|
|
506
|
+
"GitHub Push Protection đã chặn push vì phát hiện secret!",
|
|
507
|
+
),
|
|
508
|
+
border,
|
|
509
|
+
),
|
|
510
|
+
);
|
|
511
|
+
console.log(boxRow("", border));
|
|
206
512
|
|
|
207
513
|
if (types.length > 0) {
|
|
208
|
-
console.log(
|
|
514
|
+
console.log(
|
|
515
|
+
boxKvDot(
|
|
516
|
+
"Loại secret",
|
|
517
|
+
chalk.hex("#F8FAFC").bold(types.slice(0, 2).join(", ")),
|
|
518
|
+
14,
|
|
519
|
+
border,
|
|
520
|
+
),
|
|
521
|
+
);
|
|
209
522
|
}
|
|
210
523
|
if (files.length > 0) {
|
|
211
|
-
files.slice(0,
|
|
212
|
-
console.log(
|
|
524
|
+
files.slice(0, 2).forEach((f) => {
|
|
525
|
+
console.log(
|
|
526
|
+
boxKvDot("File nguồn", chalk.hex("#FCD34D")(f), 14, border),
|
|
527
|
+
);
|
|
213
528
|
});
|
|
214
529
|
}
|
|
215
530
|
|
|
216
|
-
console.log(boxRow(
|
|
217
|
-
console.log(
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
console.log(
|
|
221
|
-
|
|
531
|
+
console.log(boxRow("", border));
|
|
532
|
+
console.log(
|
|
533
|
+
boxRow(chalk.bold.hex("#F8FAFC")("» Các giải pháp khắc phục:"), border),
|
|
534
|
+
);
|
|
535
|
+
console.log(
|
|
536
|
+
boxRow(
|
|
537
|
+
chalk.hex("#E2E8F0")(" [1] Thu hồi (revoke) key ngay nếu là token thật"),
|
|
538
|
+
border,
|
|
539
|
+
),
|
|
540
|
+
);
|
|
541
|
+
console.log(
|
|
542
|
+
boxRow(
|
|
543
|
+
chalk.hex("#38BDF8")(
|
|
544
|
+
" [2] Dùng tool tự động gỡ file & squash làm sạch commit",
|
|
545
|
+
),
|
|
546
|
+
border,
|
|
547
|
+
),
|
|
548
|
+
);
|
|
549
|
+
console.log(
|
|
550
|
+
boxRow(
|
|
551
|
+
chalk.hex("#94A3B8")(
|
|
552
|
+
" [3] Xóa secret trong code / dùng biến môi trường (process.env)",
|
|
553
|
+
),
|
|
554
|
+
border,
|
|
555
|
+
),
|
|
556
|
+
);
|
|
222
557
|
|
|
223
558
|
console.log(boxBottom(border));
|
|
224
559
|
|
|
225
560
|
if (details.unblockUrl) {
|
|
226
|
-
console.log(
|
|
561
|
+
console.log(
|
|
562
|
+
chalk.hex("#F59E0B")(
|
|
563
|
+
`\n » URL mở khóa tạm thời (nếu là key mẫu thử nghiệm):\n ${chalk.underline(details.unblockUrl)}`,
|
|
564
|
+
),
|
|
565
|
+
);
|
|
227
566
|
}
|
|
228
|
-
console.log(
|
|
567
|
+
console.log(
|
|
568
|
+
chalk.hex("#64748B")(
|
|
569
|
+
`\n » Tài liệu GitHub: ${details.resolveUrl || "https://docs.github.com/code-security/secret-scanning/pushing-a-branch-blocked-by-push-protection"}\n`,
|
|
570
|
+
),
|
|
571
|
+
);
|
|
229
572
|
}
|
|
230
573
|
|
|
231
574
|
/**
|
|
232
|
-
* Card
|
|
575
|
+
* Card Thông báo Thành công sau khi Push
|
|
233
576
|
*/
|
|
234
577
|
export function printSuccessBox({ branch, repoUrl }) {
|
|
235
|
-
const border = chalk.hex(
|
|
236
|
-
const title = chalk.bold.hex(
|
|
578
|
+
const border = chalk.hex("#059669");
|
|
579
|
+
const title = chalk.bold.bgHex("#10B981").hex("#064E3B")(
|
|
580
|
+
" ĐẨY DỰ ÁN LÊN GIT THÀNH CÔNG! ",
|
|
581
|
+
);
|
|
237
582
|
|
|
238
|
-
console.log(
|
|
583
|
+
console.log("");
|
|
239
584
|
console.log(boxTop(title, border));
|
|
240
|
-
console.log(boxRow(
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
console.log(
|
|
244
|
-
console.log(
|
|
245
|
-
|
|
585
|
+
console.log(boxRow("", border));
|
|
586
|
+
|
|
587
|
+
const branchPill = chalk.bgHex("#065F46").hex("#6EE7B7").bold(` ${branch} `);
|
|
588
|
+
console.log(boxKvDot("Branch", branchPill, 12, border));
|
|
589
|
+
console.log(
|
|
590
|
+
boxKvDot("Remote", chalk.hex("#38BDF8").underline(repoUrl), 12, border),
|
|
591
|
+
);
|
|
592
|
+
console.log(
|
|
593
|
+
boxKvDot(
|
|
594
|
+
"Trạng thái",
|
|
595
|
+
chalk.hex("#A7F3D0")("Đồng bộ hoàn tất 100%"),
|
|
596
|
+
12,
|
|
597
|
+
border,
|
|
598
|
+
),
|
|
599
|
+
);
|
|
600
|
+
|
|
601
|
+
console.log(boxRow("", border));
|
|
602
|
+
console.log(
|
|
603
|
+
boxRow(
|
|
604
|
+
chalk.hex("#64748B")("» Xem lịch sử commit: git log -1 --stat"),
|
|
605
|
+
border,
|
|
606
|
+
),
|
|
607
|
+
);
|
|
246
608
|
console.log(boxBottom(border));
|
|
247
|
-
console.log(
|
|
609
|
+
console.log("");
|
|
248
610
|
}
|
|
249
611
|
|
|
250
|
-
/**
|
|
251
|
-
* Thông báo hủy thao tác
|
|
252
|
-
*/
|
|
253
612
|
export function printCancelled() {
|
|
254
|
-
const border = chalk.hex(
|
|
255
|
-
const title = chalk.bold.hex(
|
|
256
|
-
|
|
613
|
+
const border = chalk.hex("#D97706");
|
|
614
|
+
const title = chalk.bold.bgHex("#D97706").hex("#FFFFFF")(
|
|
615
|
+
" ĐÃ HỦY THAO TÁC ",
|
|
616
|
+
);
|
|
617
|
+
console.log("");
|
|
257
618
|
console.log(boxTop(title, border));
|
|
258
|
-
console.log(
|
|
619
|
+
console.log(
|
|
620
|
+
boxRow(
|
|
621
|
+
chalk.hex("#FDE68A")(
|
|
622
|
+
"Không có bất kỳ thay đổi nào được thực hiện trên repo.",
|
|
623
|
+
),
|
|
624
|
+
border,
|
|
625
|
+
),
|
|
626
|
+
);
|
|
259
627
|
console.log(boxBottom(border));
|
|
260
|
-
console.log(
|
|
628
|
+
console.log("");
|
|
261
629
|
}
|
|
262
630
|
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
const
|
|
268
|
-
|
|
269
|
-
|
|
631
|
+
export function printErrorBox(title = "XẢY RA LỖI", lines = [], hint = "") {
|
|
632
|
+
const W = getW();
|
|
633
|
+
const border = chalk.hex("#E11D48");
|
|
634
|
+
const cleanTitle = String(title || "").toUpperCase();
|
|
635
|
+
const titleStyled = chalk.bold.bgHex("#E11D48").hex("#FFFFFF")(
|
|
636
|
+
` [LỖI] ${cleanTitle} `,
|
|
637
|
+
);
|
|
638
|
+
const rows = Array.isArray(lines) ? lines : [String(lines || "")];
|
|
270
639
|
|
|
271
|
-
console.log(
|
|
640
|
+
console.log("");
|
|
272
641
|
console.log(boxTop(titleStyled, border));
|
|
273
642
|
rows.forEach((l) => {
|
|
274
643
|
if (!l) {
|
|
275
|
-
console.log(boxRow(
|
|
644
|
+
console.log(boxRow("", border));
|
|
276
645
|
} else {
|
|
277
|
-
console.log(
|
|
646
|
+
console.log(
|
|
647
|
+
boxRow(chalk.hex("#FDA4AF")(truncateVisual(String(l), W - 4)), border),
|
|
648
|
+
);
|
|
278
649
|
}
|
|
279
650
|
});
|
|
280
651
|
if (hint) {
|
|
281
|
-
console.log(boxRow(
|
|
282
|
-
console.log(
|
|
652
|
+
console.log(boxRow("", border));
|
|
653
|
+
console.log(
|
|
654
|
+
boxRow(chalk.hex("#38BDF8")(`» ${truncateVisual(hint, W - 6)}`), border),
|
|
655
|
+
);
|
|
283
656
|
}
|
|
284
657
|
console.log(boxBottom(border));
|
|
285
|
-
console.log(
|
|
658
|
+
console.log("");
|
|
286
659
|
}
|
|
287
660
|
|
|
288
|
-
/**
|
|
289
|
-
* Lỗi chưa cài Git
|
|
290
|
-
*/
|
|
291
661
|
export function printGitMissing() {
|
|
292
|
-
printErrorBox(
|
|
293
|
-
|
|
294
|
-
|
|
662
|
+
printErrorBox(
|
|
663
|
+
"CHƯA CÀI ĐẶT GIT",
|
|
664
|
+
[
|
|
665
|
+
"Không tìm thấy Git trên hệ thống máy tính của bạn.",
|
|
666
|
+
"Vui lòng tải và cài đặt Git để tiếp tục!",
|
|
667
|
+
],
|
|
668
|
+
"Tải ngay tại: https://git-scm.com/downloads",
|
|
669
|
+
);
|
|
295
670
|
}
|
|
296
671
|
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
const
|
|
303
|
-
|
|
672
|
+
export function printAuthFailedBox(message = "") {
|
|
673
|
+
const border = chalk.hex("#E11D48");
|
|
674
|
+
const title = chalk.bold.bgHex("#E11D48").hex("#FFFFFF")(
|
|
675
|
+
" LỖI XÁC THỰC GIT ",
|
|
676
|
+
);
|
|
677
|
+
const firstLine = String(message || "")
|
|
678
|
+
.split("\n")[0]
|
|
679
|
+
.slice(0, 120);
|
|
304
680
|
|
|
305
|
-
console.log(
|
|
681
|
+
console.log("");
|
|
306
682
|
console.log(boxTop(title, border));
|
|
307
|
-
console.log(
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
683
|
+
console.log(
|
|
684
|
+
boxRow(
|
|
685
|
+
chalk.hex("#FDA4AF")(
|
|
686
|
+
"Không thể xác thực với remote. Kiểm tra lại quyền truy cập.",
|
|
687
|
+
),
|
|
688
|
+
border,
|
|
689
|
+
),
|
|
690
|
+
);
|
|
691
|
+
if (firstLine)
|
|
692
|
+
console.log(
|
|
693
|
+
boxRow(
|
|
694
|
+
chalk.hex("#64748B")(truncateVisual(firstLine, getW() - 4)),
|
|
695
|
+
border,
|
|
696
|
+
),
|
|
697
|
+
);
|
|
698
|
+
console.log(boxRow("", border));
|
|
699
|
+
console.log(
|
|
700
|
+
boxRow(chalk.bold.hex("#F8FAFC")("» Cách khắc phục nhanh:"), border),
|
|
701
|
+
);
|
|
702
|
+
console.log(
|
|
703
|
+
boxRow(
|
|
704
|
+
chalk.hex("#E2E8F0")(" [1] Chạy tính năng GITHUB trong tool để kết nối tài khoản"),
|
|
705
|
+
border,
|
|
706
|
+
),
|
|
707
|
+
);
|
|
708
|
+
console.log(
|
|
709
|
+
boxRow(
|
|
710
|
+
chalk.hex("#E2E8F0")(" [2] Dùng lệnh gh: gh auth login"),
|
|
711
|
+
border,
|
|
712
|
+
),
|
|
713
|
+
);
|
|
714
|
+
console.log(
|
|
715
|
+
boxRow(
|
|
716
|
+
chalk.hex("#94A3B8")(" [3] Sử dụng SSH URL: git@github.com:user/repo.git"),
|
|
717
|
+
border,
|
|
718
|
+
),
|
|
719
|
+
);
|
|
314
720
|
console.log(boxBottom(border));
|
|
315
|
-
console.log(
|
|
721
|
+
console.log("");
|
|
316
722
|
}
|
|
317
723
|
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
const
|
|
324
|
-
|
|
724
|
+
export function printNonFastForwardBox(message = "", branch = "main") {
|
|
725
|
+
const border = chalk.hex("#D97706");
|
|
726
|
+
const title = chalk.bold.bgHex("#D97706").hex("#FFFFFF")(
|
|
727
|
+
" REMOTE ĐÃ CÓ COMMIT MỚI HƠN ",
|
|
728
|
+
);
|
|
729
|
+
const firstLine = String(message || "")
|
|
730
|
+
.split("\n")[0]
|
|
731
|
+
.slice(0, 120);
|
|
325
732
|
|
|
326
|
-
console.log(
|
|
733
|
+
console.log("");
|
|
327
734
|
console.log(boxTop(title, border));
|
|
328
|
-
console.log(
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
735
|
+
console.log(
|
|
736
|
+
boxRow(
|
|
737
|
+
chalk.hex("#FDE68A")(
|
|
738
|
+
"Kho lưu trữ trên Remote đang chứa commit mới hơn Local.",
|
|
739
|
+
),
|
|
740
|
+
border,
|
|
741
|
+
),
|
|
742
|
+
);
|
|
743
|
+
if (firstLine)
|
|
744
|
+
console.log(
|
|
745
|
+
boxRow(
|
|
746
|
+
chalk.hex("#64748B")(truncateVisual(firstLine, getW() - 4)),
|
|
747
|
+
border,
|
|
748
|
+
),
|
|
749
|
+
);
|
|
750
|
+
console.log(boxRow("", border));
|
|
751
|
+
console.log(
|
|
752
|
+
boxRow(
|
|
753
|
+
chalk.hex("#38BDF8")(
|
|
754
|
+
` » Bạn có thể chọn Pull (rebase) rồi push lại ngay trong tool`,
|
|
755
|
+
),
|
|
756
|
+
border,
|
|
757
|
+
),
|
|
758
|
+
);
|
|
759
|
+
console.log(
|
|
760
|
+
boxRow(
|
|
761
|
+
chalk.hex("#94A3B8")(
|
|
762
|
+
" Chỉ dùng --force khi bạn chắc chắn muốn ghi đè Remote.",
|
|
763
|
+
),
|
|
764
|
+
border,
|
|
765
|
+
),
|
|
766
|
+
);
|
|
333
767
|
console.log(boxBottom(border));
|
|
334
|
-
console.log(
|
|
768
|
+
console.log("");
|
|
335
769
|
}
|
|
336
770
|
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
771
|
+
export function printFixResult({
|
|
772
|
+
fixed = [],
|
|
773
|
+
gitignoreAdded = [],
|
|
774
|
+
codeFilesWithSecrets = [],
|
|
775
|
+
failed = [],
|
|
776
|
+
} = {}) {
|
|
777
|
+
const W = getW();
|
|
778
|
+
console.log("");
|
|
341
779
|
if (fixed.length > 0) {
|
|
342
|
-
console.log(
|
|
780
|
+
console.log(
|
|
781
|
+
chalk.hex("#34D399")(
|
|
782
|
+
` [OK] Đã gỡ ${fixed.length} file khỏi commit: ${truncateVisual(fixed.join(", "), W - 32)}`,
|
|
783
|
+
),
|
|
784
|
+
);
|
|
343
785
|
}
|
|
344
786
|
if (gitignoreAdded.length > 0) {
|
|
345
|
-
console.log(
|
|
787
|
+
console.log(
|
|
788
|
+
chalk.cyan(
|
|
789
|
+
` [+] Thêm vào .gitignore: ${truncateVisual(gitignoreAdded.join(", "), W - 28)}`,
|
|
790
|
+
),
|
|
791
|
+
);
|
|
346
792
|
}
|
|
347
793
|
if (codeFilesWithSecrets && codeFilesWithSecrets.length > 0) {
|
|
348
|
-
console.log(
|
|
349
|
-
|
|
350
|
-
|
|
794
|
+
console.log(
|
|
795
|
+
chalk.yellowBright(
|
|
796
|
+
" [!] File code chứa secret (đã gỡ khỏi commit, KHÔNG cho vào .gitignore):",
|
|
797
|
+
),
|
|
798
|
+
);
|
|
799
|
+
codeFilesWithSecrets.forEach((f) =>
|
|
800
|
+
console.log(chalk.yellow(` - ${f}`)),
|
|
801
|
+
);
|
|
802
|
+
console.log(
|
|
803
|
+
chalk.gray(" » Hãy xóa key trong file và thay bằng process.env."),
|
|
804
|
+
);
|
|
351
805
|
}
|
|
352
806
|
if (failed.length > 0) {
|
|
353
|
-
failed.forEach((f) => console.log(chalk.red(`
|
|
807
|
+
failed.forEach((f) => console.log(chalk.red(` [x] ${f.file}: ${f.reason}`)));
|
|
354
808
|
}
|
|
355
809
|
}
|
|
356
810
|
|
|
357
|
-
/**
|
|
358
|
-
* Card npm publish (Theme Violet)
|
|
359
|
-
*/
|
|
360
|
-
export function printNpmResult({ ok, pkgName, version, output = '', hint = '', dryRun = false }) {
|
|
361
|
-
if (ok) {
|
|
362
|
-
const border = chalk.hex('#8B5CF6');
|
|
363
|
-
const title = chalk.bold.hex('#EDE9FE')(dryRun ? ' 🧪 DRY-RUN OK — SẴN SÀNG PUBLISH ' : ' 📦 PUBLISH NPM THÀNH CÔNG! ');
|
|
364
|
-
console.log('');
|
|
365
|
-
console.log(boxTop(title, border));
|
|
366
|
-
console.log(boxRow(chalk.hex('#94A3B8')('📦 Package : ') + chalk.hex('#F8FAFC').bold(`${pkgName}@${version}`), border));
|
|
367
|
-
if (!dryRun) {
|
|
368
|
-
console.log(boxRow(chalk.hex('#94A3B8')('🔗 Xem tại : ') + chalk.hex('#38BDF8').underline(`npmjs.com/package/${pkgName}`), border));
|
|
369
|
-
} else {
|
|
370
|
-
console.log(boxRow(chalk.hex('#94A3B8')('✨ Kết quả : ') + chalk.hex('#A7F3D0')('Không lỗi — chạy thật để publish.'), border));
|
|
371
|
-
}
|
|
372
|
-
console.log(boxBottom(border));
|
|
373
|
-
console.log('');
|
|
374
|
-
return;
|
|
375
|
-
}
|
|
376
|
-
printErrorBox('PUBLISH NPM THẤT BẠI', output.split('\n').slice(-6).filter(Boolean), hint);
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
/**
|
|
380
|
-
* Footer nhỏ sau mỗi lần chạy thành công / kết thúc
|
|
381
|
-
*/
|
|
382
811
|
export function printFooter() {
|
|
383
|
-
console.log(chalk.hex(
|
|
384
|
-
console.log(
|
|
385
|
-
|
|
812
|
+
console.log(chalk.hex("#334155")(" ─── » ───"));
|
|
813
|
+
console.log(
|
|
814
|
+
chalk.hex("#64748B")(" » Mẹo: Chạy ") +
|
|
815
|
+
chalk.hex("#38BDF8").bold("git-push-time --help") +
|
|
816
|
+
chalk.hex("#64748B")(" để xem toàn bộ danh sách lệnh và phím tắt."),
|
|
817
|
+
);
|
|
818
|
+
console.log(
|
|
819
|
+
chalk.hex("#475569")(
|
|
820
|
+
" » GitHub CLI & Push Time — Chúc bạn làm việc hiệu quả!\n",
|
|
821
|
+
),
|
|
822
|
+
);
|
|
386
823
|
}
|
|
387
824
|
|
|
388
|
-
/**
|
|
389
|
-
* Style cho --help của commander cho đồng bộ theme
|
|
390
|
-
*/
|
|
391
825
|
export function styleCliHelp(program) {
|
|
392
826
|
program.configureHelp({
|
|
393
|
-
styleTitle: (t) => chalk.bold.hex(
|
|
394
|
-
styleCommandText: (t) => chalk.hex(
|
|
395
|
-
styleOptionText: (t) => chalk.hex(
|
|
396
|
-
styleArgumentText: (t) => chalk.hex(
|
|
397
|
-
styleDescriptionText: (t) => chalk.hex(
|
|
827
|
+
styleTitle: (t) => chalk.bold.hex("#38BDF8")(t),
|
|
828
|
+
styleCommandText: (t) => chalk.hex("#F8FAFC")(t),
|
|
829
|
+
styleOptionText: (t) => chalk.hex("#34D399")(t),
|
|
830
|
+
styleArgumentText: (t) => chalk.hex("#FBBF24")(t),
|
|
831
|
+
styleDescriptionText: (t) => chalk.hex("#94A3B8")(t),
|
|
398
832
|
});
|
|
399
|
-
program.addHelpText(
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
${chalk.hex(
|
|
404
|
-
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
833
|
+
program.addHelpText(
|
|
834
|
+
"after",
|
|
835
|
+
`
|
|
836
|
+
${chalk.hex("#334155")("─── » Ví dụ phổ biến ───")}
|
|
837
|
+
${chalk.hex("#34D399")("$")} ${chalk.white('git-push-time')} # Mở menu điều khiển tương tác trực quan
|
|
838
|
+
${chalk.hex("#34D399")("$")} ${chalk.white('git-push-time github create')} # Tạo nhanh repo mới trên GitHub
|
|
839
|
+
${chalk.hex("#34D399")("$")} ${chalk.white('git-push-time -r <repo-url> -d "-2d" -y')} # Push tự động lùi 2 ngày
|
|
840
|
+
${chalk.hex("#34D399")("$")} ${chalk.white('git-push-time fix')} # Quét & gỡ sạch secret
|
|
841
|
+
${chalk.hex("#34D399")("$")} ${chalk.white('git-push-time pull')} # Cập nhật code mới từ remote
|
|
842
|
+
`,
|
|
843
|
+
);
|
|
409
844
|
}
|