前语

上星期蒲公英在毫无征兆的情况下,把官网的域名给改了。本以为各种接口改成最新的域名就能接着用,但实际上jenkins打包主动上传到蒲公英成功后,iOS扫码设备失效了。众所周知苹果iOS包的网页扫码设备是由itms-services服务完结,可是条件是域名需求支撑https,我们本地已经有一台jenkis打包机了,那我本地打完包后,把ipa包放到本地http服务,不经过域名,是否也能完结内网下扫码设备呢?答案接着往下看

什么是itms-services

itms-service是apple为iOS用户提供网页、扫码设备办法所运用的协议。这种办法不需求经过APP Store分发也可以设备iOS App。(条件是你的iPhone设备ID已经参加进去打包的开发者账号、或许运用企业账号(现在基本绝种))。

items-services协议实质就是访问一个https的长途plist描绘文件,这个plist记载了app的信息和ipa包下载地址。 plist文件格局如下:


<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>items</key>
<array>
<dict>
<key>assets</key>
<array>
<dict>
<key>kind</key>
<string>software-package</string>
<key>url</key>
<string>http://192.168.1.2/xxx.ipa</string>
</dict>
</array>
<key>metadata</key>
<dict>
<key>bundle-identifier</key>
<string>com.zxh.xpzc</string>
<key>bundle-version</key>
<string>1.0.0</string>
<key>kind</key>
<string>software</string>
<key>title</key>
<string>需求下载这个标题</string>
</dict>
</dict>
</array>
</dict>
</plist>

传统方案一般是打包机打完包后,生成上面的plist文件,把ipa上传cdn服务器上,然后plist文件也上传到cdn即可。

用户只需扫码或许浏览器翻开:


itms-services://?action=download-manifest&url=https://XXXXXXXX/app.plist

就可以扫码设备。由于itms-services需求运用https链接,很多人默许以为必定要有一个域名加上配了https才行。实际上假如我本地打开一个https服务,直接https://192.168.3.x/install.plist是否可以呢?

树立本地https服务

树立本地https服务十分简略,我们只需求brew设备http-server


brew install http-server

在Mac下运用http-server打开HTTP服务后,要装备HTTPS,需求履行以下过程:

  1. 生成SSL证书和私钥:可以运用openssl指令生成自签名的SSL证书和私钥。翻开终端,履行以下指令:

openssl req -nodes -new -x509 -keyout server.key -out server.crt

这将生成一个名为server.key的私钥文件和一个名为server.crt的证书文件。

  1. 发动HTTP服务并装备HTTPS:在终端中,切换到你要发动HTTP服务的目录下,然后履行以下指令:

http-server -S -C server.crt -K server.key

这将发动一个运用HTTPS的HTTP服务。-S参数表明启用HTTPS,-C参数指定证书文件,-K参数指定私钥文件。

  1. 在浏览器中访问:翻开浏览器,输入https://localhost:8080(假定HTTP服务监听在8080端口),即可访问运用HTTPS的本地HTTP服务。

请注意,由于自签名证书不受信任的CA颁发,浏览器可能会闪现正告信息。假如需求运用受信任的证书,可以请求并购买来自公共CA的SSL证书,并将其装备到HTTP服务器中。

上面提到由于本地https是不受信任的,这导致假如ipa文件放到https服务下是不能直接下载的,所以我们这儿取巧,一起打开https和http服务,https为了触发itms-servics协议,ipa文件则放到http服务下,这样就可以完好的完结本地iOS设备包扫码设备。一起发动http和https两个服务指令如下:


http-server -p 8081 & http-server -p 8082 -S -C server.crt -K server.key

本地自建服务兼容jenkins

之前编写的jenkins主动打包是打完包后分发是根据蒲公英的,假如要支撑本地扫码设备那脚本需求兼容,脚本如下,这儿只说明怎样完结生成itms-services的plist文件,以及生成二维码,怎样完结主动打包不做说明。

打完包生成了iPA后,我们把ipa挪到到http服务写入plist如下:


def write_local_itms_services(rootPath,ipa_directory,ipa_path):
pwd='python3 ./create_config.py --root_path {} --app_directory {} --app_url {} --build_version {}'.format(rootPath,ipa_directory,ipa_path,build_version)
os.system(pwd)

ipaPath=targerIPA_parth + '/' + ipa_filename + '/' + 'App.ipa'
httpIpaPath='http://192.168.41.3:8081' + '/' + ipa_filename + '/' + 'App.ipa'
write_local_itms_services(targerIPA_parth,ipa_filename,httpIpaPath)

生成plist脚本:


#!usr/bin/python3
# -*- coding:utf-8 -*-
import os
import sys
import plistlib
from datetime import datetime, timedelta, timezone
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('--root_path', default='')
parser.add_argument('--app_directory', default='')
parser.add_argument('--app_url', default='')
parser.add_argument('--build_version', default=0)
parser.add_argument('--app_name', default='')
args = parser.parse_args()
JFAppUrl = args.app_url
JFAppDirectory = args.app_directory
JFBuildVersion = args.build_version
JFPackageRootPath = args.root_path
JFAppName = args.app_name
print(args)
def readInfoPlist(inputFilePath):
if not os.path.isfile(inputFilePath):
return None
with open(inputFilePath, 'rb') as fp:
infoPList = plistlib.load(fp)
print(infoPList)
return infoPList
def writePlist(outFilePath,fileName,packageName,title):
pl = {'items': [{
'assets': [
{'kind': 'software-package', 'url': JFAppUrl},
{'kind': 'display-image', 'url': ''},
{'kind': 'full-size-image', 'url': ''}],
'metadata':
{'bundle-identifier': packageName, 'bundle-version': JFBuildVersion, 'kind': 'software', 'title': title}
}]}
with open(outFilePath, 'wb') as fp:
plistlib.dump(pl, fp)
plistFileOut = JFPackageRootPath + "/"+ JFAppDirectory + "/" + "iOS_" + JFBuildVersion + ".plist"
infoPList = None
ipaFileName = 'App'
packageName = 'com.xxxx.xxxx'
appVersion = JFBuildVersion
title = JFAppName + '_' + JFBuildVersion
#写plist文件,用来网页设备
print(ipaFileName,packageName,appVersion,title)
writePlist(plistFileOut,ipaFileName,packageName,title)

写入plist后,我们生成二维码扫码设备,然后把二维码发到飞书(也是脚本完结)即可。

生成二维码脚本:


def write_qr_code(rootPath,ipaDirName):
saveRqCodeFileName="rqcode_" + build_number + ".png"
saveFilePath = rootPath + '/' + ipaDirName + '/' + saveRqCodeFileName
pwd = 'python3 ./createRqCode.py --build_number {} --qrcode_save_file {} --version {} --dir_name {}'.format(build_number,saveFilePath,build_version,ipaDirName)
print(pwd)
os.system(pwd)
return saveRqCodeFileName
write_local_itms_services(targerIPA_parth,ipa_filename,httpIpaPath)
imgName=write_qr_code(targerIPA_parth,ipa_filename)
//生成二维码图片保存路径,推送到飞书
imgPath=ipaPath=targerIPA_parth + '/' + ipa_filename + '/' + imgName

生成带itms-services协议二维码图片脚本:


from PIL import Image, ImageFont, ImageDraw
import os, sys
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('--build_number', default=0)
parser.add_argument('--app_name', default='')
parser.add_argument('--version', default='')
parser.add_argument('--dir_name', default='')
parser.add_argument('--qrcode_save_file', default='')
args = parser.parse_args()
JFBuildNumber = args.build_number
JFAppName = args.app_name
JFVersion = args.version
JFDirName = args.dir_name
JFQRCodeSaveFile = args.qrcode_save_file
plistFileName= "iOS_" + JFVersion + ".plist"
InstallUrl='itms-services://?action=download-manifest&url=https://192.168.41.3:8082/' + JFDirName + '/' + plistFileName
pkEnvStr = "检验"
textContent = JFBuildNumber
packageName = JFAppName + pkEnvStr
imagSize = 512
imagSaveFile = "/tmp/number.png"
JFColor = (0,255,0,255)
path=os.getcwd()+'/script/'
font = ImageFont.truetype(path + "FZZZHONGJW.ttf", size=int(imagSize / 6))
img = Image.new("RGBA",(imagSize,imagSize),color=0)
# 写文字
draw = ImageDraw.Draw(img)
textSize = draw.textbbox((0,0),text=packageName, font=font)
draw.text(xy=((imagSize- textSize[2]) / 2, (imagSize - textSize[3]) / 4), text=packageName, font=font,fill=JFColor)
textSize = draw.textbbox((0,0),text=JFBuildNumber, font=font)
draw.text(xy=((imagSize- textSize[2]) / 2, (imagSize - textSize[3]) / 1.5), text=JFBuildNumber, font=font,fill=JFColor)
img.save(imagSaveFile)
# 输出
# img.show()
print(JFQRCodeSaveFile)
from MyQR import myqr
version, level, qr_name = myqr.run(
words=InstallUrl,
version=1,
level='H',
picture=imagSaveFile,
colorized=True,
contrast=1.0,
brightness=1.0,
save_name=JFQRCodeSaveFile,
save_dir=os.getcwd()
)

结论

告别蒲公英-教你自建本地https服务建立iOS itms-services扫码安装

脚本根据不同环境情况可能要稍微调整,仅供参考,就这样成功生成二维码发送到飞书后,扫码即可完结本地https服务设备检验iOS包啦(条件是手机衔接的wifi和打包机打开的https服务在同一个内网下),再也不用蒲公英分发啦,打包速度提高100s+(不需求上传到蒲公英服务器,本地上传时间为0)。