vag_tools 0.0.3
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 +8 -0
- package/dist/vag_cli.d.ts +1 -0
- package/dist/vag_cli.js +839 -0
- package/dist/vagg_server.d.ts +1 -0
- package/dist/vagg_server.js +5 -0
- package/package.json +97 -0
package/README.md
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
package/dist/vag_cli.js
ADDED
|
@@ -0,0 +1,839 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/vag_cli.ts
|
|
4
|
+
import yargs from "yargs";
|
|
5
|
+
import { hideBin } from "yargs/helpers";
|
|
6
|
+
|
|
7
|
+
// src/vag_core.ts
|
|
8
|
+
import path from "node:path";
|
|
9
|
+
import fs from "node:fs";
|
|
10
|
+
import fsp from "node:fs/promises";
|
|
11
|
+
import fse from "fs-extra";
|
|
12
|
+
import YAML from "yaml";
|
|
13
|
+
import { simpleGit } from "simple-git";
|
|
14
|
+
|
|
15
|
+
// package.json
|
|
16
|
+
var package_default = {
|
|
17
|
+
name: "vag_tools",
|
|
18
|
+
version: "0.0.3",
|
|
19
|
+
description: "api and cli for managing sub-git-repositories",
|
|
20
|
+
type: "module",
|
|
21
|
+
main: "./dist/vag_api.js",
|
|
22
|
+
types: "./dist/vag_api.d.ts",
|
|
23
|
+
bin: {
|
|
24
|
+
vag: "dist/vag_cli.js",
|
|
25
|
+
vagg: "dist/vagg_server.js"
|
|
26
|
+
},
|
|
27
|
+
files: [
|
|
28
|
+
"dist/",
|
|
29
|
+
"!dist/**/*.map",
|
|
30
|
+
"!dist/**/*.test.*",
|
|
31
|
+
"!dist/**/*.spec.*"
|
|
32
|
+
],
|
|
33
|
+
engines: {
|
|
34
|
+
node: ">=20.10.0"
|
|
35
|
+
},
|
|
36
|
+
tsup: {
|
|
37
|
+
entry: [
|
|
38
|
+
"src/vag_cli.ts",
|
|
39
|
+
"src/vagg_server.ts"
|
|
40
|
+
],
|
|
41
|
+
format: "esm",
|
|
42
|
+
splitting: false,
|
|
43
|
+
dts: true,
|
|
44
|
+
sourcemap: true,
|
|
45
|
+
clean: true
|
|
46
|
+
},
|
|
47
|
+
scripts: {
|
|
48
|
+
dev: "tsup --watch",
|
|
49
|
+
build: "tsup",
|
|
50
|
+
check: "tsc --noEmit",
|
|
51
|
+
pretty: "prettier --check .",
|
|
52
|
+
format: "prettier --write .",
|
|
53
|
+
lint: "eslint .",
|
|
54
|
+
"test:unit": "vitest",
|
|
55
|
+
"test:unit:once": "vitest --run",
|
|
56
|
+
ci: "run-s check build pretty lint",
|
|
57
|
+
clean: "shx rm -fr build dist tmp",
|
|
58
|
+
"test:basic": "node build/tests/vag.test.js",
|
|
59
|
+
"test:cli": "tests/vag-cli.test.sh",
|
|
60
|
+
"test:coverage": "vitest run --coverage",
|
|
61
|
+
test: "run-s test:basic test:cli test:vitest",
|
|
62
|
+
"dist:build": "tsc --build tsconfig.dist.json",
|
|
63
|
+
clean_all: "run-s clean"
|
|
64
|
+
},
|
|
65
|
+
repository: {
|
|
66
|
+
type: "git",
|
|
67
|
+
url: "git+https://github.com/charlyoleg2/vag.git"
|
|
68
|
+
},
|
|
69
|
+
keywords: [
|
|
70
|
+
"git",
|
|
71
|
+
"sub-repo",
|
|
72
|
+
"multi-repo",
|
|
73
|
+
"submodule",
|
|
74
|
+
"subtree",
|
|
75
|
+
"vcs",
|
|
76
|
+
"vcstool",
|
|
77
|
+
"repositories"
|
|
78
|
+
],
|
|
79
|
+
author: "charlyoleg",
|
|
80
|
+
license: "ISC",
|
|
81
|
+
bugs: {
|
|
82
|
+
url: "https://github.com/charlyoleg2/vag/issues"
|
|
83
|
+
},
|
|
84
|
+
homepage: "https://github.com/charlyoleg2/vag#readme",
|
|
85
|
+
dependencies: {
|
|
86
|
+
"fs-extra": "^11.2.0",
|
|
87
|
+
"simple-git": "^3.22.0",
|
|
88
|
+
yaml: "^2.3.4",
|
|
89
|
+
yargs: "^17.7.2"
|
|
90
|
+
},
|
|
91
|
+
devDependencies: {
|
|
92
|
+
"@types/fs-extra": "^11.0.4",
|
|
93
|
+
"@types/node": "^20.11.19",
|
|
94
|
+
"@types/yargs": "^17.0.32",
|
|
95
|
+
eslint: "^8.56.0",
|
|
96
|
+
"npm-run-all": "^4.1.5",
|
|
97
|
+
prettier: "^3.2.5",
|
|
98
|
+
shx: "^0.3.4",
|
|
99
|
+
tsup: "^8.0.2",
|
|
100
|
+
typescript: "^5.3.3",
|
|
101
|
+
"typescript-eslint": "^7.0.1",
|
|
102
|
+
vitest: "^1.2.2"
|
|
103
|
+
},
|
|
104
|
+
prettier: {
|
|
105
|
+
useTabs: true,
|
|
106
|
+
singleQuote: true,
|
|
107
|
+
trailingComma: "none",
|
|
108
|
+
printWidth: 100,
|
|
109
|
+
plugins: [],
|
|
110
|
+
overrides: []
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
// src/vag_core.ts
|
|
115
|
+
var vag_version_short = package_default.version;
|
|
116
|
+
var regex_pointSlash = /^\.\//;
|
|
117
|
+
var regex_trailingSlash = /\/$/;
|
|
118
|
+
async function isGitRepo(pathDir2) {
|
|
119
|
+
let isRepo = false;
|
|
120
|
+
let isRepoCandidate = false;
|
|
121
|
+
const subdirs = await fse.readdir(pathDir2, { withFileTypes: true });
|
|
122
|
+
for (const subitem of subdirs) {
|
|
123
|
+
if (subitem.isDirectory() && subitem.name === ".git") {
|
|
124
|
+
isRepoCandidate = true;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
if (isRepoCandidate) {
|
|
128
|
+
try {
|
|
129
|
+
const git2 = simpleGit(pathDir2);
|
|
130
|
+
const git2cmd = await git2.revparse(["--show-prefix"]);
|
|
131
|
+
if (git2cmd === "") {
|
|
132
|
+
isRepo = true;
|
|
133
|
+
}
|
|
134
|
+
} catch (err) {
|
|
135
|
+
}
|
|
136
|
+
if (!isRepo) {
|
|
137
|
+
console.log(
|
|
138
|
+
`INFO299: the directory ${pathDir2} contains a sub-directory .git but is not a git-repository`
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return isRepo;
|
|
143
|
+
}
|
|
144
|
+
async function searchGitRepo(pathDir, deepSearch = true) {
|
|
145
|
+
let r_list = [];
|
|
146
|
+
const local_list = await fse.readdir(pathDir, { withFileTypes: true });
|
|
147
|
+
for (const item of local_list) {
|
|
148
|
+
if (item.isDirectory()) {
|
|
149
|
+
const pathDir2 = pathDir + "/" + item.name;
|
|
150
|
+
if ([".git", "node_modules"].includes(item.name)) {
|
|
151
|
+
} else {
|
|
152
|
+
const isRepo = await isGitRepo(pathDir2);
|
|
153
|
+
if (isRepo) {
|
|
154
|
+
r_list.push(pathDir2);
|
|
155
|
+
}
|
|
156
|
+
if (deepSearch || !isRepo) {
|
|
157
|
+
r_list = r_list.concat(await searchGitRepo(pathDir2, deepSearch));
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
return r_list;
|
|
163
|
+
}
|
|
164
|
+
function array_intersection(arr1, arr2) {
|
|
165
|
+
return arr1.filter((elem) => arr2.includes(elem));
|
|
166
|
+
}
|
|
167
|
+
function array_exclude(arr_base, arr_exclude) {
|
|
168
|
+
return arr_base.filter((elem) => !arr_exclude.includes(elem));
|
|
169
|
+
}
|
|
170
|
+
async function git_clone(localPath, remote_url, version) {
|
|
171
|
+
let r_code = -1;
|
|
172
|
+
try {
|
|
173
|
+
if (!fs.existsSync(localPath)) {
|
|
174
|
+
const git = simpleGit();
|
|
175
|
+
const gitlog = await git.clone(remote_url, localPath);
|
|
176
|
+
console.log(gitlog);
|
|
177
|
+
r_code = await git_checkout(localPath, version);
|
|
178
|
+
} else {
|
|
179
|
+
const fstat = await fsp.stat(localPath);
|
|
180
|
+
if (fstat.isDirectory()) {
|
|
181
|
+
const isRepo = await isGitRepo(localPath);
|
|
182
|
+
if (isRepo) {
|
|
183
|
+
const git2 = simpleGit(localPath);
|
|
184
|
+
const remote = await git2.getRemotes(true);
|
|
185
|
+
const remote_url2 = remote[0].refs.fetch;
|
|
186
|
+
if (remote_url2 === remote_url2) {
|
|
187
|
+
console.log(
|
|
188
|
+
`INFO398: the git-repo ${localPath} is already cloned! Then just git-pull!`
|
|
189
|
+
);
|
|
190
|
+
const gitlog2 = await git2.pull();
|
|
191
|
+
console.log(gitlog2);
|
|
192
|
+
r_code = await git_checkout(localPath, version);
|
|
193
|
+
} else {
|
|
194
|
+
console.log(
|
|
195
|
+
`WARN381: Warning, the git-repo ${localPath} already exist but with an unexpected remote! git-clone/pull aborted!`
|
|
196
|
+
);
|
|
197
|
+
}
|
|
198
|
+
} else {
|
|
199
|
+
console.log(
|
|
200
|
+
`WARN869: Warning, the directory ${localPath} already exist but is not a git-repo! git-clone aborted!`
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
} else {
|
|
204
|
+
console.log(
|
|
205
|
+
`WARN537: Warning, the path ${localPath} already exist and is a file! git-clone aborted!`
|
|
206
|
+
);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
} catch (error) {
|
|
210
|
+
console.log(`ERR162: Error by cloning ${localPath} from ${remote_url}`);
|
|
211
|
+
console.error(error);
|
|
212
|
+
}
|
|
213
|
+
return r_code;
|
|
214
|
+
}
|
|
215
|
+
async function git_checkout(repoPath, version) {
|
|
216
|
+
let r_code = -2;
|
|
217
|
+
try {
|
|
218
|
+
const git = simpleGit(repoPath);
|
|
219
|
+
const gitlog = await git.checkout(version);
|
|
220
|
+
console.log(gitlog);
|
|
221
|
+
r_code = 0;
|
|
222
|
+
} catch (error) {
|
|
223
|
+
console.log(`ERR523: Error by checkout ${repoPath} for version ${version}`);
|
|
224
|
+
console.error(error);
|
|
225
|
+
}
|
|
226
|
+
return r_code;
|
|
227
|
+
}
|
|
228
|
+
async function git_verify(repoPath, remote_url, version) {
|
|
229
|
+
let r_code = 0;
|
|
230
|
+
const one_info = await one_repo_info(repoPath);
|
|
231
|
+
if (one_info.url === remote_url) {
|
|
232
|
+
console.log(`remote_url: Ok`);
|
|
233
|
+
} else {
|
|
234
|
+
console.log(`remote_url: Nok ${remote_url} versus ${one_info.url}`);
|
|
235
|
+
r_code = -1;
|
|
236
|
+
}
|
|
237
|
+
if (one_info.branch === version || one_info.commit === version) {
|
|
238
|
+
console.log(`version: Ok`);
|
|
239
|
+
} else {
|
|
240
|
+
console.log(
|
|
241
|
+
`version: Nok ${version} versus ${one_info.branch} versus ${one_info.commit}`
|
|
242
|
+
);
|
|
243
|
+
r_code = -1;
|
|
244
|
+
}
|
|
245
|
+
return r_code;
|
|
246
|
+
}
|
|
247
|
+
async function git_custom(repoPath, gitCommand) {
|
|
248
|
+
let r_code = -1;
|
|
249
|
+
try {
|
|
250
|
+
const git = simpleGit(repoPath);
|
|
251
|
+
const gitCommand2 = gitCommand.split(" ");
|
|
252
|
+
const gitlog = await git.raw(...gitCommand2);
|
|
253
|
+
console.log(gitlog);
|
|
254
|
+
r_code = 0;
|
|
255
|
+
} catch (error) {
|
|
256
|
+
console.log(`ERR772: Error by git-command ${gitCommand} on repo ${repoPath}`);
|
|
257
|
+
console.error(error);
|
|
258
|
+
}
|
|
259
|
+
return r_code;
|
|
260
|
+
}
|
|
261
|
+
async function one_repo_info(localPath) {
|
|
262
|
+
let one_info = {
|
|
263
|
+
localPath: "undefined",
|
|
264
|
+
url: "undefined",
|
|
265
|
+
branch: "undefined",
|
|
266
|
+
commit: "undefined"
|
|
267
|
+
};
|
|
268
|
+
const localPath2 = localPath.replace(regex_pointSlash, "");
|
|
269
|
+
try {
|
|
270
|
+
const git = simpleGit(localPath);
|
|
271
|
+
const remote = await git.getRemotes(true);
|
|
272
|
+
const remote_url = remote[0].refs.fetch;
|
|
273
|
+
const branch = await git.branch();
|
|
274
|
+
const branch_current = branch.current;
|
|
275
|
+
const commit = await git.log();
|
|
276
|
+
const commit_hash = commit.latest.hash;
|
|
277
|
+
one_info = {
|
|
278
|
+
localPath: localPath2,
|
|
279
|
+
url: remote_url,
|
|
280
|
+
branch: branch_current,
|
|
281
|
+
commit: commit_hash
|
|
282
|
+
};
|
|
283
|
+
} catch (error) {
|
|
284
|
+
console.log(`ERR398: Error by git-operations on repo ${localPath}`);
|
|
285
|
+
console.error(error);
|
|
286
|
+
}
|
|
287
|
+
return one_info;
|
|
288
|
+
}
|
|
289
|
+
async function get_repos_info(repos) {
|
|
290
|
+
const repos_info = [];
|
|
291
|
+
for (const [idx, localPath] of repos.entries()) {
|
|
292
|
+
console.log(`===> ${idx + 1} - get info of git-repo ${localPath}`);
|
|
293
|
+
const one_info = await one_repo_info(localPath);
|
|
294
|
+
repos_info.push(one_info);
|
|
295
|
+
}
|
|
296
|
+
return repos_info;
|
|
297
|
+
}
|
|
298
|
+
async function validate_yaml_external(yamlPath) {
|
|
299
|
+
let fyaml;
|
|
300
|
+
try {
|
|
301
|
+
const fstr = await fse.readFile(yamlPath, "utf-8");
|
|
302
|
+
fyaml = YAML.parse(fstr);
|
|
303
|
+
} catch (error) {
|
|
304
|
+
console.log(`ERR439: Error by reading the yaml-file ${yamlPath}!`);
|
|
305
|
+
console.error(error);
|
|
306
|
+
return -1;
|
|
307
|
+
}
|
|
308
|
+
try {
|
|
309
|
+
if (!Object.hasOwn(fyaml, "repositories"))
|
|
310
|
+
throw 'The property "repositories" is missing!';
|
|
311
|
+
for (const repo in fyaml.repositories) {
|
|
312
|
+
if (!Object.hasOwn(fyaml.repositories[repo], "url"))
|
|
313
|
+
throw `The property "url" is missing for repo ${repo} !`;
|
|
314
|
+
if (!Object.hasOwn(fyaml.repositories[repo], "version"))
|
|
315
|
+
throw `The property "version" is missing for repo ${repo} !`;
|
|
316
|
+
if (!Object.hasOwn(fyaml.repositories[repo], "type")) {
|
|
317
|
+
console.log(`WARN390: Warning, the property "type" is missing for repo ${repo} !`);
|
|
318
|
+
} else if (fyaml.repositories[repo].type !== "git") {
|
|
319
|
+
console.log(
|
|
320
|
+
`WARN395: Warning, the property "type" of repo ${repo} is not git but ${fyaml.repositories[repo].type}!`
|
|
321
|
+
);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
console.log(`The yaml-file ${yamlPath} is valid!`);
|
|
325
|
+
return 0;
|
|
326
|
+
} catch (error) {
|
|
327
|
+
console.error(error);
|
|
328
|
+
console.log(`Invalid yaml-file ${yamlPath}!`);
|
|
329
|
+
return -2;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
function isPathAbsolute(path2) {
|
|
333
|
+
let r_absolute = false;
|
|
334
|
+
const regex_slash = /^\//;
|
|
335
|
+
if (regex_slash.test(path2)) {
|
|
336
|
+
r_absolute = true;
|
|
337
|
+
}
|
|
338
|
+
return r_absolute;
|
|
339
|
+
}
|
|
340
|
+
var Vag = class {
|
|
341
|
+
/** The top directory from where to search repositories. */
|
|
342
|
+
discoverDir;
|
|
343
|
+
/** When a repository is encountered, should it be inspected for searching sub-repositories? */
|
|
344
|
+
deepSearch;
|
|
345
|
+
/** The path to the yaml-file that provides the repositories to be cloned. */
|
|
346
|
+
importYaml;
|
|
347
|
+
/** The base directory where the directories listed in the yaml-file should be cloned. */
|
|
348
|
+
importDir;
|
|
349
|
+
/** The list of the path of the discovered repositories. It is populated by the `init()` method. */
|
|
350
|
+
listD;
|
|
351
|
+
/** An object with the content of the yaml-file. It is populated by the `init()` method. */
|
|
352
|
+
listC;
|
|
353
|
+
/**
|
|
354
|
+
* The constructor of the `Vag` class.
|
|
355
|
+
* `listD` is initialized to an empty list.
|
|
356
|
+
* `listC` is initialized to an empty object.
|
|
357
|
+
*/
|
|
358
|
+
constructor(discoverDir = ".", deepSearch = true, importYaml = "", importDir = "") {
|
|
359
|
+
this.discoverDir = discoverDir;
|
|
360
|
+
this.deepSearch = deepSearch;
|
|
361
|
+
this.importYaml = importYaml;
|
|
362
|
+
this.importDir = importDir;
|
|
363
|
+
this.listD = [];
|
|
364
|
+
this.listC = {};
|
|
365
|
+
}
|
|
366
|
+
// this init function cannot be included in the constructor because the constructor can not be async
|
|
367
|
+
async discover_repos(discoverDir = this.discoverDir, deepSearch = this.deepSearch) {
|
|
368
|
+
this.discoverDir = discoverDir;
|
|
369
|
+
this.deepSearch = deepSearch;
|
|
370
|
+
if (this.discoverDir === "") {
|
|
371
|
+
console.log(`ERR282: Error, the discoverDir cannot be an empty string`);
|
|
372
|
+
return -1;
|
|
373
|
+
}
|
|
374
|
+
if (!isPathAbsolute(this.discoverDir)) {
|
|
375
|
+
if (!regex_pointSlash.test(this.discoverDir) && this.discoverDir !== ".") {
|
|
376
|
+
this.discoverDir = "./" + this.discoverDir;
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
this.discoverDir = this.discoverDir.replace(regex_trailingSlash, "");
|
|
380
|
+
try {
|
|
381
|
+
await fse.readdir(this.discoverDir, { withFileTypes: true });
|
|
382
|
+
} catch (err) {
|
|
383
|
+
console.log(`ERR638: Error, the path ${this.discoverDir} doesn't exist!`);
|
|
384
|
+
console.log(err);
|
|
385
|
+
return -1;
|
|
386
|
+
}
|
|
387
|
+
this.listD = await searchGitRepo(this.discoverDir, this.deepSearch);
|
|
388
|
+
console.log(`Number of discovered cloned git repos: ${this.listD.length}`);
|
|
389
|
+
return 0;
|
|
390
|
+
}
|
|
391
|
+
async import_yaml(importYaml = this.importYaml, importDir = this.importDir) {
|
|
392
|
+
let r_code = 0;
|
|
393
|
+
this.importYaml = importYaml;
|
|
394
|
+
this.importDir = importDir;
|
|
395
|
+
if (this.importYaml !== "") {
|
|
396
|
+
let baseDir = path.dirname(this.importYaml);
|
|
397
|
+
if (this.importDir !== "") {
|
|
398
|
+
baseDir = this.importDir;
|
|
399
|
+
}
|
|
400
|
+
if (["", "."].includes(baseDir)) {
|
|
401
|
+
baseDir = "";
|
|
402
|
+
} else {
|
|
403
|
+
if (!regex_trailingSlash.test(baseDir)) {
|
|
404
|
+
baseDir = baseDir + "/";
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
const list_non_git = [];
|
|
408
|
+
try {
|
|
409
|
+
const fstr = await fse.readFile(this.importYaml, "utf-8");
|
|
410
|
+
const fyaml = YAML.parse(fstr);
|
|
411
|
+
for (const repoDir in fyaml.repositories) {
|
|
412
|
+
let repoDir2 = repoDir;
|
|
413
|
+
if (!isPathAbsolute(repoDir2)) {
|
|
414
|
+
repoDir2 = baseDir + repoDir;
|
|
415
|
+
if (!regex_pointSlash.test(repoDir2)) {
|
|
416
|
+
repoDir2 = "./" + repoDir2;
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
if (!Object.hasOwn(fyaml.repositories[repoDir], "type") || fyaml.repositories[repoDir].type === "git") {
|
|
420
|
+
this.listC[repoDir2] = {
|
|
421
|
+
url: fyaml.repositories[repoDir].url,
|
|
422
|
+
version: fyaml.repositories[repoDir].version
|
|
423
|
+
};
|
|
424
|
+
} else {
|
|
425
|
+
list_non_git.push(repoDir2);
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
} catch (error) {
|
|
429
|
+
console.log(
|
|
430
|
+
`ERR826: Error, the imported-yaml-file ${this.importYaml} is not valid!`
|
|
431
|
+
);
|
|
432
|
+
console.error(error);
|
|
433
|
+
r_code = -1;
|
|
434
|
+
}
|
|
435
|
+
console.log(
|
|
436
|
+
`From imported Yaml, number of git-repos: ${Object.keys(this.listC).length}`
|
|
437
|
+
);
|
|
438
|
+
console.log(`From imported Yaml, number of excluded repos: ${list_non_git.length}`);
|
|
439
|
+
for (const [idx, repoDir] of list_non_git.entries()) {
|
|
440
|
+
console.log(
|
|
441
|
+
` ${(idx + 1).toString().padStart(3, " ")} - Excluded repo: ${repoDir}`
|
|
442
|
+
);
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
return r_code;
|
|
446
|
+
}
|
|
447
|
+
/**
|
|
448
|
+
* The `init` method populate populate `listD` and `listC`.
|
|
449
|
+
* It must be called just after instanciating `Vag`.
|
|
450
|
+
* The configuration properties `discoverDir`, `deepSearch`, `importYaml` and `importDir` can be reassinged by `init`.
|
|
451
|
+
*/
|
|
452
|
+
async init(discoverDir = this.discoverDir, deepSearch = this.deepSearch, importYaml = this.importYaml, importDir = this.importDir) {
|
|
453
|
+
let r_code = 0;
|
|
454
|
+
r_code += await this.discover_repos(discoverDir, deepSearch);
|
|
455
|
+
r_code += await this.import_yaml(importYaml, importDir);
|
|
456
|
+
return r_code;
|
|
457
|
+
}
|
|
458
|
+
/** List the discovered repositories. */
|
|
459
|
+
d_list() {
|
|
460
|
+
return this.listD;
|
|
461
|
+
}
|
|
462
|
+
/** List the wished repositories (a.k.a. configured repositories). */
|
|
463
|
+
c_list() {
|
|
464
|
+
return Object.keys(this.listC);
|
|
465
|
+
}
|
|
466
|
+
/** List the git-repos which are in the D-list and in the C-list. I.e. Intersection of D and C. */
|
|
467
|
+
cd_list() {
|
|
468
|
+
return array_intersection(this.listD, Object.keys(this.listC));
|
|
469
|
+
}
|
|
470
|
+
/** List the git-repos which are in the D-list but not in the C-list. I.e. D not C. */
|
|
471
|
+
dnc_list() {
|
|
472
|
+
return array_exclude(this.listD, Object.keys(this.listC));
|
|
473
|
+
}
|
|
474
|
+
/** List the git-repos which are in the C-list but not in the D-list. I.e. C not D. */
|
|
475
|
+
cnd_list() {
|
|
476
|
+
return array_exclude(Object.keys(this.listC), this.listD);
|
|
477
|
+
}
|
|
478
|
+
/** Clone the repositories of `listC`. */
|
|
479
|
+
async c_clone() {
|
|
480
|
+
let r_code = 0;
|
|
481
|
+
for (const [idx, localPath] of Object.keys(this.listC).entries()) {
|
|
482
|
+
const repo = this.listC[localPath];
|
|
483
|
+
console.log(
|
|
484
|
+
`===> ${idx + 1} - clone ${localPath} from ${repo.url} at version ${repo.version}`
|
|
485
|
+
);
|
|
486
|
+
r_code += await git_clone(localPath, repo.url, repo.version);
|
|
487
|
+
}
|
|
488
|
+
return r_code;
|
|
489
|
+
}
|
|
490
|
+
/** Checkout the repositories listed in `listC` and `listD`. */
|
|
491
|
+
async cd_checkout() {
|
|
492
|
+
let r_code = 0;
|
|
493
|
+
const list_cd = this.cd_list();
|
|
494
|
+
for (const [idx, localPath] of list_cd.entries()) {
|
|
495
|
+
const repo = this.listC[localPath];
|
|
496
|
+
console.log(`===> ${idx + 1} - checkout ${localPath} at version ${repo.version}`);
|
|
497
|
+
r_code += await git_checkout(localPath, repo.version);
|
|
498
|
+
}
|
|
499
|
+
return r_code;
|
|
500
|
+
}
|
|
501
|
+
/** For the repositories listed in `listC` and `listD`, verify if they fit with the configuration of Yaml-file. */
|
|
502
|
+
async cd_verify() {
|
|
503
|
+
let r_code = 0;
|
|
504
|
+
const list_cd = this.cd_list();
|
|
505
|
+
for (const [idx, localPath] of list_cd.entries()) {
|
|
506
|
+
const repo = this.listC[localPath];
|
|
507
|
+
console.log(`===> ${idx + 1} - verify ${localPath}`);
|
|
508
|
+
r_code += await git_verify(localPath, repo.url, repo.version);
|
|
509
|
+
}
|
|
510
|
+
const all_nb = list_cd.length;
|
|
511
|
+
const nok_nb = Math.abs(r_code);
|
|
512
|
+
const ok_nb = all_nb - nok_nb;
|
|
513
|
+
console.log(`Verify ${all_nb} repos : ${ok_nb} Ok, ${nok_nb} Nok`);
|
|
514
|
+
return r_code;
|
|
515
|
+
}
|
|
516
|
+
/** Run a custom git-command on the discoverd repositories (`listD`). */
|
|
517
|
+
async d_custom(git_command, only_configured_repo = false) {
|
|
518
|
+
let r_code = 0;
|
|
519
|
+
let repos = this.d_list();
|
|
520
|
+
if (only_configured_repo) {
|
|
521
|
+
repos = this.cd_list();
|
|
522
|
+
}
|
|
523
|
+
for (const [idx, localPath] of repos.entries()) {
|
|
524
|
+
console.log(
|
|
525
|
+
`===> ${idx + 1} - On git-repo ${localPath} with command git ${git_command}`
|
|
526
|
+
);
|
|
527
|
+
r_code += await git_custom(localPath, git_command);
|
|
528
|
+
}
|
|
529
|
+
return r_code;
|
|
530
|
+
}
|
|
531
|
+
/** Git-fetch on the discoverd repositories (`listD`). */
|
|
532
|
+
async d_fetch(only_configured_repo = false) {
|
|
533
|
+
return await this.d_custom("fetch --prune", only_configured_repo);
|
|
534
|
+
}
|
|
535
|
+
/** Git-pull on the discoverd repositories (`listD`). */
|
|
536
|
+
async d_pull(only_configured_repo = false) {
|
|
537
|
+
return await this.d_custom("pull", only_configured_repo);
|
|
538
|
+
}
|
|
539
|
+
/** Git-push on the discoverd repositories (`listD`). */
|
|
540
|
+
async d_push(only_configured_repo = false) {
|
|
541
|
+
return await this.d_custom("push", only_configured_repo);
|
|
542
|
+
}
|
|
543
|
+
/** Show the current branch of the discoverd repositories (`listD`). */
|
|
544
|
+
async d_branch(only_configured_repo = false) {
|
|
545
|
+
return await this.d_custom("branch --show-current", only_configured_repo);
|
|
546
|
+
}
|
|
547
|
+
/** Git-status on the discoverd repositories (`listD`). */
|
|
548
|
+
async d_status(only_configured_repo = false) {
|
|
549
|
+
return await this.d_custom("status", only_configured_repo);
|
|
550
|
+
}
|
|
551
|
+
/** Git-diff on the discoverd repositories (`listD`). */
|
|
552
|
+
async d_diff(only_configured_repo = false) {
|
|
553
|
+
return await this.d_custom("diff", only_configured_repo);
|
|
554
|
+
}
|
|
555
|
+
/** `Git-log -n 3` on the discoverd repositories (`listD`). */
|
|
556
|
+
async d_log(only_configured_repo = false) {
|
|
557
|
+
return await this.d_custom("log -n 3", only_configured_repo);
|
|
558
|
+
}
|
|
559
|
+
/** `Git-remote -vv` on the discoverd repositories (`listD`). */
|
|
560
|
+
async d_remote(only_configured_repo = false) {
|
|
561
|
+
return await this.d_custom("remote -vv", only_configured_repo);
|
|
562
|
+
}
|
|
563
|
+
/** `Git-stash list` on the discoverd repositories (`listD`). */
|
|
564
|
+
async d_stash_list(only_configured_repo = false) {
|
|
565
|
+
return await this.d_custom("stash list", only_configured_repo);
|
|
566
|
+
}
|
|
567
|
+
/** `Git-clean -dxf` on the discoverd repositories (`listD`). */
|
|
568
|
+
async d_clean(only_configured_repo = false) {
|
|
569
|
+
return await this.d_custom("clean -dxf", only_configured_repo);
|
|
570
|
+
}
|
|
571
|
+
/** Export in a Yaml-file the list of discoverd repositories (`listD`). */
|
|
572
|
+
async d_export_yaml(yamlPath, commit_version = false) {
|
|
573
|
+
let r_code = -1;
|
|
574
|
+
if (yamlPath === "") {
|
|
575
|
+
console.log(`ERR482: Error, the discoverDir cannot be an empty string`);
|
|
576
|
+
return -1;
|
|
577
|
+
}
|
|
578
|
+
const repos = this.d_list();
|
|
579
|
+
const repos_info = await get_repos_info(repos);
|
|
580
|
+
const fyaml = { repositories: {} };
|
|
581
|
+
for (const repo of repos_info) {
|
|
582
|
+
let version = repo.branch;
|
|
583
|
+
if (commit_version) {
|
|
584
|
+
version = repo.commit;
|
|
585
|
+
}
|
|
586
|
+
fyaml.repositories[repo.localPath] = {
|
|
587
|
+
type: "git",
|
|
588
|
+
url: repo.url,
|
|
589
|
+
version
|
|
590
|
+
};
|
|
591
|
+
}
|
|
592
|
+
const fstr = YAML.stringify(fyaml);
|
|
593
|
+
try {
|
|
594
|
+
await fse.outputFile(yamlPath, fstr);
|
|
595
|
+
r_code = 0;
|
|
596
|
+
} catch (error) {
|
|
597
|
+
console.log(`ERR218: Error by writting the yaml-file ${yamlPath}!`);
|
|
598
|
+
console.error(error);
|
|
599
|
+
}
|
|
600
|
+
console.log(`The yaml-file ${yamlPath} has been written!`);
|
|
601
|
+
return r_code;
|
|
602
|
+
}
|
|
603
|
+
/**
|
|
604
|
+
* Validate a yaml-file that could be imported later on.
|
|
605
|
+
*
|
|
606
|
+
* @param yamlPath the path to the yaml-file to be checked/validated.
|
|
607
|
+
* @returns an integer-code. 0 if the validation is successful, negative otherwise.
|
|
608
|
+
*/
|
|
609
|
+
async validate_yaml(yamlPath) {
|
|
610
|
+
if (yamlPath === "") {
|
|
611
|
+
console.log(`ERR482: Error, the discoverDir cannot be an empty string`);
|
|
612
|
+
return -1;
|
|
613
|
+
}
|
|
614
|
+
return await validate_yaml_external(yamlPath);
|
|
615
|
+
}
|
|
616
|
+
/**
|
|
617
|
+
* Return a string with the three numbers (major, minor, patch) written in the package.json.
|
|
618
|
+
*
|
|
619
|
+
* @returns the string Major.Minor.Patch
|
|
620
|
+
*/
|
|
621
|
+
static version_short() {
|
|
622
|
+
return vag_version_short;
|
|
623
|
+
}
|
|
624
|
+
};
|
|
625
|
+
|
|
626
|
+
// src/vag_cli.ts
|
|
627
|
+
var cmd = {
|
|
628
|
+
list: false,
|
|
629
|
+
clone: false,
|
|
630
|
+
checkout: false,
|
|
631
|
+
verify: false,
|
|
632
|
+
fetch: false,
|
|
633
|
+
pull: false,
|
|
634
|
+
push: false,
|
|
635
|
+
branch: false,
|
|
636
|
+
status: false,
|
|
637
|
+
diff: false,
|
|
638
|
+
log: false,
|
|
639
|
+
remote: false,
|
|
640
|
+
stash_list: false,
|
|
641
|
+
clean: false,
|
|
642
|
+
custom: false,
|
|
643
|
+
export_yaml: false,
|
|
644
|
+
validate_yaml: false,
|
|
645
|
+
versions: false
|
|
646
|
+
};
|
|
647
|
+
var argv = yargs(hideBin(process.argv)).scriptName("vag").usage("Usage: $0 <global-options> command <command-options>").example([
|
|
648
|
+
[
|
|
649
|
+
"$0 clone --importYaml repos.yml --importDir subRepos",
|
|
650
|
+
"clone the git-repos listed in repos.yml in the directory subRepos"
|
|
651
|
+
],
|
|
652
|
+
[
|
|
653
|
+
"$0 status --discoverDir=subRepos",
|
|
654
|
+
"show the status of all discovered git-repos with the directory subRepos"
|
|
655
|
+
],
|
|
656
|
+
[
|
|
657
|
+
"$0 custom --git_command 'log -u -n1'",
|
|
658
|
+
"apply the cutom-command git-log to all discovered git-repos"
|
|
659
|
+
],
|
|
660
|
+
[
|
|
661
|
+
"$0 export_yaml --yaml_path=myRepos.yml --commit_version=true",
|
|
662
|
+
"export the discovered git-repos in the yaml-file myRepos.yml"
|
|
663
|
+
]
|
|
664
|
+
]).option("discoverDir", {
|
|
665
|
+
alias: "d",
|
|
666
|
+
type: "string",
|
|
667
|
+
description: "directory-path for searching git-repositories.",
|
|
668
|
+
default: "."
|
|
669
|
+
}).option("deepSearch", {
|
|
670
|
+
alias: "D",
|
|
671
|
+
type: "boolean",
|
|
672
|
+
description: "search further for git-repos within found git-repos.",
|
|
673
|
+
default: true
|
|
674
|
+
}).option("importYaml", {
|
|
675
|
+
alias: "y",
|
|
676
|
+
type: "string",
|
|
677
|
+
description: "path to the yaml-file containing the list of repos.",
|
|
678
|
+
default: ""
|
|
679
|
+
}).option("importDir", {
|
|
680
|
+
alias: "b",
|
|
681
|
+
type: "string",
|
|
682
|
+
description: "path to the directory where to clone the repos. If not specified, the directory of the yaml-file is used.",
|
|
683
|
+
default: ""
|
|
684
|
+
}).option("only_configured", {
|
|
685
|
+
alias: "c",
|
|
686
|
+
type: "boolean",
|
|
687
|
+
description: "not on all discovered git-repos but only if in importYaml",
|
|
688
|
+
default: false
|
|
689
|
+
}).command("list", "print the lists of git-repositories", {}, () => {
|
|
690
|
+
cmd.list = true;
|
|
691
|
+
}).command("clone", "clone the git-repositories listed in the importYaml file", {}, () => {
|
|
692
|
+
cmd.clone = true;
|
|
693
|
+
}).command("checkout", "checkout the git-repos according to the importYaml file", {}, () => {
|
|
694
|
+
cmd.checkout = true;
|
|
695
|
+
}).command("verify", "verify if the discovered git-repos fit with the importYaml", {}, () => {
|
|
696
|
+
cmd.verify = true;
|
|
697
|
+
}).command("fetch", "git fetch --prune the discovered git-repositories", {}, () => {
|
|
698
|
+
cmd.fetch = true;
|
|
699
|
+
}).command("pull", "pull the discovered git-repositories", {}, () => {
|
|
700
|
+
cmd.pull = true;
|
|
701
|
+
}).command("push", "push the discovered git-repositories", {}, () => {
|
|
702
|
+
cmd.push = true;
|
|
703
|
+
}).command("branch", "show branch of the discovered git-repositories", {}, () => {
|
|
704
|
+
cmd.branch = true;
|
|
705
|
+
}).command("status", "show status of the discovered git-repositories", {}, () => {
|
|
706
|
+
cmd.status = true;
|
|
707
|
+
}).command("diff", "show diff of the discovered git-repositories", {}, () => {
|
|
708
|
+
cmd.diff = true;
|
|
709
|
+
}).command("log", "show log of the discovered git-repositories", {}, () => {
|
|
710
|
+
cmd.log = true;
|
|
711
|
+
}).command("remote", "show remote of the discovered git-repositories", {}, () => {
|
|
712
|
+
cmd.remote = true;
|
|
713
|
+
}).command("stash_list", "show stash_list of the discovered git-repositories", {}, () => {
|
|
714
|
+
cmd.stash_list = true;
|
|
715
|
+
}).command("clean", "git clean -dxf of the discovered git-repositories", {}, () => {
|
|
716
|
+
cmd.clean = true;
|
|
717
|
+
}).command(
|
|
718
|
+
"custom",
|
|
719
|
+
"git custom command for each of the discovered git-repos",
|
|
720
|
+
{
|
|
721
|
+
git_command: {
|
|
722
|
+
type: "string",
|
|
723
|
+
description: "the git-command to be apply",
|
|
724
|
+
demandOption: true
|
|
725
|
+
}
|
|
726
|
+
},
|
|
727
|
+
() => {
|
|
728
|
+
cmd.custom = true;
|
|
729
|
+
}
|
|
730
|
+
).command(
|
|
731
|
+
"export_yaml",
|
|
732
|
+
"export the discovered git-repositories in a yaml-file",
|
|
733
|
+
{
|
|
734
|
+
yaml_path: {
|
|
735
|
+
type: "string",
|
|
736
|
+
description: "the path to the output yaml-file",
|
|
737
|
+
demandOption: true
|
|
738
|
+
},
|
|
739
|
+
commit_version: {
|
|
740
|
+
type: "boolean",
|
|
741
|
+
description: "Use commit-hash instead of branch-name for version",
|
|
742
|
+
default: false
|
|
743
|
+
}
|
|
744
|
+
},
|
|
745
|
+
() => {
|
|
746
|
+
cmd.export_yaml = true;
|
|
747
|
+
}
|
|
748
|
+
).command(
|
|
749
|
+
"validate_yaml",
|
|
750
|
+
"validate the syntax of a yaml-file",
|
|
751
|
+
{
|
|
752
|
+
yaml_path: {
|
|
753
|
+
type: "string",
|
|
754
|
+
description: "the path to the output yaml-file",
|
|
755
|
+
demandOption: true
|
|
756
|
+
}
|
|
757
|
+
},
|
|
758
|
+
() => {
|
|
759
|
+
cmd.validate_yaml = true;
|
|
760
|
+
}
|
|
761
|
+
).command("versions", "print the versions of vag", {}, () => {
|
|
762
|
+
cmd.versions = true;
|
|
763
|
+
}).strict().parseSync();
|
|
764
|
+
var vag = new Vag(argv.discoverDir, argv.deepSearch, argv.importYaml, argv.importDir);
|
|
765
|
+
await vag.init();
|
|
766
|
+
function display_repo_list(repos) {
|
|
767
|
+
for (const [idx, repo] of repos.entries()) {
|
|
768
|
+
console.log(` ${idx + 1} : ${repo}`);
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
if (cmd.list) {
|
|
772
|
+
const d_list = vag.d_list();
|
|
773
|
+
const c_list = vag.c_list();
|
|
774
|
+
const cd_list = vag.cd_list();
|
|
775
|
+
const dnc_list = vag.dnc_list();
|
|
776
|
+
const cnd_list = vag.cnd_list();
|
|
777
|
+
console.log(`List-D : ${d_list.length} discovered git-repositories`);
|
|
778
|
+
display_repo_list(d_list);
|
|
779
|
+
console.log(`List-C : ${c_list.length} configured git-repositories`);
|
|
780
|
+
display_repo_list(c_list);
|
|
781
|
+
console.log(`List-CD : ${cd_list.length} configured and discovered git-repositories`);
|
|
782
|
+
display_repo_list(cd_list);
|
|
783
|
+
console.log(`List-DnC : ${dnc_list.length} discovered git-repositories but not configured`);
|
|
784
|
+
display_repo_list(dnc_list);
|
|
785
|
+
console.log(`List-CnD : ${cnd_list.length} configured git-repositories but not discovered`);
|
|
786
|
+
display_repo_list(cnd_list);
|
|
787
|
+
}
|
|
788
|
+
if (cmd.clone) {
|
|
789
|
+
await vag.c_clone();
|
|
790
|
+
}
|
|
791
|
+
if (cmd.checkout) {
|
|
792
|
+
await vag.cd_checkout();
|
|
793
|
+
}
|
|
794
|
+
if (cmd.verify) {
|
|
795
|
+
await vag.cd_verify();
|
|
796
|
+
}
|
|
797
|
+
if (cmd.fetch) {
|
|
798
|
+
await vag.d_fetch(argv.only_configured);
|
|
799
|
+
}
|
|
800
|
+
if (cmd.pull) {
|
|
801
|
+
await vag.d_pull(argv.only_configured);
|
|
802
|
+
}
|
|
803
|
+
if (cmd.push) {
|
|
804
|
+
await vag.d_push(argv.only_configured);
|
|
805
|
+
}
|
|
806
|
+
if (cmd.branch) {
|
|
807
|
+
await vag.d_branch(argv.only_configured);
|
|
808
|
+
}
|
|
809
|
+
if (cmd.status) {
|
|
810
|
+
await vag.d_status(argv.only_configured);
|
|
811
|
+
}
|
|
812
|
+
if (cmd.diff) {
|
|
813
|
+
await vag.d_diff(argv.only_configured);
|
|
814
|
+
}
|
|
815
|
+
if (cmd.log) {
|
|
816
|
+
await vag.d_log(argv.only_configured);
|
|
817
|
+
}
|
|
818
|
+
if (cmd.remote) {
|
|
819
|
+
await vag.d_remote(argv.only_configured);
|
|
820
|
+
}
|
|
821
|
+
if (cmd.stash_list) {
|
|
822
|
+
await vag.d_stash_list(argv.only_configured);
|
|
823
|
+
}
|
|
824
|
+
if (cmd.clean) {
|
|
825
|
+
await vag.d_clean(argv.only_configured);
|
|
826
|
+
}
|
|
827
|
+
if (cmd.custom) {
|
|
828
|
+
await vag.d_custom(argv.git_command, argv.only_configured);
|
|
829
|
+
}
|
|
830
|
+
if (cmd.export_yaml) {
|
|
831
|
+
await vag.d_export_yaml(argv.yaml_path, argv.commit_version);
|
|
832
|
+
}
|
|
833
|
+
if (cmd.validate_yaml) {
|
|
834
|
+
await vag.validate_yaml(argv.yaml_path);
|
|
835
|
+
}
|
|
836
|
+
if (cmd.versions) {
|
|
837
|
+
console.log(`vag-version-short : ${Vag.version_short()}`);
|
|
838
|
+
}
|
|
839
|
+
//# sourceMappingURL=vag_cli.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
package/package.json
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "vag_tools",
|
|
3
|
+
"version": "0.0.3",
|
|
4
|
+
"description": "api and cli for managing sub-git-repositories",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/vag_api.js",
|
|
7
|
+
"types": "./dist/vag_api.d.ts",
|
|
8
|
+
"bin": {
|
|
9
|
+
"vag": "dist/vag_cli.js",
|
|
10
|
+
"vagg": "dist/vagg_server.js"
|
|
11
|
+
},
|
|
12
|
+
"files": [
|
|
13
|
+
"dist/",
|
|
14
|
+
"!dist/**/*.map",
|
|
15
|
+
"!dist/**/*.test.*",
|
|
16
|
+
"!dist/**/*.spec.*"
|
|
17
|
+
],
|
|
18
|
+
"engines": {
|
|
19
|
+
"node": ">=20.10.0"
|
|
20
|
+
},
|
|
21
|
+
"tsup": {
|
|
22
|
+
"entry": [
|
|
23
|
+
"src/vag_cli.ts",
|
|
24
|
+
"src/vagg_server.ts"
|
|
25
|
+
],
|
|
26
|
+
"format": "esm",
|
|
27
|
+
"splitting": false,
|
|
28
|
+
"dts": true,
|
|
29
|
+
"sourcemap": true,
|
|
30
|
+
"clean": true
|
|
31
|
+
},
|
|
32
|
+
"scripts": {
|
|
33
|
+
"dev": "tsup --watch",
|
|
34
|
+
"build": "tsup",
|
|
35
|
+
"check": "tsc --noEmit",
|
|
36
|
+
"pretty": "prettier --check .",
|
|
37
|
+
"format": "prettier --write .",
|
|
38
|
+
"lint": "eslint .",
|
|
39
|
+
"test:unit": "vitest",
|
|
40
|
+
"test:unit:once": "vitest --run",
|
|
41
|
+
"ci": "run-s check build pretty lint",
|
|
42
|
+
"clean": "shx rm -fr build dist tmp",
|
|
43
|
+
"test:basic": "node build/tests/vag.test.js",
|
|
44
|
+
"test:cli": "tests/vag-cli.test.sh",
|
|
45
|
+
"test:coverage": "vitest run --coverage",
|
|
46
|
+
"test": "run-s test:basic test:cli test:vitest",
|
|
47
|
+
"dist:build": "tsc --build tsconfig.dist.json",
|
|
48
|
+
"clean_all": "run-s clean"
|
|
49
|
+
},
|
|
50
|
+
"repository": {
|
|
51
|
+
"type": "git",
|
|
52
|
+
"url": "git+https://github.com/charlyoleg2/vag.git"
|
|
53
|
+
},
|
|
54
|
+
"keywords": [
|
|
55
|
+
"git",
|
|
56
|
+
"sub-repo",
|
|
57
|
+
"multi-repo",
|
|
58
|
+
"submodule",
|
|
59
|
+
"subtree",
|
|
60
|
+
"vcs",
|
|
61
|
+
"vcstool",
|
|
62
|
+
"repositories"
|
|
63
|
+
],
|
|
64
|
+
"author": "charlyoleg",
|
|
65
|
+
"license": "ISC",
|
|
66
|
+
"bugs": {
|
|
67
|
+
"url": "https://github.com/charlyoleg2/vag/issues"
|
|
68
|
+
},
|
|
69
|
+
"homepage": "https://github.com/charlyoleg2/vag#readme",
|
|
70
|
+
"dependencies": {
|
|
71
|
+
"fs-extra": "^11.2.0",
|
|
72
|
+
"simple-git": "^3.22.0",
|
|
73
|
+
"yaml": "^2.3.4",
|
|
74
|
+
"yargs": "^17.7.2"
|
|
75
|
+
},
|
|
76
|
+
"devDependencies": {
|
|
77
|
+
"@types/fs-extra": "^11.0.4",
|
|
78
|
+
"@types/node": "^20.11.19",
|
|
79
|
+
"@types/yargs": "^17.0.32",
|
|
80
|
+
"eslint": "^8.56.0",
|
|
81
|
+
"npm-run-all": "^4.1.5",
|
|
82
|
+
"prettier": "^3.2.5",
|
|
83
|
+
"shx": "^0.3.4",
|
|
84
|
+
"tsup": "^8.0.2",
|
|
85
|
+
"typescript": "^5.3.3",
|
|
86
|
+
"typescript-eslint": "^7.0.1",
|
|
87
|
+
"vitest": "^1.2.2"
|
|
88
|
+
},
|
|
89
|
+
"prettier": {
|
|
90
|
+
"useTabs": true,
|
|
91
|
+
"singleQuote": true,
|
|
92
|
+
"trailingComma": "none",
|
|
93
|
+
"printWidth": 100,
|
|
94
|
+
"plugins": [],
|
|
95
|
+
"overrides": []
|
|
96
|
+
}
|
|
97
|
+
}
|