Files
2026-09-09 14:47:29 +08:00

60 lines
4.0 KiB
JavaScript

import fs from 'node:fs'
import path from 'node:path'
import { spawnSync } from 'node:child_process'
import { PROJECT, hash } from './upload.mjs'
import { loadCosMediaManifest, isMediaFile } from '../../build/tang-detective-cos-media.mjs'
import { validateNativeOutput } from '../../build/validate-tang-detective-output.mjs'
const sourceDirectory = path.join(PROJECT, 'native/tang-detective')
const media = loadCosMediaManifest({ sourceDirectory })
if (!media || media.sourceSnapshot.actual.size !== 269 || media.sourceSnapshot.missingMedia.length !== 204
|| !media.sourceSnapshot.prune) throw new Error('FULLY_CLEANED_CHECKOUT_REQUIRED')
const receiptFile = path.join(PROJECT, 'build/tang-detective-media-prune-receipt.json')
const receipt = JSON.parse(fs.readFileSync(receiptFile))
const recoveryDirectory = '/Users/dagedagededagege/Pictures/tang-media-backup-20260908.9hdgCE/pruned-originals'
const removed = [...receipt.entries.map(entry => ({ ...entry, projectPath: `native/tang-detective/${entry.sourcePath}` })),
...receipt.additionalUnusedFiles]
for (const entry of removed) {
if (fs.existsSync(path.join(PROJECT, entry.projectPath))) throw new Error('REMOVED_PROJECT_FILE_REAPPEARED')
const bytes = fs.readFileSync(path.join(recoveryDirectory, entry.projectPath))
if (bytes.length !== entry.bytes || hash(bytes) !== entry.sha256) throw new Error('RECOVERY_BYTES_CHANGED')
}
const report = { schemaVersion: 1, startedAt: new Date().toISOString(), passed: false,
uploadRunId: media.uploadRunId, sourceManifestSha256: media.sourceManifestSha256,
mediaManifestSha256: media.manifestSha256, pruneReceiptSha256: hash(fs.readFileSync(receiptFile)),
nonMediaSourceFilesByteVerified: 269, sourceMediaFilesRemaining: 0,
removedProjectFiles: removed.length, removedProjectBytes: receipt.removedProjectBytes,
recoveryFilesByteVerified: removed.length, commands: [],
boundary: 'Actual cleaned checkout, installed compiler and offline automated tests only. Not real-device, medical, backend deployment or release acceptance.' }
const outputFile = path.join(PROJECT, 'build/tang-detective-cleanup-verification.json')
const save = () => fs.writeFileSync(outputFile, JSON.stringify(report, null, 2) + '\n')
save()
for (const [command, args] of [
['npm', ['run', 'test:tang']],
['npm', ['run', 'build:mp-weixin']],
['npm', ['run', 'check:tang-output']],
[process.execPath, ['scripts/check-tang-native-compiler.cjs']],
]) {
const result = spawnSync(command, args, { cwd: PROJECT, encoding: 'utf8', timeout: 120000, maxBuffer: 16 * 1024 * 1024 })
report.commands.push({ command: [command, ...args], exitCode: result.status, signal: result.signal,
stdout: result.stdout, stderr: result.stderr, passed: result.status === 0 && !result.error })
save()
console.log(JSON.stringify({ command: [command, ...args], exitCode: result.status }))
if (!report.commands.at(-1).passed) throw new Error('POST_CLEANUP_COMMAND_FAILED')
}
report.output = validateNativeOutput(path.join(PROJECT, 'dist/build/mp-weixin'))
const walk = (dir, prefix = '') => fs.readdirSync(dir, { withFileTypes: true }).flatMap(entry => {
if (['node_modules', 'dist', 'unpackage', '.git'].includes(entry.name)) return []
const relative = prefix + entry.name
if (entry.isSymbolicLink()) throw new Error('UNEXPECTED_OUTPUT_SYMLINK')
return entry.isDirectory() ? walk(path.join(dir, entry.name), relative + '/') : [relative]
})
report.remainingHostMedia = walk(PROJECT).filter(file => !/^(node_modules|dist|unpackage|\.git)\//.test(file) && isMediaFile(file))
report.passed = report.output.passed && report.output.mediaMode === 'cos' && report.output.packagedMediaFiles === 0
report.completedAt = new Date().toISOString()
save()
console.log(JSON.stringify({ passed: report.passed, sourceMediaFilesRemaining: 0, packagedTangMediaFiles: report.output.packagedMediaFiles,
totalBuildBytes: report.output.outputSizes.totalFileBytes, mainPackageBytes: report.output.outputSizes.mainPackageFileBytes,
remainingHostMedia: report.remainingHostMedia, outputFile }))
if (!report.passed) process.exitCode = 1