#!/usr/bin/env node /** * 为运行时包生成 OSS 上传清单(不复制文件 — 本地与 CDN 共用同一目录)。 * * node tools/write-deploy-manifest.js [packDir] [--cdn-base URL] [--manifest-dir DIR] [--zip] * * 运行时包: build/mstest5/ ← 上传此目录全部内容到 unitycdndir * 清单输出: build/deploy/UPLOAD-MANIFEST.txt(不进 OSS) */ const fs = require('fs'); const path = require('path'); const { execSync } = require('child_process'); const { dirSize, formatBytes } = require('./package-optimize'); const { listRuntimeFiles, bundleNamesFromCatalog } = require('./runtime-pack'); const argv = process.argv.slice(2); const flags = new Set(argv.filter((a) => a.startsWith('--'))); const positional = argv.filter((a) => !a.startsWith('--')); const PROJECT = path.resolve(__dirname, '..'); const packDir = path.resolve(positional[0] || path.join(PROJECT, 'build/mstest5')); const manifestDir = (() => { const i = argv.indexOf('--manifest-dir'); return path.resolve(i >= 0 ? argv[i + 1] : path.join(PROJECT, 'build/deploy')); })(); const cdnBase = (() => { const i = argv.indexOf('--cdn-base'); return i >= 0 ? argv[i + 1] : ''; })(); const makeZip = flags.has('--zip'); if (!fs.existsSync(path.join(packDir, 'Build/mstest5.loader.js'))) { console.error('错误: 无效运行时包,请先 bash tools/package-for-project.sh'); console.error(' 期望:', packDir); process.exit(1); } const files = listRuntimeFiles(packDir); const catalogPath = path.join(packDir, 'StreamingAssets/aa/catalog.json'); const bundleNames = bundleNamesFromCatalog(catalogPath); const base = (cdnBase || 'https://YOUR_CDN/unitycdndir').replace(/\/$/, ''); fs.mkdirSync(manifestDir, { recursive: true }); const manifestPath = path.join(manifestDir, 'UPLOAD-MANIFEST.txt'); const readmePath = path.join(manifestDir, 'README.txt'); const lines = [ '# OSS 上传清单(自动生成)', `# 生成时间: ${new Date().toISOString()}`, `# 运行时包目录: ${packDir}`, `# OSS 根路径 (unitycdndir): ${base}`, '#', '# 上传方式: 将「运行时包目录」内全部内容原样上传到 OSS', '# 本地 static/unity 与 OSS 文件完全一致,前端仅 config.js usecdn 切换根 URL', '#', `# ossutil cp -r "${packDir}/" oss://BUCKET/PATH/`, '', '## 运行时包文件(本地 = CDN)', ...files.map((f) => `- ${f.rel}`), '', '## OSS 目标路径', ...files.map((f) => `${base}/${f.rel}`), '', '## 上传后验证', `curl -I "${base}/Build/mstest5.loader.js"`, `curl -I "${base}/StreamingAssets/aa/catalog.json"`, `curl -I "${base}/levels-database.json"`, ...bundleNames.map((n) => `curl -I "${base}/StreamingAssets/aa/WebGL/${n}"`), ]; fs.writeFileSync(manifestPath, lines.join('\n'), 'utf8'); const readme = [ '部署说明', '========', '', `生成: ${new Date().toLocaleString('zh-CN')}`, '', '运行时包(本地与 OSS 相同):', ` ${packDir}/`, ' ├── Build/', ' ├── StreamingAssets/', ' └── levels-database.json(.br)', '', '本地: import-to-unity.sh → scratch-gui/static/unity/(同上结构)', 'CDN: 上传运行时包全部内容 → config.js unitycdndir + usecdn:true', '', `包体积: ${formatBytes(dirSize(packDir))}`, `详细路径: ${manifestPath}`, '', '独立调试页(不进 OSS / static):', ` ${path.join(path.dirname(packDir), 'standalone-player')}/index.html`, ].join('\n'); fs.writeFileSync(readmePath, readme, 'utf8'); let zipPath = ''; if (makeZip) { zipPath = path.join(path.dirname(packDir), 'mstest5-runtime.zip'); if (fs.existsSync(zipPath)) fs.unlinkSync(zipPath); const items = ['Build', 'StreamingAssets']; if (fs.existsSync(path.join(packDir, 'levels-database.json'))) items.push('levels-database.json'); if (fs.existsSync(path.join(packDir, 'levels-database.json.br'))) items.push('levels-database.json.br'); execSync(`cd "${packDir}" && zip -0 -q -r "${zipPath}" ${items.join(' ')}`, { stdio: 'pipe' }); } console.log('>>> 运行时包:', packDir); console.log(`>>> 文件数: ${files.length}, 体积: ${formatBytes(dirSize(packDir))}`); console.log('>>> 上传清单:', manifestPath); if (zipPath) { console.log(`>>> ZIP: ${zipPath} (${formatBytes(fs.statSync(zipPath).size)})`); }