pan-stack-cli 0.1.4 → 0.2.1
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 +572 -23
- package/bin/pan.js +206 -20
- package/lib/ai-skills.js +829 -0
- package/lib/create.js +22 -4
- package/lib/discover.js +69 -2
- package/lib/git/git-checkout.js +395 -0
- package/lib/git/git-clone.js +163 -28
- package/lib/git/git-fetch-all.js +100 -33
- package/lib/git/git-grv.js +68 -37
- package/lib/git/git-gst.js +97 -26
- package/lib/git/git-mr.js +546 -0
- package/lib/git/git-pull.js +104 -43
- package/lib/git/git-push.js +150 -41
- package/lib/git/git-sync-main.js +334 -0
- package/lib/git/helpers/bulk-report.js +202 -0
- package/lib/git/helpers/cli.js +41 -5
- package/lib/git/helpers/colors.js +19 -0
- package/lib/git/helpers/git.js +74 -0
- package/lib/git/helpers/manifest.js +93 -0
- package/lib/git/helpers/repo-select.js +416 -0
- package/lib/git/helpers/repo-summary.js +161 -0
- package/lib/i18n.js +61 -0
- package/lib/inquirer-theme.js +20 -0
- package/lib/language.js +121 -0
- package/lib/languages.js +14 -0
- package/lib/manifest.js +115 -14
- package/lib/menu-prompt.js +201 -0
- package/lib/menu.js +134 -0
- package/lib/messages.js +647 -0
- package/lib/run-command.js +173 -52
- package/lib/select.js +0 -0
- package/lib/serve.js +75 -0
- package/lib/skill-registry-client.js +173 -0
- package/lib/stats/aggregate.js +51 -0
- package/lib/stats/identity.js +20 -0
- package/lib/stats/index.js +189 -0
- package/lib/stats/parse.js +46 -0
- package/lib/stats/range.js +38 -0
- package/lib/stats/report.js +264 -0
- package/lib/stats/review.js +157 -0
- package/lib/stats/scan.js +92 -0
- package/lib/stats/serve.js +25 -0
- package/lib/ui/api.js +202 -0
- package/lib/ui/clone.js +115 -0
- package/lib/ui/config.js +182 -0
- package/lib/ui/diff.js +128 -0
- package/lib/ui/export-gate.js +79 -0
- package/lib/ui/gen-react-mobile-code.js +6 -0
- package/lib/ui/gen-react-web-code.js +75 -0
- package/lib/ui/index.js +89 -0
- package/lib/ui/init.js +252 -0
- package/lib/ui/login.js +128 -0
- package/lib/ui/logout.js +22 -0
- package/lib/ui/palettes.js +103 -0
- package/lib/ui/published-html.js +24 -0
- package/lib/ui/sync.js +137 -0
- package/lib/ui/workspace.js +213 -0
- package/package.json +10 -3
- package/templates/workspace/.claude/skills/writing-guidelines/SKILL.md +0 -48
- package/templates/workspace/.codex/skills/writing-guidelines/SKILL.md +0 -48
- package/templates/workspace/.cursor/skills/writing-guidelines/SKILL.md +0 -48
- package/templates/workspace/.pan.yaml +0 -25
- package/templates/workspace/.project.yaml +0 -15
- package/templates/workspace/README.md +0 -35
package/lib/git/git-pull.js
CHANGED
|
@@ -1,39 +1,80 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/*
|
|
3
|
-
* Pull latest changes for PAN Stack project repositories
|
|
4
|
-
*
|
|
5
|
-
* Skips repositories that do not exist or are not git checkouts. Failures warn
|
|
6
|
-
* and continue so one broken repo does not block the rest.
|
|
3
|
+
* Pull latest changes for PAN Stack project repositories.
|
|
4
|
+
* In compact summary table: repo · branch · result · changes · conflict.
|
|
7
5
|
*/
|
|
8
6
|
|
|
9
7
|
const { parseCommonArgs, loadManifestRepos, printCommonOptions } = require("./helpers/cli");
|
|
10
|
-
const { ensureRepoExists
|
|
8
|
+
const { ensureRepoExists } = require("./helpers/git");
|
|
9
|
+
const {
|
|
10
|
+
runGitSilent, parsePullOutput, printSummary, statusBadge, bulkColumns, pullModeLabel,
|
|
11
|
+
getCurrentBranch, listConflictedFiles,
|
|
12
|
+
} = require("./helpers/bulk-report");
|
|
13
|
+
const colors = require("./helpers/colors");
|
|
14
|
+
const { t } = require("../i18n");
|
|
11
15
|
|
|
12
16
|
function pullRepo({ name, destination, dryRun, rebase }) {
|
|
13
17
|
if (!ensureRepoExists({ name, destination })) {
|
|
14
|
-
return;
|
|
18
|
+
return { name, skipped: true };
|
|
15
19
|
}
|
|
20
|
+
const branch = getCurrentBranch(destination) || t("GST_DETACHED");
|
|
21
|
+
const gitArgs = ["pull", "--stat"];
|
|
22
|
+
if (rebase) gitArgs.push("--rebase");
|
|
16
23
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
24
|
+
if (dryRun) {
|
|
25
|
+
console.log(colors.dim(`[dry-run] (in ${destination}) git ${gitArgs.join(" ")}`));
|
|
26
|
+
return { name, branch, dryRun: true };
|
|
20
27
|
}
|
|
21
28
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
29
|
+
process.stdout.write(colors.dim(` ↻ ${name} (${branch}) ... `));
|
|
30
|
+
const res = runGitSilent(gitArgs, destination);
|
|
31
|
+
const parsed = parsePullOutput(res.stdout, res.stderr);
|
|
32
|
+
const conflictFiles = parsed.conflict ? listConflictedFiles(destination) : [];
|
|
33
|
+
const ok = res.status === 0 && !parsed.conflict;
|
|
34
|
+
|
|
35
|
+
const shortMsg = parsed.upToDate ? t("BULK_STATUS_UP_TO_DATE")
|
|
36
|
+
: parsed.conflict ? t("BULK_STATUS_CONFLICT")
|
|
37
|
+
: parsed.mode !== "unknown" ? pullModeLabel(parsed.mode)
|
|
38
|
+
: ok ? t("BULK_STATUS_PULLED") : t("BULK_STATUS_FAILED");
|
|
39
|
+
console.log((ok ? colors.green : parsed.conflict ? colors.yellow : colors.red)(shortMsg));
|
|
40
|
+
|
|
41
|
+
return {
|
|
42
|
+
name, branch,
|
|
43
|
+
status: res.status,
|
|
44
|
+
upToDate: parsed.upToDate,
|
|
45
|
+
conflict: parsed.conflict,
|
|
46
|
+
conflictFiles,
|
|
47
|
+
mode: parsed.mode,
|
|
48
|
+
filesChanged: parsed.filesChanged,
|
|
49
|
+
insertions: parsed.insertions,
|
|
50
|
+
deletions: parsed.deletions,
|
|
51
|
+
error: res.status !== 0 && !parsed.conflict ? (res.stderr.split("\n")[0] || "").slice(0, 80) : "",
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function row(r) {
|
|
56
|
+
if (r.skipped) return { repo: colors.dim(r.name), branch: "", result: colors.dim(t("BULK_STATUS_SKIPPED")), changes: "", conflict: "" };
|
|
57
|
+
if (r.dryRun) return { repo: r.name, branch: r.branch, result: colors.dim(t("BULK_STATUS_DRY_RUN")), changes: "", conflict: "" };
|
|
58
|
+
const result =
|
|
59
|
+
r.upToDate ? statusBadge("ok", t("BULK_STATUS_UP_TO_DATE"))
|
|
60
|
+
: r.conflict ? statusBadge("warn", t("BULK_STATUS_CONFLICT"))
|
|
61
|
+
: r.error ? statusBadge("fail", t("BULK_STATUS_FAILED"))
|
|
62
|
+
: statusBadge("ok", r.mode && r.mode !== "unknown" ? pullModeLabel(r.mode) : t("BULK_STATUS_PULLED"));
|
|
63
|
+
const filesLabel = r.filesChanged === 1 ? t("BULK_FILES_ONE") : t("BULK_FILES_MANY");
|
|
64
|
+
const changes = r.filesChanged
|
|
65
|
+
? `${r.filesChanged} ${filesLabel} ${colors.green("+" + r.insertions)} ${colors.red("−" + r.deletions)}`
|
|
66
|
+
: colors.dim("—");
|
|
67
|
+
const conflict = r.conflict
|
|
68
|
+
? colors.yellow(`${r.conflictFiles.length} ${r.conflictFiles.length === 1 ? t("BULK_FILES_ONE") : t("BULK_FILES_MANY")}`)
|
|
69
|
+
: colors.dim("—");
|
|
70
|
+
return { repo: r.name, branch: r.branch, result, changes, conflict };
|
|
31
71
|
}
|
|
32
72
|
|
|
33
73
|
function printHelp() {
|
|
34
74
|
console.log(`usage: git-pull.js [--base-dir BASE_DIR] [--dry-run] [--rebase] [manifest]
|
|
35
75
|
|
|
36
76
|
Pull latest changes for repositories listed in a PAN Stack clone manifest.
|
|
77
|
+
Prints a compact summary table (repo · branch · result · changes · conflict).
|
|
37
78
|
|
|
38
79
|
${printCommonOptions()}
|
|
39
80
|
--rebase Use git pull --rebase`);
|
|
@@ -42,35 +83,55 @@ ${printCommonOptions()}
|
|
|
42
83
|
function main() {
|
|
43
84
|
const args = parseCommonArgs(process.argv.slice(2), {
|
|
44
85
|
defaults: { rebase: false },
|
|
45
|
-
handlers: {
|
|
46
|
-
"--rebase": (parsed) => {
|
|
47
|
-
parsed.rebase = true;
|
|
48
|
-
},
|
|
49
|
-
},
|
|
86
|
+
handlers: { "--rebase": (p) => { p.rebase = true; } },
|
|
50
87
|
});
|
|
51
|
-
if (args.help) {
|
|
52
|
-
printHelp();
|
|
53
|
-
return;
|
|
54
|
-
}
|
|
88
|
+
if (args.help) { printHelp(); return; }
|
|
55
89
|
|
|
56
|
-
const { repos } = loadManifestRepos({
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
90
|
+
const { repos } = loadManifestRepos({ manifestPath: args.manifest, baseDir: args.baseDir });
|
|
91
|
+
const results = repos.map((r) => pullRepo({ name: r.name, destination: r.destination, dryRun: args.dryRun, rebase: args.rebase }));
|
|
92
|
+
|
|
93
|
+
const conflicts = results.filter((r) => r.conflict);
|
|
94
|
+
const pulled = results.filter((r) => !r.skipped && !r.dryRun && !r.upToDate && !r.conflict && !r.error);
|
|
95
|
+
const failed = results.filter((r) => r.error);
|
|
96
|
+
const upToDate = results.filter((r) => r.upToDate);
|
|
97
|
+
// Chỉ đưa vào bảng những repo có thay đổi / conflict / lỗi / dry-run (bỏ up-to-date).
|
|
98
|
+
const shown = results.filter((r) => r.dryRun || (!r.skipped && !r.upToDate));
|
|
99
|
+
const rows = shown.map(row);
|
|
100
|
+
|
|
101
|
+
const footer = [];
|
|
102
|
+
footer.push(colors.dim(t("PULL_FOOTER_COUNTS__TOTAL_CHANGED_UPTODATE_CONFLICT_FAILED", {
|
|
103
|
+
total: results.length, changed: pulled.length, upToDate: upToDate.length,
|
|
104
|
+
conflict: conflicts.length, failed: failed.length,
|
|
105
|
+
})));
|
|
106
|
+
if (conflicts.length) {
|
|
107
|
+
footer.push("");
|
|
108
|
+
footer.push(colors.bold(colors.yellow(t("PULL_CONFLICT_HEADER"))));
|
|
109
|
+
for (const c of conflicts) {
|
|
110
|
+
footer.push(` ${colors.cyan(c.name)} (${c.branch}): ${c.conflictFiles.join(", ") || "(git status)"}`);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
60
113
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
114
|
+
if (rows.length === 0) {
|
|
115
|
+
console.log("");
|
|
116
|
+
console.log(colors.bold(`─── ${t("PULL_TITLE")} ───`));
|
|
117
|
+
console.log(colors.green(t("PULL_ALL_UP_TO_DATE__N", { n: results.length })));
|
|
118
|
+
for (const line of footer) console.log(line);
|
|
119
|
+
} else {
|
|
120
|
+
printSummary({
|
|
121
|
+
title: t("PULL_TITLE"),
|
|
122
|
+
rows,
|
|
123
|
+
columns: bulkColumns([
|
|
124
|
+
{ key: "repo", labelKey: "BULK_COL_REPO" },
|
|
125
|
+
{ key: "branch", labelKey: "BULK_COL_BRANCH" },
|
|
126
|
+
{ key: "result", labelKey: "BULK_COL_RESULT" },
|
|
127
|
+
{ key: "changes", labelKey: "BULK_COL_CHANGES" },
|
|
128
|
+
{ key: "conflict", labelKey: "BULK_COL_CONFLICT" },
|
|
129
|
+
]),
|
|
130
|
+
footerLines: footer,
|
|
67
131
|
});
|
|
68
132
|
}
|
|
69
|
-
}
|
|
70
133
|
|
|
71
|
-
|
|
72
|
-
main();
|
|
73
|
-
} catch (error) {
|
|
74
|
-
console.error(error.message);
|
|
75
|
-
process.exit(1);
|
|
134
|
+
if (conflicts.length || failed.length) process.exitCode = 1;
|
|
76
135
|
}
|
|
136
|
+
|
|
137
|
+
try { main(); } catch (error) { console.error(error.message); process.exit(1); }
|
package/lib/git/git-push.js
CHANGED
|
@@ -1,39 +1,104 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/*
|
|
3
|
-
* Push local commits for PAN Stack project repositories
|
|
4
|
-
*
|
|
5
|
-
* Skips repositories that do not exist or are not git checkouts. Failures warn
|
|
6
|
-
* and continue so one broken repo does not block the rest.
|
|
3
|
+
* Push local commits for PAN Stack project repositories.
|
|
4
|
+
* Compact summary table: repo · branch · result · commits · MR/PR link.
|
|
7
5
|
*/
|
|
8
6
|
|
|
9
7
|
const { parseCommonArgs, loadManifestRepos, printCommonOptions } = require("./helpers/cli");
|
|
10
|
-
const { ensureRepoExists
|
|
8
|
+
const { ensureRepoExists } = require("./helpers/git");
|
|
9
|
+
const {
|
|
10
|
+
runGitSilent, parsePushOutput, printSummary, statusBadge, bulkColumns,
|
|
11
|
+
aheadBehind, aheadLineStats, getCurrentBranch,
|
|
12
|
+
} = require("./helpers/bulk-report");
|
|
13
|
+
const colors = require("./helpers/colors");
|
|
14
|
+
const { t } = require("../i18n");
|
|
15
|
+
|
|
16
|
+
function formatChanges(r) {
|
|
17
|
+
if (!r.filesChanged && !r.insertions && !r.deletions) return colors.dim("—");
|
|
18
|
+
const filesLabel = r.filesChanged === 1 ? t("BULK_FILES_ONE") : t("BULK_FILES_MANY");
|
|
19
|
+
const filesPart = r.filesChanged
|
|
20
|
+
? `${r.filesChanged} ${filesLabel} `
|
|
21
|
+
: "";
|
|
22
|
+
return `${filesPart}${colors.green("+" + (r.insertions || 0))} ${colors.red("−" + (r.deletions || 0))}`;
|
|
23
|
+
}
|
|
11
24
|
|
|
12
25
|
function pushRepo({ name, destination, dryRun, setUpstream }) {
|
|
13
26
|
if (!ensureRepoExists({ name, destination })) {
|
|
14
|
-
return;
|
|
27
|
+
return { name, skipped: true };
|
|
15
28
|
}
|
|
29
|
+
const branch = getCurrentBranch(destination) || t("GST_DETACHED");
|
|
30
|
+
// Đếm commit + line change chờ push TRƯỚC push (sau push @{u} đã bắt kịp HEAD).
|
|
31
|
+
const ab = aheadBehind(destination);
|
|
32
|
+
const commitsAhead = ab ? ab.ahead : 0;
|
|
33
|
+
const lines = commitsAhead > 0
|
|
34
|
+
? aheadLineStats(destination)
|
|
35
|
+
: { insertions: 0, deletions: 0, filesChanged: 0 };
|
|
16
36
|
|
|
17
37
|
const gitArgs = ["push"];
|
|
18
|
-
if (setUpstream)
|
|
19
|
-
|
|
38
|
+
if (setUpstream) gitArgs.push("-u", "origin", "HEAD");
|
|
39
|
+
|
|
40
|
+
if (dryRun) {
|
|
41
|
+
console.log(colors.dim(`[dry-run] (in ${destination}) git ${gitArgs.join(" ")}`));
|
|
42
|
+
return { name, branch, dryRun: true, commitsAhead, ...lines };
|
|
20
43
|
}
|
|
21
44
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
45
|
+
process.stdout.write(colors.dim(` ↑ ${name} (${branch}) ... `));
|
|
46
|
+
const res = runGitSilent(gitArgs, destination);
|
|
47
|
+
const parsed = parsePushOutput(res.stdout, res.stderr);
|
|
48
|
+
const ok = res.status === 0 && !parsed.rejected;
|
|
49
|
+
const shortMsg = parsed.rejected ? t("BULK_STATUS_REJECTED")
|
|
50
|
+
: parsed.upToDate ? t("BULK_STATUS_UP_TO_DATE")
|
|
51
|
+
: parsed.pushed ? t("BULK_STATUS_PUSHED")
|
|
52
|
+
: ok ? t("BULK_STATUS_OK")
|
|
53
|
+
: t("BULK_STATUS_FAILED");
|
|
54
|
+
console.log((ok ? (parsed.upToDate ? colors.dim : colors.green) : colors.red)(shortMsg));
|
|
55
|
+
|
|
56
|
+
return {
|
|
57
|
+
name, branch, commitsAhead,
|
|
58
|
+
insertions: lines.insertions,
|
|
59
|
+
deletions: lines.deletions,
|
|
60
|
+
filesChanged: lines.filesChanged,
|
|
61
|
+
status: res.status,
|
|
62
|
+
pushed: parsed.pushed,
|
|
63
|
+
upToDate: parsed.upToDate,
|
|
64
|
+
rejected: parsed.rejected,
|
|
65
|
+
mrLink: parsed.mrLink,
|
|
66
|
+
refs: parsed.refs,
|
|
67
|
+
error: res.status !== 0 && !parsed.rejected ? (res.stderr.split("\n")[0] || "").slice(0, 80) : "",
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function row(r) {
|
|
72
|
+
if (r.skipped) {
|
|
73
|
+
return {
|
|
74
|
+
repo: colors.dim(r.name), branch: "", result: colors.dim(t("BULK_STATUS_SKIPPED")),
|
|
75
|
+
commits: "", changes: "", link: "",
|
|
76
|
+
};
|
|
30
77
|
}
|
|
78
|
+
if (r.dryRun) {
|
|
79
|
+
return {
|
|
80
|
+
repo: r.name, branch: r.branch, result: colors.dim(t("BULK_STATUS_DRY_RUN")),
|
|
81
|
+
commits: String(r.commitsAhead || 0), changes: formatChanges(r), link: "",
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
const result =
|
|
85
|
+
r.rejected ? statusBadge("fail", t("BULK_STATUS_REJECTED"))
|
|
86
|
+
: r.upToDate ? statusBadge("skip", t("BULK_STATUS_UP_TO_DATE"))
|
|
87
|
+
: r.error ? statusBadge("fail", t("BULK_STATUS_FAILED"))
|
|
88
|
+
: r.pushed ? statusBadge("ok", t("BULK_STATUS_PUSHED"))
|
|
89
|
+
: statusBadge("ok", t("BULK_STATUS_OK"));
|
|
90
|
+
const commits = r.commitsAhead
|
|
91
|
+
? colors.cyan(String(r.commitsAhead))
|
|
92
|
+
: colors.dim("—");
|
|
93
|
+
const link = r.mrLink ? colors.cyan(r.mrLink) : colors.dim("—");
|
|
94
|
+
return { repo: r.name, branch: r.branch, result, commits, changes: formatChanges(r), link };
|
|
31
95
|
}
|
|
32
96
|
|
|
33
97
|
function printHelp() {
|
|
34
98
|
console.log(`usage: git-push.js [--base-dir BASE_DIR] [--dry-run] [--set-upstream] [manifest]
|
|
35
99
|
|
|
36
100
|
Push local commits for repositories listed in a PAN Stack clone manifest.
|
|
101
|
+
Prints a compact summary table (repo · branch · result · commits · changes · MR/PR link).
|
|
37
102
|
|
|
38
103
|
${printCommonOptions()}
|
|
39
104
|
--set-upstream Use git push -u origin HEAD for new branches`);
|
|
@@ -42,35 +107,79 @@ ${printCommonOptions()}
|
|
|
42
107
|
function main() {
|
|
43
108
|
const args = parseCommonArgs(process.argv.slice(2), {
|
|
44
109
|
defaults: { setUpstream: false },
|
|
45
|
-
handlers: {
|
|
46
|
-
"--set-upstream": (parsed) => {
|
|
47
|
-
parsed.setUpstream = true;
|
|
48
|
-
},
|
|
49
|
-
},
|
|
110
|
+
handlers: { "--set-upstream": (p) => { p.setUpstream = true; } },
|
|
50
111
|
});
|
|
51
|
-
if (args.help) {
|
|
52
|
-
printHelp();
|
|
53
|
-
return;
|
|
54
|
-
}
|
|
112
|
+
if (args.help) { printHelp(); return; }
|
|
55
113
|
|
|
56
|
-
const { repos } = loadManifestRepos({
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
114
|
+
const { repos } = loadManifestRepos({ manifestPath: args.manifest, baseDir: args.baseDir });
|
|
115
|
+
const results = repos.map((r) => pushRepo({ name: r.name, destination: r.destination, dryRun: args.dryRun, setUpstream: args.setUpstream }));
|
|
116
|
+
|
|
117
|
+
const pushed = results.filter((r) => r.pushed);
|
|
118
|
+
const rejected = results.filter((r) => r.rejected);
|
|
119
|
+
const failed = results.filter((r) => r.error);
|
|
120
|
+
const upToDate = results.filter((r) => r.upToDate);
|
|
121
|
+
const withLink = results.filter((r) => r.mrLink);
|
|
122
|
+
// Chỉ đưa vào bảng: pushed / rejected / có MR link / lỗi / dry-run (bỏ up-to-date).
|
|
123
|
+
const shown = results.filter((r) => r.dryRun || r.pushed || r.rejected || r.error || r.mrLink);
|
|
124
|
+
const rows = shown.map(row);
|
|
125
|
+
|
|
126
|
+
const linesOf = (list) => list.reduce(
|
|
127
|
+
(acc, r) => ({
|
|
128
|
+
commits: acc.commits + (r.commitsAhead || 0),
|
|
129
|
+
insertions: acc.insertions + (r.insertions || 0),
|
|
130
|
+
deletions: acc.deletions + (r.deletions || 0),
|
|
131
|
+
}),
|
|
132
|
+
{ commits: 0, insertions: 0, deletions: 0 }
|
|
133
|
+
);
|
|
134
|
+
const pushedLines = linesOf(pushed.length ? pushed : shown.filter((r) => r.dryRun && r.commitsAhead));
|
|
60
135
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
136
|
+
const footer = [];
|
|
137
|
+
footer.push(colors.dim(t("PUSH_FOOTER_COUNTS__TOTAL_PUSHED_UPTODATE_REJECTED_FAILED", {
|
|
138
|
+
total: results.length, pushed: pushed.length, upToDate: upToDate.length,
|
|
139
|
+
rejected: rejected.length, failed: failed.length,
|
|
140
|
+
})));
|
|
141
|
+
if (pushedLines.commits > 0 || pushedLines.insertions > 0 || pushedLines.deletions > 0) {
|
|
142
|
+
footer.push(colors.dim(t("PUSH_FOOTER_LINES__COMMITS_ADD_DEL", {
|
|
143
|
+
commits: pushedLines.commits,
|
|
144
|
+
add: pushedLines.insertions,
|
|
145
|
+
del: pushedLines.deletions,
|
|
146
|
+
})));
|
|
147
|
+
}
|
|
148
|
+
if (withLink.length) {
|
|
149
|
+
footer.push("");
|
|
150
|
+
footer.push(colors.bold(t("PUSH_LINKS_HEADER")));
|
|
151
|
+
for (const r of withLink) {
|
|
152
|
+
footer.push(` ${colors.cyan(r.name)} (${r.branch}): ${r.mrLink}`);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
if (rejected.length) {
|
|
156
|
+
footer.push("");
|
|
157
|
+
footer.push(colors.yellow(t("PUSH_REJECTED_HEADER")));
|
|
158
|
+
for (const r of rejected) footer.push(` ${r.name}`);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
if (rows.length === 0) {
|
|
162
|
+
console.log("");
|
|
163
|
+
console.log(colors.bold(`─── ${t("PUSH_TITLE")} ───`));
|
|
164
|
+
console.log(colors.green(t("PUSH_ALL_UP_TO_DATE__N", { n: results.length })));
|
|
165
|
+
for (const line of footer) console.log(line);
|
|
166
|
+
} else {
|
|
167
|
+
printSummary({
|
|
168
|
+
title: t("PUSH_TITLE"),
|
|
169
|
+
rows,
|
|
170
|
+
columns: bulkColumns([
|
|
171
|
+
{ key: "repo", labelKey: "BULK_COL_REPO" },
|
|
172
|
+
{ key: "branch", labelKey: "BULK_COL_BRANCH" },
|
|
173
|
+
{ key: "result", labelKey: "BULK_COL_RESULT" },
|
|
174
|
+
{ key: "commits", labelKey: "BULK_COL_COMMITS", align: "r" },
|
|
175
|
+
{ key: "changes", labelKey: "BULK_COL_CHANGES" },
|
|
176
|
+
{ key: "link", labelKey: "BULK_COL_LINK" },
|
|
177
|
+
]),
|
|
178
|
+
footerLines: footer,
|
|
67
179
|
});
|
|
68
180
|
}
|
|
69
|
-
}
|
|
70
181
|
|
|
71
|
-
|
|
72
|
-
main();
|
|
73
|
-
} catch (error) {
|
|
74
|
-
console.error(error.message);
|
|
75
|
-
process.exit(1);
|
|
182
|
+
if (rejected.length || failed.length) process.exitCode = 1;
|
|
76
183
|
}
|
|
184
|
+
|
|
185
|
+
try { main(); } catch (error) { console.error(error.message); process.exit(1); }
|