Preserve known legacy failures by reviewed identity, probe material delivery before and after releases, and retain originals and downloads.
310 lines
23 KiB
JavaScript
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 }
|
|
}
|