@leemour/cli-core 0.8.0 → 0.9.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 +42 -11
- package/dist/logger.d.ts +1 -1
- package/dist/release/index.d.ts +37 -0
- package/dist/release/index.d.ts.map +1 -0
- package/dist/release/index.js +90 -0
- package/dist/release/index.js.map +1 -0
- package/dist/release/markdown.d.ts +35 -0
- package/dist/release/markdown.d.ts.map +1 -0
- package/dist/release/markdown.js +150 -0
- package/dist/release/markdown.js.map +1 -0
- package/package.json +24 -13
package/README.md
CHANGED
|
@@ -7,8 +7,9 @@ keyring behind a testable seam, and injectable clocks.
|
|
|
7
7
|
Extracted from [`brazecli`](https://github.com/leemour/brazecli), where each piece earned its
|
|
8
8
|
shape, and shared with [`max-cli`](https://github.com/leemour/max-cli).
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
Published on npm and used by `max-cli`, `cli-messaging`, `tg-cli` and `brazecli`. What changed in
|
|
11
|
+
each version is in [`CHANGELOG.md`](CHANGELOG.md); how the package is put together and released is in
|
|
12
|
+
[`docs/dev/`](docs/dev/ARCHITECTURE.md).
|
|
12
13
|
|
|
13
14
|
## The rule this package exists to keep
|
|
14
15
|
|
|
@@ -47,6 +48,7 @@ streams.stderr // []
|
|
|
47
48
|
| `/completion` | shell completion over the registry: `suggest`, `formatSuggestions` — see below |
|
|
48
49
|
| `/update` | keeping an install current: `installerOf`, `updateCommand`, `latestVersion`, `mayNotify`, `updateNotice`, `runUpdate` — see below |
|
|
49
50
|
| `/codegen` | build time: an official API description → types, Valibot schemas, an operation manifest and a coverage page — see below |
|
|
51
|
+
| `/release` | release time: `releaseCheck` and the checks it runs — changelog shape, links, package contents, version in step — see below |
|
|
50
52
|
|
|
51
53
|
**Nothing in the root export is HTTP.** Status classification, `Retry-After` parsing and the fetch
|
|
52
54
|
seam live in `@leemour/cli-core/http`, so a CLI that speaks a socket never depends on a stack it
|
|
@@ -96,9 +98,10 @@ a throwaway config directory silently overwrites the real secret unless you pass
|
|
|
96
98
|
And a secret should never be handed to a logger in the first place — redaction by field name is the
|
|
97
99
|
second line of defence, not the first.
|
|
98
100
|
|
|
99
|
-
**Everything the environment knows
|
|
100
|
-
|
|
101
|
-
|
|
101
|
+
**Everything the environment knows can be passed in**: the clock, the sleep, the keyring, `env`,
|
|
102
|
+
`fetch` and the streams. The real ones are only defaults — `Credentials` without a `keyring` uses the
|
|
103
|
+
system keychain, `resolvePaths` without `env` reads `process.env`. Passing them is what makes a
|
|
104
|
+
timeout test finish instantly and a keyring test incapable of reaching a real keychain.
|
|
102
105
|
|
|
103
106
|
**Generating an API catalog is `@leemour/cli-core/codegen`**, a build-time tool. It reads a
|
|
104
107
|
format-neutral `ApiModel` — operations with a transport binding (`http` method and path, or `rpc`
|
|
@@ -141,6 +144,30 @@ sentence or `undefined` — it never throws. `runUpdate` starts the package mana
|
|
|
141
144
|
stderr (`spawnPlan` is the Windows `.cmd` rule). Nothing here updates or prints by itself — a CLI
|
|
142
145
|
holding somebody's credentials must not change itself unasked.
|
|
143
146
|
|
|
147
|
+
**The release checks are `@leemour/cli-core/release`** — everything about a release that a program
|
|
148
|
+
can decide. A check is `{ name, run }`, and `run` returns the problems it found, one line each; an
|
|
149
|
+
empty list is a pass. `releaseCheck(checks, { version, log })` runs them all, even after a failure,
|
|
150
|
+
prints `ok` or `FAIL` per check and returns how many failed — the caller exits. The checks every CLI
|
|
151
|
+
shares: `command(root, "pnpm", "lint")` for anything that passes by exiting 0, `notOnNpm`,
|
|
152
|
+
`packContents` against a list of what may ship, `changelogProblems` for the fixed headings and the
|
|
153
|
+
dated top section, `docsProblems` for dead links and anchors and the rules of the pages a user reads,
|
|
154
|
+
and `versionScript` — the whole of a `version:check` / `version:sync` script. Each CLI passes its
|
|
155
|
+
own headings, id prefixes and pages; the checks that belong to one messenger stay in that CLI.
|
|
156
|
+
|
|
157
|
+
```ts
|
|
158
|
+
import { command, notOnNpm, packageVersion, releaseCheck } from "@leemour/cli-core/release"
|
|
159
|
+
|
|
160
|
+
const version = packageVersion(root)
|
|
161
|
+
const failed = releaseCheck(
|
|
162
|
+
[
|
|
163
|
+
{ name: "version not on npm", run: notOnNpm(root, "@leemour/tg-cli", version) },
|
|
164
|
+
{ name: "lint", run: command(root, "pnpm", "lint") },
|
|
165
|
+
],
|
|
166
|
+
{ version, log: console.log },
|
|
167
|
+
)
|
|
168
|
+
process.exit(failed === 0 ? 0 : 1)
|
|
169
|
+
```
|
|
170
|
+
|
|
144
171
|
## Where secrets actually go, per platform
|
|
145
172
|
|
|
146
173
|
The keyring is real on every desktop and absent on most servers, so the fallback is not an edge
|
|
@@ -180,19 +207,23 @@ pnpm typecheck
|
|
|
180
207
|
pnpm build
|
|
181
208
|
```
|
|
182
209
|
|
|
210
|
+
The rest of the checks, and what each is for, are in [`docs/dev/TESTING.md`](docs/dev/TESTING.md).
|
|
211
|
+
|
|
183
212
|
## Releasing
|
|
184
213
|
|
|
185
|
-
Raise `version` in `package.json`
|
|
214
|
+
Raise `version` in `package.json` and date the `## Unreleased` section of
|
|
215
|
+
[`CHANGELOG.md`](CHANGELOG.md) as that version, through a pull request; merge it, then on `main`:
|
|
186
216
|
|
|
187
217
|
```sh
|
|
188
218
|
bin/release
|
|
189
219
|
```
|
|
190
220
|
|
|
191
|
-
It refuses a dirty tree, a branch other than `main
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
[
|
|
195
|
-
|
|
221
|
+
It refuses a dirty tree, a branch other than `main` and a `main` that is not pushed. When npm already
|
|
222
|
+
has the version, or a higher one, it commits the next free version to `main` — the next minor for
|
|
223
|
+
`x.y.0`, the next patch otherwise — renames the changelog heading to match, and publishes that. Then it starts
|
|
224
|
+
[`release.yml`](.github/workflows/release.yml) and follows it. The workflow runs every check,
|
|
225
|
+
publishes through npm's [trusted publishing](https://docs.npmjs.com/trusted-publishers/) — no npm
|
|
226
|
+
token in GitHub, and npm attaches provenance — and tags `v<version>` only once npm shows the new version.
|
|
196
227
|
|
|
197
228
|
npm trusts the workflow **by file name**: the package's Trusted Publisher settings on npmjs.com name
|
|
198
229
|
`leemour` / `cli-core` / `release.yml`. Rename the file and publishing stops until they are updated.
|
package/dist/logger.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The only logging contract core knows about. The CLI adapts Pino to it; a Worker can pass
|
|
3
|
-
* its own. Core never imports a logging library — see docs/ARCHITECTURE.md.
|
|
3
|
+
* its own. Core never imports a logging library — see docs/dev/ARCHITECTURE.md.
|
|
4
4
|
*/
|
|
5
5
|
export interface Logger {
|
|
6
6
|
debug(event: object, message?: string): void;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export { type ChangelogRules, changelogProblems, type DocsRules, docsProblems, idPattern, JOURNAL_IDS, markdownFiles, slug, } from "./markdown.js";
|
|
2
|
+
export type Check = {
|
|
3
|
+
name: string;
|
|
4
|
+
run: () => string[];
|
|
5
|
+
};
|
|
6
|
+
/**
|
|
7
|
+
* Runs every check even after one fails, so one pass shows the whole list, and returns how many
|
|
8
|
+
* failed. The caller exits: a library that calls `process.exit` cannot be tested.
|
|
9
|
+
*/
|
|
10
|
+
export declare const releaseCheck: (checks: readonly Check[], { version, log }: {
|
|
11
|
+
version: string;
|
|
12
|
+
log: (line: string) => void;
|
|
13
|
+
}) => number;
|
|
14
|
+
export declare const run: (root: string, command: string, args: readonly string[]) => {
|
|
15
|
+
ok: boolean;
|
|
16
|
+
output: string;
|
|
17
|
+
};
|
|
18
|
+
/** A check that passes when the command exits 0, and otherwise shows the last 15 lines it printed. */
|
|
19
|
+
export declare const command: (root: string, name: string, ...args: string[]) => () => string[];
|
|
20
|
+
export declare const packageVersion: (root: string) => string;
|
|
21
|
+
export declare const notOnNpm: (root: string, packageName: string, version: string) => () => string[];
|
|
22
|
+
/**
|
|
23
|
+
* `allowed` holds exact paths, and directories ending in `/`; `said` is how the problem names them.
|
|
24
|
+
*/
|
|
25
|
+
export declare const packProblems: (paths: readonly string[], allowed: readonly string[], said: string) => string[];
|
|
26
|
+
export declare const packContents: (root: string, allowed: readonly string[], said: string) => () => string[];
|
|
27
|
+
/**
|
|
28
|
+
* The whole of a `version:check` / `version:sync` script: what to print, and the exit code — 2 when
|
|
29
|
+
* either file cannot be read, 1 on drift. A CLI keeps its version in `package.json` and in a source
|
|
30
|
+
* file read at run time — `--version` must not load `package.json`, which is not there once the
|
|
31
|
+
* command is bundled — and the day they disagree is a release day.
|
|
32
|
+
*/
|
|
33
|
+
export declare const versionScript: (root: string, argv: readonly string[], file?: string) => {
|
|
34
|
+
code: 0 | 1 | 2;
|
|
35
|
+
message: string;
|
|
36
|
+
};
|
|
37
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/release/index.ts"],"names":[],"mappings":"AASA,OAAO,EACL,KAAK,cAAc,EACnB,iBAAiB,EACjB,KAAK,SAAS,EACd,YAAY,EACZ,SAAS,EACT,WAAW,EACX,aAAa,EACb,IAAI,GACL,MAAM,eAAe,CAAA;AAEtB,MAAM,MAAM,KAAK,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,MAAM,EAAE,CAAA;CAAE,CAAA;AAEzD;;;GAGG;AACH,eAAO,MAAM,YAAY,WACf,SAAS,KAAK,EAAE,oBACN;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;CAAE,WAWnE,CAAA;AAED,eAAO,MAAM,GAAG,SAAU,MAAM,WAAW,MAAM,QAAQ,SAAS,MAAM,EAAE;;;CAIzE,CAAA;AAED,sGAAsG;AACtG,eAAO,MAAM,OAAO,SACX,MAAM,QAAQ,MAAM,WAAW,MAAM,EAAE,WAC1C,MAAM,EAGT,CAAA;AAEH,eAAO,MAAM,cAAc,SAAU,MAAM,WACoD,CAAA;AAE/F,eAAO,MAAM,QAAQ,SAAU,MAAM,eAAe,MAAM,WAAW,MAAM,WAAS,MAAM,EAGlF,CAAA;AAER;;GAEG;AACH,eAAO,MAAM,YAAY,UAAW,SAAS,MAAM,EAAE,WAAW,SAAS,MAAM,EAAE,QAAQ,MAAM,aAG1B,CAAA;AAErE,eAAO,MAAM,YAAY,SAAU,MAAM,WAAW,SAAS,MAAM,EAAE,QAAQ,MAAM,WAAS,MAAM,EASjG,CAAA;AAcD;;;;;GAKG;AACH,eAAO,MAAM,aAAa,SAClB,MAAM,QACN,SAAS,MAAM,EAAE,oBAEtB;IAAE,IAAI,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAiBpC,CAAA"}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Everything about a release that a program can decide. Each check returns the problems it found,
|
|
3
|
+
* one line each, saying what and where; an empty list is a pass. The judgement half — changelog
|
|
4
|
+
* wording, docs against the diff, live checks — is each CLI's release skill.
|
|
5
|
+
*/
|
|
6
|
+
import { spawnSync } from "node:child_process";
|
|
7
|
+
import { readFileSync, writeFileSync } from "node:fs";
|
|
8
|
+
import { join } from "node:path";
|
|
9
|
+
export { changelogProblems, docsProblems, idPattern, JOURNAL_IDS, markdownFiles, slug, } from "./markdown.js";
|
|
10
|
+
/**
|
|
11
|
+
* Runs every check even after one fails, so one pass shows the whole list, and returns how many
|
|
12
|
+
* failed. The caller exits: a library that calls `process.exit` cannot be tested.
|
|
13
|
+
*/
|
|
14
|
+
export const releaseCheck = (checks, { version, log }) => {
|
|
15
|
+
let failed = 0;
|
|
16
|
+
for (const { name, run } of checks) {
|
|
17
|
+
const problems = run();
|
|
18
|
+
log(`${problems.length === 0 ? "ok " : "FAIL"} ${name}`);
|
|
19
|
+
for (const problem of problems)
|
|
20
|
+
log(problem.replace(/^/gm, " "));
|
|
21
|
+
if (problems.length > 0)
|
|
22
|
+
failed++;
|
|
23
|
+
}
|
|
24
|
+
log(failed === 0 ? `\n${version}: every check passed` : `\n${version}: ${failed} check(s) failed`);
|
|
25
|
+
return failed;
|
|
26
|
+
};
|
|
27
|
+
export const run = (root, command, args) => {
|
|
28
|
+
const done = spawnSync(command, args, { cwd: root, encoding: "utf8", maxBuffer: 64 * 1024 * 1024 });
|
|
29
|
+
const output = `${done.stdout ?? ""}${done.stderr ?? ""}${done.error ? String(done.error) : ""}`;
|
|
30
|
+
return { ok: done.status === 0, output };
|
|
31
|
+
};
|
|
32
|
+
/** A check that passes when the command exits 0, and otherwise shows the last 15 lines it printed. */
|
|
33
|
+
export const command = (root, name, ...args) => () => {
|
|
34
|
+
const { ok, output } = run(root, name, args);
|
|
35
|
+
return ok ? [] : [output.trim().split("\n").slice(-15).join("\n") || `exit ${name} ${args.join(" ")}`];
|
|
36
|
+
};
|
|
37
|
+
export const packageVersion = (root) => JSON.parse(readFileSync(join(root, "package.json"), "utf8")).version;
|
|
38
|
+
export const notOnNpm = (root, packageName, version) => () => run(root, "npm", ["view", `${packageName}@${version}`, "version", "--prefer-online"]).ok
|
|
39
|
+
? [`${version} is already on npm — raise the version`]
|
|
40
|
+
: [];
|
|
41
|
+
/**
|
|
42
|
+
* `allowed` holds exact paths, and directories ending in `/`; `said` is how the problem names them.
|
|
43
|
+
*/
|
|
44
|
+
export const packProblems = (paths, allowed, said) => paths
|
|
45
|
+
.filter((path) => !allowed.some((entry) => (entry.endsWith("/") ? path.startsWith(entry) : path === entry)))
|
|
46
|
+
.map((path) => `npm pack: ${path} would ship — only ${said} may`);
|
|
47
|
+
export const packContents = (root, allowed, said) => () => {
|
|
48
|
+
const { ok, output } = run(root, "npm", ["pack", "--dry-run", "--json", "--ignore-scripts"]);
|
|
49
|
+
if (!ok)
|
|
50
|
+
return [output.trim()];
|
|
51
|
+
const [packed] = JSON.parse(output.slice(output.indexOf("[")));
|
|
52
|
+
return packProblems((packed?.files ?? []).map(({ path }) => path), allowed, said);
|
|
53
|
+
};
|
|
54
|
+
const VERSION = /export const VERSION = "([^"]*)"/;
|
|
55
|
+
const versionsOf = (root, file) => {
|
|
56
|
+
const declared = packageVersion(root);
|
|
57
|
+
if (typeof declared !== "string" || !/^\d+\.\d+\.\d+(-[0-9A-Za-z.-]+)?$/.test(declared))
|
|
58
|
+
return `package.json has no usable "version" (got ${JSON.stringify(declared)})`;
|
|
59
|
+
const source = readFileSync(join(root, file), "utf8");
|
|
60
|
+
const current = VERSION.exec(source)?.[1];
|
|
61
|
+
if (!current)
|
|
62
|
+
return `${file} does not export a VERSION string`;
|
|
63
|
+
return { declared, current, source };
|
|
64
|
+
};
|
|
65
|
+
/**
|
|
66
|
+
* The whole of a `version:check` / `version:sync` script: what to print, and the exit code — 2 when
|
|
67
|
+
* either file cannot be read, 1 on drift. A CLI keeps its version in `package.json` and in a source
|
|
68
|
+
* file read at run time — `--version` must not load `package.json`, which is not there once the
|
|
69
|
+
* command is bundled — and the day they disagree is a release day.
|
|
70
|
+
*/
|
|
71
|
+
export const versionScript = (root, argv, file = "src/version.ts") => {
|
|
72
|
+
const found = versionsOf(root, file);
|
|
73
|
+
if (typeof found === "string")
|
|
74
|
+
return { code: 2, message: found };
|
|
75
|
+
const { declared, current, source } = found;
|
|
76
|
+
if (argv.includes("--sync")) {
|
|
77
|
+
if (current === declared)
|
|
78
|
+
return { code: 0, message: `version ${declared} — already in step` };
|
|
79
|
+
writeFileSync(join(root, file), source.replace(`"${current}"`, `"${declared}"`));
|
|
80
|
+
return { code: 0, message: `${file}: ${current} → ${declared}` };
|
|
81
|
+
}
|
|
82
|
+
if (current !== declared)
|
|
83
|
+
return {
|
|
84
|
+
code: 1,
|
|
85
|
+
message: `version drift: package.json says ${declared}, ${file} says ${current}.\n` +
|
|
86
|
+
"Run `pnpm version:sync` — package.json is the one that is edited.",
|
|
87
|
+
};
|
|
88
|
+
return { code: 0, message: `version ${declared} — package.json and ${file} agree` };
|
|
89
|
+
};
|
|
90
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/release/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/** The prefixes of the owner's journal ids; a project adds its own (`CLI`, `MAX`, …). */
|
|
2
|
+
export declare const JOURNAL_IDS: readonly ["NEED", "FIND", "BUG", "SEC", "PERF", "UX", "IDEA", "RISK", "DEBT", "ASK", "TASK"];
|
|
3
|
+
export declare const idPattern: (prefixes: readonly string[]) => RegExp;
|
|
4
|
+
export type ChangelogRules = {
|
|
5
|
+
/** The only `###` headings a section may have. */
|
|
6
|
+
headings: readonly string[];
|
|
7
|
+
/** The top section's title until it is dated, `Unreleased` or `Не выпущено`. */
|
|
8
|
+
unreleased: string;
|
|
9
|
+
ids: readonly string[];
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* `release`: the top section must be `version`, and nothing may be left unreleased — a release
|
|
13
|
+
* workflow builds its notes by matching `## <version> `, so a heading slightly off publishes empty
|
|
14
|
+
* notes without an error.
|
|
15
|
+
*/
|
|
16
|
+
export declare const changelogProblems: (text: string, { version, release, headings, unreleased, ids }: ChangelogRules & {
|
|
17
|
+
version?: string;
|
|
18
|
+
release: boolean;
|
|
19
|
+
}) => string[];
|
|
20
|
+
/** GitHub's heading anchor: lower case, only letters, digits, `-`, `_` and spaces kept, spaces to `-`. */
|
|
21
|
+
export declare const slug: (heading: string) => string;
|
|
22
|
+
/** Every `.md` under `directory`, not descending into a directory named in `skip`. */
|
|
23
|
+
export declare const markdownFiles: (directory: string, skip?: ReadonlySet<string>) => string[];
|
|
24
|
+
export type DocsRules = {
|
|
25
|
+
/** Absolute paths; a missing one is passed over. */
|
|
26
|
+
files: readonly string[];
|
|
27
|
+
ids: readonly string[];
|
|
28
|
+
/** A page a user reads, by its path relative to `root`: no correction mark, struck-out text or id. */
|
|
29
|
+
userPage?: (name: string) => boolean;
|
|
30
|
+
correction?: RegExp;
|
|
31
|
+
/** A link not to follow — a private or sibling checkout that CI does not have. */
|
|
32
|
+
skipLink?: (target: string, destination: string) => boolean;
|
|
33
|
+
};
|
|
34
|
+
export declare const docsProblems: (root: string, { files, ids, userPage, correction, skipLink }: DocsRules) => string[];
|
|
35
|
+
//# sourceMappingURL=markdown.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"markdown.d.ts","sourceRoot":"","sources":["../../src/release/markdown.ts"],"names":[],"mappings":"AAGA,yFAAyF;AACzF,eAAO,MAAM,WAAW,YAAI,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,CAAU,CAAA;AAEvH,eAAO,MAAM,SAAS,aAAc,SAAS,MAAM,EAAE,WAA4D,CAAA;AAIjH,MAAM,MAAM,cAAc,GAAG;IAC3B,kDAAkD;IAClD,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAA;IAC3B,gFAAgF;IAChF,UAAU,EAAE,MAAM,CAAA;IAClB,GAAG,EAAE,SAAS,MAAM,EAAE,CAAA;CACvB,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,SACtB,MAAM,mDACqC,cAAc,GAAG;IAAE,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,aAyCzG,CAAA;AAED,0GAA0G;AAC1G,eAAO,MAAM,IAAI,YAAa,MAAM,WAMb,CAAA;AA2BvB,sFAAsF;AACtF,eAAO,MAAM,aAAa,cAAe,MAAM,SAAQ,WAAW,CAAC,MAAM,CAAC,KAAe,MAAM,EAQvF,CAAA;AAER,MAAM,MAAM,SAAS,GAAG;IACtB,oDAAoD;IACpD,KAAK,EAAE,SAAS,MAAM,EAAE,CAAA;IACxB,GAAG,EAAE,SAAS,MAAM,EAAE,CAAA;IACtB,sGAAsG;IACtG,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAA;IACpC,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,kFAAkF;IAClF,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,KAAK,OAAO,CAAA;CAC5D,CAAA;AAED,eAAO,MAAM,YAAY,SACjB,MAAM,kDACsF,SAAS,aA8C5G,CAAA"}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
|
|
2
|
+
import { dirname, join, relative, resolve } from "node:path";
|
|
3
|
+
/** The prefixes of the owner's journal ids; a project adds its own (`CLI`, `MAX`, …). */
|
|
4
|
+
export const JOURNAL_IDS = ["NEED", "FIND", "BUG", "SEC", "PERF", "UX", "IDEA", "RISK", "DEBT", "ASK", "TASK"];
|
|
5
|
+
export const idPattern = (prefixes) => new RegExp(`\\b(?:${prefixes.join("|")})-\\d+\\b`, "g");
|
|
6
|
+
const VERSION_HEADING = /^## (\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?) — (\d{2})\.(\d{2})\.(\d{4})$/;
|
|
7
|
+
/**
|
|
8
|
+
* `release`: the top section must be `version`, and nothing may be left unreleased — a release
|
|
9
|
+
* workflow builds its notes by matching `## <version> `, so a heading slightly off publishes empty
|
|
10
|
+
* notes without an error.
|
|
11
|
+
*/
|
|
12
|
+
export const changelogProblems = (text, { version, release, headings, unreleased, ids }) => {
|
|
13
|
+
const problems = [];
|
|
14
|
+
const id = idPattern(ids);
|
|
15
|
+
let section;
|
|
16
|
+
let subheadings = new Set();
|
|
17
|
+
let first = true;
|
|
18
|
+
text.split("\n").forEach((line, index) => {
|
|
19
|
+
const where = `CHANGELOG.md:${index + 1}`;
|
|
20
|
+
if (line.startsWith("## ")) {
|
|
21
|
+
section = line.slice(3);
|
|
22
|
+
subheadings = new Set();
|
|
23
|
+
if (section === unreleased) {
|
|
24
|
+
if (release)
|
|
25
|
+
problems.push(`${where}: "${unreleased}" is left — date it as ${version} before releasing`);
|
|
26
|
+
else if (!first)
|
|
27
|
+
problems.push(`${where}: "${unreleased}" must be the top section`);
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
const found = VERSION_HEADING.exec(line);
|
|
31
|
+
if (!found)
|
|
32
|
+
problems.push(`${where}: "${line}" is not "## <version> — DD.MM.YYYY"`);
|
|
33
|
+
else {
|
|
34
|
+
const [, heading, day, month] = found;
|
|
35
|
+
if (Number(day) < 1 || Number(day) > 31 || Number(month) < 1 || Number(month) > 12)
|
|
36
|
+
problems.push(`${where}: "${line}" has no such date`);
|
|
37
|
+
if (release && first && heading !== version)
|
|
38
|
+
problems.push(`${where}: the top section is ${heading}, package.json says ${version}`);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
first = false;
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
if (line.startsWith("### ")) {
|
|
45
|
+
const heading = line.slice(4);
|
|
46
|
+
if (!headings.includes(heading))
|
|
47
|
+
problems.push(`${where}: "${heading}" is not one of: ${headings.join(", ")}`);
|
|
48
|
+
if (subheadings.has(heading))
|
|
49
|
+
problems.push(`${where}: "${heading}" twice in "${section}"`);
|
|
50
|
+
subheadings.add(heading);
|
|
51
|
+
}
|
|
52
|
+
for (const found of line.match(id) ?? [])
|
|
53
|
+
problems.push(`${where}: internal id ${found} — say what changed instead`);
|
|
54
|
+
});
|
|
55
|
+
if (release && first)
|
|
56
|
+
problems.push(`CHANGELOG.md: no section for ${version}`);
|
|
57
|
+
return problems;
|
|
58
|
+
};
|
|
59
|
+
/** GitHub's heading anchor: lower case, only letters, digits, `-`, `_` and spaces kept, spaces to `-`. */
|
|
60
|
+
export const slug = (heading) => heading
|
|
61
|
+
.replace(/!?\[([^\]]*)\]\([^)]*\)/g, "$1")
|
|
62
|
+
.replace(/<[^>]+>/g, "")
|
|
63
|
+
.toLowerCase()
|
|
64
|
+
.replace(/[^\p{L}\p{M}\p{N}\-_ ]/gu, "")
|
|
65
|
+
.replace(/ /g, "-");
|
|
66
|
+
const withoutCode = (text) => text
|
|
67
|
+
.replace(/^(```|~~~)[^\n]*\n[\s\S]*?^\1[^\n]*$/gm, (block) => block.replace(/[^\n]/g, " "))
|
|
68
|
+
.replace(/`[^`\n]*`/g, (span) => " ".repeat(span.length));
|
|
69
|
+
// Headings are read from the raw text, so code inside a heading still counts toward its anchor.
|
|
70
|
+
const anchorsOf = (text) => {
|
|
71
|
+
const raw = text.split("\n");
|
|
72
|
+
const anchors = new Set();
|
|
73
|
+
const counts = new Map();
|
|
74
|
+
withoutCode(text)
|
|
75
|
+
.split("\n")
|
|
76
|
+
.forEach((masked, index) => {
|
|
77
|
+
if (!/^#{1,6} /.test(masked))
|
|
78
|
+
return;
|
|
79
|
+
const heading = /^#{1,6} (.+?)\s*#*\s*$/.exec(raw[index] ?? "")?.[1];
|
|
80
|
+
if (heading === undefined)
|
|
81
|
+
return;
|
|
82
|
+
const base = slug(heading);
|
|
83
|
+
const seen = counts.get(base) ?? 0;
|
|
84
|
+
counts.set(base, seen + 1);
|
|
85
|
+
anchors.add(seen === 0 ? base : `${base}-${seen}`);
|
|
86
|
+
});
|
|
87
|
+
for (const [, name = ""] of text.matchAll(/<a\s+(?:name|id)="([^"]+)"/g))
|
|
88
|
+
anchors.add(name);
|
|
89
|
+
return anchors;
|
|
90
|
+
};
|
|
91
|
+
/** Every `.md` under `directory`, not descending into a directory named in `skip`. */
|
|
92
|
+
export const markdownFiles = (directory, skip = new Set()) => existsSync(directory)
|
|
93
|
+
? readdirSync(directory, { withFileTypes: true }).flatMap((entry) => {
|
|
94
|
+
if (skip.has(entry.name))
|
|
95
|
+
return [];
|
|
96
|
+
const path = join(directory, entry.name);
|
|
97
|
+
if (entry.isDirectory())
|
|
98
|
+
return markdownFiles(path, skip);
|
|
99
|
+
return entry.name.endsWith(".md") ? [path] : [];
|
|
100
|
+
})
|
|
101
|
+
: [];
|
|
102
|
+
export const docsProblems = (root, { files, ids, userPage = () => false, correction = /correction \d{4}/i, skipLink = () => false }) => {
|
|
103
|
+
const id = idPattern(ids);
|
|
104
|
+
const cache = new Map();
|
|
105
|
+
const anchors = (path) => {
|
|
106
|
+
let found = cache.get(path);
|
|
107
|
+
if (!found) {
|
|
108
|
+
found = anchorsOf(readFileSync(path, "utf8"));
|
|
109
|
+
cache.set(path, found);
|
|
110
|
+
}
|
|
111
|
+
return found;
|
|
112
|
+
};
|
|
113
|
+
const problems = [];
|
|
114
|
+
for (const path of files.filter((file) => existsSync(file))) {
|
|
115
|
+
const name = relative(root, path);
|
|
116
|
+
const user = userPage(name);
|
|
117
|
+
withoutCode(readFileSync(path, "utf8"))
|
|
118
|
+
.split("\n")
|
|
119
|
+
.forEach((line, index) => {
|
|
120
|
+
const where = `${name}:${index + 1}`;
|
|
121
|
+
for (const [, target] of line.matchAll(/!?\[[^\]]*\]\(([^)\s]+)(?:\s+"[^"]*")?\)/g)) {
|
|
122
|
+
if (!target || /^[a-z][a-z0-9+.-]*:/i.test(target))
|
|
123
|
+
continue;
|
|
124
|
+
const [file = "", anchor] = decodeURIComponent(target).split("#");
|
|
125
|
+
const destination = file === "" ? path : resolve(dirname(path), file);
|
|
126
|
+
if (skipLink(file, destination))
|
|
127
|
+
continue;
|
|
128
|
+
if (!existsSync(destination)) {
|
|
129
|
+
problems.push(`${where}: link to ${file} — no such file`);
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
if (anchor &&
|
|
133
|
+
destination.endsWith(".md") &&
|
|
134
|
+
statSync(destination).isFile() &&
|
|
135
|
+
!anchors(destination).has(anchor))
|
|
136
|
+
problems.push(`${where}: link to ${file}#${anchor} — no such heading`);
|
|
137
|
+
}
|
|
138
|
+
if (!user)
|
|
139
|
+
return;
|
|
140
|
+
if (correction.test(line))
|
|
141
|
+
problems.push(`${where}: a correction mark on a user page`);
|
|
142
|
+
if (line.includes("~~"))
|
|
143
|
+
problems.push(`${where}: struck-out text on a user page`);
|
|
144
|
+
for (const found of line.match(id) ?? [])
|
|
145
|
+
problems.push(`${where}: internal id ${found} on a user page`);
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
return problems;
|
|
149
|
+
};
|
|
150
|
+
//# sourceMappingURL=markdown.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"markdown.js","sourceRoot":"","sources":["../../src/release/markdown.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@leemour/cli-core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "The parts every command line tool needs: output modes, terminal rendering, error model, exit codes, credentials, clocks",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Viacheslav Ptsarev",
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
"engines": {
|
|
13
13
|
"node": ">=22"
|
|
14
14
|
},
|
|
15
|
+
"packageManager": "pnpm@11.20.0",
|
|
15
16
|
"files": [
|
|
16
17
|
"dist",
|
|
17
18
|
"README.md",
|
|
@@ -49,8 +50,27 @@
|
|
|
49
50
|
"./codegen/runtime": {
|
|
50
51
|
"types": "./dist/codegen/runtime.d.ts",
|
|
51
52
|
"default": "./dist/codegen/runtime.js"
|
|
53
|
+
},
|
|
54
|
+
"./release": {
|
|
55
|
+
"types": "./dist/release/index.d.ts",
|
|
56
|
+
"default": "./dist/release/index.js"
|
|
52
57
|
}
|
|
53
58
|
},
|
|
59
|
+
"scripts": {
|
|
60
|
+
"build": "tsc --build",
|
|
61
|
+
"typecheck": "tsc --build --force --emitDeclarationOnly && tsc -p tsconfig.test.json && tsc -p tsconfig.scripts.json",
|
|
62
|
+
"lint": "biome check .",
|
|
63
|
+
"lint:fix": "biome check --write .",
|
|
64
|
+
"docs:check": "pnpm build && node --experimental-strip-types scripts/docs-check.ts",
|
|
65
|
+
"test": "vitest run",
|
|
66
|
+
"test:watch": "vitest",
|
|
67
|
+
"test:coverage": "vitest run --coverage",
|
|
68
|
+
"test:slow": "vitest run --reporter=json --outputFile=coverage/tests.json >/dev/null; node --experimental-strip-types scripts/slow-tests.ts",
|
|
69
|
+
"smoke:bun": "bun run scripts/smoke.ts",
|
|
70
|
+
"prepare": "lefthook install || true",
|
|
71
|
+
"secrets:scan": "gitleaks git --no-banner --redact .",
|
|
72
|
+
"prepublishOnly": "pnpm build"
|
|
73
|
+
},
|
|
54
74
|
"dependencies": {
|
|
55
75
|
"cli-table3": "^0.6.5",
|
|
56
76
|
"env-paths": "^4.0.0",
|
|
@@ -64,10 +84,11 @@
|
|
|
64
84
|
"devDependencies": {
|
|
65
85
|
"@biomejs/biome": "^2.3.14",
|
|
66
86
|
"@types/node": "^22.20.4",
|
|
87
|
+
"@vitest/coverage-v8": "^5.0.2",
|
|
67
88
|
"commander": "^15.0.0",
|
|
68
89
|
"lefthook": "^2.1.8",
|
|
69
90
|
"typescript": "^7.0.2",
|
|
70
|
-
"vitest": "^5.0.
|
|
91
|
+
"vitest": "^5.0.2"
|
|
71
92
|
},
|
|
72
93
|
"publishConfig": {
|
|
73
94
|
"access": "public"
|
|
@@ -79,15 +100,5 @@
|
|
|
79
100
|
"commander": {
|
|
80
101
|
"optional": true
|
|
81
102
|
}
|
|
82
|
-
},
|
|
83
|
-
"scripts": {
|
|
84
|
-
"build": "tsc --build",
|
|
85
|
-
"typecheck": "tsc --build --force --emitDeclarationOnly",
|
|
86
|
-
"lint": "biome check .",
|
|
87
|
-
"lint:fix": "biome check --write .",
|
|
88
|
-
"test": "vitest run",
|
|
89
|
-
"test:watch": "vitest",
|
|
90
|
-
"smoke:bun": "bun run scripts/smoke.ts",
|
|
91
|
-
"secrets:scan": "gitleaks git --no-banner --redact ."
|
|
92
103
|
}
|
|
93
|
-
}
|
|
104
|
+
}
|