-
Notifications
You must be signed in to change notification settings - Fork 57
Expand file tree
/
Copy pathpre-release-check.ts
More file actions
178 lines (154 loc) · 5.87 KB
/
pre-release-check.ts
File metadata and controls
178 lines (154 loc) · 5.87 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
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
#!/usr/bin/env bun
/**
* Pre-release sanity check — run BEFORE tagging a release.
*
* Verifies:
* 1. All required external NAPI modules are in package.json dependencies
* 2. The publish script will include them in the wrapper package
* 3. A local build produces a binary that actually starts
*
* Usage: bun run packages/opencode/script/pre-release-check.ts
*/
import fs from "fs"
import path from "path"
import { spawnSync } from "child_process"
import { fileURLToPath } from "url"
const __dirname = path.dirname(fileURLToPath(import.meta.url))
const pkgDir = path.resolve(__dirname, "..")
const repoRoot = path.resolve(pkgDir, "../..")
const pkg = JSON.parse(fs.readFileSync(path.join(pkgDir, "package.json"), "utf-8"))
let failures = 0
function pass(msg: string) {
console.log(` ✓ ${msg}`)
}
function fail(msg: string) {
console.error(` ✗ ${msg}`)
failures++
}
// ---------------------------------------------------------------------------
// Check 1: Required externals are in package.json dependencies
// ---------------------------------------------------------------------------
console.log("\n[1/5] Checking required externals in package.json...")
const requiredExternals = ["@altimateai/altimate-core"]
for (const ext of requiredExternals) {
if (pkg.dependencies?.[ext]) {
pass(`${ext} is in dependencies (${pkg.dependencies[ext]})`)
} else {
fail(`${ext} is NOT in dependencies — binary will crash at runtime`)
}
}
// ---------------------------------------------------------------------------
// Check 2: Required externals are resolvable in node_modules
// ---------------------------------------------------------------------------
console.log("\n[2/5] Checking required externals are installed...")
for (const ext of requiredExternals) {
try {
require.resolve(ext)
pass(`${ext} resolves from node_modules`)
} catch {
fail(`${ext} is NOT installed — run \`bun install\``)
}
}
// ---------------------------------------------------------------------------
// Check 2b: Verify altimate-core napi binary has all expected exports
// ---------------------------------------------------------------------------
console.log("\n[2b/5] Verifying altimate-core napi exports...")
const CRITICAL_EXPORTS = [
"getStatementTypes", "formatSql", "lint", "validate", "transpile",
"extractMetadata", "columnLineage", "trackLineage", "diffSchemas",
"importDdl", "exportDdl", "optimizeContext", "pruneSchema",
"compareQueries", "classifyPii", "checkQueryPii", "parseDbtProject",
]
try {
const core = require("@altimateai/altimate-core")
const missing = CRITICAL_EXPORTS.filter((name) => typeof core[name] !== "function")
if (missing.length > 0) {
fail(
`altimate-core binary is missing ${missing.length} export(s): ${missing.join(", ")}.\n` +
` The platform binary may be stale. Fix: rm -rf node_modules && bun install`,
)
} else {
pass(`All ${CRITICAL_EXPORTS.length} critical napi exports verified`)
}
} catch (e: any) {
fail(`altimate-core failed to load: ${e.message}`)
}
// ---------------------------------------------------------------------------
// Check 3: Build and smoke-test the binary
// ---------------------------------------------------------------------------
console.log("\n[3/5] Building local binary...")
const buildResult = spawnSync("bun", ["run", "build:local"], {
cwd: pkgDir,
encoding: "utf-8",
timeout: 120_000,
env: {
...process.env,
MODELS_DEV_API_JSON: path.join(pkgDir, "test/tool/fixtures/models-api.json"),
},
})
if (buildResult.status !== 0) {
fail(`Build failed:\n${buildResult.stderr}`)
} else {
pass("Local build succeeded")
// Find the binary — walk recursively for scoped packages (@altimateai/...)
const distDir = path.join(pkgDir, "dist")
let binaryPath: string | undefined
const binaryNames = process.platform === "win32" ? ["altimate.exe", "altimate"] : ["altimate"]
function searchDist(dir: string): string | undefined {
if (!fs.existsSync(dir)) return undefined
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
if (!entry.isDirectory()) continue
const sub = path.join(dir, entry.name)
for (const name of binaryNames) {
const candidate = path.join(sub, "bin", name)
if (fs.existsSync(candidate)) return candidate
}
const nested = searchDist(sub)
if (nested) return nested
}
return undefined
}
binaryPath = searchDist(distDir)
if (!binaryPath) {
fail("No binary found in dist/ after build")
} else {
console.log("\n[4/5] Smoke-testing compiled binary...")
// Resolve NODE_PATH like the bin wrapper does — start from pkgDir
// to include workspace-level node_modules where NAPI modules live
const nodePaths: string[] = []
let current = pkgDir
for (;;) {
const nm = path.join(current, "node_modules")
if (fs.existsSync(nm)) nodePaths.push(nm)
const parent = path.dirname(current)
if (parent === current) break
current = parent
}
const smokeResult = spawnSync(binaryPath, ["--version"], {
encoding: "utf-8",
timeout: 15_000,
env: {
...process.env,
NODE_PATH: nodePaths.join(path.delimiter),
OPENCODE_DISABLE_TELEMETRY: "1",
},
})
if (smokeResult.status === 0) {
const version = (smokeResult.stdout ?? "").trim()
pass(`Binary starts successfully (${version})`)
} else {
const output = (smokeResult.stdout ?? "") + (smokeResult.stderr ?? "")
fail(`Binary crashed on startup:\n${output}`)
}
}
}
// ---------------------------------------------------------------------------
// Summary
// ---------------------------------------------------------------------------
console.log("")
if (failures > 0) {
console.error(`FAILED: ${failures} check(s) failed. Do NOT tag a release.`)
process.exit(1)
} else {
console.log("ALL CHECKS PASSED. Safe to tag a release.")
}