pan-stack-cli 0.1.5 → 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 -183
- package/bin/pan.js +4992 -123
- package/package.json +24 -5
- package/lib/create.js +0 -87
- package/lib/discover.js +0 -236
- package/lib/git/git-clone.js +0 -71
- package/lib/git/git-fetch-all.js +0 -62
- package/lib/git/git-grv.js +0 -94
- package/lib/git/git-gst.js +0 -94
- package/lib/git/git-pull.js +0 -76
- package/lib/git/git-push.js +0 -76
- package/lib/git/git-sync-main.js +0 -320
- package/lib/git/helpers/cli.js +0 -153
- package/lib/git/helpers/colors.js +0 -19
- package/lib/git/helpers/git.js +0 -136
- package/lib/git/helpers/manifest.js +0 -130
- package/lib/git/helpers/repo-select.js +0 -97
- package/lib/manifest.js +0 -366
- package/lib/platform.js +0 -140
- package/lib/run-command.js +0 -213
- 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
DELETED
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/*
|
|
3
|
-
* Pull latest changes for PAN Stack project repositories from a clone manifest.
|
|
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.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
const { parseCommonArgs, loadManifestRepos, printCommonOptions } = require("./helpers/cli");
|
|
10
|
-
const { ensureRepoExists, runGit } = require("./helpers/git");
|
|
11
|
-
|
|
12
|
-
function pullRepo({ name, destination, dryRun, rebase }) {
|
|
13
|
-
if (!ensureRepoExists({ name, destination })) {
|
|
14
|
-
return;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
const gitArgs = ["pull"];
|
|
18
|
-
if (rebase) {
|
|
19
|
-
gitArgs.push("--rebase");
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
const status = runGit(gitArgs, {
|
|
23
|
-
cwd: destination,
|
|
24
|
-
dryRun,
|
|
25
|
-
action: "pull",
|
|
26
|
-
name,
|
|
27
|
-
});
|
|
28
|
-
if (status !== 0) {
|
|
29
|
-
console.log(`[warn] ${name}: pull failed, check branch state or remote access`);
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
function printHelp() {
|
|
34
|
-
console.log(`usage: git-pull.js [--base-dir BASE_DIR] [--dry-run] [--rebase] [manifest]
|
|
35
|
-
|
|
36
|
-
Pull latest changes for repositories listed in a PAN Stack clone manifest.
|
|
37
|
-
|
|
38
|
-
${printCommonOptions()}
|
|
39
|
-
--rebase Use git pull --rebase`);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
function main() {
|
|
43
|
-
const args = parseCommonArgs(process.argv.slice(2), {
|
|
44
|
-
defaults: { rebase: false },
|
|
45
|
-
handlers: {
|
|
46
|
-
"--rebase": (parsed) => {
|
|
47
|
-
parsed.rebase = true;
|
|
48
|
-
},
|
|
49
|
-
},
|
|
50
|
-
});
|
|
51
|
-
if (args.help) {
|
|
52
|
-
printHelp();
|
|
53
|
-
return;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
const { repos } = loadManifestRepos({
|
|
57
|
-
manifestPath: args.manifest,
|
|
58
|
-
baseDir: args.baseDir,
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
for (const repo of repos) {
|
|
62
|
-
pullRepo({
|
|
63
|
-
name: repo.name,
|
|
64
|
-
destination: repo.destination,
|
|
65
|
-
dryRun: args.dryRun,
|
|
66
|
-
rebase: args.rebase,
|
|
67
|
-
});
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
try {
|
|
72
|
-
main();
|
|
73
|
-
} catch (error) {
|
|
74
|
-
console.error(error.message);
|
|
75
|
-
process.exit(1);
|
|
76
|
-
}
|
package/lib/git/git-push.js
DELETED
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/*
|
|
3
|
-
* Push local commits for PAN Stack project repositories from a clone manifest.
|
|
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.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
const { parseCommonArgs, loadManifestRepos, printCommonOptions } = require("./helpers/cli");
|
|
10
|
-
const { ensureRepoExists, runGit } = require("./helpers/git");
|
|
11
|
-
|
|
12
|
-
function pushRepo({ name, destination, dryRun, setUpstream }) {
|
|
13
|
-
if (!ensureRepoExists({ name, destination })) {
|
|
14
|
-
return;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
const gitArgs = ["push"];
|
|
18
|
-
if (setUpstream) {
|
|
19
|
-
gitArgs.push("-u", "origin", "HEAD");
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
const status = runGit(gitArgs, {
|
|
23
|
-
cwd: destination,
|
|
24
|
-
dryRun,
|
|
25
|
-
action: "push",
|
|
26
|
-
name,
|
|
27
|
-
});
|
|
28
|
-
if (status !== 0) {
|
|
29
|
-
console.log(`[warn] ${name}: push failed, check branch state or remote access`);
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
function printHelp() {
|
|
34
|
-
console.log(`usage: git-push.js [--base-dir BASE_DIR] [--dry-run] [--set-upstream] [manifest]
|
|
35
|
-
|
|
36
|
-
Push local commits for repositories listed in a PAN Stack clone manifest.
|
|
37
|
-
|
|
38
|
-
${printCommonOptions()}
|
|
39
|
-
--set-upstream Use git push -u origin HEAD for new branches`);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
function main() {
|
|
43
|
-
const args = parseCommonArgs(process.argv.slice(2), {
|
|
44
|
-
defaults: { setUpstream: false },
|
|
45
|
-
handlers: {
|
|
46
|
-
"--set-upstream": (parsed) => {
|
|
47
|
-
parsed.setUpstream = true;
|
|
48
|
-
},
|
|
49
|
-
},
|
|
50
|
-
});
|
|
51
|
-
if (args.help) {
|
|
52
|
-
printHelp();
|
|
53
|
-
return;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
const { repos } = loadManifestRepos({
|
|
57
|
-
manifestPath: args.manifest,
|
|
58
|
-
baseDir: args.baseDir,
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
for (const repo of repos) {
|
|
62
|
-
pushRepo({
|
|
63
|
-
name: repo.name,
|
|
64
|
-
destination: repo.destination,
|
|
65
|
-
dryRun: args.dryRun,
|
|
66
|
-
setUpstream: args.setUpstream,
|
|
67
|
-
});
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
try {
|
|
72
|
-
main();
|
|
73
|
-
} catch (error) {
|
|
74
|
-
console.error(error.message);
|
|
75
|
-
process.exit(1);
|
|
76
|
-
}
|
package/lib/git/git-sync-main.js
DELETED
|
@@ -1,320 +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 {
|
|
17
|
-
ensureRepoExists,
|
|
18
|
-
runGit,
|
|
19
|
-
runGitCapture,
|
|
20
|
-
getCurrentBranch,
|
|
21
|
-
refExists,
|
|
22
|
-
listConflictedFiles,
|
|
23
|
-
isMergeOrRebaseInProgress,
|
|
24
|
-
countDirtyFiles,
|
|
25
|
-
} = require("./helpers/git");
|
|
26
|
-
|
|
27
|
-
const ICON = {
|
|
28
|
-
ok: "✅",
|
|
29
|
-
warn: "⚠️",
|
|
30
|
-
conflict: "⛔",
|
|
31
|
-
skip: "⏭️",
|
|
32
|
-
info: "ℹ️",
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
function printHelp() {
|
|
36
|
-
console.log(`usage: pan sync-main [repo|all] [options] (alias: pan sm)
|
|
37
|
-
|
|
38
|
-
Fetch remote main (or --ref) and merge into the current branch for workspace and/or child repos.
|
|
39
|
-
|
|
40
|
-
positional:
|
|
41
|
-
repo|all Repo name from .project.yaml, or "all". Interactive picker if omitted (TTY).
|
|
42
|
-
|
|
43
|
-
${printCommonOptions()}
|
|
44
|
-
--ref REF Remote-tracking ref to merge (default: origin/main)
|
|
45
|
-
--rebase Use git rebase instead of merge
|
|
46
|
-
--no-workspace Only manifest child repos (skip workspace stack root)`);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
function parseSyncMainArgs(argv) {
|
|
50
|
-
const args = {
|
|
51
|
-
help: false,
|
|
52
|
-
manifest: "",
|
|
53
|
-
baseDir: ".",
|
|
54
|
-
dryRun: false,
|
|
55
|
-
ref: "origin/main",
|
|
56
|
-
rebase: false,
|
|
57
|
-
noWorkspace: false,
|
|
58
|
-
selection: "",
|
|
59
|
-
};
|
|
60
|
-
|
|
61
|
-
for (let index = 0; index < argv.length; index += 1) {
|
|
62
|
-
const arg = argv[index];
|
|
63
|
-
if (arg === "-h" || arg === "--help") {
|
|
64
|
-
args.help = true;
|
|
65
|
-
} else if (arg === "--base-dir") {
|
|
66
|
-
args.baseDir = argv[++index] || ".";
|
|
67
|
-
} else if (arg === "--dry-run") {
|
|
68
|
-
args.dryRun = true;
|
|
69
|
-
} else if (arg === "--ref") {
|
|
70
|
-
args.ref = argv[++index] || "origin/main";
|
|
71
|
-
} else if (arg === "--rebase") {
|
|
72
|
-
args.rebase = true;
|
|
73
|
-
} else if (arg === "--no-workspace") {
|
|
74
|
-
args.noWorkspace = true;
|
|
75
|
-
} else if (!arg.startsWith("-")) {
|
|
76
|
-
if (looksLikeManifest(arg) && !args.manifest) {
|
|
77
|
-
args.manifest = arg;
|
|
78
|
-
} else if (!args.selection) {
|
|
79
|
-
args.selection = arg;
|
|
80
|
-
} else {
|
|
81
|
-
throw new Error(`Unexpected argument: ${arg}`);
|
|
82
|
-
}
|
|
83
|
-
} else {
|
|
84
|
-
throw new Error(`Unexpected argument: ${arg}`);
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
return args;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
function printEarlyConflict({ name, branch, ref, files, mode }) {
|
|
92
|
-
const headline = colors.bold(
|
|
93
|
-
colors.red(`${ICON.conflict} [conflict] ${name} (${branch})`)
|
|
94
|
-
);
|
|
95
|
-
const detail = colors.yellow(
|
|
96
|
-
`${mode} ${ref} thất bại — ${files.length} file conflict`
|
|
97
|
-
);
|
|
98
|
-
console.log(`${headline} — ${detail}`);
|
|
99
|
-
for (const file of files.slice(0, 8)) {
|
|
100
|
-
console.log(colors.dim(` • ${file}`));
|
|
101
|
-
}
|
|
102
|
-
if (files.length > 8) {
|
|
103
|
-
console.log(colors.dim(` … và ${files.length - 8} file khác`));
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
function syncMainRepo({
|
|
108
|
-
name,
|
|
109
|
-
destination,
|
|
110
|
-
dryRun,
|
|
111
|
-
ref,
|
|
112
|
-
rebase,
|
|
113
|
-
}) {
|
|
114
|
-
const result = {
|
|
115
|
-
name,
|
|
116
|
-
destination,
|
|
117
|
-
branch: "",
|
|
118
|
-
status: "skipped",
|
|
119
|
-
message: "",
|
|
120
|
-
conflictFiles: [],
|
|
121
|
-
mode: rebase ? "rebase" : "merge",
|
|
122
|
-
};
|
|
123
|
-
|
|
124
|
-
if (!ensureRepoExists({ name, destination })) {
|
|
125
|
-
result.message = "repo không tồn tại hoặc chưa clone";
|
|
126
|
-
return result;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
const branch = getCurrentBranch(destination);
|
|
130
|
-
result.branch = branch || "(detached HEAD)";
|
|
131
|
-
if (!branch) {
|
|
132
|
-
result.message = "đang detached HEAD — bỏ qua";
|
|
133
|
-
console.log(`${ICON.skip} [skip] ${name}: ${result.message}`);
|
|
134
|
-
return result;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
const inProgress = isMergeOrRebaseInProgress(destination);
|
|
138
|
-
if (inProgress) {
|
|
139
|
-
const files = listConflictedFiles(destination);
|
|
140
|
-
result.status = "conflict";
|
|
141
|
-
result.conflictFiles = files;
|
|
142
|
-
result.message = `đang dở ${inProgress} — cần resolve trước`;
|
|
143
|
-
printEarlyConflict({ name, branch, ref, files, mode: inProgress });
|
|
144
|
-
return result;
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
const dirtyCount = countDirtyFiles(destination);
|
|
148
|
-
if (dirtyCount > 0) {
|
|
149
|
-
console.log(
|
|
150
|
-
`${ICON.warn} ${colors.yellow(`[warn] ${name}: working tree có ${dirtyCount} thay đổi chưa commit`)}`
|
|
151
|
-
);
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
if (dryRun) {
|
|
155
|
-
console.log(
|
|
156
|
-
`[dry-run] (in ${destination}) git fetch origin && git ${rebase ? "rebase" : "merge"} ${ref}`
|
|
157
|
-
);
|
|
158
|
-
result.status = "dry-run";
|
|
159
|
-
result.message = "dry-run";
|
|
160
|
-
return result;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
console.log(`[fetch] ${name}: ${destination}`);
|
|
164
|
-
const fetch = runGitCapture(["fetch", "origin"], { cwd: destination });
|
|
165
|
-
if (fetch.status !== 0) {
|
|
166
|
-
result.status = "error";
|
|
167
|
-
result.message = fetch.stderr || "git fetch origin thất bại";
|
|
168
|
-
console.log(`${ICON.warn} ${colors.yellow(`[warn] ${name}: ${result.message}`)}`);
|
|
169
|
-
return result;
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
if (!refExists(destination, ref)) {
|
|
173
|
-
result.status = "error";
|
|
174
|
-
result.message = `không tìm thấy ref ${ref} (chạy fetch trước?)`;
|
|
175
|
-
console.log(`${ICON.warn} ${colors.yellow(`[warn] ${name}: ${result.message}`)}`);
|
|
176
|
-
return result;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
const gitArgs = rebase ? ["rebase", ref] : ["merge", "--no-edit", ref];
|
|
180
|
-
console.log(`[${result.mode}] ${name} (${branch}) ← ${ref}`);
|
|
181
|
-
const action = runGit(gitArgs, {
|
|
182
|
-
cwd: destination,
|
|
183
|
-
dryRun: false,
|
|
184
|
-
action: result.mode,
|
|
185
|
-
name,
|
|
186
|
-
});
|
|
187
|
-
|
|
188
|
-
const conflictFiles = listConflictedFiles(destination);
|
|
189
|
-
if (conflictFiles.length > 0 || action !== 0) {
|
|
190
|
-
result.status = "conflict";
|
|
191
|
-
result.conflictFiles = conflictFiles.length > 0 ? conflictFiles : ["(xem git status)"];
|
|
192
|
-
result.message = `${result.mode} conflict`;
|
|
193
|
-
printEarlyConflict({
|
|
194
|
-
name,
|
|
195
|
-
branch,
|
|
196
|
-
ref,
|
|
197
|
-
files: result.conflictFiles,
|
|
198
|
-
mode: result.mode,
|
|
199
|
-
});
|
|
200
|
-
return result;
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
result.status = "ok";
|
|
204
|
-
result.message = "đã cập nhật";
|
|
205
|
-
console.log(`${ICON.ok} ${colors.green(`[ok] ${name}: ${branch} đã sync ${ref}`)}`);
|
|
206
|
-
return result;
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
function printSummary(results, { ref, rebase }) {
|
|
210
|
-
const ok = results.filter((item) => item.status === "ok");
|
|
211
|
-
const conflicts = results.filter((item) => item.status === "conflict");
|
|
212
|
-
const errors = results.filter((item) => item.status === "error");
|
|
213
|
-
const skipped = results.filter(
|
|
214
|
-
(item) => item.status === "skipped" || item.status === "dry-run"
|
|
215
|
-
);
|
|
216
|
-
|
|
217
|
-
console.log("");
|
|
218
|
-
console.log(colors.bold("─── sync-main summary ───"));
|
|
219
|
-
console.log(
|
|
220
|
-
`${ICON.info} ref: ${ref} · mode: ${rebase ? "rebase" : "merge"} · repos: ${results.length}`
|
|
221
|
-
);
|
|
222
|
-
console.log(
|
|
223
|
-
` ${ICON.ok} ok: ${ok.length} · ${ICON.conflict} conflict: ${conflicts.length} · ${ICON.warn} error: ${errors.length} · ${ICON.skip} skip: ${skipped.length}`
|
|
224
|
-
);
|
|
225
|
-
|
|
226
|
-
if (conflicts.length > 0) {
|
|
227
|
-
console.log("");
|
|
228
|
-
console.log(colors.bold(colors.red(`${ICON.conflict} CONFLICT — cần xử lý thủ công`)));
|
|
229
|
-
for (const item of conflicts) {
|
|
230
|
-
console.log(colors.yellow(`\n ${item.name} (${item.branch})`));
|
|
231
|
-
console.log(colors.dim(` ${item.destination}`));
|
|
232
|
-
for (const file of item.conflictFiles) {
|
|
233
|
-
console.log(` • ${file}`);
|
|
234
|
-
}
|
|
235
|
-
const hint = item.mode === "rebase"
|
|
236
|
-
? "git add <file>… && git rebase --continue (hoặc git rebase --abort)"
|
|
237
|
-
: "git add <file>… && git commit";
|
|
238
|
-
console.log(colors.dim(` → ${hint}`));
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
if (errors.length > 0) {
|
|
243
|
-
console.log("");
|
|
244
|
-
console.log(colors.bold(colors.yellow(`${ICON.warn} LỖI`)));
|
|
245
|
-
for (const item of errors) {
|
|
246
|
-
console.log(` • ${item.name}: ${item.message}`);
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
if (conflicts.length > 0) {
|
|
251
|
-
console.log("");
|
|
252
|
-
console.log(
|
|
253
|
-
colors.bold(
|
|
254
|
-
colors.red(
|
|
255
|
-
`${ICON.conflict} Kết thúc với conflict — resolve từng repo ở trên rồi commit/rebase --continue`
|
|
256
|
-
)
|
|
257
|
-
)
|
|
258
|
-
);
|
|
259
|
-
} else if (errors.length > 0) {
|
|
260
|
-
console.log("");
|
|
261
|
-
console.log(
|
|
262
|
-
colors.bold(colors.yellow(`${ICON.warn} Kết thúc với lỗi — xem chi tiết ở trên`))
|
|
263
|
-
);
|
|
264
|
-
} else if (ok.length > 0) {
|
|
265
|
-
console.log("");
|
|
266
|
-
console.log(colors.green(`${ICON.ok} Tất cả repo đã chọn đã sync ${ref}`));
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
async function main() {
|
|
271
|
-
const argv = process.argv.slice(2);
|
|
272
|
-
const args = parseSyncMainArgs(argv);
|
|
273
|
-
if (args.help) {
|
|
274
|
-
printHelp();
|
|
275
|
-
return;
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
const { repos, workspaceRoot } = loadManifestRepos({
|
|
279
|
-
manifestPath: args.manifest,
|
|
280
|
-
baseDir: args.baseDir,
|
|
281
|
-
});
|
|
282
|
-
|
|
283
|
-
const targets = await resolveTargets({
|
|
284
|
-
repos,
|
|
285
|
-
workspaceRoot,
|
|
286
|
-
includeWorkspace: !args.noWorkspace,
|
|
287
|
-
selection: args.selection,
|
|
288
|
-
});
|
|
289
|
-
|
|
290
|
-
console.log(
|
|
291
|
-
`${ICON.info} sync-main: ${targets.length} repo · ${args.rebase ? "rebase" : "merge"} ← ${args.ref}`
|
|
292
|
-
);
|
|
293
|
-
|
|
294
|
-
const results = [];
|
|
295
|
-
for (const target of targets) {
|
|
296
|
-
results.push(
|
|
297
|
-
syncMainRepo({
|
|
298
|
-
name: target.name,
|
|
299
|
-
destination: target.destination,
|
|
300
|
-
dryRun: args.dryRun,
|
|
301
|
-
ref: args.ref,
|
|
302
|
-
rebase: args.rebase,
|
|
303
|
-
})
|
|
304
|
-
);
|
|
305
|
-
console.log("");
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
printSummary(results, { ref: args.ref, rebase: args.rebase });
|
|
309
|
-
|
|
310
|
-
const hasConflict = results.some((item) => item.status === "conflict");
|
|
311
|
-
const hasError = results.some((item) => item.status === "error");
|
|
312
|
-
if (hasConflict || hasError) {
|
|
313
|
-
process.exit(1);
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
main().catch((error) => {
|
|
318
|
-
console.error(error.message);
|
|
319
|
-
process.exit(1);
|
|
320
|
-
});
|
package/lib/git/helpers/cli.js
DELETED
|
@@ -1,153 +0,0 @@
|
|
|
1
|
-
const path = require("path");
|
|
2
|
-
const fs = require("fs");
|
|
3
|
-
const { findNearestProjectManifest } = require("../../discover");
|
|
4
|
-
const { parseManifest, manifestRepos, validateRepo } = require("./manifest");
|
|
5
|
-
|
|
6
|
-
function parseCommonArgs(argv, extraFlags = {}) {
|
|
7
|
-
const args = {
|
|
8
|
-
manifest: "",
|
|
9
|
-
baseDir: ".",
|
|
10
|
-
dryRun: false,
|
|
11
|
-
...extraFlags.defaults,
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
for (let index = 0; index < argv.length; index += 1) {
|
|
15
|
-
const arg = argv[index];
|
|
16
|
-
if (arg === "-h" || arg === "--help") {
|
|
17
|
-
args.help = true;
|
|
18
|
-
} else if (arg === "--base-dir") {
|
|
19
|
-
args.baseDir = argv[++index] || ".";
|
|
20
|
-
} else if (arg === "--dry-run") {
|
|
21
|
-
args.dryRun = true;
|
|
22
|
-
} else if (extraFlags.handlers && extraFlags.handlers[arg]) {
|
|
23
|
-
extraFlags.handlers[arg](args, argv, () => {
|
|
24
|
-
index += 1;
|
|
25
|
-
return argv[index];
|
|
26
|
-
});
|
|
27
|
-
} else if (extraFlags.passthrough) {
|
|
28
|
-
if (!args.manifest && looksLikeManifest(arg)) {
|
|
29
|
-
args.manifest = arg;
|
|
30
|
-
} else {
|
|
31
|
-
args.gitArgs = args.gitArgs || [];
|
|
32
|
-
args.gitArgs.push(arg);
|
|
33
|
-
}
|
|
34
|
-
} else if (!args.manifest) {
|
|
35
|
-
args.manifest = arg;
|
|
36
|
-
} else {
|
|
37
|
-
throw new Error(`Unexpected argument: ${arg}`);
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
return args;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
function looksLikeManifest(arg) {
|
|
45
|
-
if (arg.endsWith(".yaml") || arg.endsWith(".yml")) {
|
|
46
|
-
return true;
|
|
47
|
-
}
|
|
48
|
-
try {
|
|
49
|
-
return fs.existsSync(arg) && fs.statSync(arg).isFile();
|
|
50
|
-
} catch {
|
|
51
|
-
return false;
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
function resolveProjectWorkspace({
|
|
56
|
-
manifestPath = "",
|
|
57
|
-
baseDir = ".",
|
|
58
|
-
startDir = process.cwd(),
|
|
59
|
-
}) {
|
|
60
|
-
const manifestName = manifestPath || ".project.yaml";
|
|
61
|
-
const cwdManifest = path.isAbsolute(manifestName)
|
|
62
|
-
? manifestName
|
|
63
|
-
: path.resolve(startDir, manifestName);
|
|
64
|
-
|
|
65
|
-
let resolvedManifest = "";
|
|
66
|
-
let usedWalkUp = false;
|
|
67
|
-
|
|
68
|
-
if (fs.existsSync(cwdManifest)) {
|
|
69
|
-
resolvedManifest = cwdManifest;
|
|
70
|
-
} else {
|
|
71
|
-
const found = findNearestProjectManifest(startDir);
|
|
72
|
-
if (!found) {
|
|
73
|
-
const label = manifestPath || ".project.yaml";
|
|
74
|
-
throw new Error(
|
|
75
|
-
`Manifest not found: ${label} (walked up from ${startDir})`
|
|
76
|
-
);
|
|
77
|
-
}
|
|
78
|
-
resolvedManifest = found;
|
|
79
|
-
usedWalkUp = true;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
const workspaceRoot = path.dirname(resolvedManifest);
|
|
83
|
-
let resolvedBaseDir;
|
|
84
|
-
if (baseDir === ".") {
|
|
85
|
-
resolvedBaseDir = usedWalkUp ? workspaceRoot : path.resolve(startDir, ".");
|
|
86
|
-
} else if (path.isAbsolute(baseDir)) {
|
|
87
|
-
resolvedBaseDir = baseDir;
|
|
88
|
-
} else {
|
|
89
|
-
resolvedBaseDir = path.resolve(startDir, baseDir);
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
return {
|
|
93
|
-
manifestPath: resolvedManifest,
|
|
94
|
-
baseDir: resolvedBaseDir,
|
|
95
|
-
workspaceRoot,
|
|
96
|
-
};
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
function loadManifestRepos({ manifestPath, baseDir, startDir = process.cwd() }) {
|
|
100
|
-
const workspace = resolveProjectWorkspace({ manifestPath, baseDir, startDir });
|
|
101
|
-
const { manifestPath: resolvedManifest, baseDir: resolvedBaseDir, workspaceRoot } =
|
|
102
|
-
workspace;
|
|
103
|
-
|
|
104
|
-
const data = parseManifest(resolvedManifest);
|
|
105
|
-
const repos = manifestRepos(data);
|
|
106
|
-
if (repos.length === 0) {
|
|
107
|
-
throw new Error("No repositories found in manifest.");
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
const results = [];
|
|
111
|
-
for (const repo of repos) {
|
|
112
|
-
try {
|
|
113
|
-
const { name, url, repoPath } = validateRepo(repo);
|
|
114
|
-
results.push({
|
|
115
|
-
name,
|
|
116
|
-
url,
|
|
117
|
-
repoPath,
|
|
118
|
-
destination: path.resolve(resolvedBaseDir, repoPath),
|
|
119
|
-
});
|
|
120
|
-
} catch (error) {
|
|
121
|
-
console.log(`[warn] ${error.message}`);
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
if (results.length === 0) {
|
|
126
|
-
throw new Error("No valid repositories found in manifest.");
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
return {
|
|
130
|
-
repos: results,
|
|
131
|
-
manifestPath: resolvedManifest,
|
|
132
|
-
baseDir: resolvedBaseDir,
|
|
133
|
-
workspaceRoot,
|
|
134
|
-
};
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
function printCommonOptions() {
|
|
138
|
-
return `positional arguments:
|
|
139
|
-
manifest Path to clone manifest YAML. Default: .project.yaml
|
|
140
|
-
|
|
141
|
-
options:
|
|
142
|
-
-h, --help show this help message and exit
|
|
143
|
-
--base-dir BASE_DIR Base directory for relative repo paths. Default: directory containing .project.yaml (walk-up from cwd when needed).
|
|
144
|
-
--dry-run Print commands only`;
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
module.exports = {
|
|
148
|
-
parseCommonArgs,
|
|
149
|
-
looksLikeManifest,
|
|
150
|
-
resolveProjectWorkspace,
|
|
151
|
-
loadManifestRepos,
|
|
152
|
-
printCommonOptions,
|
|
153
|
-
};
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
const supportsColor =
|
|
2
|
-
Boolean(process.stdout.isTTY) && !process.env.NO_COLOR && process.env.FORCE_COLOR !== "0";
|
|
3
|
-
|
|
4
|
-
function wrap(code, text) {
|
|
5
|
-
if (!supportsColor || text == null) {
|
|
6
|
-
return String(text ?? "");
|
|
7
|
-
}
|
|
8
|
-
return `\u001b[${code}m${text}\u001b[0m`;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
module.exports = {
|
|
12
|
-
supportsColor,
|
|
13
|
-
bold: (text) => wrap("1", text),
|
|
14
|
-
red: (text) => wrap("31", text),
|
|
15
|
-
green: (text) => wrap("32", text),
|
|
16
|
-
yellow: (text) => wrap("33", text),
|
|
17
|
-
cyan: (text) => wrap("36", text),
|
|
18
|
-
dim: (text) => wrap("2", text),
|
|
19
|
-
};
|