Jenkins 持续集成打包 IOS和Android 测试包:自动生成二维码

风不起浪,为什么会做这个事情,就要由前几天讲起了。

悲剧了

小公司没有资源,因为很多内测都是用第三方的,这边用的是蒲公英;

一开始以为是自己手误,然后再上传几次,依然显示这个界面,也没有任何报错信息,懵逼啊,之前都用的好好的,什么鬼?

折腾半天,无望,拿起手机,看到有短信,点开一开,显示这个:

这里面说到不再接受金融类应用在该平台分发,我司产品虽然是资讯类产品,但内容的确是金融相关的,好像没毛病;

操作起来有点麻烦,公司某项目的打包产品是一个zip,是当打包完成后把apk跟ipa压缩成一个zip输出,而使用者需要下载这个zip,解压,电脑连手机/模拟器安装,方可体验;

整个链路过长,也比较麻烦,因此就想着两点:

打包拆分,支持安卓、ios分开打包,不然有时候验证一个平台的问题需要打两个包,打包时长成本问题;

打包产物显示二维码便捷下载

在正式开始之前,先说明下,testerhome其实有类似的文章,如下图:

对应的文章都写的挺好的,但是轮子嘛,还是要亲力亲为印象才深刻,而且会针对对应文章缺乏的内容进行相应补充,尽可能更加详细把过程写出来;

jenkins显示图片

这里不会再讲述jenkins是什么,怎么安装之类的内容,如果有疑问,请点百度查看;

想要做成的效果是这样的:

支持修改文件描述

支持显示二维码

插件安装

jenkins不支持上面两个操作的,因此需要安装插件来使用;

Build Name Setter ,用于修改Build名称

description setter,用于在修改Build描述信息,在描述信息中增加显示QRCode(二维码)

直接在Jenkins的插件管理页面搜索上述插件,点击安装即可

怎么用

点击对应job的设置界面;

Build Name Setter

点击Build Environment,找到set build name,默认是#$,这里可以自定义,如下修改成#$jbtest,执行任务后的结果是这样的;

description setter

这个是在Post-build Actions里面,将写入到build描述信息中即可;

但填写完发现跟预想的不一致,这是因为Jenkins出于安全的考虑,所有描述信息的Markup Formatter默认都是采用Plain text模式,在这种模式下是不会对build描述信息中的HTML编码进行解析的。

要改变也很容易,Manage Jenkins -> Configure Global Security,将Markup Formatter的设置更改为Safe HTML即可。

更改配置后,我们就可以在build描述信息中采用HTML的img标签插入图片了。

保存后,执行任务,会就会显示url对应的图片了;

到这里,jenkins上显示图片的问题,就这样解决啦~

小小结

jenkins显示图片及修改任务描述,需要安装两个插件,并且需要传一个图片的img标签过来即可;

jenkins任务结果收集产物

这里额外提及一个点,如果job里面是有产物的,比如apk等文件,默认构建后是不会显示出来的,如下图:

那怎样让其在右侧显示出来?还是打开job的设置项,Post-build Actions,选择归档成品/Archives build artifacts,在Files to archive里面输入内容就好啦;

定位文件时,可以通过正则表达式进行匹配,也可以调用项目的环境变量;多个文件通过逗号进行分隔;

$/*.ipa,*.txt,QRCode.png

添加后的配置页面如下图所示:

重新构建任务,就可以看到对应的产物啦;

分发平台

首先说明,非广告贴,非广告贴,这节除了介绍分发平台,也会介绍不使用分发平台时怎么搞,任君选择;

上网搜了下,目前国内比较有名且还能用的分发平台,就是蒲公英跟fir.im

蒲公英

仔细看了下response,有二维码地址,good,就是你啦;

常规参数说明

_api_key跟userKey在登录状态下,点击网页的按钮即可获取;

上传App

参数太多了,懒的贴了,直接上代码吧;

Linux

这是官网给的例子,Linux下直接使用curl命令上传即可;

执行后等待上传完即可:

从返回的结果来看,是有一个buildQRCodeURL,就是拿这个给到jenkins那边的;

Python

如果是需要传参给脚本,就直接用sys.argv来获取,脚本本来没做太多兼容处理,将就用吧;

最后会输出二维码地址,拿这个地址传给jenkins就好啦;

结合jenkins

上面提及到,jenkins显示二维码是利用img src来处理,但是这个蒲公英返回的二维码地址是每次都不同的呢,那怎么搞?按照常理来说,是把src的值写成变量就好啦;

其实就是写成一个变量就好了,但是也因为url本身每次都变化,因此不能直接贴url,而是把url下载下来,固定下来一个名称,变量直接取这个路径即可;

那上面的img标签就会变成这样啦:

# 上面有多余的样式,因为图片是随便复制的,比较大,因此做了下限制,非必选;

这里可能有同学会问题,这个$是怎么来的,代表什么意思;

$是jenkins的内置变量,代表着显示当前构建的URL地址,文章尾部会列出常用的jenkins变量;

比如上,假如二维码的链接是这样:

那么,jenkineUrl/job/jobName/34这一串就是$

既然需要图片,那我们就下载图片吧,反正都有url了;

结果截图

因pgy需要上传apk或ipa,因为为了方便,直接hardcore了一个图片url来演示结果;

fir.im

点击这里跳转到官网,看了下,实名认证用户有 100 次/日的免费下载限额,未实名,仅有10次/日的免费下载限额;

一般来说,小公司,每天100次够用啦,除非产品够多,或者打包频繁;

实名好麻烦,还要手持证件照,没关系,反正有10次,够用啦;

然后去看api文章,咦,response居然没有二维码字段?那手动上传一个应用试试看,结果。。

当时心里的疑问就如下图一样,好吧,再见;

nginx同样是一款开源的HTTP服务器软件;

主要拿来干嘛

反向代理

负载均衡

HTTP服务器(包含动静分离)

正向代理

反向代理

反向代理应该是Nginx做的最多的一件事了;

什么是反向代理呢,以下是百度百科的说法:

反向代理(Reverse Proxy)方式是指以代理服务器来接受internet上的连接请求,然后将请求转发给内部网络上的服务器,并将从服务器上得到的结果返回给internet上请求连接的客户端,此时代理服务器对外就表现为一个反向代理服务器。简单来说就是真实的服务器不能直接被外部网络访问,所以需要一台代理服务器,而代理服务器能被外部网络访问的同时又跟真实服务器在同一个网络环境,当然也可能是同一台服务器,端口不同而已。

下面贴上一段简单的实现反向代理的代码

保存配置文件后启动Nginx,这样当我们访问localhost的时候,就相当于访问localhost:8080了;

负载均衡

负载均衡其意思就是分摊到多个操作单元上进行执行,例如Web服务器、FTP服务器、企业关键应用服务器和其它关键任务服务器等,从而共同完成工作任务。

简单而言就是当有2台或以上服务器时,根据规则随机的将请求分发到指定的服务器上处理,负载均衡配置一般都需要同时配置反向代理,通过反向代理跳转到负载均衡。

HTTP服务器

Nginx本身也是一个静态资源的服务器,当只有静态资源的时候,就可以使用Nginx来做服务器,同时现在也很流行动静分离,就可以通过Nginx来实现,首先看看Nginx做静态资源服务器;

这样如果访问http://localhost 就会默认访问到E盘wwwroot目录下面的index.html,如果一个网站只是静态页面的话,那么就可以通过这种方式来实现部署。

动静分离

动静分离是让动态网站里的动态网页根据一定规则把不变的资源和经常变的资源区分开来,动静资源做好了拆分以后,我们就可以根据静态资源的特点将其做缓存操作,这就是网站静态化处理的核心思路;

这样就可以吧HTML以及图片和css以及js放到wwwroot目录下,而tomcat只负责处理jsp和请求;

例如当我们后缀为gif的时候,Nginx默认会从wwwroot获取到当前请求的动态图文件返回,当然这里的静态文件跟Nginx是同一台服务器,我们也可以在另外一台服务器,然后通过反向代理和负载均衡配置过去就好了,只要搞清楚了最基本的流程,很多配置就很简单了,另外localtion后面其实是一个正则表达式,所以非常灵活;

正向代理

意思是一个位于客户端和原始服务器(origin server)之间的服务器,为了从原始服务器取得内容,客户端向代理发送一个请求并指定目标(原始服务器),然后代理向原始服务器转交请求并将获得的内容返回给客户端。客户端才能使用正向代理。

nginx常用命令

端口开放

因阿里云默认是安装了nginx 1.6版本,因此这块不说明;

直接在阿里云找到安全组规则,添加对应的对口,就可以用公网IP访问啦;

修改默认端口

因nginx默认是使用80端口的,如果需要修改,需要去配置文件修改;

nginx安装文件在/etc/nginx,打开后发现里面有个nginx.conf,查看发现里面没有端口信息,但是最后一行插入了*.conf文件,那我们就跟着这目录找;

cd到conf.d目录,发现里面只有一个default.conf文件,编辑查看,发现里面有个listen端口,这个就是了,修改成像要的端口,保存即可;

然后输入nginx -s reload重启服务器,然后再用公网IP+端口访问下,也会显示Welcome to nginx!

增加端口

有同学可能问,那想加多几个端口可以吗?

没问题的,还是来到default.conf文件,在原来的server下新增一个就好啦,如下:

autoindex

nginx有一个目录浏览功能(autoindex),但是呢,默认是不允许列出整个目录的,如果有需求,就用上面的新增端口的方式来操作就好啦;

最后,整个效果如下:

还可以解析README.md

简介

nginx指定文件路径有两种方式root和alias;

root与alias主要区别在于nginx如何解释location后面的uri,这会使两者分别以不同的方式将请求映射到服务器文件上。

语法

当客户端请求 /request_path/image/file.ext的时候,Nginx把请求解析映射为/local_path/dirt/request_path/dirt/file.ext

实例2:

如果一个请求的URI是/t/a.html时,web服务器将会返回服务器上的/www/root/html/t/a.html的文件;

当客户端请求 /request_path/dirt/file.ext 的时候,Nginx把请求映射为/local_path/dirt/file/file.ext

注意这里是file目录,因为alias会把location后面配置的路径丢弃掉(比如/request_path/dirt/one.html,到alias那里就剩one.html了),把当前匹配到的目录指向到指定的目录。

示例2

如果一个请求的URI是/t/a.html时,web服务器将会返回服务器上的/www/root/html/new_t/a.html的文件;

综合例子

在这段配置下,http://test/abc/a.html就指定的是/home/html/abc/a.html;

这段配置亦可改成使用 root 标签:

这样,nginx 就会去找 /home/html/ 目录下的 abc 目录了,得到的结果是相同的。

但是,如果把 alias 的配置改成

那么 nginx 将会直接从 /home/html/def/ 取数据,例如访问 http://test/abc/a.html 指向的是 /home/html/def/a.html;这段配置还不能直接使用 root 配置,如果非要配置,只有在 /home/html/ 下建立一个 def->abc的软 link(快捷方式)了。一般情况下,在 location / 中配置 root,在 location /other 中配置 alias 是一个好习惯。

其他

使用alias时,目录名后面一定要加"/",不然会认为是个文件。

alias在使用正则匹配时,location后uri中捕捉到要匹配的内容后,并在指定的alias规则内容处使用。

alias只能位于location块中,而root的权限不限于location。

举个例子

你提供地址,当你在家时:

1.朋友想去找你,看到阿姨,阿姨说在家,那朋友得到的是你提供的地址+阿姨说的,这就是root,会把两个地址串起来;

2.班主任想去找你,看到阿姨,阿姨说,你直接跟我说就好了,那班主任得到的就是阿姨说的,这就是alias,会把location后面配置的路径弃掉,把当前匹配到的目录指向到指定目录;

当你不在家时(动静分离):

1.(alias)班主任找你的结果不变,结果依然是以阿姨说的为主;

2.(root)因为root是把连个地址串一起的原因,这种情况不太适用,如果非要用root,要在阿姨身上加一个电话,直接call你(软link)

上传文件

既然可以访问了,那就写个简单的HTML上传文件吧,关于后端的话,本来想用php,毕竟这块网上例子很多,比如这里,都有源码了;

但是呢,毕竟不懂php,即使它是最棒的语言,考虑到后面维护麻烦,还是选择用flask吧;

直接通过pip命令进行安装即可:

pip install flask

官方的一个最简单示例:

这里不会详细介绍flask,感兴趣的同学可以來官网看看;

对于简单的上传,一般只需要3个步骤:

1. 创建上传表单

2. 获取文件

当点击上传/提交按钮,要获取到上传的文件,通过requests对象中的files就可以获取到啦~

3. 保存文件

获取到文件,接着就是保存了,指定路径和文件名;

file.save(path + filename)

配置文件

实际在上传文件的时候,会做下限制,比如限制文件大小、文件夹地址、上传文件扩展名等,而在实际项目,还会有密钥、数据库地址等等,这些都是属于配置项;

一般有3种方式:

直接写入脚本

当你脚本是轻量,配置项不多的情况下,可以直接写到脚本里面;

当然也可以用字典来简化代码:

单独配置文件

这种适用于配置项多离的情况,可以创建一个独立的配置文件,如

然后导入配置:

或者:

不同的配置类

当需要多个配置配合,比如测试配置、开发配置、运营配置,这时候就需要在配置文件中创建不同的配置类,然后在创建程序实例时引入相应的配置类;

这里继续以config.py为例子,创建一个存储通用配置的类;

这里说明下,上面是把配置写入系统环境变量,然后使用os模块的getenv()方法获取,第二个参数作为默认值;

通过from_object()方法导入配置:

那回到这次的功能上,我们只需要写到脚本里面即可;

当然还要考虑安全问题,如文件名校验之类的,具体的话,看源码:

这里说个小事情,一开始执行上面的代码,会报错:

网上找了下,原因是在Subline3遇到的都是看似空格实则没有空格引起的::

解决方法:

就是打开subline的空格制表显示就可以清楚的显示出自己是否真的空格了。

第一次遇到这问题,详情请点击这里查看;

执行脚本,上传文件,上传的文件就是在file_upload目录下的;

功能是有的,就是界面low了点。。

身为一个测试同学,对UI肯定要有点追求,并且希望可以提供下载,因此就上网找了个插件,点击这里,看看github上的截图:

听漂亮的,这样上传就有了进度,并且支持多文件上传,但依然会有个问题,上传完去哪里看?想下载怎么办?

这里想说一个问题,上面的例子,如果大家有细心看的话,会发现上传的文件都会去到一个叫file_upload文件夹,上传是没问题的,但是下载就有问题了;

jb在下载的时候,不管这个下载地址怎么拼,页面都会无情报错,说这个地址不存在;

但是呢,如果把上传目录修改成static,却是正常的;

包括如果这个static不在根目录,也一样有问题,那为什么当static是根目录且放到这里面就这样?

Flask资源定位是依靠

__name__参数(文件名或包名),所以相对定位一定要基于这个文件路径。

为什么会在static文件夹路径下会正确?Flask默认静态文件在static文件下。

那如果一定要上传到file_upload文件夹,怎么办?

那就修改的flask默认的static文件夹只需要在创建Flask实例的时候,把static_folder和static_url_path参数设置为空字符串即可。

访问的时候用url_for函数,res文件夹和static文件夹同一级:

url_for('static', filename='res/sheeta.jpg')

最终就成了这样:

good,问题搞定了,体验下:

生成二维码

支持点击打开/下载,那如果是上传apk/ipa那就麻烦了,jenkins那边,所以还是希望能生成一个二维码;

而Python生成二维码的话,有qrcode,同时也需要处理下二维码图片,因此需要安装qrcode、pillow:

最简单的demo:

这里面是有一些参数的,但是边幅原因,请各自去了解;

这时候就生成一个二维码了,但是呢,上面的demo图片太小了,而且想弄个定制化的二维码,怎么破?不细说,直接源码拿走不谢~

可以直接拿来复用,需要修改的也就几个参数,简单便捷,效果图如下:

其他小系列

1、中文名称被不显示的问题

是这样的,如果一个文件里面有中文,比如消息.jpg,把这个地址塞到二维码,然后扫描,会这样的:

中文不见了,如果是英文或字母,都很正常,另外,如果是用浏览器扫描,也都没问题,就微信会这样;

很简单,encode下就好了;

编码:

这样后,就能在微信打开啦~

ios不能直接下载ipa包

来到这里,主路径都是通的,二维码也是可以生成的,用android手机试下,没问题,可以在线预览图片或者下载文件;

但是用ios手机扫描ipa包的链接,结果不会像安卓那样下载ipa包安装,而是load半天,然后这样显示:

网上找了下,很多这样的例子,这里参考的是这里,

大致的步骤就是:

准备plist、ipa包、icon、HTMLdemo;

plist上传到https地址;

HTML里面加一个点击事件;

原理:

是通过Safari解析链接中的"itms-services://"来实现的。

例如:

Iphone Download

Safari会去读取installIPA.plist中的信息,如:iOS应用的名称、版本、安装地址等。

test.html

因篇幅问题,只留必备项,只输入填入ipa包地址,底部那块元数据信息即可;

最终的结果就是这样啦:

主流程总算通了,深呼一口气;

这里说明下,如果没有https,测试的话,可以试试上传的github,这里是https的;

当长期考虑,还是要弄一个,公司有就用公司的,公司没有就自己买一个,jb是用aly,所以也在aly买了个,有兴趣的同学点击这里,购买、认证、解析、申请证书,就可以了,这块不说明,感兴趣的可自行上网查询,

这里演示的是demo,因此url都是hardcore的,实际还要处理plist的路径等,上传一个ipa就生成一个plist,这块自行处理吧;

源码:加文末图片上的QQ群获取

源码在此,就不再单独解释了,可能会有一些问题,但是模型大致就这样啦,因时间问题,年后再优化,反正就是缺什么就import什么就好了;

小结

本文折腾很久,主要是因为重新看回flask跟学习下ng,以及ios的解决方案,外加年底工作很繁忙,因此陆陆续续折腾了快半个月的时间,本来还想把所有优化都做好再放出来,但怕开年后更加忙了,那这文章就烂尾了,因此就先发出来了;

本文涉及到的内容比较多,包括jenkins如何显示图片、pgy分发平台的使用、自己搭一个文件上传的轮子,涉及到的只是有ng、flask,都是比较简单的内容,但是是否做过是两回事,从小白的角度出发来落地;

如果有更好的方案,欢迎一起交流~

  • 发表于:
  • 原文链接:https://kuaibao.qq.com/s/20190809A0BDUU00?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。

扫码关注云+社区

领取腾讯云代金券