Skip to content

Commit 27cc633

Browse files
committed
chore: adopt socket-repo-template kind config + schema
Adds the new fleet-canonical socket-repo-template config: - `.socket-repo-template.json` declaring this repo's kind, repoName, and schema version. Read by sync-scaffolding's kind-aware check. - `scripts/socket-repo-template-schema.mts` — TypeBox source of truth (single source for the JSON Schema below + future runtime validation). - `scripts/socket-repo-template-emit-schema.mts` — emitter that regenerates the JSON Schema from the TypeBox source. - `socket-repo-template-schema.json` — generated draft 2020-12 JSON Schema (referenced by `.socket-repo-template.json`'s `$schema` for editor autocompletion). Synced from socket-repo-template via sync-scaffolding.
1 parent 36075a2 commit 27cc633

4 files changed

Lines changed: 468 additions & 0 deletions

File tree

.socket-repo-template.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"$schema": "./socket-repo-template-schema.json",
3+
"schemaVersion": 1,
4+
"repoName": "socket-lib",
5+
"kind": "single-package"
6+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/**
2+
* @fileoverview Emit `socket-repo-template-schema.json` from the
3+
* TypeBox source.
4+
*
5+
* Run via `pnpm run socket-repo-template:emit-schema` from a fleet
6+
* repo (the worktree where TypeBox is installed). Mirrors the xport
7+
* emit pattern.
8+
*/
9+
10+
import { writeFileSync } from 'node:fs'
11+
import path from 'node:path'
12+
import { fileURLToPath } from 'node:url'
13+
14+
import { getDefaultLogger } from '@socketsecurity/lib/logger'
15+
16+
import { SocketRepoTemplateConfigSchema } from './socket-repo-template-schema.mts'
17+
18+
const logger = getDefaultLogger()
19+
20+
const __dirname = path.dirname(fileURLToPath(import.meta.url))
21+
const rootDir = path.resolve(__dirname, '..')
22+
const outPath = path.join(rootDir, 'socket-repo-template-schema.json')
23+
24+
const enriched = {
25+
$schema: 'https://json-schema.org/draft/2020-12/schema',
26+
$id: 'https://github.com/SocketDev/socket-repo-template-schema.json',
27+
title: 'socket-repo-template per-repo config',
28+
...SocketRepoTemplateConfigSchema,
29+
}
30+
31+
writeFileSync(outPath, JSON.stringify(enriched, null, 2) + '\n', 'utf8')
32+
logger.success(`wrote ${path.relative(rootDir, outPath)}`)
Lines changed: 263 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,263 @@
1+
/**
2+
* @fileoverview TypeBox schema for `.socket-repo-template.json` — the
3+
* per-fleet-repo config consumed by `sync-scaffolding`.
4+
*
5+
* Each fleet repo (socket-lib, socket-cli, ultrathink, …) ships a
6+
* `.socket-repo-template.json` at its root declaring its kind +
7+
* any per-repo opt-ins. The runner reads it to decide which optional
8+
* files the repo is expected to ship and which it must not ship.
9+
*
10+
* Source-of-truth flow:
11+
* - This TypeBox source → `Static<typeof SocketRepoTemplateConfigSchema>`
12+
* for typed reads in the runner.
13+
* - `socket-repo-template-emit-schema.mts` writes
14+
* `socket-repo-template-schema.json` (draft 2020-12) at the repo root.
15+
* - `.socket-repo-template.json` references the JSON Schema via its
16+
* `$schema` field for IDE autocompletion.
17+
*
18+
* Byte-identical across the fleet via sync-scaffolding's IDENTICAL_FILES.
19+
*/
20+
21+
import { Type, type Static } from '@sinclair/typebox'
22+
23+
// ---------------------------------------------------------------------------
24+
// Kind enum.
25+
// ---------------------------------------------------------------------------
26+
27+
const KindSchema = Type.Union(
28+
[
29+
Type.Literal('single-package'),
30+
Type.Literal('mono-no-native'),
31+
Type.Literal('consumer'),
32+
Type.Literal('producer'),
33+
Type.Literal('both'),
34+
Type.Literal('lang-ports'),
35+
],
36+
{
37+
description:
38+
'Fleet repo category. Determines which opt-in files the repo must ship and which it must not. See README.md "Fleet kinds" for the table.',
39+
},
40+
)
41+
42+
// ---------------------------------------------------------------------------
43+
// Hooks block — git hook variant selection.
44+
// ---------------------------------------------------------------------------
45+
46+
const HooksSchema = Type.Object(
47+
{
48+
enablePrePush: Type.Optional(
49+
Type.Boolean({
50+
description:
51+
'Wire `.husky/pre-push` → `.git-hooks/pre-push.mts`. Mandatory security gate; default true.',
52+
}),
53+
),
54+
enableCommitMsg: Type.Optional(
55+
Type.Boolean({
56+
description:
57+
'Wire `.husky/commit-msg` → `.git-hooks/commit-msg.mts`. Strips AI attribution; default true.',
58+
}),
59+
),
60+
enablePreCommit: Type.Optional(
61+
Type.Boolean({
62+
description:
63+
'Wire `.husky/pre-commit` → `.git-hooks/pre-commit.mts`. Lint + secret scan on staged files; default true.',
64+
}),
65+
),
66+
preCommitVariant: Type.Optional(
67+
Type.Union([Type.Literal('lint-only'), Type.Literal('lint-test')], {
68+
description:
69+
'`lint-only` runs format + secret scan; `lint-test` adds vitest on touched packages. Default `lint-test`.',
70+
}),
71+
),
72+
},
73+
{ description: 'Git-hook opt-ins.' },
74+
)
75+
76+
// ---------------------------------------------------------------------------
77+
// Scripts block — package.json script declarations.
78+
// ---------------------------------------------------------------------------
79+
80+
const ScriptsSchema = Type.Object(
81+
{
82+
required: Type.Optional(
83+
Type.Array(Type.String(), {
84+
description:
85+
'Override REQUIRED_SCRIPTS from manifest.mts. Usually omitted — the fleet default applies.',
86+
}),
87+
),
88+
optional: Type.Optional(
89+
Type.Record(Type.String(), Type.Boolean(), {
90+
description:
91+
'Per-script opt-in map keyed by script name. `true` = repo ships this RECOMMENDED script; `false` = explicit opt-out.',
92+
}),
93+
),
94+
bodyExempt: Type.Optional(
95+
Type.Array(Type.String(), {
96+
description:
97+
'Script names whose body is allowed to drift from the canonical form (e.g. socket-lib runs a richer test runner than the standard `node scripts/test.mts`). Each entry is the script name only.',
98+
}),
99+
),
100+
},
101+
{ description: 'package.json script tracking overrides.' },
102+
)
103+
104+
// ---------------------------------------------------------------------------
105+
// Lint block — oxlint profile selection.
106+
// ---------------------------------------------------------------------------
107+
108+
const LintSchema = Type.Object(
109+
{
110+
profile: Type.Optional(
111+
Type.Union([Type.Literal('standard'), Type.Literal('rich')], {
112+
description:
113+
'`standard` requires the fleet plugin set (import + typescript + unicorn). `rich` opts into a wider set; check the runner for the exact basenames currently exempted.',
114+
}),
115+
),
116+
},
117+
{ description: 'oxlint profile.' },
118+
)
119+
120+
// ---------------------------------------------------------------------------
121+
// Workflows block — GitHub Actions opt-ins.
122+
// ---------------------------------------------------------------------------
123+
124+
const WorkflowsSchema = Type.Object(
125+
{
126+
ci: Type.Optional(
127+
Type.Boolean({ description: 'Ship `.github/workflows/ci.yml`.' }),
128+
),
129+
weeklyUpdate: Type.Optional(
130+
Type.Boolean({
131+
description: 'Ship `.github/workflows/weekly-update.yml`.',
132+
}),
133+
),
134+
provenance: Type.Optional(
135+
Type.Boolean({
136+
description:
137+
'Repo publishes with npm provenance (OIDC). Hint for setup helpers; not enforced by the checker today.',
138+
}),
139+
),
140+
requirePinnedFullSha: Type.Optional(
141+
Type.Boolean({
142+
description:
143+
'Enforce 40-char SHA pins on every `uses:` ref. Defaults to true; an opt-out is reserved for special cases (e.g. workflow-dispatch test rigs) and currently has no consumer.',
144+
}),
145+
),
146+
},
147+
{ description: 'CI workflow opt-ins.' },
148+
)
149+
150+
// ---------------------------------------------------------------------------
151+
// Claude block — opt-in agents/skills/commands.
152+
// ---------------------------------------------------------------------------
153+
154+
const ClaudeSchema = Type.Object(
155+
{
156+
includeSecurityScanSkill: Type.Optional(
157+
Type.Boolean({
158+
description: 'Ship `.claude/skills/security-scan/SKILL.md`.',
159+
}),
160+
),
161+
includeSharedSkills: Type.Optional(
162+
Type.Boolean({
163+
description:
164+
'Ship `.claude/skills/_shared/*` — env-check, path-guard-rule, report-format, security-tools, verify-build.',
165+
}),
166+
),
167+
includeUpdatingSkill: Type.Optional(
168+
Type.Boolean({
169+
description:
170+
'Ship the dependency-update skill. Reserved — no consumer wired today.',
171+
}),
172+
),
173+
},
174+
{ description: 'Claude Code opt-ins.' },
175+
)
176+
177+
// ---------------------------------------------------------------------------
178+
// Workspace block — pnpm-workspace.yaml derived settings.
179+
// ---------------------------------------------------------------------------
180+
181+
const WorkspaceSchema = Type.Object(
182+
{
183+
allowBuilds: Type.Optional(
184+
Type.Record(Type.String(), Type.Boolean(), {
185+
description:
186+
'pnpm `onlyBuiltDependencies` allowlist. Map a package name to true/false to grant/deny build scripts.',
187+
}),
188+
),
189+
blockExoticSubdeps: Type.Optional(
190+
Type.Boolean({
191+
description:
192+
'Refuse transitive git/tarball subdeps (direct git deps still allowed). Required true; the field exists so a repo can document the intent locally.',
193+
}),
194+
),
195+
minimumReleaseAge: Type.Optional(
196+
Type.Integer({
197+
minimum: 0,
198+
description:
199+
'Soak window in minutes before installing freshly-published packages. Fleet default 10080 (= 7 days).',
200+
}),
201+
),
202+
minimumReleaseAgeExclude: Type.Optional(
203+
Type.Array(Type.String(), {
204+
description:
205+
'Scopes / package patterns exempt from the soak window. Socket-owned scopes typically listed here.',
206+
}),
207+
),
208+
resolutionMode: Type.Optional(
209+
Type.Union([Type.Literal('highest'), Type.Literal('lowest-direct')], {
210+
description: 'pnpm `resolutionMode`. Fleet default `highest`.',
211+
}),
212+
),
213+
trustPolicy: Type.Optional(
214+
Type.Union([Type.Literal('no-downgrade'), Type.Literal('match-spec')], {
215+
description: 'pnpm `trustPolicy`. Fleet default `no-downgrade`.',
216+
}),
217+
),
218+
},
219+
{
220+
description:
221+
'pnpm-workspace.yaml setting hints. The runner reads from the YAML; this block exists for repos that prefer to declare intent in JSON.',
222+
},
223+
)
224+
225+
// ---------------------------------------------------------------------------
226+
// Top-level config.
227+
// ---------------------------------------------------------------------------
228+
229+
export const SocketRepoTemplateConfigSchema = Type.Object(
230+
{
231+
$schema: Type.Optional(
232+
Type.String({
233+
description:
234+
'JSON Schema reference for editor autocompletion. Conventionally `./socket-repo-template-schema.json`.',
235+
}),
236+
),
237+
schemaVersion: Type.Literal(1, {
238+
description:
239+
'Schema version. Bump on breaking changes; readers gate on it.',
240+
}),
241+
repoName: Type.String({
242+
pattern: '^[a-z0-9][a-z0-9-]*$',
243+
description:
244+
'Canonical repo basename (e.g. `socket-lib`, `ultrathink`). Used for kind-independent exemptions like the oxlint `socket-lib` carve-out.',
245+
}),
246+
kind: KindSchema,
247+
hooks: Type.Optional(HooksSchema),
248+
scripts: Type.Optional(ScriptsSchema),
249+
lint: Type.Optional(LintSchema),
250+
workflows: Type.Optional(WorkflowsSchema),
251+
claude: Type.Optional(ClaudeSchema),
252+
workspace: Type.Optional(WorkspaceSchema),
253+
},
254+
{
255+
description:
256+
'Per-repo socket-repo-template config. Lives at the fleet repo root as `.socket-repo-template.json`.',
257+
},
258+
)
259+
260+
export type SocketRepoTemplateConfig = Static<
261+
typeof SocketRepoTemplateConfigSchema
262+
>
263+
export type Kind = Static<typeof KindSchema>

0 commit comments

Comments
 (0)