pan-stack-cli 0.2.1 → 0.2.2
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 +37 -731
- package/bin/pan.js +4955 -269
- package/package.json +17 -5
- package/lib/ai-skills.js +0 -829
- package/lib/create.js +0 -105
- package/lib/discover.js +0 -294
- package/lib/git/git-checkout.js +0 -395
- package/lib/git/git-clone.js +0 -206
- package/lib/git/git-fetch-all.js +0 -129
- package/lib/git/git-grv.js +0 -125
- package/lib/git/git-gst.js +0 -165
- package/lib/git/git-mr.js +0 -546
- package/lib/git/git-pull.js +0 -137
- package/lib/git/git-push.js +0 -185
- package/lib/git/git-sync-main.js +0 -334
- package/lib/git/helpers/bulk-report.js +0 -202
- package/lib/git/helpers/cli.js +0 -188
- package/lib/git/helpers/colors.js +0 -19
- package/lib/git/helpers/git.js +0 -136
- package/lib/git/helpers/manifest.js +0 -223
- package/lib/git/helpers/repo-select.js +0 -416
- package/lib/git/helpers/repo-summary.js +0 -161
- package/lib/i18n.js +0 -61
- package/lib/inquirer-theme.js +0 -20
- package/lib/language.js +0 -121
- package/lib/languages.js +0 -14
- package/lib/manifest.js +0 -467
- package/lib/menu-prompt.js +0 -201
- package/lib/menu.js +0 -134
- package/lib/messages.js +0 -647
- package/lib/platform.js +0 -140
- package/lib/run-command.js +0 -334
- package/lib/select.js +0 -0
- package/lib/serve.js +0 -75
- package/lib/skill-registry-client.js +0 -173
- package/lib/stats/aggregate.js +0 -51
- package/lib/stats/identity.js +0 -20
- package/lib/stats/index.js +0 -189
- package/lib/stats/parse.js +0 -46
- package/lib/stats/range.js +0 -38
- package/lib/stats/report.js +0 -264
- package/lib/stats/review.js +0 -157
- package/lib/stats/scan.js +0 -92
- package/lib/stats/serve.js +0 -25
- package/lib/ui/api.js +0 -202
- package/lib/ui/clone.js +0 -115
- package/lib/ui/config.js +0 -182
- package/lib/ui/diff.js +0 -128
- package/lib/ui/export-gate.js +0 -79
- package/lib/ui/gen-react-mobile-code.js +0 -6
- package/lib/ui/gen-react-web-code.js +0 -75
- package/lib/ui/index.js +0 -89
- package/lib/ui/init.js +0 -252
- package/lib/ui/login.js +0 -128
- package/lib/ui/logout.js +0 -22
- package/lib/ui/palettes.js +0 -103
- package/lib/ui/published-html.js +0 -24
- package/lib/ui/sync.js +0 -137
- package/lib/ui/workspace.js +0 -213
package/lib/git/git-push.js
DELETED
|
@@ -1,185 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/*
|
|
3
|
-
* Push local commits for PAN Stack project repositories.
|
|
4
|
-
* Compact summary table: repo · branch · result · commits · MR/PR link.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
const { parseCommonArgs, loadManifestRepos, printCommonOptions } = require("./helpers/cli");
|
|
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
|
-
}
|
|
24
|
-
|
|
25
|
-
function pushRepo({ name, destination, dryRun, setUpstream }) {
|
|
26
|
-
if (!ensureRepoExists({ name, destination })) {
|
|
27
|
-
return { name, skipped: true };
|
|
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 };
|
|
36
|
-
|
|
37
|
-
const gitArgs = ["push"];
|
|
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 };
|
|
43
|
-
}
|
|
44
|
-
|
|
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
|
-
};
|
|
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 };
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
function printHelp() {
|
|
98
|
-
console.log(`usage: git-push.js [--base-dir BASE_DIR] [--dry-run] [--set-upstream] [manifest]
|
|
99
|
-
|
|
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).
|
|
102
|
-
|
|
103
|
-
${printCommonOptions()}
|
|
104
|
-
--set-upstream Use git push -u origin HEAD for new branches`);
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
function main() {
|
|
108
|
-
const args = parseCommonArgs(process.argv.slice(2), {
|
|
109
|
-
defaults: { setUpstream: false },
|
|
110
|
-
handlers: { "--set-upstream": (p) => { p.setUpstream = true; } },
|
|
111
|
-
});
|
|
112
|
-
if (args.help) { printHelp(); return; }
|
|
113
|
-
|
|
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));
|
|
135
|
-
|
|
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,
|
|
179
|
-
});
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
if (rejected.length || failed.length) process.exitCode = 1;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
try { main(); } catch (error) { console.error(error.message); process.exit(1); }
|
package/lib/git/git-sync-main.js
DELETED
|
@@ -1,334 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/*
|
|
3
|
-
* Sync origin/main (or --ref) into the current branch for selected PAN Stack repos.
|
|
4
|
-
*
|
|
5
|
-
* Fetches first, then merge (default) or rebase (--rebase). Interactive repo picker
|
|
6
|
-
* when TTY; pass repo name or "all" as first positional argument.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
const {
|
|
10
|
-
looksLikeManifest,
|
|
11
|
-
loadManifestRepos,
|
|
12
|
-
printCommonOptions,
|
|
13
|
-
} = require("./helpers/cli");
|
|
14
|
-
const colors = require("./helpers/colors");
|
|
15
|
-
const { resolveTargets } = require("./helpers/repo-select");
|
|
16
|
-
const { t } = require("../i18n");
|
|
17
|
-
const {
|
|
18
|
-
ensureRepoExists,
|
|
19
|
-
runGit,
|
|
20
|
-
runGitCapture,
|
|
21
|
-
getCurrentBranch,
|
|
22
|
-
refExists,
|
|
23
|
-
listConflictedFiles,
|
|
24
|
-
isMergeOrRebaseInProgress,
|
|
25
|
-
countDirtyFiles,
|
|
26
|
-
} = require("./helpers/git");
|
|
27
|
-
|
|
28
|
-
const ICON = {
|
|
29
|
-
ok: "✅",
|
|
30
|
-
warn: "⚠️",
|
|
31
|
-
conflict: "⛔",
|
|
32
|
-
skip: "⏭️",
|
|
33
|
-
info: "ℹ️",
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
function printHelp() {
|
|
37
|
-
console.log(`usage: pan sync-main [repo|all] [options] (alias: pan sm)
|
|
38
|
-
|
|
39
|
-
Fetch remote main (or --ref) and merge into the current branch for workspace and/or child repos.
|
|
40
|
-
|
|
41
|
-
positional:
|
|
42
|
-
repo|all Repo name from .project.yaml, or "all". Interactive picker if omitted (TTY).
|
|
43
|
-
|
|
44
|
-
${printCommonOptions()}
|
|
45
|
-
--ref REF Remote-tracking ref to merge (default: origin/main)
|
|
46
|
-
--rebase Use git rebase instead of merge
|
|
47
|
-
--no-workspace Only manifest child repos (skip workspace stack root)`);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
function parseSyncMainArgs(argv) {
|
|
51
|
-
const args = {
|
|
52
|
-
help: false,
|
|
53
|
-
manifest: "",
|
|
54
|
-
baseDir: ".",
|
|
55
|
-
dryRun: false,
|
|
56
|
-
ref: "origin/main",
|
|
57
|
-
rebase: false,
|
|
58
|
-
noWorkspace: false,
|
|
59
|
-
selection: "",
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
for (let index = 0; index < argv.length; index += 1) {
|
|
63
|
-
const arg = argv[index];
|
|
64
|
-
if (arg === "-h" || arg === "--help") {
|
|
65
|
-
args.help = true;
|
|
66
|
-
} else if (arg === "--base-dir") {
|
|
67
|
-
args.baseDir = argv[++index] || ".";
|
|
68
|
-
} else if (arg === "--dry-run") {
|
|
69
|
-
args.dryRun = true;
|
|
70
|
-
} else if (arg === "--ref") {
|
|
71
|
-
args.ref = argv[++index] || "origin/main";
|
|
72
|
-
} else if (arg === "--rebase") {
|
|
73
|
-
args.rebase = true;
|
|
74
|
-
} else if (arg === "--no-workspace") {
|
|
75
|
-
args.noWorkspace = true;
|
|
76
|
-
} else if (!arg.startsWith("-")) {
|
|
77
|
-
if (looksLikeManifest(arg) && !args.manifest) {
|
|
78
|
-
args.manifest = arg;
|
|
79
|
-
} else if (!args.selection) {
|
|
80
|
-
args.selection = arg;
|
|
81
|
-
} else {
|
|
82
|
-
throw new Error(`Unexpected argument: ${arg}`);
|
|
83
|
-
}
|
|
84
|
-
} else {
|
|
85
|
-
throw new Error(`Unexpected argument: ${arg}`);
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
return args;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
function printEarlyConflict({ name, branch, ref, files, mode }) {
|
|
93
|
-
const headline = colors.bold(
|
|
94
|
-
colors.red(`${ICON.conflict} [conflict] ${name} (${branch})`)
|
|
95
|
-
);
|
|
96
|
-
const detail = colors.yellow(
|
|
97
|
-
t("SYNCMAIN_CONFLICT_SHORT__MODE_REF_N", { mode, ref, n: files.length })
|
|
98
|
-
);
|
|
99
|
-
console.log(`${headline} — ${detail}`);
|
|
100
|
-
for (const file of files.slice(0, 8)) {
|
|
101
|
-
console.log(colors.dim(` • ${file}`));
|
|
102
|
-
}
|
|
103
|
-
if (files.length > 8) {
|
|
104
|
-
console.log(
|
|
105
|
-
colors.dim(` ${t("SYNCMAIN_MORE_FILES__N", { n: files.length - 8 })}`)
|
|
106
|
-
);
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
function syncMainRepo({
|
|
111
|
-
name,
|
|
112
|
-
destination,
|
|
113
|
-
dryRun,
|
|
114
|
-
ref,
|
|
115
|
-
rebase,
|
|
116
|
-
}) {
|
|
117
|
-
const result = {
|
|
118
|
-
name,
|
|
119
|
-
destination,
|
|
120
|
-
branch: "",
|
|
121
|
-
status: "skipped",
|
|
122
|
-
message: "",
|
|
123
|
-
conflictFiles: [],
|
|
124
|
-
mode: rebase ? "rebase" : "merge",
|
|
125
|
-
};
|
|
126
|
-
|
|
127
|
-
if (!ensureRepoExists({ name, destination })) {
|
|
128
|
-
result.message = t("SYNCMAIN_MSG_NOT_CLONED");
|
|
129
|
-
return result;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
const branch = getCurrentBranch(destination);
|
|
133
|
-
result.branch = branch || t("SYNCMAIN_DETACHED_LABEL");
|
|
134
|
-
if (!branch) {
|
|
135
|
-
result.message = t("SYNCMAIN_MSG_DETACHED");
|
|
136
|
-
console.log(`${ICON.skip} [skip] ${name}: ${result.message}`);
|
|
137
|
-
return result;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
const inProgress = isMergeOrRebaseInProgress(destination);
|
|
141
|
-
if (inProgress) {
|
|
142
|
-
const files = listConflictedFiles(destination);
|
|
143
|
-
result.status = "conflict";
|
|
144
|
-
result.conflictFiles = files;
|
|
145
|
-
result.message = t("SYNCMAIN_MSG_IN_PROGRESS__MODE", { mode: inProgress });
|
|
146
|
-
printEarlyConflict({ name, branch, ref, files, mode: inProgress });
|
|
147
|
-
return result;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
const dirtyCount = countDirtyFiles(destination);
|
|
151
|
-
if (dirtyCount > 0) {
|
|
152
|
-
console.log(
|
|
153
|
-
`${ICON.warn} ${colors.yellow(t("SYNCMAIN_MSG_DIRTY__NAME_N", { name, n: dirtyCount }))}`
|
|
154
|
-
);
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
if (dryRun) {
|
|
158
|
-
console.log(
|
|
159
|
-
`[dry-run] (in ${destination}) git fetch origin && git ${rebase ? "rebase" : "merge"} ${ref}`
|
|
160
|
-
);
|
|
161
|
-
result.status = "dry-run";
|
|
162
|
-
result.message = "dry-run";
|
|
163
|
-
return result;
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
console.log(`[fetch] ${name}: ${destination}`);
|
|
167
|
-
const fetch = runGitCapture(["fetch", "origin"], { cwd: destination });
|
|
168
|
-
if (fetch.status !== 0) {
|
|
169
|
-
result.status = "error";
|
|
170
|
-
result.message = fetch.stderr || t("SYNCMAIN_MSG_FETCH_FAILED");
|
|
171
|
-
console.log(`${ICON.warn} ${colors.yellow(`[warn] ${name}: ${result.message}`)}`);
|
|
172
|
-
return result;
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
if (!refExists(destination, ref)) {
|
|
176
|
-
result.status = "error";
|
|
177
|
-
result.message = t("SYNCMAIN_MSG_REF_MISSING__REF", { ref });
|
|
178
|
-
console.log(`${ICON.warn} ${colors.yellow(`[warn] ${name}: ${result.message}`)}`);
|
|
179
|
-
return result;
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
const gitArgs = rebase ? ["rebase", ref] : ["merge", "--no-edit", ref];
|
|
183
|
-
console.log(`[${result.mode}] ${name} (${branch}) ← ${ref}`);
|
|
184
|
-
const action = runGit(gitArgs, {
|
|
185
|
-
cwd: destination,
|
|
186
|
-
dryRun: false,
|
|
187
|
-
action: result.mode,
|
|
188
|
-
name,
|
|
189
|
-
});
|
|
190
|
-
|
|
191
|
-
const conflictFiles = listConflictedFiles(destination);
|
|
192
|
-
if (conflictFiles.length > 0 || action !== 0) {
|
|
193
|
-
result.status = "conflict";
|
|
194
|
-
result.conflictFiles =
|
|
195
|
-
conflictFiles.length > 0 ? conflictFiles : [t("SYNCMAIN_MSG_SEE_STATUS")];
|
|
196
|
-
result.message = t("SYNCMAIN_MSG_CONFLICT__MODE", { mode: result.mode });
|
|
197
|
-
printEarlyConflict({
|
|
198
|
-
name,
|
|
199
|
-
branch,
|
|
200
|
-
ref,
|
|
201
|
-
files: result.conflictFiles,
|
|
202
|
-
mode: result.mode,
|
|
203
|
-
});
|
|
204
|
-
return result;
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
result.status = "ok";
|
|
208
|
-
result.message = t("SYNCMAIN_MSG_UPDATED");
|
|
209
|
-
console.log(
|
|
210
|
-
`${ICON.ok} ${colors.green(t("SYNCMAIN_MSG_SYNCED__NAME_BRANCH_REF", { name, branch, ref }))}`
|
|
211
|
-
);
|
|
212
|
-
return result;
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
function printSummary(results, { ref, rebase }) {
|
|
216
|
-
const ok = results.filter((item) => item.status === "ok");
|
|
217
|
-
const conflicts = results.filter((item) => item.status === "conflict");
|
|
218
|
-
const errors = results.filter((item) => item.status === "error");
|
|
219
|
-
const skipped = results.filter(
|
|
220
|
-
(item) => item.status === "skipped" || item.status === "dry-run"
|
|
221
|
-
);
|
|
222
|
-
|
|
223
|
-
console.log("");
|
|
224
|
-
console.log(colors.bold(t("SYNCMAIN_HEADER")));
|
|
225
|
-
console.log(
|
|
226
|
-
`${ICON.info} ${t("SYNCMAIN_INFO__REF_MODE_REPOS", {
|
|
227
|
-
ref, mode: rebase ? "rebase" : "merge", repos: results.length,
|
|
228
|
-
})}`
|
|
229
|
-
);
|
|
230
|
-
console.log(
|
|
231
|
-
t("SYNCMAIN_COUNTS__OK_CONFLICT_ERR_SKIP", {
|
|
232
|
-
ok: ok.length, conflict: conflicts.length, err: errors.length, skip: skipped.length,
|
|
233
|
-
})
|
|
234
|
-
);
|
|
235
|
-
|
|
236
|
-
if (conflicts.length > 0) {
|
|
237
|
-
console.log("");
|
|
238
|
-
console.log(
|
|
239
|
-
colors.bold(colors.red(`${ICON.conflict} ${t("SYNCMAIN_CONFLICT_HEADER")}`))
|
|
240
|
-
);
|
|
241
|
-
for (const item of conflicts) {
|
|
242
|
-
console.log(colors.yellow(`\n ${item.name} (${item.branch})`));
|
|
243
|
-
console.log(colors.dim(` ${item.destination}`));
|
|
244
|
-
for (const file of item.conflictFiles) {
|
|
245
|
-
console.log(` • ${file}`);
|
|
246
|
-
}
|
|
247
|
-
const hint = item.mode === "rebase"
|
|
248
|
-
? t("SYNCMAIN_HINT_REBASE")
|
|
249
|
-
: t("SYNCMAIN_HINT_MERGE");
|
|
250
|
-
console.log(colors.dim(` → ${hint}`));
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
if (errors.length > 0) {
|
|
255
|
-
console.log("");
|
|
256
|
-
console.log(colors.bold(colors.yellow(`${ICON.warn} ${t("SYNCMAIN_ERRORS_HEADER")}`)));
|
|
257
|
-
for (const item of errors) {
|
|
258
|
-
console.log(` • ${item.name}: ${item.message}`);
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
if (conflicts.length > 0) {
|
|
263
|
-
console.log("");
|
|
264
|
-
console.log(
|
|
265
|
-
colors.bold(colors.red(`${ICON.conflict} ${t("SYNCMAIN_END_CONFLICT")}`))
|
|
266
|
-
);
|
|
267
|
-
} else if (errors.length > 0) {
|
|
268
|
-
console.log("");
|
|
269
|
-
console.log(
|
|
270
|
-
colors.bold(colors.yellow(`${ICON.warn} ${t("SYNCMAIN_END_ERROR")}`))
|
|
271
|
-
);
|
|
272
|
-
} else if (ok.length > 0) {
|
|
273
|
-
console.log("");
|
|
274
|
-
console.log(
|
|
275
|
-
colors.green(`${ICON.ok} ${t("SYNCMAIN_END_ALL_SYNCED__REF", { ref })}`)
|
|
276
|
-
);
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
async function main() {
|
|
281
|
-
const argv = process.argv.slice(2);
|
|
282
|
-
const args = parseSyncMainArgs(argv);
|
|
283
|
-
if (args.help) {
|
|
284
|
-
printHelp();
|
|
285
|
-
return;
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
const { repos, workspaceRoot } = loadManifestRepos({
|
|
289
|
-
manifestPath: args.manifest,
|
|
290
|
-
baseDir: args.baseDir,
|
|
291
|
-
});
|
|
292
|
-
|
|
293
|
-
const targets = await resolveTargets({
|
|
294
|
-
repos,
|
|
295
|
-
workspaceRoot,
|
|
296
|
-
includeWorkspace: !args.noWorkspace,
|
|
297
|
-
selection: args.selection,
|
|
298
|
-
});
|
|
299
|
-
|
|
300
|
-
console.log(
|
|
301
|
-
`${ICON.info} ${t("SYNCMAIN_INTRO__COUNT_MODE_REF", {
|
|
302
|
-
count: targets.length,
|
|
303
|
-
mode: args.rebase ? "rebase" : "merge",
|
|
304
|
-
ref: args.ref,
|
|
305
|
-
})}`
|
|
306
|
-
);
|
|
307
|
-
|
|
308
|
-
const results = [];
|
|
309
|
-
for (const target of targets) {
|
|
310
|
-
results.push(
|
|
311
|
-
syncMainRepo({
|
|
312
|
-
name: target.name,
|
|
313
|
-
destination: target.destination,
|
|
314
|
-
dryRun: args.dryRun,
|
|
315
|
-
ref: args.ref,
|
|
316
|
-
rebase: args.rebase,
|
|
317
|
-
})
|
|
318
|
-
);
|
|
319
|
-
console.log("");
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
printSummary(results, { ref: args.ref, rebase: args.rebase });
|
|
323
|
-
|
|
324
|
-
const hasConflict = results.some((item) => item.status === "conflict");
|
|
325
|
-
const hasError = results.some((item) => item.status === "error");
|
|
326
|
-
if (hasConflict || hasError) {
|
|
327
|
-
process.exit(1);
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
main().catch((error) => {
|
|
332
|
-
console.error(error.message);
|
|
333
|
-
process.exit(1);
|
|
334
|
-
});
|