/**
|
* 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/<env>.js(唯一数据源):
|
* - 接口/埋点等运行时参数:包内自动按小程序 appid 识别,无需在此处理
|
* - APPID:作为 --appid 传给微信 CI 上传
|
*
|
* 密钥文件:keys/private.<env>.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();
|