git-push-time 1.1.3 → 1.1.4
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 +109 -111
- package/bin/index.js +45 -46
- package/lib/commands/edit-time.js +27 -27
- package/lib/commands/fix.js +19 -19
- package/lib/commands/github.js +98 -98
- package/lib/commands/pull.js +13 -13
- package/lib/commands/push.js +147 -147
- package/lib/config.js +17 -17
- package/lib/fix.js +34 -34
- package/lib/git.js +88 -88
- package/lib/github.js +39 -39
- package/lib/prompt-helpers.js +19 -20
- package/lib/secrets.js +12 -12
- package/lib/ui.js +122 -125
- package/lib/utils.js +15 -15
- package/package.json +1 -1
package/lib/ui.js
CHANGED
|
@@ -3,16 +3,15 @@ import boxen from "boxen";
|
|
|
3
3
|
import stringWidth from "string-width";
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
*
|
|
7
|
-
* -
|
|
8
|
-
*
|
|
9
|
-
* - Terminal hẹp thì co theo `cols - 2`, terminal rộng thì cap 76.
|
|
6
|
+
* Shared frame width for the entire UI.
|
|
7
|
+
* - Keep dashboards, banners, prompts, and detail boxes aligned.
|
|
8
|
+
* - Shrink to `cols - 2` in narrow terminals and cap at 76 columns.
|
|
10
9
|
*/
|
|
11
10
|
export function getW() {
|
|
12
11
|
const cols = process.stdout.columns;
|
|
13
12
|
if (!cols || cols < 10) return 72;
|
|
14
13
|
const avail = cols - 2;
|
|
15
|
-
//
|
|
14
|
+
// On very narrow terminals (<34 columns), use the available width to prevent wrapping.
|
|
16
15
|
if (avail < 34) return Math.max(24, avail);
|
|
17
16
|
return Math.min(76, avail);
|
|
18
17
|
}
|
|
@@ -81,10 +80,10 @@ export function truncateVisual(str, maxLen) {
|
|
|
81
80
|
return fitStyled(str, maxLen);
|
|
82
81
|
}
|
|
83
82
|
|
|
84
|
-
/**
|
|
83
|
+
/** Consistent title frame for each interactive terminal step. */
|
|
85
84
|
export function framePrompt(message) {
|
|
86
|
-
//
|
|
87
|
-
//
|
|
85
|
+
// Use the same width as other boxes to keep frames aligned and prevent narrow prompts
|
|
86
|
+
// from breaking against a wider dashboard.
|
|
88
87
|
return boxen(String(message ?? ""), {
|
|
89
88
|
borderStyle: "round",
|
|
90
89
|
borderColor: "#818CF8",
|
|
@@ -95,15 +94,15 @@ export function framePrompt(message) {
|
|
|
95
94
|
}
|
|
96
95
|
|
|
97
96
|
/* ==========================================================================
|
|
98
|
-
BOX ENGINE
|
|
97
|
+
SHARED BOX ENGINE (rounded corners and precise alignment)
|
|
99
98
|
========================================================================== */
|
|
100
99
|
|
|
101
100
|
export function boxTop(title = "", borderColor = chalk.hex("#334155")) {
|
|
102
101
|
const W = getW();
|
|
103
102
|
if (!title) return borderColor("╭" + "─".repeat(Math.max(2, W - 2)) + "╮");
|
|
104
103
|
|
|
105
|
-
//
|
|
106
|
-
//
|
|
104
|
+
// Center the title with even spacing on both sides: ╭──── Title ────╮
|
|
105
|
+
// Leave one space beside the title and at least two dashes on each side.
|
|
107
106
|
const maxTitle = Math.max(4, W - 8);
|
|
108
107
|
const fittedTitle = fitStyled(title, maxTitle);
|
|
109
108
|
const titleWidth = stringWidth(fittedTitle);
|
|
@@ -117,7 +116,7 @@ export function boxTop(title = "", borderColor = chalk.hex("#334155")) {
|
|
|
117
116
|
);
|
|
118
117
|
}
|
|
119
118
|
|
|
120
|
-
/**
|
|
119
|
+
/** Left-aligned title frame matching the content margin of boxRow. */
|
|
121
120
|
export function boxTopLeft(title = "", borderColor = chalk.hex("#334155")) {
|
|
122
121
|
const W = getW();
|
|
123
122
|
if (!title) return borderColor("╭" + "─".repeat(Math.max(2, W - 2)) + "╮");
|
|
@@ -144,14 +143,12 @@ export function boxDivider(borderColor = chalk.hex("#334155")) {
|
|
|
144
143
|
}
|
|
145
144
|
|
|
146
145
|
/**
|
|
147
|
-
*
|
|
148
|
-
*
|
|
149
|
-
* Windows PowerShell
|
|
150
|
-
* terminal hẹp nhờ công thức fitted mới.
|
|
146
|
+
* Key-value row connected with dot leaders.
|
|
147
|
+
* Use "·" (U+00B7) instead of "․" (U+2024) for compatibility with
|
|
148
|
+
* Windows PowerShell and Consolas. The label width adapts to narrow terminals.
|
|
151
149
|
*/
|
|
152
150
|
/**
|
|
153
|
-
*
|
|
154
|
-
* Căn lề chuẩn xác, khoảng cách đều và không bị vỡ font trên mọi terminal.
|
|
151
|
+
* Key-value row with dot leaders, precise alignment, and consistent spacing.
|
|
155
152
|
*/
|
|
156
153
|
export function boxKvDot(
|
|
157
154
|
label,
|
|
@@ -260,7 +257,7 @@ export function printBanner(version = "1.1.1") {
|
|
|
260
257
|
"#F472B6",
|
|
261
258
|
]);
|
|
262
259
|
const subtitle = chalk.hex("#94A3B8")(
|
|
263
|
-
"GitHub · Push / Pull ·
|
|
260
|
+
"GitHub · Push / Pull · Commit history · Secret scan",
|
|
264
261
|
);
|
|
265
262
|
const mascot = mascotLines();
|
|
266
263
|
const body = [...mascot, "", title, subtitle, "", ...rows].join("\n");
|
|
@@ -275,7 +272,7 @@ export function printBanner(version = "1.1.1") {
|
|
|
275
272
|
}
|
|
276
273
|
|
|
277
274
|
/**
|
|
278
|
-
*
|
|
275
|
+
* Display a concise status dashboard at the top of the menu.
|
|
279
276
|
*/
|
|
280
277
|
export function printDashboardStatus({
|
|
281
278
|
targetDir = process.cwd(),
|
|
@@ -288,34 +285,34 @@ export function printDashboardStatus({
|
|
|
288
285
|
}) {
|
|
289
286
|
const border = chalk.hex("#6366F1");
|
|
290
287
|
const W = getW();
|
|
291
|
-
const remote = remoteUrl ? formatRemoteLabel(remoteUrl) : chalk.hex("#94A3B8")("
|
|
288
|
+
const remote = remoteUrl ? formatRemoteLabel(remoteUrl) : chalk.hex("#94A3B8")("Not linked");
|
|
292
289
|
|
|
293
290
|
const ghStatus = githubUser
|
|
294
|
-
? `${chalk.hex("#38BDF8").bold(`@${githubUser.login}`)} ${chalk.bgHex("#064E3B").hex("#34D399").bold(" [✓
|
|
295
|
-
: chalk.bgHex("#1E1B4B").hex("#A5B4FC").bold(" [○
|
|
291
|
+
? `${chalk.hex("#38BDF8").bold(`@${githubUser.login}`)} ${chalk.bgHex("#064E3B").hex("#34D399").bold(" [✓ CONNECTED] ")}`
|
|
292
|
+
: chalk.bgHex("#1E1B4B").hex("#A5B4FC").bold(" [○ SIGNED OUT] ");
|
|
296
293
|
|
|
297
294
|
const branchPill = changes === 0
|
|
298
|
-
? chalk.bgHex("#064E3B").hex("#34D399").bold(" [✓
|
|
299
|
-
: chalk.bgHex("#78350F").hex("#FDE047").bold(` [● ${changes}
|
|
295
|
+
? chalk.bgHex("#064E3B").hex("#34D399").bold(" [✓ CLEAN] ")
|
|
296
|
+
: chalk.bgHex("#78350F").hex("#FDE047").bold(` [● ${changes} CHANGES] `);
|
|
300
297
|
|
|
301
298
|
const branchDisplay = `${chalk.hex("#38BDF8").bold(currentBranch)} ${branchPill}`;
|
|
302
299
|
|
|
303
300
|
console.log("");
|
|
304
301
|
console.log(
|
|
305
302
|
boxTop(
|
|
306
|
-
`${chalk.bold.hex("#F8FAFC")("
|
|
303
|
+
`${chalk.bold.hex("#F8FAFC")(" GIT DASHBOARD ")} ${chalk.hex("#94A3B8")(`v${version}`)}`,
|
|
307
304
|
border,
|
|
308
305
|
),
|
|
309
306
|
);
|
|
310
307
|
console.log(boxRow("", border));
|
|
311
|
-
console.log(boxKvDot("
|
|
312
|
-
console.log(boxKvDot("
|
|
308
|
+
console.log(boxKvDot("Folder", chalk.hex("#F1F5F9").bold(targetDir), 14, border));
|
|
309
|
+
console.log(boxKvDot("Branch", branchDisplay, 14, border));
|
|
313
310
|
console.log(boxKvDot("GitHub", ghStatus, 14, border));
|
|
314
311
|
console.log(boxKvDot("Remote", remote, 14, border));
|
|
315
312
|
|
|
316
313
|
if (lastCommit && lastCommit.hash) {
|
|
317
314
|
const commitText = `${chalk.hex("#A78BFA").bold(lastCommit.hash)} ${chalk.hex("#CBD5E1")(truncateVisual(lastCommit.message || "", 26))} ${chalk.hex("#64748B")(`(${lastCommit.relativeTime || ""})`)}`;
|
|
318
|
-
console.log(boxKvDot("
|
|
315
|
+
console.log(boxKvDot("Latest commit", commitText, 14, border));
|
|
319
316
|
}
|
|
320
317
|
|
|
321
318
|
console.log(boxRow("", border));
|
|
@@ -338,7 +335,7 @@ export function printPushProgress({ current, total = 6, title, completed = [] })
|
|
|
338
335
|
});
|
|
339
336
|
const rail = nodes.join(chalk.hex("#334155")("──"));
|
|
340
337
|
const lead = `${chalk.hex("#38BDF8").bold("PUSH PROGRESS")} ${rail}`;
|
|
341
|
-
const detail = chalk.hex("#94A3B8")(`[
|
|
338
|
+
const detail = chalk.hex("#94A3B8")(`[Step ${current}/${total} · ${title}]`);
|
|
342
339
|
console.log("");
|
|
343
340
|
if (stringWidth(`${lead} ${detail}`) <= getW() - 2) console.log(`${lead} ${detail}`);
|
|
344
341
|
else {
|
|
@@ -356,7 +353,7 @@ export function printPushProgress({ current, total = 6, title, completed = [] })
|
|
|
356
353
|
}
|
|
357
354
|
|
|
358
355
|
/**
|
|
359
|
-
*
|
|
356
|
+
* Section heading with a color accent.
|
|
360
357
|
*/
|
|
361
358
|
export function printSection(title) {
|
|
362
359
|
const W = getW();
|
|
@@ -377,7 +374,7 @@ export function printSection(title) {
|
|
|
377
374
|
}
|
|
378
375
|
|
|
379
376
|
/**
|
|
380
|
-
*
|
|
377
|
+
* Step badge (for example, 1/6).
|
|
381
378
|
*/
|
|
382
379
|
export function stepLabel(current, total, text) {
|
|
383
380
|
const pill = chalk.bgHex("#2563EB").hex("#FFFFFF").bold(` ${current}/${total} `);
|
|
@@ -385,23 +382,23 @@ export function stepLabel(current, total, text) {
|
|
|
385
382
|
}
|
|
386
383
|
|
|
387
384
|
/**
|
|
388
|
-
*
|
|
385
|
+
* Summary panel displayed before pushing.
|
|
389
386
|
*/
|
|
390
387
|
export function printSummary({ repoUrl, commitMsg, dateDisplay, branch, force, sourceDir }) {
|
|
391
388
|
const border = chalk.hex("#38BDF8");
|
|
392
|
-
const title = chalk.bold.hex("#F8FAFC")("
|
|
389
|
+
const title = chalk.bold.hex("#F8FAFC")(" REVIEW PUSH DETAILS ");
|
|
393
390
|
console.log("");
|
|
394
391
|
console.log(boxTop(title, border));
|
|
395
392
|
console.log(boxRow("", border));
|
|
396
|
-
if (sourceDir) console.log(boxKvDot("
|
|
397
|
-
console.log(boxKvDot("
|
|
398
|
-
console.log(boxKvDot("
|
|
399
|
-
console.log(boxKvDot("
|
|
400
|
-
console.log(boxKvDot("
|
|
401
|
-
console.log(boxKvDot("
|
|
393
|
+
if (sourceDir) console.log(boxKvDot("Source folder", chalk.hex("#F1F5F9")(sourceDir), 16, border));
|
|
394
|
+
console.log(boxKvDot("Repository", formatRemoteLabel(repoUrl), 16, border));
|
|
395
|
+
console.log(boxKvDot("Target branch", chalk.hex("#38BDF8").bold(branch), 16, border));
|
|
396
|
+
console.log(boxKvDot("Message", chalk.hex("#FDE047")(`"${commitMsg}"`), 16, border));
|
|
397
|
+
console.log(boxKvDot("Commit time", chalk.hex("#A78BFA")(dateDisplay), 16, border));
|
|
398
|
+
console.log(boxKvDot("Secret scan", chalk.hex("#34D399")("✓ Automatically checked before push"), 16, border));
|
|
402
399
|
if (force) {
|
|
403
400
|
console.log(boxRow("", border));
|
|
404
|
-
console.log(boxRow(chalk.hex("#F43F5E").bold(" [!]
|
|
401
|
+
console.log(boxRow(chalk.hex("#F43F5E").bold(" [!] WARNING: Force push (--force) is enabled!"), border));
|
|
405
402
|
}
|
|
406
403
|
console.log(boxRow("", border));
|
|
407
404
|
console.log(boxBottom(border));
|
|
@@ -411,18 +408,18 @@ export function printTargetDir(dir = "") {
|
|
|
411
408
|
const W = getW();
|
|
412
409
|
console.log(
|
|
413
410
|
chalk.hex("#64748B")(
|
|
414
|
-
`
|
|
411
|
+
` Folder: ${truncateVisual(String(dir || ""), W - 13)}`,
|
|
415
412
|
),
|
|
416
413
|
);
|
|
417
414
|
}
|
|
418
415
|
|
|
419
416
|
/**
|
|
420
|
-
*
|
|
417
|
+
* Display the GitHub account profile card.
|
|
421
418
|
*/
|
|
422
419
|
export function printGitHubProfile(user, source = '') {
|
|
423
420
|
const border = chalk.hex("#4F46E5");
|
|
424
421
|
const title = chalk.bold.bgHex("#4F46E5").hex("#FFFFFF")(
|
|
425
|
-
"
|
|
422
|
+
" CONNECTED GITHUB ACCOUNT ",
|
|
426
423
|
);
|
|
427
424
|
|
|
428
425
|
console.log("");
|
|
@@ -432,31 +429,31 @@ export function printGitHubProfile(user, source = '') {
|
|
|
432
429
|
boxKvDot("Username", chalk.hex("#38BDF8").bold(`@${user.login}`), 16, border)
|
|
433
430
|
);
|
|
434
431
|
if (user.name) {
|
|
435
|
-
console.log(boxKvDot("
|
|
432
|
+
console.log(boxKvDot("Display name", chalk.hex("#F8FAFC")(user.name), 16, border));
|
|
436
433
|
}
|
|
437
434
|
if (user.email) {
|
|
438
435
|
console.log(boxKvDot("Email", chalk.hex("#94A3B8")(user.email), 16, border));
|
|
439
436
|
}
|
|
440
437
|
if (user.bio) {
|
|
441
|
-
console.log(boxKvDot("
|
|
438
|
+
console.log(boxKvDot("Bio", chalk.hex("#CBD5E1")(truncateVisual(user.bio, 40)), 16, border));
|
|
442
439
|
}
|
|
443
440
|
|
|
444
441
|
const repoStats = `${user.public_repos || 0} public • ${user.total_private_repos || 0} private`;
|
|
445
442
|
console.log(boxKvDot("Repositories", chalk.hex("#34D399").bold(repoStats), 16, border));
|
|
446
443
|
|
|
447
444
|
if (source) {
|
|
448
|
-
const srcDesc = source === 'gh-cli' ? 'GitHub CLI (gh)' : source === 'env' ? '
|
|
449
|
-
console.log(boxKvDot("
|
|
445
|
+
const srcDesc = source === 'gh-cli' ? 'GitHub CLI (gh)' : source === 'env' ? 'Environment variable' : 'Local config';
|
|
446
|
+
console.log(boxKvDot("Auth source", chalk.hex("#A78BFA")(srcDesc), 16, border));
|
|
450
447
|
}
|
|
451
448
|
|
|
452
449
|
console.log(boxDivider(border));
|
|
453
|
-
console.log(boxRow(chalk.hex("#64748B")(`»
|
|
450
|
+
console.log(boxRow(chalk.hex("#64748B")(`» Profile: ${user.html_url}`), border));
|
|
454
451
|
console.log(boxBottom(border));
|
|
455
452
|
console.log("");
|
|
456
453
|
}
|
|
457
454
|
|
|
458
455
|
/**
|
|
459
|
-
*
|
|
456
|
+
* Display the GitHub Device Flow verification code.
|
|
460
457
|
*/
|
|
461
458
|
export function printDeviceCodePrompt({ userCode, verificationUri }) {
|
|
462
459
|
const border = chalk.hex("#6366F1");
|
|
@@ -467,27 +464,27 @@ export function printDeviceCodePrompt({ userCode, verificationUri }) {
|
|
|
467
464
|
console.log("");
|
|
468
465
|
console.log(boxTop(title, border));
|
|
469
466
|
console.log(boxRow("", border));
|
|
470
|
-
console.log(boxRow(chalk.hex("#F8FAFC").bold("
|
|
467
|
+
console.log(boxRow(chalk.hex("#F8FAFC").bold("Step 1: Open this link in your browser:"), border));
|
|
471
468
|
console.log(boxRow(chalk.hex("#38BDF8").bold.underline(` ${verificationUri}`), border));
|
|
472
469
|
console.log(boxRow("", border));
|
|
473
|
-
console.log(boxRow(chalk.hex("#F8FAFC").bold("
|
|
470
|
+
console.log(boxRow(chalk.hex("#F8FAFC").bold("Step 2: Enter this user code:"), border));
|
|
474
471
|
|
|
475
472
|
const codeBox = chalk.bgHex("#1E1B4B").hex("#A5B4FC").bold(` >>> ${userCode} <<< `);
|
|
476
473
|
console.log(boxRow(` ${codeBox}`, border));
|
|
477
474
|
console.log(boxRow("", border));
|
|
478
|
-
console.log(boxRow(chalk.hex("#94A3B8")("»
|
|
479
|
-
console.log(boxRow(chalk.hex("#64748B")("»
|
|
475
|
+
console.log(boxRow(chalk.hex("#94A3B8")("» Your browser is opening automatically..."), border));
|
|
476
|
+
console.log(boxRow(chalk.hex("#64748B")("» Select Authorize in your browser to finish."), border));
|
|
480
477
|
console.log(boxBottom(border));
|
|
481
478
|
console.log("");
|
|
482
479
|
}
|
|
483
480
|
|
|
484
481
|
/**
|
|
485
|
-
*
|
|
482
|
+
* Display a success message after creating a GitHub repository.
|
|
486
483
|
*/
|
|
487
484
|
export function printRepoCreatedBox(repo) {
|
|
488
485
|
const border = chalk.hex("#059669");
|
|
489
486
|
const title = chalk.bold.bgHex("#10B981").hex("#064E3B")(
|
|
490
|
-
"
|
|
487
|
+
" GITHUB REPOSITORY CREATED ",
|
|
491
488
|
);
|
|
492
489
|
|
|
493
490
|
console.log("");
|
|
@@ -498,28 +495,28 @@ export function printRepoCreatedBox(repo) {
|
|
|
498
495
|
? chalk.bgHex("#7F1D1D").hex("#FCA5A5").bold(" PRIVATE ")
|
|
499
496
|
: chalk.bgHex("#065F46").hex("#6EE7B7").bold(" PUBLIC ");
|
|
500
497
|
|
|
501
|
-
console.log(boxKvDot("
|
|
502
|
-
console.log(boxKvDot("
|
|
498
|
+
console.log(boxKvDot("Repository", chalk.hex("#F8FAFC").bold(repo.fullName || repo.name), 14, border));
|
|
499
|
+
console.log(boxKvDot("Visibility", visibilityPill, 14, border));
|
|
503
500
|
if (repo.description) {
|
|
504
|
-
console.log(boxKvDot("
|
|
501
|
+
console.log(boxKvDot("Description", chalk.hex("#CBD5E1")(truncateVisual(repo.description, 42)), 14, border));
|
|
505
502
|
}
|
|
506
503
|
console.log(boxKvDot("HTTPS Clone", chalk.hex("#38BDF8").underline(repo.cloneUrl), 14, border));
|
|
507
504
|
console.log(boxKvDot("SSH Clone", chalk.hex("#A78BFA")(repo.sshUrl), 14, border));
|
|
508
505
|
|
|
509
506
|
console.log(boxRow("", border));
|
|
510
507
|
console.log(boxDivider(border));
|
|
511
|
-
console.log(boxRow(chalk.hex("#6EE7B7")(`»
|
|
508
|
+
console.log(boxRow(chalk.hex("#6EE7B7")(`» View on GitHub: ${repo.htmlUrl}`), border));
|
|
512
509
|
console.log(boxBottom(border));
|
|
513
510
|
console.log("");
|
|
514
511
|
}
|
|
515
512
|
|
|
516
513
|
/**
|
|
517
|
-
*
|
|
514
|
+
* Warn when a folder is nested inside a parent repository.
|
|
518
515
|
*/
|
|
519
516
|
export function printNestedRepoWarning({ targetDir = "", topLevel = "" } = {}) {
|
|
520
517
|
const border = chalk.hex("#D97706");
|
|
521
518
|
const title = chalk.bold.bgHex("#D97706").hex("#FFFFFF")(
|
|
522
|
-
" FOLDER
|
|
519
|
+
" FOLDER INSIDE A PARENT REPOSITORY ",
|
|
523
520
|
);
|
|
524
521
|
|
|
525
522
|
console.log("");
|
|
@@ -527,7 +524,7 @@ export function printNestedRepoWarning({ targetDir = "", topLevel = "" } = {}) {
|
|
|
527
524
|
console.log(
|
|
528
525
|
boxRow(
|
|
529
526
|
chalk.hex("#FDE047")(
|
|
530
|
-
"
|
|
527
|
+
"The source folder is inside the parent Git repository:",
|
|
531
528
|
),
|
|
532
529
|
border,
|
|
533
530
|
),
|
|
@@ -540,7 +537,7 @@ export function printNestedRepoWarning({ targetDir = "", topLevel = "" } = {}) {
|
|
|
540
537
|
console.log(
|
|
541
538
|
boxRow(
|
|
542
539
|
chalk.hex("#94A3B8")(
|
|
543
|
-
"
|
|
540
|
+
"Pushing may include the parent folder's history and files.",
|
|
544
541
|
),
|
|
545
542
|
border,
|
|
546
543
|
),
|
|
@@ -548,7 +545,7 @@ export function printNestedRepoWarning({ targetDir = "", topLevel = "" } = {}) {
|
|
|
548
545
|
console.log(
|
|
549
546
|
boxRow(
|
|
550
547
|
chalk.hex("#34D399")(
|
|
551
|
-
"
|
|
548
|
+
"Tip: Create a SEPARATE repository to push only this folder.",
|
|
552
549
|
),
|
|
553
550
|
border,
|
|
554
551
|
),
|
|
@@ -557,12 +554,12 @@ export function printNestedRepoWarning({ targetDir = "", topLevel = "" } = {}) {
|
|
|
557
554
|
}
|
|
558
555
|
|
|
559
556
|
/**
|
|
560
|
-
*
|
|
557
|
+
* Warn about sensitive secrets.
|
|
561
558
|
*/
|
|
562
559
|
export function printPrePushSecretWarning({ riskyFiles = [], findings = [] }) {
|
|
563
560
|
const border = chalk.hex("#D97706");
|
|
564
561
|
const title = chalk.bold.bgHex("#D97706").hex("#FFFFFF")(
|
|
565
|
-
"
|
|
562
|
+
" SENSITIVE DATA DETECTED ",
|
|
566
563
|
);
|
|
567
564
|
|
|
568
565
|
console.log("");
|
|
@@ -570,7 +567,7 @@ export function printPrePushSecretWarning({ riskyFiles = [], findings = [] }) {
|
|
|
570
567
|
console.log(
|
|
571
568
|
boxRow(
|
|
572
569
|
chalk.hex("#FDE047")(
|
|
573
|
-
"GitHub
|
|
570
|
+
"GitHub may block the push if it detects a secret (GH013)",
|
|
574
571
|
),
|
|
575
572
|
border,
|
|
576
573
|
),
|
|
@@ -584,7 +581,7 @@ export function printPrePushSecretWarning({ riskyFiles = [], findings = [] }) {
|
|
|
584
581
|
|
|
585
582
|
if (riskyFiles.length > 0) {
|
|
586
583
|
console.log(
|
|
587
|
-
boxRow(chalk.bold.hex("#F8FAFC")("
|
|
584
|
+
boxRow(chalk.bold.hex("#F8FAFC")("Sensitive files:"), border),
|
|
588
585
|
);
|
|
589
586
|
const showFiles = riskyFiles.slice(0, 4);
|
|
590
587
|
showFiles.forEach((f) => {
|
|
@@ -596,7 +593,7 @@ export function printPrePushSecretWarning({ riskyFiles = [], findings = [] }) {
|
|
|
596
593
|
if (riskyFiles.length > 4) {
|
|
597
594
|
console.log(
|
|
598
595
|
boxRow(
|
|
599
|
-
chalk.hex("#64748B")(` +- ...
|
|
596
|
+
chalk.hex("#64748B")(` +- ...and ${riskyFiles.length - 4} more file(s)`),
|
|
600
597
|
border,
|
|
601
598
|
),
|
|
602
599
|
);
|
|
@@ -607,7 +604,7 @@ export function printPrePushSecretWarning({ riskyFiles = [], findings = [] }) {
|
|
|
607
604
|
if (riskyFiles.length > 0) console.log(boxRow("", border));
|
|
608
605
|
console.log(
|
|
609
606
|
boxRow(
|
|
610
|
-
chalk.bold.hex("#F8FAFC")("
|
|
607
|
+
chalk.bold.hex("#F8FAFC")("Possible secrets in source files:"),
|
|
611
608
|
border,
|
|
612
609
|
),
|
|
613
610
|
);
|
|
@@ -637,7 +634,7 @@ export function printPrePushSecretWarning({ riskyFiles = [], findings = [] }) {
|
|
|
637
634
|
if (findings.length > 4) {
|
|
638
635
|
console.log(
|
|
639
636
|
boxRow(
|
|
640
|
-
chalk.hex("#64748B")(` +- ...
|
|
637
|
+
chalk.hex("#64748B")(` +- ...and ${findings.length - 4} more location(s)`),
|
|
641
638
|
border,
|
|
642
639
|
),
|
|
643
640
|
);
|
|
@@ -648,7 +645,7 @@ export function printPrePushSecretWarning({ riskyFiles = [], findings = [] }) {
|
|
|
648
645
|
console.log(
|
|
649
646
|
boxRow(
|
|
650
647
|
chalk.hex("#34D399")(
|
|
651
|
-
'
|
|
648
|
+
'Tip: Choose "Auto-fix" to safely unstage files and update .gitignore',
|
|
652
649
|
),
|
|
653
650
|
border,
|
|
654
651
|
),
|
|
@@ -657,14 +654,14 @@ export function printPrePushSecretWarning({ riskyFiles = [], findings = [] }) {
|
|
|
657
654
|
}
|
|
658
655
|
|
|
659
656
|
/**
|
|
660
|
-
*
|
|
657
|
+
* Warn when GitHub Push Protection blocks a push (GH013).
|
|
661
658
|
*/
|
|
662
659
|
export function printSecretBlockedBox(rawMessage, details = {}) {
|
|
663
660
|
const border = chalk.hex("#E11D48");
|
|
664
661
|
const files = details.files || [];
|
|
665
662
|
const types = details.secretTypes || [];
|
|
666
663
|
const title = chalk.bold.bgHex("#E11D48").hex("#FFFFFF")(
|
|
667
|
-
" PUSH
|
|
664
|
+
" PUSH BLOCKED — SECRET DETECTED (GH013) ",
|
|
668
665
|
);
|
|
669
666
|
|
|
670
667
|
console.log("");
|
|
@@ -672,7 +669,7 @@ export function printSecretBlockedBox(rawMessage, details = {}) {
|
|
|
672
669
|
console.log(
|
|
673
670
|
boxRow(
|
|
674
671
|
chalk.hex("#FDA4AF")(
|
|
675
|
-
"GitHub Push Protection
|
|
672
|
+
"GitHub Push Protection blocked the push because it detected a secret.",
|
|
676
673
|
),
|
|
677
674
|
border,
|
|
678
675
|
),
|
|
@@ -682,7 +679,7 @@ export function printSecretBlockedBox(rawMessage, details = {}) {
|
|
|
682
679
|
if (types.length > 0) {
|
|
683
680
|
console.log(
|
|
684
681
|
boxKvDot(
|
|
685
|
-
"
|
|
682
|
+
"Secret type",
|
|
686
683
|
chalk.hex("#F8FAFC").bold(types.slice(0, 2).join(", ")),
|
|
687
684
|
14,
|
|
688
685
|
border,
|
|
@@ -692,25 +689,25 @@ export function printSecretBlockedBox(rawMessage, details = {}) {
|
|
|
692
689
|
if (files.length > 0) {
|
|
693
690
|
files.slice(0, 2).forEach((f) => {
|
|
694
691
|
console.log(
|
|
695
|
-
boxKvDot("
|
|
692
|
+
boxKvDot("Source file", chalk.hex("#FCD34D")(f), 14, border),
|
|
696
693
|
);
|
|
697
694
|
});
|
|
698
695
|
}
|
|
699
696
|
|
|
700
697
|
console.log(boxRow("", border));
|
|
701
698
|
console.log(
|
|
702
|
-
boxRow(chalk.bold.hex("#F8FAFC")("»
|
|
699
|
+
boxRow(chalk.bold.hex("#F8FAFC")("» How to resolve this:"), border),
|
|
703
700
|
);
|
|
704
701
|
console.log(
|
|
705
702
|
boxRow(
|
|
706
|
-
chalk.hex("#E2E8F0")(" [1]
|
|
703
|
+
chalk.hex("#E2E8F0")(" [1] Revoke the key immediately if it is real"),
|
|
707
704
|
border,
|
|
708
705
|
),
|
|
709
706
|
);
|
|
710
707
|
console.log(
|
|
711
708
|
boxRow(
|
|
712
709
|
chalk.hex("#38BDF8")(
|
|
713
|
-
" [2]
|
|
710
|
+
" [2] Use this tool to unstage files and squash the commits",
|
|
714
711
|
),
|
|
715
712
|
border,
|
|
716
713
|
),
|
|
@@ -718,7 +715,7 @@ export function printSecretBlockedBox(rawMessage, details = {}) {
|
|
|
718
715
|
console.log(
|
|
719
716
|
boxRow(
|
|
720
717
|
chalk.hex("#94A3B8")(
|
|
721
|
-
" [3]
|
|
718
|
+
" [3] Remove the secret from source code and use an environment variable (process.env)",
|
|
722
719
|
),
|
|
723
720
|
border,
|
|
724
721
|
),
|
|
@@ -729,40 +726,40 @@ export function printSecretBlockedBox(rawMessage, details = {}) {
|
|
|
729
726
|
if (details.unblockUrl) {
|
|
730
727
|
console.log(
|
|
731
728
|
chalk.hex("#F59E0B")(
|
|
732
|
-
`\n »
|
|
729
|
+
`\n » Temporary unblock URL (for test credentials only):\n ${chalk.underline(details.unblockUrl)}`,
|
|
733
730
|
),
|
|
734
731
|
);
|
|
735
732
|
}
|
|
736
733
|
console.log(
|
|
737
734
|
chalk.hex("#64748B")(
|
|
738
|
-
`\n »
|
|
735
|
+
`\n » GitHub documentation: ${details.resolveUrl || "https://docs.github.com/code-security/secret-scanning/pushing-a-branch-blocked-by-push-protection"}\n`,
|
|
739
736
|
),
|
|
740
737
|
);
|
|
741
738
|
}
|
|
742
739
|
|
|
743
740
|
/**
|
|
744
|
-
*
|
|
741
|
+
* Success card displayed after a push.
|
|
745
742
|
*/
|
|
746
743
|
/**
|
|
747
|
-
*
|
|
744
|
+
* Success card displayed after a push.
|
|
748
745
|
*/
|
|
749
746
|
export function printSuccessBox({
|
|
750
747
|
branch,
|
|
751
748
|
repoUrl,
|
|
752
749
|
durationSeconds = null,
|
|
753
|
-
secretStatus = "
|
|
750
|
+
secretStatus = "Scanned (no risks)",
|
|
754
751
|
}) {
|
|
755
752
|
const border = chalk.hex("#10B981");
|
|
756
|
-
const title = chalk.bold.hex("#F8FAFC")(" PUSH
|
|
753
|
+
const title = chalk.bold.hex("#F8FAFC")(" PUSH SUCCEEDED ");
|
|
757
754
|
console.log("");
|
|
758
755
|
console.log(boxTop(title, border));
|
|
759
756
|
console.log(boxRow("", border));
|
|
760
|
-
console.log(boxKvDot("
|
|
761
|
-
console.log(boxKvDot("
|
|
757
|
+
console.log(boxKvDot("Target branch", chalk.hex("#34D399").bold(branch), 18, border));
|
|
758
|
+
console.log(boxKvDot("Secret scan", chalk.hex("#38BDF8")(secretStatus), 18, border));
|
|
762
759
|
if (durationSeconds !== null) {
|
|
763
760
|
console.log(
|
|
764
761
|
boxKvDot(
|
|
765
|
-
"
|
|
762
|
+
"Duration",
|
|
766
763
|
chalk.hex("#FBBF24").bold(`${durationSeconds.toFixed(1)}s`),
|
|
767
764
|
18,
|
|
768
765
|
border,
|
|
@@ -775,7 +772,7 @@ export function printSuccessBox({
|
|
|
775
772
|
console.log(boxDivider(border));
|
|
776
773
|
console.log(
|
|
777
774
|
boxRow(
|
|
778
|
-
`${chalk.hex("#34D399")("»
|
|
775
|
+
`${chalk.hex("#34D399")("» View on GitHub:")} ${chalk.hex("#38BDF8").underline(webUrl)}`,
|
|
779
776
|
border,
|
|
780
777
|
),
|
|
781
778
|
);
|
|
@@ -795,7 +792,7 @@ function toGitHubWebUrl(url) {
|
|
|
795
792
|
export function printCancelled() {
|
|
796
793
|
const border = chalk.hex("#64748B");
|
|
797
794
|
const title = chalk.bold.hex("#F8FAFC")(
|
|
798
|
-
"
|
|
795
|
+
" OPERATION CANCELLED ",
|
|
799
796
|
);
|
|
800
797
|
console.log("");
|
|
801
798
|
console.log(boxTop(title, border));
|
|
@@ -803,7 +800,7 @@ export function printCancelled() {
|
|
|
803
800
|
console.log(
|
|
804
801
|
boxRow(
|
|
805
802
|
chalk.hex("#94A3B8")(
|
|
806
|
-
"
|
|
803
|
+
"The operation was safely stopped. No changes were made.",
|
|
807
804
|
),
|
|
808
805
|
border,
|
|
809
806
|
),
|
|
@@ -813,11 +810,11 @@ export function printCancelled() {
|
|
|
813
810
|
console.log("");
|
|
814
811
|
}
|
|
815
812
|
|
|
816
|
-
export function printErrorBox(title = "
|
|
813
|
+
export function printErrorBox(title = "AN ERROR OCCURRED", lines = [], hint = "") {
|
|
817
814
|
const W = getW();
|
|
818
815
|
const border = chalk.hex("#E11D48");
|
|
819
816
|
const cleanTitle = String(title || "").toUpperCase();
|
|
820
|
-
const titleStyled = chalk.bold.bgHex("#E11D48").hex("#FFFFFF")(` [
|
|
817
|
+
const titleStyled = chalk.bold.bgHex("#E11D48").hex("#FFFFFF")(` [ERROR] ${cleanTitle} `);
|
|
821
818
|
const rows = Array.isArray(lines) ? lines : [String(lines || "")];
|
|
822
819
|
|
|
823
820
|
console.log("");
|
|
@@ -843,19 +840,19 @@ export function printErrorBox(title = "XẢY RA LỖI", lines = [], hint = "") {
|
|
|
843
840
|
|
|
844
841
|
export function printGitMissing() {
|
|
845
842
|
printErrorBox(
|
|
846
|
-
"
|
|
843
|
+
"GIT IS NOT INSTALLED",
|
|
847
844
|
[
|
|
848
|
-
"
|
|
849
|
-
"
|
|
845
|
+
"Git was not found on this system.",
|
|
846
|
+
"Download and install Git to continue.",
|
|
850
847
|
],
|
|
851
|
-
"
|
|
848
|
+
"Download it at: https://git-scm.com/downloads",
|
|
852
849
|
);
|
|
853
850
|
}
|
|
854
851
|
|
|
855
852
|
export function printAuthFailedBox(message = "") {
|
|
856
853
|
const border = chalk.hex("#E11D48");
|
|
857
854
|
const title = chalk.bold.bgHex("#E11D48").hex("#FFFFFF")(
|
|
858
|
-
"
|
|
855
|
+
" GIT AUTHENTICATION FAILED ",
|
|
859
856
|
);
|
|
860
857
|
const firstLine = String(message || "")
|
|
861
858
|
.split("\n")[0]
|
|
@@ -866,7 +863,7 @@ export function printAuthFailedBox(message = "") {
|
|
|
866
863
|
console.log(
|
|
867
864
|
boxRow(
|
|
868
865
|
chalk.hex("#FDA4AF")(
|
|
869
|
-
"
|
|
866
|
+
"Could not authenticate with the remote. Check your access permissions.",
|
|
870
867
|
),
|
|
871
868
|
border,
|
|
872
869
|
),
|
|
@@ -880,23 +877,23 @@ export function printAuthFailedBox(message = "") {
|
|
|
880
877
|
);
|
|
881
878
|
console.log(boxRow("", border));
|
|
882
879
|
console.log(
|
|
883
|
-
boxRow(chalk.bold.hex("#F8FAFC")("»
|
|
880
|
+
boxRow(chalk.bold.hex("#F8FAFC")("» Quick fixes:"), border),
|
|
884
881
|
);
|
|
885
882
|
console.log(
|
|
886
883
|
boxRow(
|
|
887
|
-
chalk.hex("#E2E8F0")(" [1]
|
|
884
|
+
chalk.hex("#E2E8F0")(" [1] Use the GITHUB menu to connect your account"),
|
|
888
885
|
border,
|
|
889
886
|
),
|
|
890
887
|
);
|
|
891
888
|
console.log(
|
|
892
889
|
boxRow(
|
|
893
|
-
chalk.hex("#E2E8F0")(" [2]
|
|
890
|
+
chalk.hex("#E2E8F0")(" [2] Use GitHub CLI: gh auth login"),
|
|
894
891
|
border,
|
|
895
892
|
),
|
|
896
893
|
);
|
|
897
894
|
console.log(
|
|
898
895
|
boxRow(
|
|
899
|
-
chalk.hex("#94A3B8")(" [3]
|
|
896
|
+
chalk.hex("#94A3B8")(" [3] Use an SSH URL: git@github.com:user/repo.git"),
|
|
900
897
|
border,
|
|
901
898
|
),
|
|
902
899
|
);
|
|
@@ -907,7 +904,7 @@ export function printAuthFailedBox(message = "") {
|
|
|
907
904
|
export function printNonFastForwardBox(message = "", branch = "main") {
|
|
908
905
|
const border = chalk.hex("#D97706");
|
|
909
906
|
const title = chalk.bold.bgHex("#D97706").hex("#FFFFFF")(
|
|
910
|
-
" REMOTE
|
|
907
|
+
" REMOTE HAS NEWER COMMITS ",
|
|
911
908
|
);
|
|
912
909
|
const firstLine = String(message || "")
|
|
913
910
|
.split("\n")[0]
|
|
@@ -918,7 +915,7 @@ export function printNonFastForwardBox(message = "", branch = "main") {
|
|
|
918
915
|
console.log(
|
|
919
916
|
boxRow(
|
|
920
917
|
chalk.hex("#FDE68A")(
|
|
921
|
-
"
|
|
918
|
+
"The remote repository has commits that are newer than your local branch.",
|
|
922
919
|
),
|
|
923
920
|
border,
|
|
924
921
|
),
|
|
@@ -934,7 +931,7 @@ export function printNonFastForwardBox(message = "", branch = "main") {
|
|
|
934
931
|
console.log(
|
|
935
932
|
boxRow(
|
|
936
933
|
chalk.hex("#38BDF8")(
|
|
937
|
-
` »
|
|
934
|
+
` » Choose Pull (rebase), then push again from this tool.`,
|
|
938
935
|
),
|
|
939
936
|
border,
|
|
940
937
|
),
|
|
@@ -942,7 +939,7 @@ export function printNonFastForwardBox(message = "", branch = "main") {
|
|
|
942
939
|
console.log(
|
|
943
940
|
boxRow(
|
|
944
941
|
chalk.hex("#94A3B8")(
|
|
945
|
-
"
|
|
942
|
+
" Use --force only when you are certain you want to overwrite the remote.",
|
|
946
943
|
),
|
|
947
944
|
border,
|
|
948
945
|
),
|
|
@@ -959,7 +956,7 @@ export function printFixResult({
|
|
|
959
956
|
} = {}) {
|
|
960
957
|
const W = getW();
|
|
961
958
|
const border = chalk.hex("#10B981");
|
|
962
|
-
const title = chalk.bold.hex("#F8FAFC")("
|
|
959
|
+
const title = chalk.bold.hex("#F8FAFC")(" SECRET PROTECTION RESULTS ");
|
|
963
960
|
|
|
964
961
|
console.log("");
|
|
965
962
|
console.log(boxTop(title, border));
|
|
@@ -968,7 +965,7 @@ export function printFixResult({
|
|
|
968
965
|
if (fixed.length > 0) {
|
|
969
966
|
console.log(
|
|
970
967
|
boxRow(
|
|
971
|
-
`${chalk.hex("#34D399").bold("[OK]
|
|
968
|
+
`${chalk.hex("#34D399").bold("[OK] Removed from commit:")} ${chalk.hex("#F1F5F9")(truncateVisual(fixed.join(", "), W - 28))}`,
|
|
972
969
|
border,
|
|
973
970
|
),
|
|
974
971
|
);
|
|
@@ -976,7 +973,7 @@ export function printFixResult({
|
|
|
976
973
|
if (gitignoreAdded.length > 0) {
|
|
977
974
|
console.log(
|
|
978
975
|
boxRow(
|
|
979
|
-
`${chalk.hex("#38BDF8").bold("[+]
|
|
976
|
+
`${chalk.hex("#38BDF8").bold("[+] Added to .gitignore:")} ${chalk.hex("#CBD5E1")(truncateVisual(gitignoreAdded.join(", "), W - 30))}`,
|
|
980
977
|
border,
|
|
981
978
|
),
|
|
982
979
|
);
|
|
@@ -985,7 +982,7 @@ export function printFixResult({
|
|
|
985
982
|
console.log(boxRow("", border));
|
|
986
983
|
console.log(
|
|
987
984
|
boxRow(
|
|
988
|
-
chalk.hex("#FBBF24").bold("[!]
|
|
985
|
+
chalk.hex("#FBBF24").bold("[!] Source files containing secrets (unstaged, not ignored):"),
|
|
989
986
|
border,
|
|
990
987
|
),
|
|
991
988
|
);
|
|
@@ -993,7 +990,7 @@ export function printFixResult({
|
|
|
993
990
|
console.log(boxRow(` - ${chalk.hex("#CBD5E1")(truncateVisual(f, W - 10))}`, border)),
|
|
994
991
|
);
|
|
995
992
|
console.log(
|
|
996
|
-
boxRow(chalk.hex("#94A3B8")(" »
|
|
993
|
+
boxRow(chalk.hex("#94A3B8")(" » Remove the key from the file and use process.env instead."), border),
|
|
997
994
|
);
|
|
998
995
|
}
|
|
999
996
|
if (failed.length > 0) {
|
|
@@ -1010,7 +1007,7 @@ export function printFixResult({
|
|
|
1010
1007
|
|
|
1011
1008
|
export function printFooter() {
|
|
1012
1009
|
const W = getW();
|
|
1013
|
-
//
|
|
1010
|
+
// Full-width divider, aligned like the other frames.
|
|
1014
1011
|
const core = "»";
|
|
1015
1012
|
const remaining = Math.max(4, W - 3); // 1 core + 2 space
|
|
1016
1013
|
const leftLen = Math.ceil(remaining / 2);
|
|
@@ -1021,10 +1018,10 @@ export function printFooter() {
|
|
|
1021
1018
|
chalk.hex("#334155")(" " + "─".repeat(rightLen))
|
|
1022
1019
|
);
|
|
1023
1020
|
const tip1 =
|
|
1024
|
-
chalk.hex("#64748B")("»
|
|
1021
|
+
chalk.hex("#64748B")("» Tip: Run ") +
|
|
1025
1022
|
chalk.hex("#38BDF8").bold("git-push-time --help") +
|
|
1026
|
-
chalk.hex("#64748B")("
|
|
1027
|
-
const tip2 = chalk.hex("#475569")("»
|
|
1023
|
+
chalk.hex("#64748B")(" to see all commands.");
|
|
1024
|
+
const tip2 = chalk.hex("#475569")("» Git & GitHub workspace — Happy coding!");
|
|
1028
1025
|
for (const tip of [tip1, tip2]) {
|
|
1029
1026
|
const w = stringWidth(tip);
|
|
1030
1027
|
if (w >= W) {
|
|
@@ -1049,12 +1046,12 @@ export function styleCliHelp(program) {
|
|
|
1049
1046
|
program.addHelpText(
|
|
1050
1047
|
"after",
|
|
1051
1048
|
`
|
|
1052
|
-
${chalk.hex("#334155")("─── »
|
|
1053
|
-
${chalk.hex("#34D399")("$")} ${chalk.white('git-push-time')} #
|
|
1054
|
-
${chalk.hex("#34D399")("$")} ${chalk.white('git-push-time github create')} #
|
|
1055
|
-
${chalk.hex("#34D399")("$")} ${chalk.white('git-push-time -r <repo-url> -d "-2d" -y')} # Push
|
|
1056
|
-
${chalk.hex("#34D399")("$")} ${chalk.white('git-push-time fix')} #
|
|
1057
|
-
${chalk.hex("#34D399")("$")} ${chalk.white('git-push-time pull')} #
|
|
1049
|
+
${chalk.hex("#334155")("─── » Common examples ───")}
|
|
1050
|
+
${chalk.hex("#34D399")("$")} ${chalk.white('git-push-time')} # Open the interactive dashboard
|
|
1051
|
+
${chalk.hex("#34D399")("$")} ${chalk.white('git-push-time github create')} # Create a GitHub repository
|
|
1052
|
+
${chalk.hex("#34D399")("$")} ${chalk.white('git-push-time -r <repo-url> -d "-2d" -y')} # Push with a commit date two days ago
|
|
1053
|
+
${chalk.hex("#34D399")("$")} ${chalk.white('git-push-time fix')} # Scan for and remove staged secrets
|
|
1054
|
+
${chalk.hex("#34D399")("$")} ${chalk.white('git-push-time pull')} # Pull the latest changes
|
|
1058
1055
|
`,
|
|
1059
1056
|
);
|
|
1060
1057
|
}
|