@casomoltd/tooling 0.29.0 → 0.30.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 +2 -2
- package/bin/check-personal-data.mjs +12 -12
- package/bin/check-private-refs.mjs +12 -13
- package/bin/utils.mjs +48 -6
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -30,8 +30,8 @@ Shared linting, formatting, commit config, and CLI tools for Casomo Ltd's repos.
|
|
|
30
30
|
| `build-report` | Compile a Typst client report to PDF with the house template |
|
|
31
31
|
| `render-figures` | Pre-render a design spec's mermaid figures to static SVG |
|
|
32
32
|
| `spec-check` | Check a design spec's anchors, contents, figures and numbering |
|
|
33
|
-
| `check-private-refs` | In a public repo, refuse a private tracker link or a name listed in `
|
|
34
|
-
| `check-personal-data` | Refuse an email, National Insurance number, UK mobile number, date of birth or locally denylisted name; per-repo allowances in `
|
|
33
|
+
| `check-private-refs` | In a public repo, refuse a private tracker link or a name listed under `privateRefs` in `casomo.config.mjs` |
|
|
34
|
+
| `check-personal-data` | Refuse an email, National Insurance number, UK mobile number, date of birth or locally denylisted name; per-repo allowances under `personalData` in `casomo.config.mjs` |
|
|
35
35
|
|
|
36
36
|
## Install
|
|
37
37
|
|
|
@@ -16,16 +16,18 @@
|
|
|
16
16
|
*
|
|
17
17
|
* Deliberately configurable, because this file ships in a public package and
|
|
18
18
|
* cannot name anybody's domains or reference formats. A consuming repo adds
|
|
19
|
-
* its own in `
|
|
19
|
+
* its own under `personalData` in its `casomo.config.mjs`:
|
|
20
20
|
*
|
|
21
21
|
* export default {
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
22
|
+
* personalData: {
|
|
23
|
+
* allowEmail: [/@example-org\.com$/i], // addresses that are not people
|
|
24
|
+
* rules: [ // repo-specific identifiers
|
|
25
|
+
* {name: "case reference", re: /\bCASE-\d{4,}\b/g,
|
|
26
|
+
* allow: (m) => m === "CASE-0000"},
|
|
27
|
+
* ],
|
|
28
|
+
* denylist: ".personal-data-denylist", // the default; gitignore it
|
|
29
|
+
* allow: [/\.test\.mjs$/], // paths exempted, with care
|
|
30
|
+
* },
|
|
29
31
|
* };
|
|
30
32
|
*
|
|
31
33
|
* Usage:
|
|
@@ -35,7 +37,7 @@
|
|
|
35
37
|
import {execFileSync} from "node:child_process";
|
|
36
38
|
import {existsSync, readFileSync, realpathSync, statSync} from "node:fs";
|
|
37
39
|
import {pathToFileURL} from "node:url";
|
|
38
|
-
import {
|
|
40
|
+
import {CASOMO_CONFIG_KEYS, loadCasomoConfig} from "./utils.mjs";
|
|
39
41
|
|
|
40
42
|
/** Addresses in every repo's own tooling that name no person. */
|
|
41
43
|
const ALLOWED_EMAIL = [
|
|
@@ -217,9 +219,7 @@ export function scan(fileList, read, rules, names) {
|
|
|
217
219
|
}
|
|
218
220
|
|
|
219
221
|
async function main() {
|
|
220
|
-
const
|
|
221
|
-
// `loadConfig` returns the module, so the config is its default export.
|
|
222
|
-
const config = mod?.default ?? {};
|
|
222
|
+
const config = await loadCasomoConfig(CASOMO_CONFIG_KEYS.personalData);
|
|
223
223
|
const denylist = config.denylist ?? ".personal-data-denylist";
|
|
224
224
|
const rules = [...identityRules(config.allowEmail), ...(config.rules ?? [])];
|
|
225
225
|
const names = denied(denylist);
|
|
@@ -21,21 +21,24 @@
|
|
|
21
21
|
* a public package, so it cannot itself name anybody's private repos,
|
|
22
22
|
* boards or hosts — that would be the disclosure it exists to prevent.
|
|
23
23
|
* The defaults cover hosted trackers that are private by default; a
|
|
24
|
-
* consuming repo adds its own names in
|
|
24
|
+
* consuming repo adds its own names under `privateRefs` in its
|
|
25
|
+
* `casomo.config.mjs`:
|
|
25
26
|
*
|
|
26
27
|
* export default {
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
28
|
+
* privateRefs: {
|
|
29
|
+
* visibility: 'public', // optional; skips the gh lookup
|
|
30
|
+
* forbidden: [ // strings or RegExp
|
|
31
|
+
* {pattern: /internal\.example\.net/, why: 'internal hostname'},
|
|
32
|
+
* ],
|
|
33
|
+
* allow: [/CHANGELOG\.md$/], // paths exempted, with care
|
|
34
|
+
* },
|
|
32
35
|
* };
|
|
33
36
|
*/
|
|
34
37
|
|
|
35
38
|
import {execFileSync} from "node:child_process";
|
|
36
39
|
import fs from "node:fs";
|
|
37
40
|
import path from "node:path";
|
|
38
|
-
import {
|
|
41
|
+
import {CASOMO_CONFIG_KEYS, loadCasomoConfig} from "./utils.mjs";
|
|
39
42
|
|
|
40
43
|
/**
|
|
41
44
|
* Hosted trackers whose links are private by default.
|
|
@@ -95,11 +98,7 @@ function toMatchers(entries) {
|
|
|
95
98
|
}
|
|
96
99
|
|
|
97
100
|
async function main() {
|
|
98
|
-
const
|
|
99
|
-
// `loadConfig` returns the MODULE, so the config is its default
|
|
100
|
-
// export. Reading the module object itself silently found no
|
|
101
|
-
// patterns and reported a clean repo.
|
|
102
|
-
const config = mod?.default ?? {};
|
|
101
|
+
const config = await loadCasomoConfig(CASOMO_CONFIG_KEYS.privateRefs);
|
|
103
102
|
const visibility = repoVisibility(config.visibility);
|
|
104
103
|
|
|
105
104
|
if (visibility === null) {
|
|
@@ -109,7 +108,7 @@ async function main() {
|
|
|
109
108
|
console.warn(
|
|
110
109
|
"check-private-refs: SKIPPED — could not determine repo "
|
|
111
110
|
+ "visibility (is `gh` installed and authenticated?). Set "
|
|
112
|
-
+ "`visibility` in
|
|
111
|
+
+ "`privateRefs.visibility` in casomo.config.mjs to be explicit.",
|
|
113
112
|
);
|
|
114
113
|
return;
|
|
115
114
|
}
|
package/bin/utils.mjs
CHANGED
|
@@ -39,13 +39,55 @@ export function openPath(target) {
|
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
/**
|
|
42
|
-
* Load an optional per-repo config file from
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
42
|
+
* Load an optional per-repo config file from `dir` (the working directory by
|
|
43
|
+
* default): the module, or `{}` where there is no file. The file-level loader
|
|
44
|
+
* under `loadCasomoConfig`, which is where a gate's settings belong; a tool
|
|
45
|
+
* still reading its own `<tool>.config.mjs` (`readability`) calls this.
|
|
46
46
|
*/
|
|
47
|
-
export async function loadConfig(filename) {
|
|
48
|
-
const cfgPath = path.join(
|
|
47
|
+
export async function loadConfig(filename, dir = process.cwd()) {
|
|
48
|
+
const cfgPath = path.join(dir, filename);
|
|
49
49
|
if (!fs.existsSync(cfgPath)) return {};
|
|
50
50
|
return import(pathToFileURL(cfgPath).href);
|
|
51
51
|
}
|
|
52
|
+
|
|
53
|
+
/** The keys a repo's `casomo.config.mjs` may hold, one per gate. */
|
|
54
|
+
export const CASOMO_CONFIG_KEYS = {
|
|
55
|
+
personalData: "personalData",
|
|
56
|
+
privateRefs: "privateRefs",
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
/** The files each gate read before `casomo.config.mjs`, by the key their
|
|
60
|
+
* settings now go under. A repo still holding one would have its settings
|
|
61
|
+
* ignored, so their presence fails the run. */
|
|
62
|
+
const RETIRED_CONFIGS = {
|
|
63
|
+
"check-personal-data.config.mjs": CASOMO_CONFIG_KEYS.personalData,
|
|
64
|
+
"check-private-refs.config.mjs": CASOMO_CONFIG_KEYS.privateRefs,
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* One gate's settings from the repo's `casomo.config.mjs`, by the key it
|
|
69
|
+
* reads: every gate a repo configures is configured in that one file, so a
|
|
70
|
+
* repo carries one config rather than one per tool. An absent file or key
|
|
71
|
+
* reads as no settings; a retired per-tool file, or a key no gate reads, is
|
|
72
|
+
* refused, because either would otherwise read as no settings too and a
|
|
73
|
+
* gate would pass on less than it was given.
|
|
74
|
+
*/
|
|
75
|
+
export async function loadCasomoConfig(key, dir = process.cwd()) {
|
|
76
|
+
for (const [file, moveTo] of Object.entries(RETIRED_CONFIGS)) {
|
|
77
|
+
if (fs.existsSync(path.join(dir, file))) {
|
|
78
|
+
throw new Error(`${file} is no longer read: move its settings under `
|
|
79
|
+
+ `\`${moveTo}\` in casomo.config.mjs and delete it`);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
const mod = await loadConfig("casomo.config.mjs", dir);
|
|
83
|
+
// The settings are the module's default export; the module object itself
|
|
84
|
+
// holds no gate key and would read as no settings.
|
|
85
|
+
const config = mod?.default ?? {};
|
|
86
|
+
const known = Object.values(CASOMO_CONFIG_KEYS);
|
|
87
|
+
const unknown = Object.keys(config).filter((k) => !known.includes(k));
|
|
88
|
+
if (unknown.length) {
|
|
89
|
+
throw new Error(`casomo.config.mjs holds ${unknown.join(", ")}, which no `
|
|
90
|
+
+ `gate reads; the keys are ${known.join(", ")}`);
|
|
91
|
+
}
|
|
92
|
+
return config[key] ?? {};
|
|
93
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@casomoltd/tooling",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.30.0",
|
|
4
4
|
"description": "Casomo's shared dev config: ESLint, Prettier, commitlint, knip & jscpd presets and dev CLIs (git hooks, readability, screenshots, design specs)",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"scripts": {
|
|
24
24
|
"check": "node bin/check-gates.mjs && node bin/verify-pack.mjs && npm run check:private-refs && npm run check:personal-data && npm run typecheck && npm run test && npm run lint:md",
|
|
25
25
|
"typecheck": "tsc --noEmit -p hooks/tsconfig.json",
|
|
26
|
-
"test": "node hooks/test.mts && node bin/check-gates.test.mjs && node bin/check-personal-data.test.mjs && node commitlint/index.test.mjs",
|
|
26
|
+
"test": "node hooks/test.mts && node bin/check-gates.test.mjs && node bin/check-personal-data.test.mjs && node bin/casomo-config.test.mjs && node commitlint/index.test.mjs",
|
|
27
27
|
"lint:md": "remark --frail --quiet --no-stdout --rc-path bin/skills-lint.config.mjs skills agents docs",
|
|
28
28
|
"build": "echo 'no-op: source-only repo, nothing to build'",
|
|
29
29
|
"prepare": "husky || true",
|