#!/usr/bin/env node import { spawnSync } from 'node:child_process' import { existsSync, readFileSync, readdirSync, statSync } from 'node:fs' import path from 'node:path' import process from 'node:process' const root = path.resolve(import.meta.dirname, '..') const releaseId = 'cache-contract-test' const yarn = process.platform === 'win32' ? 'yarn.cmd' : 'yarn' function run(args) { const result = spawnSync(yarn, args, { cwd: root, env: { ...process.env, VITE_RELEASE_ID: releaseId }, stdio: 'inherit', }) if (result.status !== 0) process.exit(result.status ?? 1) } function files(directory, suffix) { return readdirSync(directory, { withFileTypes: true }).flatMap(entry => { const current = path.join(directory, entry.name) if (entry.isDirectory()) return files(current, suffix) return entry.name.endsWith(suffix) ? [current] : [] }) } function requireText(file, values) { const content = readFileSync(file, 'utf8') for (const value of values) { if (!content.includes(value)) { throw new Error(`${path.relative(root, file)} 缺少缓存契约:${value}`) } } } function verifyBuild(output, publicPrefix) { const assetDirectory = path.join(output, 'releases', releaseId, 'assets') if (!existsSync(assetDirectory) || !statSync(assetDirectory).isDirectory()) { throw new Error(`${path.relative(root, output)} 未生成版本化 assets 目录`) } const html = files(output, '.html').map(file => readFileSync(file, 'utf8')).join('\n') const expected = `${publicPrefix}releases/${releaseId}/assets/` if (!html.includes(expected)) { throw new Error(`${path.relative(root, output)} 的 HTML 未引用 ${expected}`) } } run(['workspace', 'tenant-platform', 'build']) run(['workspace', 'ops-platform', 'build']) run(['workspace', '@xuqm/docs-site', 'build']) verifyBuild(path.join(root, 'tenant-platform', 'dist'), '/') verifyBuild(path.join(root, 'ops-platform', 'dist'), '/') verifyBuild(path.join(root, 'docs-site', 'docs', '.vitepress', 'dist'), '/docs/') for (const nginx of ['nginx/tenant.conf', 'nginx/ops.conf']) { requireText(path.join(root, nginx), [ 'no-cache, no-store, must-revalidate', 'public, max-age=31536000, immutable', 'try_files $uri =404', ]) } for (const dockerfile of ['Dockerfile.tenant', 'Dockerfile.ops']) { requireText(path.join(root, dockerfile), [ 'ARG PREVIOUS_IMAGE=', '/etc/xuqm_asset_layout_v2', 'COPY --from=previous', 'VITE_RELEASE_ID=${SERVICE_VERSION}', ]) } console.log('Web 缓存与上一版本资源保留契约验证通过')