/** * 微信小程序自动化构建 & 上传脚本 */ import { execSync } from 'node:child_process'; import fs from 'node:fs'; import path from 'node:path'; import ci from 'miniprogram-ci'; // --- 0. 常量定义与配置 --- const ENV_CONFIG = { allow: ['dev', 'test', 'prod'], default: 'dev', }; const UNI_BUILD_DIR = 'dist/build/mp-weixin'; const QRCODE_OUTPUT_DIR = path.resolve(process.cwd(), UNI_BUILD_DIR); const SETTING = { // 统一的上传/预览配置 es6: true, minify: true, // 其他常用配置 autoPrefixWXSS: true, disableUseStrict: false, }; const MODE = process.argv[2]; const ENV_ARG = process.argv[3]; const PROJECT_ROOT = process.cwd(); // --- 1. 环境解析 --- const inputEnv = (ENV_ARG || process.env.MINI_ENV || ENV_CONFIG.default).toLowerCase(); const ENV = ENV_CONFIG.allow.includes(inputEnv) ? inputEnv : ENV_CONFIG.default; console.log(`📌 当前构建环境: ${ENV}`); // --- 2. 私钥 & AppID 解析 (优先使用环境变量) --- function getAuthInfo() { const scriptsDir = path.resolve(PROJECT_ROOT, 'scripts'); // 优先级 1: 环境变量 let appid = process.env.MINI_APPID; let privateKey = process.env.MINI_PRIVATE_KEY; if (appid && privateKey) { console.log('🔑 使用环境变量 MINI_APPID 和 MINI_PRIVATE_KEY'); return { appid, privateKey: privateKey.replace(/\\n/g, '\n').trim() }; } // 优先级 2: 自动解析私钥文件 if (!fs.existsSync(scriptsDir)) { console.error('❌ 未找到 scripts/ 目录,且环境变量未设置'); process.exit(1); } const keyFile = fs.readdirSync(scriptsDir).find(f => /^private\.[^.]+\.key$/.test(f)); if (!keyFile) { console.error(`❌ scripts/ 目录下未找到私钥文件 private..key`); process.exit(1); } const match = keyFile.match(/^private\.(.+)\.key$/); appid = match ? match[1] : null; if (!appid) { console.error(`❌ 无法从文件名解析 appid: ${keyFile}`); process.exit(1); } const keyPath = path.resolve(scriptsDir, keyFile); privateKey = fs.readFileSync(keyPath, 'utf8').replace(/\\n/g, '\n').trim(); console.log(`🔑 使用私钥文件: ${keyFile}`); console.log(`📌 解析得到 appid: ${appid}`); return { appid, privateKey }; } const { appid, privateKey } = getAuthInfo(); // --- 3. 规范化版本信息 --- function getVersionInfo(env) { try { const pkgVersion = require(path.resolve(PROJECT_ROOT, 'package.json')).version; const commit = execSync('git rev-parse --short HEAD').toString().trim(); const date = new Date(); const ymd = `${date.getFullYear()}${String(date.getMonth() + 1).padStart(2, '0')}${String(date.getDate()).padStart(2, '0')}`; // CI 实际上传的 version 必须是 x.y.z 格式,建议使用 package.json 版本 const uploadVersion = pkgVersion; // 描述信息更详细,包含环境、版本、日期和 Commit const description = `[${env}] ${pkgVersion} (Build ${ymd}-${commit})`; return { version: uploadVersion, desc: description, }; } catch (e) { console.warn('⚠️ 获取 Git 版本失败,使用 fallback: 1.0.0', e); return { version: '1.0.0', desc: `[${env}] Fallback 1.0.0`, }; } } const versionInfo = getVersionInfo(ENV); // --- 4. miniprogram-ci Project 实例 --- if (!fs.existsSync(path.resolve(PROJECT_ROOT, UNI_BUILD_DIR))) { console.error(`❌ 找不到 uni-app 编译产物目录: ${UNI_BUILD_DIR}`); console.error('请先执行 uni-app 编译命令 (例如: npm run build:mp-weixin)'); process.exit(1); } const project = new ci.Project({ appid, type: 'miniProgram', projectPath: PROJECT_ROOT, privateKey, // uni-app 产物目录 miniprogramPath: UNI_BUILD_DIR, // 排除不需要上传的文件 ignores: [ 'node_modules/**/*', 'src/**/*', '.git/**/*', 'dist/dev/**/*', 'dist/build/h5/**/*', // 排除其他平台产物 ], }); // --- 5. 辅助函数:输出二维码 HTML --- function writeQRCodeHtml(env, buildDir) { const filename = `preview-${env}.png`; const html = `

小程序预览环境:${env}

`; const output = path.resolve(buildDir, `preview-${env}.html`); fs.writeFileSync(output, html, 'utf8'); console.log(`📰 已输出二维码 HTML: ${output}`); } // --- 6. 主流程 --- async function run() { // 确保二维码输出目录存在 fs.mkdirSync(QRCODE_OUTPUT_DIR, { recursive: true }); // ----------- preview ----------- if (MODE === 'preview') { const qrcodePath = path.resolve(QRCODE_OUTPUT_DIR, `preview-${ENV}.png`); console.log(`📡 开始预览,描述: ${versionInfo.desc}`); const r = await ci.preview({ project, desc: versionInfo.desc, setting: SETTING, qrcodeFormat: 'image', qrcodeOutputDest: qrcodePath, }); console.log('📦 PREVIEW RESULT:\n', JSON.stringify(r, null, 2)); writeQRCodeHtml(ENV, QRCODE_OUTPUT_DIR); return; } // ----------- upload ----------- if (MODE === 'upload') { console.log(`⬆️ 开始上传体验版 version=${versionInfo.version}, env=${ENV}`); const r = await ci.upload({ project, version: versionInfo.version, desc: versionInfo.desc, setting: SETTING, }); console.log('📦 UPLOAD RESULT:\n', JSON.stringify(r, null, 2)); return; } console.error('❌ 未识别命令:只能使用 preview 或 upload'); process.exit(1); } // ---------------------- ERROR ---------------------- run().catch((e) => { console.error('❌ CI 执行失败:'); // 打印完整错误信息 console.error(e.stack || e); process.exit(1); });