微信小程序持续集成

2,775 阅读8分钟

作者 | 周周酱

本文写于2019年12月,可能有部分内容过时。首发于周周酱个人博客,转载请注明出处。

微信小程序的发布流程和常规web项目不太一样,它比较依赖微信开发者工具和开发者,在我们迭代的过程中,暴露了一些问题,和一些可以改进的流程。所以我们想要合理地运用现有工具,减少重复工作,解决发布流程以及开发和产品环境差异的问题,提升研发效率。

问题背景

在没有持续集成方案的情况下,我们常常会遇到几个场景:

  1. 测试环节频繁需要发布小程序,小程序项目众多,发布操作繁琐且工作内容重复,造成研发思路打断,时间的浪费。
  2. 测试同学无法自己构建上传测试版本的小程序,需要依赖开发同学提供测试二维码。
  3. 由开发者在本地上传的体验版小程序可能会出现携带本地开发中的代码、或未及时拉取分支造成的差异问题。

所以我们需要达到的目的是,研发只需提交代码到git,剩下的时候都交由机器来完成,研发同学就可以专心地搬砖了。

知识储备

持续集成

持续集成是一种软件开发实践,即团队开发成员经常集成他们的工作,通常每个成员每天至少集成一次,也就意味着每天可能会发生多次集成。每次集成都通过自动化的构建(包括编译,发布,自动化测试)来验证,从而尽早地发现集成错误。(摘自百度百科) 持续集成工具有很多,最常用的有Jenkins和Gitlab CI,解决方案都是十分成熟的。

微信开发者工具能力

决定微信小程序能够实现持续集成的原因,主要是微信开发者工具开放了http和命令行的方式来调用开发者工具。提供的接口有:1.启动工具 2.登录 3.预览 4.自动预览 5.上传代码 6.关闭开发者工具 需要注意的是微信开发者工具只提供了window版本和macOS版本。 HTTP调用 命令行调用

docker基本命令

  • 新建并启动容器

docker run [OPTIONS] IMAGE IMAGE是镜像ID或镜像名称 OPTIONS说明:  --name=“容器新名字”:为容器指定一个名称  -d:后台运行容器,并返回容器ID,也即启动守护式容器  -i:以交互模式运行容器,通常与-t同时使用  -t:为容器重新分配一个伪输入终端,通常与-i同时使用    -v:绑定挂载目录  -P:随机端口映射  -p:指定端口映射

  • 进入容器

docker exec -it containerID /bin/bash

环境支持

1.搭建了Jenkins环境的机器 2.一台Linux服务器 我们有一台部署测试环境node应用的linux服务器,所以打算把打包环境搭在这台机子上。

开始

微信小程序CI_看图王.png

基本流程: jenkins拉取最新代码->构建代码->将工作空间的dist目录打包代码拷贝到linux服务器->在linux服务器执行微信开发者工具发布脚本,上传小程序至微信后台->钉钉机器人推送发布结果。

1.开发者工具安装

若使用windows或者mac作为打包机的话,直接下载微信官方的开发者工具安装即可。由于官方并未提供linux版本的微信开发者工具,所以使用linux服务器作为打包机的话,需要对开发者工具做些改造,我们需要了解一些微信开发者工具的底层原理,这个可以作为额外的研究对象。 这里我们已经有了可以使用的微信开发工具的docker镜像,可以在linux环境上使用。  WeChat WebDevTool in Docker(With LXDE)

下载镜像并启动容器: docker run -d --name wxdevtool -p 8080:80 -p 6666:9000 -v /var/opt/wxproject:/projects seancheung/wxdevtool:latest 容器内开放了两个端口,80端口是该镜像提供的vnc端口,访问该端口可以通过图形化界面操作开发者工具。9000端口则是微信开发者工具HTTP 服务端口号的代理端口。HTTP 服务每次启动的端口号并不固定,但会记录在用户目录下,在容器内会获取每次的端口号,并使用nginx代理到9000端口,使用-p映射到我们的宿主机,即可在宿主环境下访问。然后我们会将小程序的代码放在/var/opt/wxproject目录下,所以将该目录挂载到容器的/``projects目录,在容器内部我们就能访问到小程序代码,并执行相应地上传预览操作。

2.命令调用

容器启动后,可以通过命令行的方式和http方式来调用开发者工具。

  • 命令行调用:
docker exec -t wxdevtool wxdevtool exec --login

微信图片_20191218160918.jpg

  • http调用:    
curl http://127.0.0.1:6666/login

两种方式都能完成微信开发者工具的基本操作,根据实际的使用场景来选择。经过后续的实践,http调用的方式会更适合我的场景,主要是因为 1.使用http方式更容易获取调用的结果。我们需要在执行上传命令后,获取返回结果,如若上传失败,需要将失败原因同步到钉钉群,如若登录状态失效,我们会获取最新的登录二维码发送到群里。http方式可以更好地满足。 2.执行命令行操作,若操作失败,会自动退出shell进程,无法进行后面的操作,这对我们的CI操作的流程控制和条件判断是不利的,而http请求无论成功失败都不会阻塞或退出后续流程。当然这也许是我的使用方式有问题,实际有应该有其他解决方案,毕竟对shell操作我还不是非常在行。

3.jenkins job配置

我们已经能成功使用http请求来完成开发者工具登录,小程序上传等操作,接下来需要结合到持续集成工具里。在开始配置之前,理下我们要的功能 1.代码git分支可配置 2.打包版本可选(测试版和正式版),我们构建上传时,正式版和测试版主要是构建命令不一样,以及appId不一样,其他都没有区别,所以使用同一个job。 3.小程序版本号和备注信息可配置 4.代码提交后自动构建 5.钉钉机器人接入

  • 创建一个自由风格的项目

微信图片_20191207160208.png

  • 设置源码管理,分支名使用构建参数传入

微信图片_20191207160931.png

  • 构建环境配置

需要远程访问打包服务器,在这之前需要添加远程服务器ssh credential,类型是ssh username with private。然后绑定private key和用户名变量,透出参数供脚本使用。

微信图片_20191207162134.png

  • jenkins脚本

下面进入重点的脚本部分,jenkins这边配置的会非常简单,主要执行依赖安装,代码构建,拷贝代码至远程服务器。而对开发者工具的调用则放在单独的脚本文件中以供复用。以下是jenkins shell脚本,解读也放在注释中。

# set +x
#远程服务器地址
REMOTE_HOST=XXX

#项目文件夹名称
PROJECT="assistance"

#自定义方法 拷贝文件至远程目录
remote_scp()
{
  scp -rvi "$REMOTE_KEY" -o StrictHostKeyChecking=no "$1" "$REMOTE_USER"@"$REMOTE_HOST":"$2"
}

#自定义方法 远程执行服务器操作命令
remote_exec()
{
  ssh -i "$REMOTE_KEY" -o StrictHostKeyChecking=no "$REMOTE_USER"@"$REMOTE_HOST" "$@"
}

# 安装依赖
echo "--installing dependencies--"
npm i --no-progress

# 根据环境参数构建项目
echo "--building sources--"
case "$BUILD_TYPE" in
prod)
  npm run build:dist
  ;;
*)
  npm run build:test
  ;;
esac

# 先清空远程项目目录,再拷贝构建后的代码至远程服务器打包目录
echo "--updating remote source--"
remote_exec rm -rf /var/opt/wxproject/$PROJECT
remote_scp ./dist /var/opt/wxproject/$PROJECT

# 执行上传小程序脚本(脚本放在远程服务器上,xcx命令为对应脚本文件的自定义命令,关于自定义shell
命令可以查一下相关资料)
echo "--uploading package--"
sleep 2
remote_exec xcx $PROJECT $UPLOAD_VERSION $UPLOAD_DESC
  • 开发者工具打包脚本

执行的操作也是很简单,打包脚本放在安装开发者工具的机器上,所以使用http调用开发者工具的上传接口就行了。使用upload接口,有以下参数

微信图片_20191207164744.png

调用本机127.0.0.1下的6666端口(还记得吧,我们在启动时将容器内的开发者工具http服务9000端口映射到宿主机的6666端口)。几个参数在调用时已经传入,分别对应jenkins下的参数PROJECTPROJECT,UPLOAD_VERSION,$UPLOAD_DESC。由于我们在发布成功后想要推送当次的代码包信息,所以使用infooutput参数将发布信息存储下来,以便后面使用。

REQUEST_URL=http://127.0.0.1:6666
result=$(curl -H "Connection:keep-alive" $REQUEST_URL/upload\?projectpath\=/projects/$1\&
version\=$2\&desc\=$3\&infooutput\=/projects/$1/uploadInfo.json)

此次请求的结果在result中可以拿到,针对不同的结果会做不同的处理。开发者工具的登录状态隔一段时间是会过期的,所以此时发布失败需要重新获取登录二维码,并推送到钉钉工作群,让我们及时去登录。由于钉钉推送图片,需要用网络地址,所以这边会将二维码文件上传至静态资源服务器。

RELOGIN="需要重新登录"
WEBHOOK="https://oapi.dingtalk.com/robot/send?access_token=XXX"
# 登录二维码cdn地址
QRCODEURL="https://XXXX"
# 图片上传接口
UPLOAD=""
send_ding() {
    curl $WEBHOOK -H 'Content-Type: application/json' -d '
        {
            "msgtype": "markdown",
            "markdown": {
                "title":"小程序发布失败",
                "text": "小程序发布失败\n\n >开发者工具需要重新登录\n\n > ![screenshot]('$QRCODEURL')\n"
            }
        }'
}
if [ -n "$(echo ${result} | grep $RELOGIN )" ]; then 
    curl ${REQUEST_URL}/login\?format\=image\&qroutput\=/projects/login.png
    curl -F "folderName=wxdevtool" -F "file=@/var/opt/wxproject/login.png" $UPLOAD
    send_ding
    exit 1
fi

同上,发布成功后也可以推送本次发布的代码包信息,我们已经拿到本次上传信息并存储在uploadInfo.json文件中,只需使用cat命令拿到文件内容摘取想要的信息即可,可能会需要对json进行处理,shell处理解析json并不是很方便,这里推荐使用jq这个包来处理json。

  • 代码提交后自动构建

使用gitlab Webhooks结合jenkins 构建触发器。

  • 机器人推送结果

微信图片_20191207173842.png

微信图片_20191207173839.png

微信图片_20191218160923.png

小结

到这里已经完成微信小程序持续集成基本功能,理论上并不复杂,前端工程化的很多重复工作都应该交给自动化工具来完成。 如若本文有描述不当的地方,还请大家不吝赐教,在评论中指出。