@lzear/forge 4.2.2 → 4.3.0
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 +19 -0
- package/dist/bin.js +92 -45
- package/dist/repo-lint.d.ts +1 -1
- package/dist/repo-lint.js +9 -2
- package/package.json +25 -19
package/README.md
CHANGED
|
@@ -19,6 +19,7 @@ Requires Node ≥ 20.
|
|
|
19
19
|
|
|
20
20
|
```sh
|
|
21
21
|
yarn forge check # audit this repo against forge standards
|
|
22
|
+
yarn forge update # update deps, packageManager & version files
|
|
22
23
|
yarn forge setup # check and set required GitHub secrets
|
|
23
24
|
yarn forge sync # pull shared files from forge into this repo
|
|
24
25
|
```
|
|
@@ -35,6 +36,24 @@ forge check --json # machine-readable output
|
|
|
35
36
|
forge check --skip-remote # skip GitHub secret checks
|
|
36
37
|
```
|
|
37
38
|
|
|
39
|
+
### `forge update`
|
|
40
|
+
|
|
41
|
+
One-stop repo freshening. Detects the package manager (npm, yarn, pnpm, or bun — from the `packageManager` field or the lockfile) and updates everything that needs regular bumping:
|
|
42
|
+
|
|
43
|
+
- dependency ranges in all workspaces (via `npm-check-updates`; honors `.ncurc{,.json,.js,.cjs,.mjs}` — e.g. `reject` to pin packages)
|
|
44
|
+
- the `packageManager` field (yarn ≥ 2 resolved from `@yarnpkg/cli-dist`)
|
|
45
|
+
- `.nvmrc` / `.node-version` → latest Node LTS
|
|
46
|
+
- `.bun-version` → latest Bun
|
|
47
|
+
- LICENSE copyright year (extends `2023` → `2023-2026`)
|
|
48
|
+
- then installs, refreshes the lockfile, and dedupes with the detected package manager (`yarn up -R '*' && yarn dedupe`, `pnpm update -r && pnpm dedupe`, `npm update && npm dedupe`, or `bun update`)
|
|
49
|
+
|
|
50
|
+
```sh
|
|
51
|
+
forge update # update everything, then install
|
|
52
|
+
forge update --dry # preview changes without writing
|
|
53
|
+
forge update --no-install # write updates but skip install
|
|
54
|
+
forge update --json # machine-readable output
|
|
55
|
+
```
|
|
56
|
+
|
|
38
57
|
### `forge setup`
|
|
39
58
|
|
|
40
59
|
Checks and sets the required GitHub secrets (`NPM_TOKEN`, `CODACY_PROJECT_TOKEN`) for a repo.
|
package/dist/bin.js
CHANGED
|
@@ -9,36 +9,40 @@ import path from "path";
|
|
|
9
9
|
import * as clack from "@clack/prompts";
|
|
10
10
|
import { Command } from "commander";
|
|
11
11
|
import pc from "picocolors";
|
|
12
|
-
import {
|
|
12
|
+
import {
|
|
13
|
+
checkLocal,
|
|
14
|
+
checkRepo,
|
|
15
|
+
runUpdate
|
|
16
|
+
} from "@lzear/repo-lint";
|
|
13
17
|
var require2 = createRequire(import.meta.url);
|
|
14
18
|
var { version } = require2("../package.json");
|
|
15
19
|
var isTTY = process.stdout.isTTY;
|
|
16
20
|
var isDebug = process.env.DEBUG?.includes("forge") ?? false;
|
|
17
|
-
var debug = (...
|
|
18
|
-
if (isDebug) console.error(pc.dim(`[debug] ${
|
|
21
|
+
var debug = (...arguments_) => {
|
|
22
|
+
if (isDebug) console.error(pc.dim(`[debug] ${arguments_.join(" ")}`));
|
|
19
23
|
};
|
|
20
24
|
var log2 = {
|
|
21
|
-
intro: (
|
|
22
|
-
isTTY ? clack.intro(
|
|
25
|
+
intro: (message) => {
|
|
26
|
+
isTTY ? clack.intro(message) : console.log(message);
|
|
23
27
|
},
|
|
24
|
-
outro: (
|
|
25
|
-
isTTY ? clack.outro(
|
|
28
|
+
outro: (message) => {
|
|
29
|
+
isTTY ? clack.outro(message) : console.log(message);
|
|
26
30
|
},
|
|
27
|
-
success: (
|
|
28
|
-
isTTY ? clack.log.success(
|
|
31
|
+
success: (message) => {
|
|
32
|
+
isTTY ? clack.log.success(message) : console.log(`\u2713 ${message}`);
|
|
29
33
|
},
|
|
30
|
-
error: (
|
|
31
|
-
isTTY ? clack.log.error(
|
|
34
|
+
error: (message) => {
|
|
35
|
+
isTTY ? clack.log.error(message) : console.error(`\u2717 ${message}`);
|
|
32
36
|
},
|
|
33
|
-
warn: (
|
|
34
|
-
isTTY ? clack.log.warn(
|
|
37
|
+
warn: (message) => {
|
|
38
|
+
isTTY ? clack.log.warn(message) : console.warn(`! ${message}`);
|
|
35
39
|
},
|
|
36
40
|
spinner: () => isTTY ? clack.spinner() : {
|
|
37
|
-
start: (
|
|
38
|
-
debug(
|
|
41
|
+
start: (message) => {
|
|
42
|
+
debug(message);
|
|
39
43
|
},
|
|
40
|
-
stop: (
|
|
41
|
-
debug(
|
|
44
|
+
stop: (message) => {
|
|
45
|
+
debug(message);
|
|
42
46
|
}
|
|
43
47
|
}
|
|
44
48
|
};
|
|
@@ -46,16 +50,16 @@ var program = new Command();
|
|
|
46
50
|
program.name("forge").description("lzear repo tooling").version(version, "-v, --version").helpOption("-h, --help", "show help");
|
|
47
51
|
var DEFAULT_DIR = path.join(homedir(), ".cache", "forge");
|
|
48
52
|
var printResults = (report, json) => {
|
|
49
|
-
const
|
|
53
|
+
const isAnyFail = report.results.some((r) => !r.pass);
|
|
50
54
|
if (json) {
|
|
51
55
|
console.log(
|
|
52
56
|
JSON.stringify(
|
|
53
|
-
{ repo: report.repo, results: report.results, pass: !
|
|
57
|
+
{ repo: report.repo, results: report.results, pass: !isAnyFail },
|
|
54
58
|
null,
|
|
55
59
|
2
|
|
56
60
|
)
|
|
57
61
|
);
|
|
58
|
-
return
|
|
62
|
+
return isAnyFail;
|
|
59
63
|
}
|
|
60
64
|
const pass = report.results.filter((r) => r.pass).length;
|
|
61
65
|
const total = report.results.length;
|
|
@@ -73,45 +77,45 @@ var printResults = (report, json) => {
|
|
|
73
77
|
process.stdout.write(`
|
|
74
78
|
${score} checks passed
|
|
75
79
|
`);
|
|
76
|
-
return
|
|
80
|
+
return isAnyFail;
|
|
77
81
|
};
|
|
78
|
-
var runCheck = async (repos,
|
|
79
|
-
if (!
|
|
82
|
+
var runCheck = async (repos, options) => {
|
|
83
|
+
if (!options.json) process.stdout.write(`
|
|
80
84
|
${pc.bold("forge check")}
|
|
81
85
|
|
|
82
86
|
`);
|
|
83
87
|
if (!repos) {
|
|
84
88
|
debug("checkLocal", process.cwd());
|
|
85
|
-
const result = await checkLocal({ skipRemote:
|
|
86
|
-
process.exit(printResults(result,
|
|
89
|
+
const result = await checkLocal({ skipRemote: options.skipRemote });
|
|
90
|
+
process.exit(printResults(result, options.json) ? 1 : 0);
|
|
87
91
|
}
|
|
88
92
|
const repoList = repos.split(",").map((r) => r.trim());
|
|
89
|
-
let
|
|
93
|
+
let isAnyFail = false;
|
|
90
94
|
for (const repo of repoList) {
|
|
91
|
-
if (!
|
|
95
|
+
if (!options.json)
|
|
92
96
|
process.stdout.write(` ${pc.dim("checking " + repo + "\u2026")}
|
|
93
97
|
`);
|
|
94
98
|
debug("checkRepo", repo);
|
|
95
99
|
try {
|
|
96
100
|
const result = await checkRepo(repo, {
|
|
97
|
-
baseDir:
|
|
98
|
-
skipRemote:
|
|
101
|
+
baseDir: options.dir,
|
|
102
|
+
skipRemote: options.skipRemote
|
|
99
103
|
});
|
|
100
|
-
if (printResults(result,
|
|
104
|
+
if (printResults(result, options.json)) isAnyFail = true;
|
|
101
105
|
} catch (error) {
|
|
102
106
|
debug(String(error));
|
|
103
107
|
process.stderr.write(` ${pc.red("\u2717 " + repo + " \u2014 could not clone")}
|
|
104
108
|
`);
|
|
105
|
-
|
|
109
|
+
isAnyFail = true;
|
|
106
110
|
}
|
|
107
111
|
}
|
|
108
|
-
process.exit(
|
|
112
|
+
process.exit(isAnyFail ? 1 : 0);
|
|
109
113
|
};
|
|
110
114
|
program.command("check").description("audit repo(s) against forge standards").option(
|
|
111
115
|
"--repos <repos>",
|
|
112
116
|
"comma-separated list of repos (default: current repo)"
|
|
113
117
|
).option("--skip-remote", "skip secret checks", false).option("--dir <dir>", "cache dir for cloned repos", DEFAULT_DIR).option("--json", "output results as JSON", false).action(
|
|
114
|
-
(
|
|
118
|
+
(options) => runCheck(options.repos, options)
|
|
115
119
|
);
|
|
116
120
|
var promptAndSet = async (s, repo) => {
|
|
117
121
|
if (!isTTY) {
|
|
@@ -150,13 +154,13 @@ var detectRepo = () => {
|
|
|
150
154
|
const match = /github\.com[:/]([^/]+\/[^/.]+)(?:\.git)?$/.exec(url);
|
|
151
155
|
return match?.[1];
|
|
152
156
|
};
|
|
153
|
-
program.command("setup").description("check and set required GitHub secrets for a repo").option("--repo <repo>", "repo to set up (e.g. lzear/my-repo)").option("--dry", "check only, do not prompt for values", false).option("--json", "output results as JSON", false).action(async (
|
|
154
|
-
const repo =
|
|
157
|
+
program.command("setup").description("check and set required GitHub secrets for a repo").option("--repo <repo>", "repo to set up (e.g. lzear/my-repo)").option("--dry", "check only, do not prompt for values", false).option("--json", "output results as JSON", false).action(async (options) => {
|
|
158
|
+
const repo = options.repo ?? detectRepo();
|
|
155
159
|
if (!repo) {
|
|
156
160
|
log2.error("Could not detect repo. Pass --repo <owner/repo>.");
|
|
157
161
|
process.exit(1);
|
|
158
162
|
}
|
|
159
|
-
const { dry, json } =
|
|
163
|
+
const { dry, json } = options;
|
|
160
164
|
if (!json) log2.intro(pc.bold(`forge setup \xB7 ${pc.cyan(repo)}`));
|
|
161
165
|
if (spawnSync("gh", ["--version"], { stdio: "ignore" }).status !== 0) {
|
|
162
166
|
log2.error("gh CLI not found \u2014 install at https://cli.github.com");
|
|
@@ -208,35 +212,78 @@ program.command("setup").description("check and set required GitHub secrets for
|
|
|
208
212
|
for (const s of missing) await promptAndSet(s, repo);
|
|
209
213
|
log2.outro(pc.green("Done."));
|
|
210
214
|
});
|
|
215
|
+
var formatPackageManager = (pm) => {
|
|
216
|
+
const version2 = pm.version ? `@${pm.version}` : "";
|
|
217
|
+
return `package manager: ${pm.name}${version2} (${pm.source})`;
|
|
218
|
+
};
|
|
219
|
+
var updateMark = (r) => {
|
|
220
|
+
if (!r.pass) return pc.red("\u2717");
|
|
221
|
+
return r.changed ? pc.yellow("\u2191") : pc.green("\u2713");
|
|
222
|
+
};
|
|
223
|
+
var printUpdateReport = (report) => {
|
|
224
|
+
process.stdout.write(
|
|
225
|
+
` ${pc.dim(formatPackageManager(report.packageManager))}
|
|
226
|
+
|
|
227
|
+
`
|
|
228
|
+
);
|
|
229
|
+
for (const r of report.results) {
|
|
230
|
+
process.stdout.write(` ${updateMark(r)} ${r.desc}
|
|
231
|
+
`);
|
|
232
|
+
if (r.detail && (r.changed || !r.pass))
|
|
233
|
+
for (const line of r.detail.split("\n"))
|
|
234
|
+
process.stdout.write(` ${pc.dim(line)}
|
|
235
|
+
`);
|
|
236
|
+
}
|
|
237
|
+
process.stdout.write("\n");
|
|
238
|
+
};
|
|
239
|
+
program.command("update").description("update dependencies, packageManager, and runtime version files").option("--dry", "preview changes, do not write", false).option("--no-install", "skip install & lockfile refresh").option("--json", "output results as JSON", false).action(
|
|
240
|
+
async (options) => {
|
|
241
|
+
const { dry: isDry, install: isInstall, json: isJson } = options;
|
|
242
|
+
if (!isJson)
|
|
243
|
+
process.stdout.write(
|
|
244
|
+
`
|
|
245
|
+
${pc.bold("forge update")}${isDry ? pc.dim(" (dry run)") : ""}
|
|
246
|
+
|
|
247
|
+
`
|
|
248
|
+
);
|
|
249
|
+
debug("runUpdate", process.cwd());
|
|
250
|
+
const report = await runUpdate({ dry: isDry, install: isInstall });
|
|
251
|
+
const isAnyFail = report.results.some((r) => !r.pass);
|
|
252
|
+
if (isJson)
|
|
253
|
+
console.log(JSON.stringify({ ...report, pass: !isAnyFail }, null, 2));
|
|
254
|
+
else printUpdateReport(report);
|
|
255
|
+
process.exit(isAnyFail ? 1 : 0);
|
|
256
|
+
}
|
|
257
|
+
);
|
|
211
258
|
var SYNC_FILES = [
|
|
212
259
|
{ src: "template/.editorconfig", dest: ".editorconfig" },
|
|
213
260
|
{ src: "template/.codacy.yml", dest: ".codacy.yml" },
|
|
214
261
|
{ src: "template/lefthook.yml", dest: "lefthook.yml" }
|
|
215
262
|
];
|
|
216
263
|
var RAW_BASE = "https://raw.githubusercontent.com/lzear/forge/main";
|
|
217
|
-
program.command("sync").description("sync template files from forge into this repo").option("--dry", "print what would change, do not write", false).action(async (
|
|
264
|
+
program.command("sync").description("sync template files from forge into this repo").option("--dry", "print what would change, do not write", false).action(async (options) => {
|
|
218
265
|
log2.intro(pc.bold("forge sync"));
|
|
219
|
-
let
|
|
266
|
+
let isAnyFail = false;
|
|
220
267
|
for (const { src, dest } of SYNC_FILES) {
|
|
221
268
|
const url = `${RAW_BASE}/${src}`;
|
|
222
|
-
const
|
|
269
|
+
const destinationPath = path.join(process.cwd(), dest);
|
|
223
270
|
try {
|
|
224
271
|
const res = await fetch(url);
|
|
225
272
|
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
226
273
|
const content = await res.text();
|
|
227
|
-
if (
|
|
274
|
+
if (options.dry) log2.success(`${dest} ${pc.dim("(dry)")}`);
|
|
228
275
|
else {
|
|
229
|
-
await mkdir(path.dirname(
|
|
230
|
-
await writeFile(
|
|
276
|
+
await mkdir(path.dirname(destinationPath), { recursive: true });
|
|
277
|
+
await writeFile(destinationPath, content, "utf8");
|
|
231
278
|
log2.success(dest);
|
|
232
279
|
}
|
|
233
280
|
} catch (error) {
|
|
234
281
|
log2.error(`${dest} \u2014 ${String(error)}`);
|
|
235
|
-
|
|
282
|
+
isAnyFail = true;
|
|
236
283
|
}
|
|
237
284
|
}
|
|
238
|
-
log2.outro(
|
|
239
|
-
if (
|
|
285
|
+
log2.outro(isAnyFail ? pc.red("Done with errors.") : pc.green("Done."));
|
|
286
|
+
if (isAnyFail) process.exit(1);
|
|
240
287
|
});
|
|
241
288
|
var handleSignal = () => {
|
|
242
289
|
if (isTTY) clack.cancel("Cancelled.");
|
package/dist/repo-lint.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { Check, CheckLocalOptions, CheckRepoOptions, CheckResult, LocalCheck, RemoteCheck, RepoReport, checkLocal, checkRepo } from '@lzear/repo-lint';
|
|
1
|
+
export { Check, CheckLocalOptions, CheckRepoOptions, CheckResult, LocalCheck, PackageManager, PackageManagerName, RemoteCheck, RepoReport, UpdateOptions, UpdateReport, UpdateResult, checkLocal, checkRepo, detectPackageManager, runUpdate } from '@lzear/repo-lint';
|
package/dist/repo-lint.js
CHANGED
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
// src/repo-lint.ts
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
checkLocal,
|
|
4
|
+
checkRepo,
|
|
5
|
+
detectPackageManager,
|
|
6
|
+
runUpdate
|
|
7
|
+
} from "@lzear/repo-lint";
|
|
3
8
|
export {
|
|
4
9
|
checkLocal,
|
|
5
|
-
checkRepo
|
|
10
|
+
checkRepo,
|
|
11
|
+
detectPackageManager,
|
|
12
|
+
runUpdate
|
|
6
13
|
};
|
package/package.json
CHANGED
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lzear/forge",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.3.0",
|
|
4
4
|
"description": "Umbrella package for all lzear dev tooling",
|
|
5
|
+
"license": "MIT",
|
|
5
6
|
"repository": {
|
|
6
7
|
"type": "git",
|
|
7
8
|
"url": "git+https://github.com/lzear/forge.git",
|
|
8
9
|
"directory": "packages/forge"
|
|
9
10
|
},
|
|
10
11
|
"author": "lzear",
|
|
11
|
-
"license": "MIT",
|
|
12
|
-
"sideEffects": false,
|
|
13
12
|
"type": "module",
|
|
14
13
|
"exports": {
|
|
15
14
|
"./commitlint": {
|
|
@@ -49,29 +48,43 @@
|
|
|
49
48
|
}
|
|
50
49
|
},
|
|
51
50
|
"bin": "./dist/bin.js",
|
|
52
|
-
"
|
|
53
|
-
|
|
54
|
-
"
|
|
55
|
-
|
|
51
|
+
"sideEffects": false,
|
|
52
|
+
"engines": {
|
|
53
|
+
"node": ">=24"
|
|
54
|
+
},
|
|
55
|
+
"publishConfig": {
|
|
56
|
+
"access": "public",
|
|
57
|
+
"provenance": true
|
|
58
|
+
},
|
|
56
59
|
"scripts": {
|
|
57
60
|
"build": "tsup",
|
|
58
61
|
"dev": "tsup --watch",
|
|
59
62
|
"typecheck": "tsc --noEmit"
|
|
60
63
|
},
|
|
64
|
+
"files": [
|
|
65
|
+
"dist",
|
|
66
|
+
"tsconfig"
|
|
67
|
+
],
|
|
68
|
+
"keywords": [
|
|
69
|
+
"cli",
|
|
70
|
+
"dev-tools",
|
|
71
|
+
"eslint-config",
|
|
72
|
+
"monorepo",
|
|
73
|
+
"tooling"
|
|
74
|
+
],
|
|
61
75
|
"dependencies": {
|
|
62
76
|
"@clack/prompts": "^1",
|
|
63
|
-
"@lzear/configs": "4.
|
|
64
|
-
"@lzear/eslint-config": "4.
|
|
65
|
-
"@lzear/repo-lint": "4.
|
|
77
|
+
"@lzear/configs": "4.3.0",
|
|
78
|
+
"@lzear/eslint-config": "4.3.0",
|
|
79
|
+
"@lzear/repo-lint": "4.3.0",
|
|
66
80
|
"commander": "^15",
|
|
67
81
|
"picocolors": "^1"
|
|
68
82
|
},
|
|
69
83
|
"devDependencies": {
|
|
70
|
-
"@types/node": "^
|
|
84
|
+
"@types/node": "^26",
|
|
71
85
|
"@vitejs/plugin-react": "^6",
|
|
72
86
|
"eslint": "^10",
|
|
73
87
|
"tsup": "^8",
|
|
74
|
-
"typescript": "^6",
|
|
75
88
|
"vite": "^8",
|
|
76
89
|
"vitest": "^4"
|
|
77
90
|
},
|
|
@@ -98,12 +111,5 @@
|
|
|
98
111
|
"vitest": {
|
|
99
112
|
"optional": true
|
|
100
113
|
}
|
|
101
|
-
},
|
|
102
|
-
"engines": {
|
|
103
|
-
"node": ">=24"
|
|
104
|
-
},
|
|
105
|
-
"publishConfig": {
|
|
106
|
-
"access": "public",
|
|
107
|
-
"provenance": true
|
|
108
114
|
}
|
|
109
115
|
}
|