New check: check:origins, no deployment address baked into the site
bun scripts/check-runtime-origins.ts (bun run check:origins) holds the branch's rule: - nuxt.config declares neither per-deployment address; - under app/, only the runtime loader reads dxMapsBase, and only it and the menu read researchGalleryUrl; - no source file or static page names a deployment host (nuxtbase.app, sslip.io, a *.localhost address or a 46xx laptop port). It refuses to pass on fewer than 50 files. This branch: PASS, 306 files. The subdomain branch it was cut from fails with 12 problems: two addresses in nuxt.config, six reads under app/, and the hardcoded maps host in nuxt.config and in the two static pages.
This commit is contained in:
parent
5dea900183
commit
80b6cd5775
2 changed files with 84 additions and 1 deletions
|
|
@ -10,7 +10,8 @@
|
|||
"start": "bun run .output/server/index.mjs",
|
||||
"postinstall": "nuxt prepare",
|
||||
"lint": "eslint .",
|
||||
"typecheck": "nuxt typecheck"
|
||||
"typecheck": "nuxt typecheck",
|
||||
"check:origins": "bun scripts/check-runtime-origins.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-s3": "^3.914.0",
|
||||
|
|
|
|||
82
scripts/check-runtime-origins.ts
Normal file
82
scripts/check-runtime-origins.ts
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
#!/usr/bin/env bun
|
||||
// check-runtime-origins: no deployment's address is baked into this site.
|
||||
//
|
||||
// The plugin-studio branch sends readers to the Research Gallery and loads its maps from the gallery's
|
||||
// /cdn. Both addresses differ per deployment, so the server reads them at REQUEST time
|
||||
// (server/utils/dx-runtime.ts), and pages ask /api/dx-runtime or link through /research-gallery. About
|
||||
// 4,000 pages are prerendered, and a prerendered page carries the build's config. An address that
|
||||
// reaches nuxt.config's runtimeConfig, or a page, is baked into the build, which is how the CDN branch
|
||||
// failed. This check holds the line:
|
||||
// 1. nuxt.config.ts declares neither address;
|
||||
// 2. under app/, only app/utils/dxRuntime.ts touches dxMapsBase, and only it, app/utils/nav.ts and
|
||||
// app/components/public/TheHeader.vue touch researchGalleryUrl (the menu's key, resolved to
|
||||
// /research-gallery);
|
||||
// 3. no source file or static page names a deployment host (nuxtbase.app, sslip.io, a *.localhost
|
||||
// address or a 46xx laptop port), since those belong in the environment.
|
||||
// Static: reads files, starts nothing. `bun scripts/check-runtime-origins.ts [root]`. Exit 0 pass, 1 fail.
|
||||
import { readdirSync, readFileSync, statSync } from 'node:fs'
|
||||
import { join, relative, resolve } from 'node:path'
|
||||
|
||||
const ROOT = resolve(process.argv[2] || join(import.meta.dir, '..'))
|
||||
const fails: string[] = []
|
||||
let scanned = 0
|
||||
|
||||
function walk(dir: string, out: string[] = []): string[] {
|
||||
for (const e of readdirSync(dir)) {
|
||||
if (e === 'node_modules' || e.startsWith('.')) continue
|
||||
const p = join(dir, e)
|
||||
if (statSync(p).isDirectory()) walk(p, out)
|
||||
else if (/\.(vue|ts|js|mjs|html)$/.test(e)) out.push(p)
|
||||
}
|
||||
return out
|
||||
}
|
||||
const rel = (p: string) => relative(ROOT, p)
|
||||
const lines = (text: string, re: RegExp) =>
|
||||
text.split('\n').map((l, i) => [i + 1, l] as const).filter(([, l]) => re.test(l))
|
||||
|
||||
// 1. nuxt.config.ts declares neither address.
|
||||
const config = readFileSync(join(ROOT, 'nuxt.config.ts'), 'utf8')
|
||||
scanned += 1
|
||||
for (const [n, l] of lines(config, /\b(dxMapsBase|researchGalleryUrl)\s*:/)) {
|
||||
fails.push(`nuxt.config.ts:${n} declares a per-deployment address in the build's config: ${l.trim()}`)
|
||||
}
|
||||
|
||||
// 2. Only the runtime loader (and the menu's key) touch the two names under app/.
|
||||
const ALLOWED: Record<string, string[]> = {
|
||||
dxMapsBase: ['app/utils/dxRuntime.ts'],
|
||||
researchGalleryUrl: ['app/utils/dxRuntime.ts', 'app/utils/nav.ts', 'app/components/public/TheHeader.vue']
|
||||
}
|
||||
for (const file of walk(join(ROOT, 'app'))) {
|
||||
scanned += 1
|
||||
const text = readFileSync(file, 'utf8')
|
||||
for (const [name, allowed] of Object.entries(ALLOWED)) {
|
||||
if (allowed.includes(rel(file))) continue
|
||||
for (const [n, l] of lines(text, new RegExp(`\\b${name}\\b`))) {
|
||||
fails.push(`${rel(file)}:${n} reads ${name} outside the runtime loader (use loadDxRuntime or /research-gallery): ${l.trim()}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 3. No deployment host in source or static pages (data under public/assets is not scanned).
|
||||
const HOST = /(\bnuxtbase\.app\b|\bsslip\.io\b|\b[a-z0-9-]+\.localhost\b|localhost:46\d\d)/
|
||||
const sources = [
|
||||
...walk(join(ROOT, 'app')), ...walk(join(ROOT, 'server')), ...walk(join(ROOT, 'modules')),
|
||||
join(ROOT, 'nuxt.config.ts'),
|
||||
...walk(join(ROOT, 'public')).filter((f) => f.endsWith('.html') && !rel(f).startsWith('public/assets/'))
|
||||
]
|
||||
for (const file of sources) {
|
||||
scanned += 1
|
||||
for (const [n, l] of lines(readFileSync(file, 'utf8'), HOST)) {
|
||||
fails.push(`${rel(file)}:${n} names a deployment host (it belongs in the environment): ${l.trim().slice(0, 160)}`)
|
||||
}
|
||||
}
|
||||
|
||||
// Refuse to pass vacuously: the walk must have read the site.
|
||||
if (scanned < 50) fails.push(`only ${scanned} file(s) scanned under ${ROOT}: the walk is broken or the root is wrong`)
|
||||
|
||||
if (fails.length) {
|
||||
console.error(`RUNTIME-ORIGINS: FAIL — ${fails.length} problem(s)`)
|
||||
for (const f of fails) console.error(` ✗ ${f}`)
|
||||
process.exit(1)
|
||||
}
|
||||
console.log(`RUNTIME-ORIGINS: PASS — ${scanned} files; no deployment address in the build's config, the pages or the static pages`)
|
||||
Loading…
Reference in a new issue