gongxiangp2/scripts/material-preview-release-gate.mjs
WuYongJie a890660853 fix(materials): audit historical previews and gate production releases
Preserve known legacy failures by reviewed identity, probe material delivery before and after releases, and retain originals and downloads.
2026-10-23 19:47:58 +08:00

310 lines
23 KiB
JavaScript

// Read-only release inventory and bounded probes. No writes to materials or storage.
import { createHash } from 'node:crypto'
import { readFile } from 'node:fs/promises'
import { createRequire } from 'node:module'
import path from 'node:path'
import { fileURLToPath } from 'node:url'
import { checkMaterialPreviewHealth, directFetch } from './check-material-preview-health.mjs'
import { collectMaterialPreviewInventory } from './export-material-preview-health-manifest.mjs'
import { LEGACY_DOMAINS } from './check-legacy-storage-boundaries.mjs'
export const POLICY = Object.freeze({ version: 1, concurrency: 4, probes: 768, bytes: 128 * 1024 * 1024, wallMs: 360_000 })
export const CRITICAL_MATERIAL_IDS = Object.freeze([
'6e763f52-a21f-4b4e-99fb-634478f68eed', '82f3a6b7-6323-4cf5-a741-d990182f382c',
'd3a0addb-b4f0-4110-958d-3f897490fa4c', '72a39203-7f61-43b5-b243-73fe19ffde99',
'a6b97e8a-bd68-483f-b8f8-743e8baef850', '0329debe-054d-4ee7-9411-59f7cb2cdcc1',
'b9a9947c-ef68-4616-9144-a0d1fd912799', 'ac19c78a-c09f-4ed9-a3c3-12bf9c950a56',
'ea4cb1d2-fd25-46cb-aeba-93508c0793f8', '6c2675ab-c495-4c63-8d63-24ddee9e6535',
'c9abdd91-a666-4f36-a475-6e082906a94e', 'd0e929f7-1186-4eac-acef-3af74a9567ee',
])
export const DELIVERY_FILES = Object.freeze(['lib/url-rewrite.ts', 'lib/proxy-media-security.ts', 'app/api/proxy-media/route.ts', 'lib/s3.ts', 'lib/network-detection.ts'])
export const KNOWN_FRP_FAILURE = Object.freeze({ bytes: 489, bodySha256: '967e369d3be4fab5eb1229ec015b2923abf6bc81ebe3aaa662cb095cbec81023' })
export const KNOWN_COMPATIBILITY_PROOF = Object.freeze({ materialId: '6e763f52-a21f-4b4e-99fb-634478f68eed',
bodySha256: 'b40687cac0cbd537e209f8caa56c642bf501a658d0c3585140e8b9950937da53', width: 768, height: 1600 })
export const KNOWN_LEGACY_MEMBERS_SHA256 = '953e6b1c9e10ea05e30e289ebbd5364569d918b5dd4a1273af52abc531fe475c'
export const DELIVERY_CONFIG_KEYS = Object.freeze(['S3_ENDPOINT', 'S3_BUCKET', 'S3_FILE_PATH', 'S3_PUBLIC_DOMAIN', 'S3_REGION', 'SERVER_IP', 'MINIO_API_PORT', 'APP_PUBLIC_URL'])
// bundleReleaseGate replaces this literal with hashes from the exact build checkout.
const PINNED_TARGET_DELIVERY_CODE = null
const sha = value => createHash('sha256').update(value).digest('hex')
const digest = value => sha(JSON.stringify(value))
const runtimeRequire = createRequire(import.meta.url)
export class GateError extends Error { constructor(code) { super(code); this.code = code } }
const fail = code => { throw new GateError(code) }
const sourceKey = row => `${row.materialId}:${row.sourceSha256}`
const probeKey = row => `${row.materialId}:${row.role}:${row.sourceSha256}`
async function readKnownLegacyProof(root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..')) {
const policy = JSON.parse(await readFile(path.join(root, 'scripts/fixtures/material-preview-known-legacy-20261023.json'), 'utf8'))
if (policy?.version !== 1 || policy.reviewedAuditSha256 !== '40eb8d834d5c6bd9edcdc49e69ae227a83be2374bb9b525a0cec0bcd652c4e0d'
|| policy.members?.length !== 472 || new Set(policy.members.map(row => row.materialId)).size !== 472
|| digest(policy.members) !== KNOWN_LEGACY_MEMBERS_SHA256 || policy.membersSha256 !== KNOWN_LEGACY_MEMBERS_SHA256) fail('reviewed_legacy_membership_proof_changed')
return policy.members
}
// The exact-checkout bundler inlines this reviewed ID/hash-only proof.
const PINNED_KNOWN_LEGACY_MEMBERS = await readKnownLegacyProof()
export function normalizeDeliveryConfig(raw) {
if (!raw || Object.keys(raw).sort().join('|') !== [...DELIVERY_CONFIG_KEYS].sort().join('|')) fail('complete_delivery_configuration_required')
const values = Object.fromEntries(DELIVERY_CONFIG_KEYS.map(key => {
if (typeof raw[key] !== 'string' || raw[key].length > 2048 || /[\x00-\x1f\x7f]/.test(raw[key])) fail('invalid_delivery_configuration')
return [key, raw[key].trim()]
}))
for (const key of ['S3_ENDPOINT', 'S3_PUBLIC_DOMAIN', 'APP_PUBLIC_URL']) {
let url
try { url = new URL(values[key]) } catch { fail('invalid_delivery_configuration_url') }
if (!['http:', 'https:'].includes(url.protocol) || url.username || url.password || url.search || url.hash) fail('credential_or_query_in_delivery_configuration')
values[key] = url.href.replace(/\/$/, '')
}
if (!/^[a-z0-9][a-z0-9.-]{1,61}[a-z0-9]$/.test(values.S3_BUCKET) || values.S3_BUCKET.includes('..')) fail('invalid_delivery_bucket')
if (values.SERVER_IP && !/^[A-Za-z0-9.:\[\]-]+$/.test(values.SERVER_IP)) fail('invalid_delivery_server')
values.MINIO_API_PORT ||= '9000'
if (!/^\d{1,5}$/.test(values.MINIO_API_PORT) || Number(values.MINIO_API_PORT) < 1 || Number(values.MINIO_API_PORT) > 65535) fail('invalid_delivery_port')
values.S3_REGION ||= 'us-east-1'
return values
}
export function canonicalDeliverySource(source, publicDomain) {
const current = new URL(publicDomain)
if (current.href.replace(/\/$/, '') !== 'https://s3.jiansun.vip/gongxiangpan') fail('untrusted_public_bucket_configuration')
const url = new URL(source)
// Independent historical bucket is deliberately never mapped to the current bucket.
if (url.hostname === LEGACY_DOMAINS[0] && url.pathname.startsWith('/jsxiangche/')) return url.href
if (!url.pathname.startsWith('/gongxiangpan/')) return url.href
url.protocol = current.protocol; url.hostname = current.hostname; url.port = ''
return url.href
}
export function inventoryIndex(inventory) {
const { audit, manifests } = inventory
if (audit?.inventoryComplete !== true || audit.activeCount !== audit.exportedCount + audit.uncheckableCount
|| manifests.reduce((n, batch) => n + batch.materials.length, 0) !== audit.exportedCount) fail('inventory_coverage_incomplete')
const index = [
...manifests.flatMap(batch => batch.materials.map(row => ({ materialId: row.materialId, sourceSha256: sha(row.registeredSource), registeredSource: row.registeredSource }))),
...audit.uncheckable.map(row => ({ materialId: row.materialId, sourceSha256: row.sourceSha256, uncheckable: { classification: row.classification, code: row.code } })),
].sort((a, b) => a.materialId.localeCompare(b.materialId, 'en'))
if (index.length !== audit.activeCount || new Set(index.map(row => row.materialId)).size !== index.length) fail('inventory_coverage_incomplete')
return index
}
function validateIndex(index) {
if (!Array.isArray(index) || new Set(index.map(row => row.materialId)).size !== index.length
|| index.some(row => typeof row.materialId !== 'string' || !/^[a-f0-9]{64}$/.test(row.sourceSha256)
|| (row.registeredSource ? sha(row.registeredSource) !== row.sourceSha256 : !row.uncheckable))) fail('invalid_inventory_index')
}
export function buildPlan(index, prior, { publicDomain, criticalIds = CRITICAL_MATERIAL_IDS, expandedScope = false, bootstrap = false } = {}) {
validateIndex(index)
if (!bootstrap) validateAccepted(prior)
const previous = new Map((prior?.inventory || []).map(row => [row.materialId, row]))
const required = new Set(criticalIds), selected = [], unverified = [], changedUncheckable = []
for (const row of index) {
const old = previous.get(row.materialId)
const changed = !old || sourceKey(old) !== sourceKey(row)
if (!row.registeredSource) {
if (!bootstrap && changed) changedUncheckable.push(row.materialId)
unverified.push({ materialId: row.materialId, reason: 'unsupported_or_missing_registered_source' })
continue
}
const source = new URL(row.registeredSource)
const independentLegacy = source.hostname === LEGACY_DOMAINS[0] && source.pathname.startsWith('/jsxiangche/')
const reasons = [...(independentLegacy ? ['independent_legacy_storage'] : []), ...(required.has(row.materialId) ? ['critical_material'] : []),
...(!bootstrap && changed ? ['new_or_changed_source'] : []), ...(expandedScope ? ['delivery_dependency_changed'] : [])]
if (!reasons.length) { unverified.push({ materialId: row.materialId, reason: 'unchanged_current_source_outside_bounded_scope' }); continue }
const delivery = canonicalDeliverySource(row.registeredSource, publicDomain)
selected.push({ materialId: row.materialId, registeredSource: row.registeredSource, sourceSha256: row.sourceSha256, reasons,
deliverySource: delivery, independentLegacy })
}
const present = new Set(selected.map(row => row.materialId))
if ([...required].some(id => !present.has(id))) fail('critical_material_coverage_missing')
if (changedUncheckable.length) fail('new_or_changed_uncheckable_preview')
const probes = selected.flatMap(row => [
{ materialId: row.materialId, role: 'registered', registeredSource: row.registeredSource, sourceSha256: row.sourceSha256 },
...(row.deliverySource === row.registeredSource ? [] : [{ materialId: row.materialId, role: 'canonical', registeredSource: row.deliverySource, sourceSha256: sha(row.deliverySource) }]),
])
if (!probes.length || probes.length > POLICY.probes) fail('probe_budget_requires_independent_full_acceptance')
return { selected, probes, unverified, expandedScope, publicDomain, criticalIds: [...criticalIds] }
}
function failureSignature(result) {
return digest([result.classification, result.code, result.status, result.contentType, result.bytes, result.bodySha256 ?? null])
}
function validateChecks(plan, checks) {
if (!Array.isArray(checks) || checks.length !== plan.probes.length) fail('probe_coverage_incomplete')
const expected = new Set(plan.probes.map(probeKey)), seen = new Set()
for (const check of checks) {
const key = probeKey(check)
if (!expected.has(key) || seen.has(key) || check.result?.source?.sha256 !== check.sourceSha256 || check.result.attempts !== 1
|| check.result.materialId !== check.materialId || typeof check.result.ok !== 'boolean') fail('probe_identity_or_coverage_mismatch')
if (check.result.ok && (!/^[a-f0-9]{64}$/.test(check.result.bodySha256) || check.result.classification !== 'healthy'
|| check.result.status < 200 || check.result.status >= 300 || !Number.isSafeInteger(check.result.decoded?.width) || !Number.isSafeInteger(check.result.decoded?.height)
|| check.result.decoded.width < 1 || check.result.decoded.height < 1 || check.result.decoded.width * check.result.decoded.height > 25_000_000)) fail('undecoded_healthy_probe')
seen.add(key)
}
}
export function validateAccepted(artifact) {
if (artifact?.version !== 1 || artifact.policyVersion !== POLICY.version || artifact.passed !== true
|| !['bootstrap', 'post'].includes(artifact.stage) || !/^[a-f0-9]{64}$/.test(artifact.inventorySha256)) fail('reviewed_baseline_required')
validateIndex(artifact.inventory)
if (digest(normalizeDeliveryConfig(artifact.deliveryConfig)) !== artifact.deliveryConfigSha256
|| artifact.knownLegacyMembersSha256 !== KNOWN_LEGACY_MEMBERS_SHA256) fail('baseline_delivery_or_legacy_proof_mismatch')
if (digest(artifact.inventory) !== artifact.inventorySha256 || digest(artifact.plan) !== artifact.planSha256) fail('baseline_digest_mismatch')
validateChecks(artifact.plan, artifact.checks)
if (digest(artifact.checks) !== artifact.checksSha256) fail('baseline_checks_digest_mismatch')
}
export function compareChecks(plan, checks, baseline, { bootstrap = false, legacyFailureProof = KNOWN_FRP_FAILURE, compatibilityProof = KNOWN_COMPATIBILITY_PROOF,
legacyMembers = PINNED_KNOWN_LEGACY_MEMBERS } = {}) {
validateChecks(plan, checks)
const known = new Map((baseline?.checks || []).map(row => [probeKey(row), row.result]))
const checksMap = new Map(checks.map(row => [probeKey(row), row.result]))
const legacyIdentities = new Set(legacyMembers.map(sourceKey))
const problems = [], knownFailures = []
for (const check of checks) {
const row = plan.selected.find(item => item.materialId === check.materialId)
const old = known.get(probeKey(check)), result = check.result
if (['decoder_unavailable', 'invalid_manifest', 'source_rejected'].includes(result.classification)) { problems.push({ materialId: check.materialId, role: check.role, code: 'detector_or_source_validation_failure' }); continue }
if (result.ok) {
if (old?.ok && result.bodySha256 !== old.bodySha256) problems.push({ materialId: check.materialId, role: check.role, code: 'unchanged_url_content_changed' })
continue
}
// Bootstrap is explicitly installed by a human after reviewing its exact artifact SHA.
// It may document the incident's known FRP misses, never arbitrary new failures.
const legacyFrp = row.independentLegacy && result.classification === 'routing_failure' && result.code === 'frp_default_404' && result.status === 404
&& result.bytes === legacyFailureProof.bytes && result.bodySha256 === legacyFailureProof.bodySha256
&& legacyIdentities.has(sourceKey(row))
const canonical = checksMap.get(probeKey({ materialId: row.materialId, role: 'canonical', sourceSha256: sha(row.deliverySource) }))
const rawCompatUnavailable = check.role === 'registered' && row.deliverySource !== row.registeredSource && canonical?.ok
&& row.materialId === compatibilityProof.materialId && canonical.bodySha256 === compatibilityProof.bodySha256
&& canonical.decoded.width === compatibilityProof.width && canonical.decoded.height === compatibilityProof.height
&& ['network_failure', 'timeout'].includes(result.classification)
if (bootstrap ? (legacyFrp || rawCompatUnavailable) : old && !old.ok && failureSignature(result) === failureSignature(old)) {
knownFailures.push({ materialId: check.materialId, role: check.role, classification: result.classification, code: result.code, status: result.status })
} else problems.push({ materialId: check.materialId, role: check.role, code: 'new_or_changed_failure' })
}
// Canonical delivery must be healthy except for the explicitly documented independent legacy incident.
for (const row of plan.selected) {
const role = row.deliverySource === row.registeredSource ? 'registered' : 'canonical'
const result = checksMap.get(probeKey({ materialId: row.materialId, role, sourceSha256: sha(row.deliverySource) }))
if (!result?.ok && !row.independentLegacy) problems.push({ materialId: row.materialId, role, code: 'delivery_preview_unavailable' })
}
return { passed: problems.length === 0, problems, knownFailures, allSelectedHealthy: checks.every(row => row.result.ok) }
}
export async function runProbes(plan, { fetchImpl = directFetch, decodeImage, now = Date.now } = {}) {
if (!Array.isArray(plan?.probes) || !plan.probes.length || plan.probes.length > POLICY.probes) fail('probe_budget_requires_independent_full_acceptance')
const start = now(), controller = new AbortController()
let bytes = 0, cursor = 0, budgetFailure = null
const timer = setTimeout(() => { budgetFailure = 'release_probe_wall_budget'; controller.abort() }, POLICY.wallMs)
const checks = new Array(plan.probes.length)
const guardedFetch = async (url, options) => {
const response = await fetchImpl(url, { ...options, signal: AbortSignal.any([options.signal, controller.signal]) })
if (!response.body) return response
const guarded = new Response(response.body.pipeThrough(new TransformStream({ transform(chunk, output) {
bytes += chunk.byteLength
if (bytes > POLICY.bytes) { budgetFailure = 'release_probe_byte_budget'; controller.abort(); throw new GateError(budgetFailure) }
output.enqueue(chunk)
} })), { status: response.status, headers: response.headers })
Object.defineProperties(guarded, { redirected: { value: response.redirected }, url: { value: response.url } })
return guarded
}
try {
await Promise.all(Array.from({ length: Math.min(POLICY.concurrency, plan.probes.length) }, async () => {
while (cursor < plan.probes.length && !controller.signal.aborted) {
if (now() - start >= POLICY.wallMs) { budgetFailure = 'release_probe_wall_budget'; controller.abort(); break }
const index = cursor++, probe = plan.probes[index]
const manifest = { version: 1, reviewed: true, materials: [{ materialId: probe.materialId, registeredSource: probe.registeredSource }] }
const report = await checkMaterialPreviewHealth(manifest, { fetchImpl: guardedFetch, ...(decodeImage ? { decodeImage } : {}) })
checks[index] = { ...probe, result: report.results[0] }
}
}))
} finally { clearTimeout(timer); controller.abort() }
if (budgetFailure) fail(budgetFailure)
validateChecks(plan, checks)
return { checks, budget: { ...POLICY, actualBytes: bytes, elapsedMs: now() - start, requests: checks.length, retries: 0 } }
}
async function deliveryCode(root) {
return Object.fromEntries(await Promise.all(DELIVERY_FILES.map(async file => [file, sha(await readFile(path.join(root, file)))])))
}
export async function runReleaseStage(input, { prismaClient, collectInventory = collectMaterialPreviewInventory, probe = runProbes,
root = '/app', readDeliveryCode = deliveryCode, environment = process.env, targetDeliveryCode = PINNED_TARGET_DELIVERY_CODE,
legacyFailureProof = KNOWN_FRP_FAILURE, compatibilityProof = KNOWN_COMPATIBILITY_PROOF, legacyMembers = PINNED_KNOWN_LEGACY_MEMBERS } = {}) {
if (input?.version !== 1 || !['bootstrap', 'capture', 'candidate', 'post'].includes(input.stage)
|| !/^[a-f0-9]{40}$/.test(input.commit)) fail('invalid_release_stage')
const bootstrap = input.stage === 'bootstrap'
if (!bootstrap) validateAccepted(input.accepted)
const owned = !prismaClient
const prisma = prismaClient || new (runtimeRequire('@prisma/client').PrismaClient)({ log: [] })
let collected
try { collected = await collectInventory(prisma) } finally { if (owned) await prisma.$disconnect() }
const inventory = inventoryIndex(collected), inventorySha256 = digest(inventory)
const code = await readDeliveryCode(root)
if (Object.keys(code).sort().join('|') !== [...DELIVERY_FILES].sort().join('|') || Object.values(code).some(v => !/^[a-f0-9]{64}$/.test(v))) fail('delivery_code_identity_unavailable')
const deliveryConfig = normalizeDeliveryConfig(Object.fromEntries(DELIVERY_CONFIG_KEYS.map(key => [key, environment[key] ?? ''])))
const publicDomain = deliveryConfig.S3_PUBLIC_DOMAIN
const targetConfig = bootstrap ? deliveryConfig : normalizeDeliveryConfig(input.targetDeliveryConfig)
let plan, expected
if (bootstrap || input.stage === 'capture') {
const expandedScope = !bootstrap && (!targetDeliveryCode || digest(code) !== digest(targetDeliveryCode)
|| digest(input.accepted.deliveryCode) !== digest(code) || digest(input.accepted.deliveryConfig) !== digest(deliveryConfig)
|| digest(targetConfig) !== digest(deliveryConfig))
plan = buildPlan(inventory, input.accepted, { publicDomain, expandedScope, bootstrap })
expected = input.accepted
} else {
const before = input.capture
if (before?.stage !== 'capture' || before.passed !== true || before.commit !== input.commit || before.inventorySha256 !== inventorySha256
|| before.metadataAudit?.inventorySha256 !== collected.audit.inventorySha256
|| digest(before.inventory) !== inventorySha256 || digest(before.plan) !== before.planSha256
|| before.acceptedArtifactSha256 !== digest(input.accepted)) fail('inventory_or_capture_drift')
if (!targetDeliveryCode || digest(code) !== digest(targetDeliveryCode) || digest(deliveryConfig) !== digest(before.targetDeliveryConfig)
|| digest(targetConfig) !== digest(before.targetDeliveryConfig) || publicDomain !== before.plan.publicDomain) fail('candidate_delivery_identity_mismatch')
validateChecks(before.plan, before.checks)
if (digest(before.checks) !== before.checksSha256) fail('capture_checks_digest_mismatch')
plan = before.plan
expected = before
if (input.stage === 'post') {
if (input.candidate?.stage !== 'candidate' || input.candidate.passed !== true || input.candidate.planSha256 !== before.planSha256
|| input.candidate.commit !== input.commit || input.candidate.acceptedArtifactSha256 !== digest(input.accepted)
|| digest(input.candidate.plan) !== before.planSha256 || digest(input.candidate.inventory) !== inventorySha256
|| input.candidate.metadataAudit?.inventorySha256 !== collected.audit.inventorySha256
|| input.candidate.inventorySha256 !== inventorySha256 || digest(input.candidate.checks) !== input.candidate.checksSha256) fail('candidate_receipt_required')
validateChecks(plan, input.candidate.checks)
expected = input.candidate
}
}
const { checks, budget } = await probe(plan)
const comparison = compareChecks(plan, checks, expected, { bootstrap, legacyFailureProof, compatibilityProof, legacyMembers })
const artifact = { version: 1, policyVersion: POLICY.version, readOnly: true, stage: input.stage, commit: input.commit,
capturedAt: new Date().toISOString(), ...comparison, inventory, inventorySha256, metadataAudit: collected.audit,
plan, planSha256: digest(plan), checks, checksSha256: digest(checks), budget, deliveryCode: code,
deliveryConfig, deliveryConfigSha256: digest(deliveryConfig), targetDeliveryConfig: targetConfig,
knownLegacyMembersSha256: KNOWN_LEGACY_MEMBERS_SHA256,
...(bootstrap ? { bootstrapRequiresExplicitShaReview: true } : { acceptedArtifactSha256: digest(input.accepted) }),
scopeNotice: 'Only selected previews were fetched; all remaining metadata rows are explicitly unverified. Known incident failures are not healthy.' }
return artifact
}
/** Package only exact-checkout code, with dependency hashes; old images need no new files. */
export async function bundleReleaseGate(root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..'), { runtimeBase = 'file:///app/package.json' } = {}) {
const moduleUrl = source => 'data:text/javascript;base64,' + Buffer.from(source).toString('base64')
const adapt = source => source.replaceAll('import.meta.url', JSON.stringify(runtimeBase))
const registry = moduleUrl(`export const LEGACY_DOMAINS=${JSON.stringify(LEGACY_DOMAINS)}`)
const checker = moduleUrl(adapt(await readFile(path.join(root, 'scripts/check-material-preview-health.mjs'), 'utf8')).replace("'./check-legacy-storage-boundaries.mjs'", JSON.stringify(registry)))
const exporter = moduleUrl(adapt(await readFile(path.join(root, 'scripts/export-material-preview-health-manifest.mjs'), 'utf8'))
.replace("'./check-legacy-storage-boundaries.mjs'", JSON.stringify(registry)).replace("'./check-material-preview-health.mjs'", JSON.stringify(checker)))
const gate = adapt((await readFile(path.join(root, 'scripts/material-preview-release-gate.mjs'), 'utf8'))
.replace('const PINNED_KNOWN_LEGACY_MEMBERS = await readKnownLegacyProof()', `const PINNED_KNOWN_LEGACY_MEMBERS = ${JSON.stringify(await readKnownLegacyProof(root))}`))
.replace('const PINNED_TARGET_DELIVERY_CODE = null', `const PINNED_TARGET_DELIVERY_CODE = ${JSON.stringify(await deliveryCode(root))}`)
.replace("'./check-legacy-storage-boundaries.mjs'", JSON.stringify(registry))
.replace("'./check-material-preview-health.mjs'", JSON.stringify(checker))
.replace("'./export-material-preview-health-manifest.mjs'", JSON.stringify(exporter))
return moduleUrl(gate)
}
if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
if (process.argv.length === 3 && process.argv[2] === 'bundle') process.stdout.write(await bundleReleaseGate())
else { console.error('Usage: node scripts/material-preview-release-gate.mjs bundle'); process.exitCode = 1 }
}