【若川视野 x 源码共读】第30期 | taroのci

341 阅读4分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第2天,点击查看活动详情

本文参加了由公众号@若川视野 发起的每周源码共读活动,点击了解详情一起参与。

参考

使用

可以使用@tarojs/plugin-mini-ci进行微信、字节、支付宝、百度各种小程序的ci,也就是打包上传。

  • config/index.js
// 如果你使用 VSCode 作为开发工具, 你还可以使用注释的语法引入插件包含的声明文件,可获得类似于 Typescript 的友好提示
/**
 * @typedef { import("@tarojs/plugin-mini-ci").CIOptions } CIOptions
 * @type {CIOptions}
 */
const CIPluginOpt = {
  // 微信小程序
  weapp: {
    appid: "微信小程序appid",
    privateKeyPath: "密钥文件相对项目根目录的相对路径,例如 key/private.appid.key"
  },
  // 字节跳动小程序
  tt: {
    email: "字节小程序邮箱",
    password: "字节小程序密码"
  },
  // 支付宝小程序
  alipay: {
    appId: "支付宝小程序appId",
    toolId: "工具id",
    privateKeyPath: "密钥文件相对项目根目录的相对路径,例如 key/pkcs8-private-pem"
  },
  // 百度小程序
  swan: {
    token: "鉴权需要的token令牌"
  },
  // 版本号
  version: "1.0.0",
  // 版本发布描述
  desc: "版本描述"
}

const config = {
  plugins: [
    ["@tarojs/plugin-mini-ci", CIPluginOpt]
  ]
}
  • package.json
{
  "scripts": {
    // 构建完后自动 “打开开发者工具”
    "build:weapp": "taro build --type weapp --open",
    // 构建完后自动“上传代码作为体验版”
    "build:weapp:upload": "taro build --type weapp --upload",

源码

github1s.com/NervJS/taro…

学艺不精,上面的小程序配置代码没看懂,ctx,joi什么的,挑能看懂的说,把weapp流程讲一遍
# index.ts
// 打包的时候命令行输入 taro build --type weapp --open
//那么可以提取出 weapp 和 open ,很容易得出是 weapp,
onBuildDone(async () => {
    const args = minimist(process.argv.slice(2), {
      boolean: ['open', 'upload', 'preview']
    })
    const { printLog, processTypeEnum } = ctx.helper
    const platform = ctx.runOpts.options.platform
    let ci
    switch (platform) {
            // 这里去实例化ci
      case 'weapp':
        ci = new WeappCI(ctx, pluginOpts)
        break
      。。。其他平台
    }
    // 再判断有其他的什么参数,有open,走open,这里很牛逼,我第一次看到这种switch (true),我个人是习惯用if判断的,但是如果并行判断的话,那么用这种,确实更有范儿。
    switch (true) {
      case args.open:
        ci.open()
        break
      case args.upload:
        ci.upload()
        break
      case args.preview:
        ci.preview()
        break
      default:
        break
    }
  })

实例weapp

先要了解miniprogram-cidevelopers.weixin.qq.com/miniprogram…

这里面的预览和上传都主要靠它。

  • \packages\taro-plugin-mini-ci\src\WeappCI.ts
/* eslint-disable no-console */
import * as ci from 'miniprogram-ci'
import * as path from 'path'
import * as os from 'os'
import * as cp from 'child_process'
import { Project } from 'miniprogram-ci'
import BaseCI from './BaseCi'

export default class WeappCI extends BaseCI {
  private instance: Project
  /** 微信开发者安装路径 */
  private devToolsInstallPath: string
// 配置信息有个devToolsInstallPath是填微信开发工具的安装路径的,没填就认为是默认路径,再判断秘钥privateKeyPath有没有填
  // 封装 ci ,方便调用 预览体验和上传
  _init () {
    const { outputPath, appPath } = this.ctx.paths
    const { fs } = this.ctx.helper
    if (this.pluginOpts.weapp == null) {
      throw new Error('请为"@tarojs/plugin-mini-ci"插件配置 "weapp" 选项')
    }
    this.devToolsInstallPath = this.pluginOpts.weapp.devToolsInstallPath || (process.platform === 'darwin' ? '/Applications/wechatwebdevtools.app' : 'C:\\Program Files (x86)\\Tencent\\微信web开发者工具')
    delete this.pluginOpts.weapp.devToolsInstallPath

    const weappConfig: any = {
      type: 'miniProgram',
      projectPath: outputPath,
      ignores: ['node_modules/**/*'],
      ...this.pluginOpts.weapp!
    }
    const privateKeyPath = path.isAbsolute(weappConfig.privateKeyPath) ? weappConfig.privateKeyPath : path.join(appPath, weappConfig.privateKeyPath)
    if (!fs.pathExistsSync(privateKeyPath)) {
      throw new Error(`"weapp.privateKeyPath"选项配置的路径不存在,本次上传终止:${privateKeyPath}`)
    }
    this.instance = new ci.Project(weappConfig)
  }
// 这里就是用child_process去执行命令行 微信开发者安命令行工具路径 open --project 项目路径
//其中有不少平台兼容代码,检查安装路径是否存在,判断命令行工具是否开启了命令行,
  async open () {
    const { fs, printLog, processTypeEnum, getUserHomeDir } = this.ctx.helper
    const { appPath } = this.ctx.paths
    // 检查安装路径是否存在
    if (!(await fs.pathExists(this.devToolsInstallPath))) {
      printLog(processTypeEnum.ERROR, '微信开发者工具安装路径不存在', this.devToolsInstallPath)
      return
    }
    /** 命令行工具所在路径 */
    const cliPath = path.join(this.devToolsInstallPath, os.platform() === 'win32' ? '/cli.bat' : '/Contents/MacOS/cli')
    const isWindows = os.platform() === 'win32'

    // 检查是否开启了命令行
    const errMesg = '工具的服务端口已关闭。要使用命令行调用工具,请打开工具 -> 设置 -> 安全设置,将服务端口开启。详细信息: https://developers.weixin.qq.com/miniprogram/dev/devtools/cli.html'
    const installPath = isWindows ? this.devToolsInstallPath : `${this.devToolsInstallPath}/Contents/MacOS`
    const md5 = require('crypto').createHash('md5').update(installPath).digest('hex')
    const ideStatusFile = path.join(
        //ide-status这种东西只能说特别硬核了
      getUserHomeDir(),
      isWindows
        ? `/AppData/Local/微信开发者工具/User Data/${md5}/Default/.ide-status`
        : `/Library/Application Support/微信开发者工具/${md5}/Default/.ide-status`
    )
    if (!(await fs.pathExists(ideStatusFile))) {
      printLog(processTypeEnum.ERROR, errMesg)
      return
    }
    const ideStatus = await fs.readFile(ideStatusFile, 'utf-8')
    if (ideStatus === 'Off') {
      printLog(processTypeEnum.ERROR, errMesg)
      return
    }

    if (!(await fs.pathExists(cliPath))) {
      printLog(processTypeEnum.ERROR, '命令行工具路径不存在', cliPath)
    }
    printLog(processTypeEnum.START, '微信开发者工具...')
    cp.exec(`${cliPath} open --project ${appPath}`, (err) => {
      if (err) {
        printLog(processTypeEnum.ERROR, err.message)
      }
    })
  }
// 体验版 用了其他库 miniprogram-ci 传参就行了 ,以及成功和失败的打印。
  async preview () {
    const { chalk, printLog, processTypeEnum } = this.ctx.helper
    try {
      printLog(processTypeEnum.START, '上传开发版代码到微信后台并预览')
        // 
      const uploadResult = await ci.preview({
        project: this.instance,
        version: this.version,
        desc: this.desc,
        onProgressUpdate: undefined
      })

      if (uploadResult.subPackageInfo) {
        const allPackageInfo = uploadResult.subPackageInfo.find((item) => item.name === '__FULL__')
        const mainPackageInfo = uploadResult.subPackageInfo.find((item) => item.name === '__APP__')
        const extInfo = `本次上传${allPackageInfo!.size / 1024}kb ${mainPackageInfo ? ',其中主包' + mainPackageInfo.size + 'kb' : ''}`
        console.log(chalk.green(`上传成功 ${new Date().toLocaleString()} ${extInfo}`))
      }
    } catch (error) {
      console.log(chalk.red(`上传失败 ${new Date().toLocaleString()} \n${error.message}`))
    }
  }
// 上传,同体验,没什么好说的。上传,以及成功和失败的打印。
  async upload () {
    const { chalk, printLog, processTypeEnum } = this.ctx.helper
    try {
      printLog(processTypeEnum.START, '上传体验版代码到微信后台')
      printLog(processTypeEnum.REMIND, `本次上传版本号为:"${this.version}",上传描述为:“${this.desc}”`)
      const uploadResult = await ci.upload({
        project: this.instance,
        version: this.version,
        desc: this.desc,
        onProgressUpdate: undefined
      })

      if (uploadResult.subPackageInfo) {
        const allPackageInfo = uploadResult.subPackageInfo.find((item) => item.name === '__FULL__')
        const mainPackageInfo = uploadResult.subPackageInfo.find((item) => item.name === '__APP__')
        const extInfo = `本次上传${allPackageInfo!.size / 1024}kb ${mainPackageInfo ? ',其中主包' + mainPackageInfo.size + 'kb' : ''}`
        console.log(chalk.green(`上传成功 ${new Date().toLocaleString()} ${extInfo}`))
      }
    } catch (error) {
      console.log(chalk.red(`上传失败 ${new Date().toLocaleString()} \n${error.message}`))
    }
  }
}

瞄了一眼其他平台的,大同小异,不多说了

其他

多亏了川哥的一些源码解读文章,我也搞出了公司自用的cicd库,也是推给了同事用了

github.com/xiaojiahao/…

总结

上传和体验版主要是靠miniprogram-ci,一些流程、文件判断和平台差异还是可以学习的。

小疑问

  • taro build --type weapp --open"是怎么指定用@tarojs/plugin-mini-ci执行的,我观察到taropackage.json没看到有什么相关的,为什么里面没有bin去设置taro,那么taro是怎么跑的