publish-weapp.js 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. const ci = require('miniprogram-ci')
  2. const path = require('path')
  3. const { execSync } = require('child_process')
  4. const fs = require('fs')
  5. // 公网访问域名(从环境变量读取,默认本地)
  6. const WEB_HOST = process.env.WEB_HOST || 'localhost:8080'
  7. const PUBLIC_HOST = WEB_HOST.startsWith('http') ? WEB_HOST : `https://${WEB_HOST}`
  8. // 小程序配置
  9. const MINI_CONFIGS = {
  10. enterprise: {
  11. name: '企业小程序',
  12. appid: 'wx1e791ed2e0229eb8',
  13. projectPath: path.resolve(__dirname, '../mini/dist/weapp/production'),
  14. privateKeyPath: path.resolve(__dirname, '../mini/certs/private.upload.key'),
  15. buildCmd: 'cd mini && pnpm build:weapp',
  16. distPath: 'dist/weapp',
  17. },
  18. talent: {
  19. name: '人才小程序',
  20. appid: 'wx3c47dbce1ea7d43c',
  21. projectPath: path.resolve(__dirname, '../mini-talent/dist/weapp/production'),
  22. privateKeyPath: path.resolve(__dirname, '../mini-talent/certs/private.upload.key'),
  23. buildCmd: 'cd mini-talent && pnpm build:weapp',
  24. distPath: 'dist/weapp',
  25. },
  26. }
  27. /**
  28. * 构建小程序
  29. */
  30. function buildMiniProject(config) {
  31. console.log(`\n🔨 正在构建 ${config.name}...`)
  32. try {
  33. execSync(config.buildCmd, { stdio: 'inherit' })
  34. console.log(`✅ ${config.name} 构建完成`)
  35. } catch (error) {
  36. console.error(`❌ ${config.name} 构建失败`)
  37. throw error
  38. }
  39. }
  40. /**
  41. * 发布小程序体验版到微信服务器
  42. */
  43. async function uploadExperienceProject(config, options) {
  44. const { version, desc, robot = 1 } = options
  45. console.log(`\n📤 正在发布体验版 ${config.name}...`)
  46. console.log(` 版本: ${version}`)
  47. console.log(` 描述: ${desc}`)
  48. const project = new ci.Project({
  49. appid: config.appid,
  50. type: 'miniProgram',
  51. projectPath: config.projectPath,
  52. privateKeyPath: config.privateKeyPath,
  53. ignores: ['node_modules/**/*'],
  54. })
  55. try {
  56. const result = await ci.upload({
  57. project,
  58. version,
  59. desc,
  60. setting: {
  61. useProjectConfig: true,
  62. es7: true,
  63. minify: true,
  64. minifyJS: true,
  65. minifyWXML: true,
  66. minifyWXSS: true,
  67. autoPrefixWXSS: true,
  68. },
  69. onProgressUpdate: (progress) => {
  70. process.stdout.write(`\r 上传进度: ${progress}%`)
  71. },
  72. robot,
  73. })
  74. console.log(`\n✅ ${config.name}体验版上传成功!`)
  75. console.log(` 时间: ${result.time}`)
  76. console.log(` 版本: ${result.version}`)
  77. console.log(` 请前往小程序后台查看: https://mp.weixin.qq.com/`)
  78. } catch (error) {
  79. console.error(`\n❌ ${config.name}体验版上传失败`)
  80. console.error(error.message)
  81. throw error
  82. }
  83. }
  84. /**
  85. * 预览小程序(生成二维码)
  86. */
  87. async function previewDevProject(config, options) {
  88. const { desc, robot = 1 } = options
  89. console.log(`\n👀 正在生成 ${config.name} 预览二维码...`)
  90. const project = new ci.Project({
  91. appid: config.appid,
  92. type: 'miniProgram',
  93. projectPath: config.projectPath,
  94. privateKeyPath: config.privateKeyPath,
  95. ignores: ['node_modules/**/*'],
  96. })
  97. // 二维码保存目录
  98. const qrcodeDir = path.resolve(__dirname, '../web/public/qrcode')
  99. const qrcodeFileName = `qrcode-${config.appid}.jpg`
  100. const qrcodePath = path.join(qrcodeDir, qrcodeFileName)
  101. // 确保 qrcode 目录存在
  102. if (!fs.existsSync(qrcodeDir)) {
  103. fs.mkdirSync(qrcodeDir, { recursive: true })
  104. }
  105. try {
  106. await ci.preview({
  107. project,
  108. desc,
  109. setting: { useProjectConfig: true },
  110. qrcodeFormat: 'image',
  111. qrcodeOutputDest: qrcodePath,
  112. onProgressUpdate: (progress) => {
  113. process.stdout.write(`\r 生成进度: ${progress}%`)
  114. },
  115. robot,
  116. })
  117. // 公网访问 URL
  118. const publicUrl = `${PUBLIC_HOST}/qrcode/${qrcodeFileName}`
  119. console.log(`\n✅ 开发版预览二维码已生成!`)
  120. console.log(`📱 本地路径: ${qrcodePath}`)
  121. console.log(`🌐 公网访问: ${publicUrl}`)
  122. console.log(`\n>>>QRCode_IMAGE_START<<<`)
  123. console.log(qrcodePath)
  124. console.log(`>>>QRCode_URL_START<<<`)
  125. console.log(publicUrl)
  126. console.log(`>>>QRCode_URL_END<<<`)
  127. } catch (error) {
  128. console.error(`\n❌ 开发版预览二维码生成失败`)
  129. console.error(error.message)
  130. throw error
  131. }
  132. }
  133. /**
  134. * 主函数
  135. */
  136. async function main() {
  137. const args = process.argv.slice(2)
  138. const noBuildIndex = args.indexOf('--no-build')
  139. const noBuild = noBuildIndex !== -1
  140. if (noBuild) {
  141. args.splice(noBuildIndex, 1)
  142. }
  143. const [miniType, action = 'experience'] = args
  144. // 解析参数
  145. const options = {
  146. version: process.env.VERSION || '1.0.0',
  147. desc: process.env.DESC || '自动化发布',
  148. robot: parseInt(process.env.ROBOT || '1'),
  149. }
  150. if (!miniType || !MINI_CONFIGS[miniType]) {
  151. console.error('❌ 请指定小程序类型: enterprise 或 talent')
  152. console.error('示例: node scripts/publish-weapp.js enterprise')
  153. process.exit(1)
  154. }
  155. const config = MINI_CONFIGS[miniType]
  156. try {
  157. // 构建项目(除非使用 --no-build)
  158. if (!noBuild) {
  159. buildMiniProject(config)
  160. } else {
  161. console.log(`\n⏭️ 跳过构建,使用现有构建输出`)
  162. }
  163. // 执行操作
  164. if (action === 'experience') {
  165. await uploadExperienceProject(config, options)
  166. } else if (action === 'dev') {
  167. await previewDevProject(config, {
  168. desc: options.desc,
  169. robot: options.robot,
  170. })
  171. } else {
  172. console.error(`❌ 操作无效: ${action}`)
  173. process.exit(1)
  174. }
  175. } catch (error) {
  176. process.exit(1)
  177. }
  178. }
  179. main()