const ci = require('miniprogram-ci') const path = require('path') const { execSync } = require('child_process') const fs = require('fs') // 公网访问域名(从环境变量读取,默认本地) const WEB_HOST = process.env.WEB_HOST || 'localhost:8080' const PUBLIC_HOST = WEB_HOST.startsWith('http') ? WEB_HOST : `https://${WEB_HOST}` // 小程序配置 const MINI_CONFIGS = { enterprise: { name: '企业小程序', appid: 'wx1e791ed2e0229eb8', projectPath: path.resolve(__dirname, '../mini/dist/weapp/production'), privateKeyPath: path.resolve(__dirname, '../mini/certs/private.upload.key'), buildCmd: 'cd mini && pnpm build:weapp', distPath: 'dist/weapp', }, talent: { name: '人才小程序', appid: 'wx3c47dbce1ea7d43c', projectPath: path.resolve(__dirname, '../mini-talent/dist/weapp/production'), privateKeyPath: path.resolve(__dirname, '../mini-talent/certs/private.upload.key'), buildCmd: 'cd mini-talent && pnpm build:weapp', distPath: 'dist/weapp', }, } /** * 构建小程序 */ function buildMiniProject(config) { console.log(`\n🔨 正在构建 ${config.name}...`) try { execSync(config.buildCmd, { stdio: 'inherit' }) console.log(`✅ ${config.name} 构建完成`) } catch (error) { console.error(`❌ ${config.name} 构建失败`) throw error } } /** * 发布小程序体验版到微信服务器 */ async function uploadExperienceProject(config, options) { const { version, desc, robot = 1 } = options console.log(`\n📤 正在发布体验版 ${config.name}...`) console.log(` 版本: ${version}`) console.log(` 描述: ${desc}`) const project = new ci.Project({ appid: config.appid, type: 'miniProgram', projectPath: config.projectPath, privateKeyPath: config.privateKeyPath, ignores: ['node_modules/**/*'], }) try { const result = await ci.upload({ project, version, desc, setting: { useProjectConfig: true, es7: true, minify: true, minifyJS: true, minifyWXML: true, minifyWXSS: true, autoPrefixWXSS: true, }, onProgressUpdate: (progress) => { process.stdout.write(`\r 上传进度: ${progress}%`) }, robot, }) console.log(`\n✅ ${config.name}体验版上传成功!`) console.log(` 时间: ${result.time}`) console.log(` 版本: ${result.version}`) console.log(` 请前往小程序后台查看: https://mp.weixin.qq.com/`) } catch (error) { console.error(`\n❌ ${config.name}体验版上传失败`) console.error(error.message) throw error } } /** * 预览小程序(生成二维码) */ async function previewDevProject(config, options) { const { desc, robot = 1 } = options console.log(`\n👀 正在生成 ${config.name} 预览二维码...`) const project = new ci.Project({ appid: config.appid, type: 'miniProgram', projectPath: config.projectPath, privateKeyPath: config.privateKeyPath, ignores: ['node_modules/**/*'], }) // 二维码保存目录 const qrcodeDir = path.resolve(__dirname, '../web/public/qrcode') const qrcodeFileName = `qrcode-${config.appid}.jpg` const qrcodePath = path.join(qrcodeDir, qrcodeFileName) // 确保 qrcode 目录存在 if (!fs.existsSync(qrcodeDir)) { fs.mkdirSync(qrcodeDir, { recursive: true }) } try { await ci.preview({ project, desc, setting: { useProjectConfig: true }, qrcodeFormat: 'image', qrcodeOutputDest: qrcodePath, onProgressUpdate: (progress) => { process.stdout.write(`\r 生成进度: ${progress}%`) }, robot, }) // 公网访问 URL const publicUrl = `${PUBLIC_HOST}/qrcode/${qrcodeFileName}` console.log(`\n✅ 开发版预览二维码已生成!`) console.log(`📱 本地路径: ${qrcodePath}`) console.log(`🌐 公网访问: ${publicUrl}`) console.log(`\n>>>QRCode_IMAGE_START<<<`) console.log(qrcodePath) console.log(`>>>QRCode_URL_START<<<`) console.log(publicUrl) console.log(`>>>QRCode_URL_END<<<`) } catch (error) { console.error(`\n❌ 开发版预览二维码生成失败`) console.error(error.message) throw error } } /** * 主函数 */ async function main() { const args = process.argv.slice(2) const noBuildIndex = args.indexOf('--no-build') const noBuild = noBuildIndex !== -1 if (noBuild) { args.splice(noBuildIndex, 1) } const [miniType, action = 'experience'] = args // 解析参数 const options = { version: process.env.VERSION || '1.0.0', desc: process.env.DESC || '自动化发布', robot: parseInt(process.env.ROBOT || '1'), } if (!miniType || !MINI_CONFIGS[miniType]) { console.error('❌ 请指定小程序类型: enterprise 或 talent') console.error('示例: node scripts/publish-weapp.js enterprise') process.exit(1) } const config = MINI_CONFIGS[miniType] try { // 构建项目(除非使用 --no-build) if (!noBuild) { buildMiniProject(config) } else { console.log(`\n⏭️ 跳过构建,使用现有构建输出`) } // 执行操作 if (action === 'experience') { await uploadExperienceProject(config, options) } else if (action === 'dev') { await previewDevProject(config, { desc: options.desc, robot: options.robot, }) } else { console.error(`❌ 操作无效: ${action}`) process.exit(1) } } catch (error) { process.exit(1) } } main()