@davidvornholt/standards 0.26.1 → 0.26.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/package.json +1 -2
- package/src/cli.ts +1 -1
- package/src/structure-check.ts +10 -12
- package/src/structure-readme.ts +0 -85
package/README.md
CHANGED
|
@@ -97,7 +97,7 @@ bun standards structure --profile source
|
|
|
97
97
|
|
|
98
98
|
`doctor` checks project-owned integration points such as `biome.jsonc`, `AGENTS.local.md`, package scripts, sync policy, settings overlays, and Dependabot inputs.
|
|
99
99
|
|
|
100
|
-
`structure` checks the exact Bun declaration, workspace scripts, internal dependency versions, package exports, TypeScript inheritance, accessibility wiring,
|
|
100
|
+
`structure` checks the exact Bun declaration, workspace scripts, internal dependency versions, package exports, TypeScript inheritance, accessibility wiring, and CI secret structure. The `source` profile records the few intentional differences in this repository itself.
|
|
101
101
|
|
|
102
102
|
## dependabot
|
|
103
103
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@davidvornholt/standards",
|
|
3
|
-
"version": "0.26.
|
|
3
|
+
"version": "0.26.2",
|
|
4
4
|
"description": "Bootstrap, synchronize, and validate davidvornholt/standards consumers",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -166,7 +166,6 @@
|
|
|
166
166
|
"src/structure-check.ts",
|
|
167
167
|
"src/structure-bun-version.ts",
|
|
168
168
|
"src/structure-profile.ts",
|
|
169
|
-
"src/structure-readme.ts",
|
|
170
169
|
"src/structure-secret-document.ts",
|
|
171
170
|
"src/structure-script.ts",
|
|
172
171
|
"src/structure-secrets.ts",
|
package/src/cli.ts
CHANGED
|
@@ -1189,7 +1189,7 @@ const parseArgs = (argv: ReadonlyArray<string>): CliOptions => {
|
|
|
1189
1189
|
|
|
1190
1190
|
// Canonical monorepo structure gate: workspace and root script shapes,
|
|
1191
1191
|
// internal versioning, `exports`, tsconfig inheritance, a11y wiring,
|
|
1192
|
-
//
|
|
1192
|
+
// and the undecrypted CI secrets contract
|
|
1193
1193
|
// (SOPS shape mirroring plus the keys the synced workflows require).
|
|
1194
1194
|
// The `source` profile instead pins the standards template repository's own
|
|
1195
1195
|
// deliberate exceptions to the consumer contract.
|
package/src/structure-check.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { existsSync } from 'node:fs';
|
|
2
2
|
import { readdir } from 'node:fs/promises';
|
|
3
3
|
import { isAbsolute, join } from 'node:path';
|
|
4
|
+
import { isContainedPath } from './contained-path';
|
|
4
5
|
import { isRecord } from './github-settings-parse';
|
|
5
6
|
import { readJsonFile } from './json-file';
|
|
6
7
|
import { collectBunVersionProblems } from './structure-bun-version';
|
|
@@ -9,7 +10,6 @@ import {
|
|
|
9
10
|
rootScriptExpectations,
|
|
10
11
|
type StructureProfile,
|
|
11
12
|
} from './structure-profile';
|
|
12
|
-
import { collectWorkspaceReadmeProblems } from './structure-readme';
|
|
13
13
|
import { filteredTurboAliasProblem, hasSafeCommands } from './structure-script';
|
|
14
14
|
import { collectCiSecretsProblems } from './structure-secrets';
|
|
15
15
|
import { inspectWorkspace, type Workspace } from './structure-workspace';
|
|
@@ -106,6 +106,12 @@ const loadWorkspace = async (
|
|
|
106
106
|
if (!existsSync(path)) {
|
|
107
107
|
return { workspace: null, problem: null };
|
|
108
108
|
}
|
|
109
|
+
if (!isContainedPath(consumer, `${rel}/package.json`, 'file')) {
|
|
110
|
+
return {
|
|
111
|
+
workspace: null,
|
|
112
|
+
problem: `${rel}: package.json must be a contained regular file; symlinked paths are not allowed`,
|
|
113
|
+
};
|
|
114
|
+
}
|
|
109
115
|
const manifest = await readJsonFile(path);
|
|
110
116
|
if (manifest === null) {
|
|
111
117
|
return {
|
|
@@ -174,16 +180,9 @@ export const collectStructureProblems = async (
|
|
|
174
180
|
.map((ws) => ws.manifest.name)
|
|
175
181
|
.filter((name): name is string => typeof name === 'string'),
|
|
176
182
|
);
|
|
177
|
-
const
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
),
|
|
181
|
-
collectWorkspaceReadmeProblems(
|
|
182
|
-
consumer,
|
|
183
|
-
profile,
|
|
184
|
-
workspaces.map((ws) => ws.rel),
|
|
185
|
-
),
|
|
186
|
-
]);
|
|
183
|
+
const inspections = await Promise.all(
|
|
184
|
+
workspaces.map((ws) => inspectWorkspace(ws, workspaceNames, profile)),
|
|
185
|
+
);
|
|
187
186
|
const requireA11y = inspections.some((i) => i.hasA11ySuite);
|
|
188
187
|
return [
|
|
189
188
|
...declaration.problems,
|
|
@@ -193,7 +192,6 @@ export const collectStructureProblems = async (
|
|
|
193
192
|
...collectBunVersionProblems(root),
|
|
194
193
|
...inspectRootScripts(root, profile, requireA11y),
|
|
195
194
|
...inspections.flatMap((i) => [...i.problems]),
|
|
196
|
-
...readmeProblems,
|
|
197
195
|
...secretsProblems,
|
|
198
196
|
];
|
|
199
197
|
};
|
package/src/structure-readme.ts
DELETED
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
import { lstat, readFile } from 'node:fs/promises';
|
|
2
|
-
import { join } from 'node:path';
|
|
3
|
-
import { isContainedPath } from './contained-path';
|
|
4
|
-
import { readJsonFile } from './json-file';
|
|
5
|
-
import type { StructureProfile } from './structure-profile';
|
|
6
|
-
|
|
7
|
-
const SYNC_MANIFEST = 'sync-standards.json';
|
|
8
|
-
const TRAILING_SLASHES = /\/+$/u;
|
|
9
|
-
const MANIFEST_REQUIREMENT = `${SYNC_MANIFEST}: must contain a JSON object with a "paths" array of strings; the structure gate reads it to tell canonical workspaces from repo-owned ones`;
|
|
10
|
-
const MANIFEST_CONTAINMENT = `${SYNC_MANIFEST}: must be a contained regular file; symlinked paths are not allowed`;
|
|
11
|
-
|
|
12
|
-
type CanonicalRels = {
|
|
13
|
-
readonly rels: ReadonlySet<string>;
|
|
14
|
-
readonly problem: string | null;
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
// Canonical workspaces are synced from the standards template and cannot be
|
|
18
|
-
// fixed in a consumer checkout, so the consumer profile exempts them from
|
|
19
|
-
// repo-owned documentation rules. The source repository owns those workspaces,
|
|
20
|
-
// so the source profile exempts nothing.
|
|
21
|
-
const canonicalWorkspaceRels = async (
|
|
22
|
-
consumer: string,
|
|
23
|
-
profile: StructureProfile,
|
|
24
|
-
): Promise<CanonicalRels> => {
|
|
25
|
-
if (profile === 'source') {
|
|
26
|
-
return { rels: new Set(), problem: null };
|
|
27
|
-
}
|
|
28
|
-
const manifestPath = join(consumer, SYNC_MANIFEST);
|
|
29
|
-
const manifestInfo = await lstat(manifestPath).catch(() => null);
|
|
30
|
-
if (
|
|
31
|
-
manifestInfo !== null &&
|
|
32
|
-
!isContainedPath(consumer, SYNC_MANIFEST, 'file')
|
|
33
|
-
) {
|
|
34
|
-
return { rels: new Set(), problem: MANIFEST_CONTAINMENT };
|
|
35
|
-
}
|
|
36
|
-
const manifest = await readJsonFile(manifestPath);
|
|
37
|
-
const paths = manifest?.paths;
|
|
38
|
-
if (
|
|
39
|
-
!(
|
|
40
|
-
Array.isArray(paths) &&
|
|
41
|
-
paths.every((path): path is string => typeof path === 'string')
|
|
42
|
-
)
|
|
43
|
-
) {
|
|
44
|
-
return { rels: new Set(), problem: MANIFEST_REQUIREMENT };
|
|
45
|
-
}
|
|
46
|
-
return {
|
|
47
|
-
rels: new Set(paths.map((path) => path.replace(TRAILING_SLASHES, ''))),
|
|
48
|
-
problem: null,
|
|
49
|
-
};
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
const readmeProblem = async (
|
|
53
|
-
consumer: string,
|
|
54
|
-
rel: string,
|
|
55
|
-
): Promise<string | null> => {
|
|
56
|
-
const readmeRel = `${rel}/README.md`;
|
|
57
|
-
const readmePath = join(consumer, readmeRel);
|
|
58
|
-
const info = await lstat(readmePath).catch(() => null);
|
|
59
|
-
if (info !== null && !isContainedPath(consumer, readmeRel, 'file')) {
|
|
60
|
-
return `${rel}: README.md must be a contained regular file; symlinked paths are not allowed`;
|
|
61
|
-
}
|
|
62
|
-
const raw = await readFile(readmePath, 'utf8').catch(() => null);
|
|
63
|
-
return raw !== null && raw.trim() !== ''
|
|
64
|
-
? null
|
|
65
|
-
: `${rel}: repo-owned workspace must have a non-empty README.md`;
|
|
66
|
-
};
|
|
67
|
-
|
|
68
|
-
export const collectWorkspaceReadmeProblems = async (
|
|
69
|
-
consumer: string,
|
|
70
|
-
profile: StructureProfile,
|
|
71
|
-
rels: ReadonlyArray<string>,
|
|
72
|
-
): Promise<ReadonlyArray<string>> => {
|
|
73
|
-
const canonical = await canonicalWorkspaceRels(consumer, profile);
|
|
74
|
-
if (canonical.problem !== null) {
|
|
75
|
-
// Without the manifest the gate cannot tell canonical workspaces from
|
|
76
|
-
// repo-owned ones, so it fails on the manifest instead of guessing.
|
|
77
|
-
return [canonical.problem];
|
|
78
|
-
}
|
|
79
|
-
const problems = await Promise.all(
|
|
80
|
-
rels
|
|
81
|
-
.filter((rel) => !canonical.rels.has(rel))
|
|
82
|
-
.map((rel) => readmeProblem(consumer, rel)),
|
|
83
|
-
);
|
|
84
|
-
return problems.filter((problem): problem is string => problem !== null);
|
|
85
|
-
};
|