/** * HBuilderX CLI 命令行发布:按环境发布微信小程序 * * 用法: * node scripts/publish.js sit # 编译 + 上传到 SIT 小程序(微信 CI,无需打开微信开发者工具) * node scripts/publish.js prod # 编译 + 上传到 PROD 小程序 * node scripts/publish.js sit --local # 只本地编译,不上传 * node scripts/publish.js sit --dry-run # 只打印将要执行的命令 * node scripts/publish.js sit --version 2.0.0 --robot 3 # 额外参数原样透传给 cli * * 环境相关的值统一取自 config/env/.js(唯一数据源): * - 接口/埋点等运行时参数:包内自动按小程序 appid 识别,无需在此处理 * - APPID:作为 --appid 传给微信 CI 上传 * * 密钥文件:keys/private..key * 获取方式:微信公众平台 → 开发管理 → 开发设置 → 小程序代码上传 → 生成上传密钥 * 注意:密钥属于机密,已通过 .gitignore 排除,切勿提交 */ const { spawnSync } = require('child_process'); const fs = require('fs'); const path = require('path'); const ROOT = path.resolve(__dirname, '..'); const ENV_DIR = path.join(ROOT, 'config', 'env'); // HBuilderX cli.exe 路径:优先读环境变量,便于换机器 / 换安装位置 const HX_CLI = process.env.HBUILDERX_CLI || 'C:\\Users\\Administrator\\Desktop\\HBuilderX\\cli.exe'; const argv = process.argv.slice(2); const envName = (argv.find(function(a) { return a.charAt(0) !== '-'; }) || '').trim(); const localOnly = argv.indexOf('--local') > -1; const dryRun = argv.indexOf('--dry-run') > -1; // 默认不改 manifest.json:上传用 --appid 指定目标小程序,编译产物里的 appid 不参与校验。 // 若遇到上传失败/appid 不一致,加 --sync-manifest 先同步 manifest 再发布。 const syncManifest = argv.indexOf('--sync-manifest') > -1; const OWN_ARGS = ['--local', '--dry-run', '--sync-manifest']; // 透传给 cli 的额外参数(保留 "参数 值" 的成对关系,如 --robot 2) const passthrough = []; let envNameSkipped = false; argv.forEach(function(a) { if (OWN_ARGS.indexOf(a) > -1) return; if (!envNameSkipped && a === envName) { envNameSkipped = true; return; } passthrough.push(a); }); function fail(msg) { console.error('[publish] ' + msg); process.exit(1); } function gitShortHash() { const res = spawnSync('git', ['rev-parse', '--short', 'HEAD'], { cwd: ROOT, encoding: 'utf8' }); return res.status === 0 ? String(res.stdout).trim() : ''; } function readManifestVersion() { try { const txt = fs.readFileSync(path.join(ROOT, 'manifest.json'), 'utf8'); const m = txt.match(/"versionName"\s*:\s*"([^"]*)"/); return m ? m[1] : '1.0.0'; } catch (e) { return '1.0.0'; } } function main() { const available = fs.existsSync(ENV_DIR) ? fs.readdirSync(ENV_DIR).filter(f => f.endsWith('.js') && f !== 'index.js').map(f => f.replace(/\.js$/, '')) : []; if (!envName) fail('请指定环境名,可选:' + available.join(' / ') + '\n 例:node scripts/publish.js sit'); if (available.indexOf(envName) < 0) fail('未找到 config/env/' + envName + '.js,可选:' + available.join(' / ')); const env = require(path.join(ENV_DIR, envName + '.js')); // 1) 校验 cli.exe if (!fs.existsSync(HX_CLI)) { fail('未找到 HBuilderX cli.exe:' + HX_CLI + '\n 请确认 HBuilderX 安装路径,或设置环境变量 HBUILDERX_CLI 指向 cli.exe'); } // 2) 组装 cli 参数 const cliArgs = ['publish', '--platform', 'mp-weixin', '--project', ROOT]; if (!localOnly) { const keyFile = path.join(ROOT, 'keys', 'private.' + envName + '.key'); if (!fs.existsSync(keyFile)) { fail('未找到上传密钥文件:' + keyFile + '\n 获取方式:微信公众平台 → 开发管理 → 开发设置 → 小程序代码上传 → 生成上传密钥' + '\n 若只想本地编译不上传,请加 --local'); } const stamp = new Date().toISOString().slice(0, 16).replace('T', ' '); const hash = gitShortHash(); cliArgs.push('--upload', 'true'); cliArgs.push('--appid', env.APPID); cliArgs.push('--privatekey', keyFile); cliArgs.push('--version', readManifestVersion()); cliArgs.push('--description', '[' + env.ENV_NAME + '] ' + stamp + (hash ? ' ' + hash : '')); } console.log('[publish] 环境 : ' + envName + '(' + env.ENV_NAME + ')'); console.log('[publish] 小程序appid: ' + env.APPID); console.log('[publish] 接口地址 : ' + env.HTTP_REQUEST_URL); console.log('[publish] 产物目录 : ' + path.join('unpackage', 'dist', 'build', 'mp-weixin')); console.log('[publish] 模式 : ' + (localOnly ? '仅本地编译' : '编译 + 上传到微信平台')); console.log('[publish] DCloud标识 : ' + env.UNI_APPID + '(manifest.json 里的值,仅影响非小程序端与云服务识别)'); console.log('[publish] 命令 : "' + HX_CLI + '" ' + cliArgs.concat(passthrough).join(' ')); if (dryRun) { console.log('[publish] --dry-run,未执行'); return; } // 3) 可选:先把 appid 同步进 manifest.json(走 set-env 的同一套逻辑) if (syncManifest) { console.log('[publish] --sync-manifest:先同步 manifest.json 的 appid'); const sync = spawnSync(process.execPath, [path.join(__dirname, 'set-env.js'), envName], { cwd: ROOT, stdio: 'inherit' }); if (sync.status !== 0) fail('同步 manifest.json 失败'); } // 4) 执行(HBuilderX 会在其控制台输出编译与上传日志) const res = spawnSync(HX_CLI, cliArgs.concat(passthrough), { cwd: ROOT, stdio: 'inherit' }); if (res.error) fail('调用 cli.exe 失败:' + res.error.message); if (res.status !== 0) fail('cli.exe 退出码 ' + res.status + ',请查看 HBuilderX 控制台输出'); console.log('[publish] 完成。若上传成功,可在微信公众平台 → 版本管理 中看到对应版本'); } main();