Repository navigation
Expand file tree
/
Copy pathgenerate-mship-contracts.ts
More file actions
147 lines (131 loc) · 4.86 KB
/
Copy pathgenerate-mship-contracts.ts
File metadata and controls
147 lines (131 loc) · 4.86 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
#!/usr/bin/env bun
// Drive every mothership contract generator, then biome-format the
// outputs so the committed files match what biome produces on commit
// (avoids the stale-drift that comes from comparing raw json2ts output
// against biome-formatted source).
//
// `--check` regenerates into a temp directory, formats identically,
// and compares against the committed files — same semantics as the
// old per-script `--check`, but accounts for post-generate formatting.
import { spawnSync } from 'node:child_process'
import { readFileSync, unlinkSync, writeFileSync } from 'node:fs'
import { dirname, join, resolve } from 'node:path'
import { fileURLToPath } from 'node:url'
const ROOT = resolve(dirname(fileURLToPath(import.meta.url)), '..')
const GENERATORS = [
'scripts/sync-mothership-stream-contract.ts',
'scripts/sync-billing-protocol-contract.ts',
'scripts/sync-tool-catalog.ts',
'scripts/sync-trace-spans-contract.ts',
'scripts/sync-trace-attributes-contract.ts',
'scripts/sync-trace-attribute-values-contract.ts',
'scripts/sync-trace-events-contract.ts',
'scripts/sync-metrics-contract.ts',
'scripts/sync-vfs-snapshot-contract.ts',
]
// Generated files under this path. We biome-format this whole dir on
// each generate (and the temp copy on each check).
const GENERATED_DIR = 'apps/sim/lib/mothership/generated'
// `tool-schemas-v1.ts` goes through biome's `--unsafe` bracket-quote
// fixer which reformats every key of TOOL_RUNTIME_SCHEMAS. Strip it
// from the format pass so generator output stays stable on both sides.
const FORMAT_EXCLUDE = new Set(['tool-schemas-v1.ts'])
function run(cmd: string[], cwd: string, env: NodeJS.ProcessEnv = process.env): void {
const result = spawnSync(cmd[0], cmd.slice(1), {
cwd,
env,
stdio: 'inherit',
})
if (result.status !== 0) {
process.exit(result.status ?? 1)
}
}
function runGenerators(outputOverride?: string): void {
const env = { ...process.env }
for (const script of GENERATORS) {
const args = ['bun', 'run', script]
if (outputOverride) {
// Individual scripts don't accept a custom output dir; for
// --check we generate in place and snapshot before/after via
// git-index comparison (see runCheck).
}
run(args, ROOT, env)
}
}
function formatGenerated(dir: string): void {
// biome.json excludes the generated dir, and `biome check` on excluded paths
// exits nonzero with "No files were processed" — each sync script already
// formats its own output through a neutral stdin path, so this batch pass is
// a per-file re-check that must not consult the ignore list.
const files = readdirNoThrow(dir).filter((f) => !FORMAT_EXCLUDE.has(f) && f.endsWith('.ts'))
if (files.length === 0) return
const paths = files.map((f) => join(dir, f))
run(
[
'bunx',
'biome',
'check',
'--write',
'--files-ignore-unknown=true',
...paths,
'--no-errors-on-unmatched',
],
ROOT
)
}
function readdirNoThrow(dir: string): string[] {
try {
// Bun has fs.readdirSync available as a CommonJS import
const fs = require('node:fs') as typeof import('node:fs')
return fs.readdirSync(dir)
} catch {
return []
}
}
function runCheck(): void {
const targetDir = resolve(ROOT, GENERATED_DIR)
// Snapshot current committed state
const committed: Record<string, string> = {}
for (const f of readdirNoThrow(targetDir)) {
if (!f.endsWith('.ts')) continue
committed[f] = readFileSync(join(targetDir, f), 'utf8')
}
// Regenerate in place + format, then diff against the snapshot
runGenerators()
formatGenerated(targetDir)
const stale: string[] = []
const generated = readdirNoThrow(targetDir).filter((f) => f.endsWith('.ts'))
const generatedSet = new Set(generated)
for (const [name, oldContent] of Object.entries(committed)) {
if (FORMAT_EXCLUDE.has(name)) continue
if (!generatedSet.has(name)) {
stale.push(name)
continue
}
const newContent = readFileSync(join(targetDir, name), 'utf8')
if (newContent !== oldContent) stale.push(name)
}
for (const name of generated) {
if (!(name in committed)) stale.push(name)
}
// Restore the committed state regardless of outcome (--check is readonly).
for (const [name, content] of Object.entries(committed)) {
writeFileSync(join(targetDir, name), content, 'utf8')
}
for (const name of generated) {
if (!(name in committed)) unlinkSync(join(targetDir, name))
}
if (stale.length > 0) {
console.error(`Generated contracts are stale: ${stale.join(', ')}. Run: bun run mship:generate`)
process.exit(1)
}
console.log('All generated contracts up to date.')
}
function runGenerate(): void {
runGenerators()
formatGenerated(resolve(ROOT, GENERATED_DIR))
console.log('Generated + formatted mothership contracts.')
}
const checkOnly = process.argv.includes('--check')
if (checkOnly) runCheck()
else runGenerate()