使用Python脚本半自动化iOS企业版包更新

背景

在我之前一篇文章里 iOS企业内部应用分发 讲述了 iOS 企业帐号打包的 ipa 分发,但是如果每次 ipa 更新,重新上传 *.ipa 后还要手动编辑 *.plist 的新的 ipaurl 和包的相关信息,这就和打包一样(使用 fastlane),是毫无技术含量但是花时间的事情,作为一个爱偷懒的人,我总是想利用自身所学或者一些工具来代替我做一些重复又没技术含量的事,在本文涉及的这件事里,显然,脚本要派上用场,最终选用了 Python 而没有用 Bash —- 从 ipa 读取相关信息并写入到目的 plistPython 只是业余,如您有建议或改进,欢迎留言评论指出,感激不尽!

直接上代码

#!/usr/bin/python3
#coding:utf-8
 
import zipfile, plistlib, sys, re, os.path, io

sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')

global plist_root # Info.plist
global ipa_name # ipa 名称

def analyze_ipa_with_plistlib(ipa_path):
    global plist_root
    ipa_file = zipfile.ZipFile(ipa_path)
    plist_path = find_plist_path(ipa_file)
    plist_data = ipa_file.read(plist_path)
    plist_root = plistlib.loads(plist_data)
    print_ipa_info (plist_root)
 
def find_plist_path(zip_file):
    name_list = zip_file.namelist()
    pattern = re.compile(r'Payload/[^/]*.app/Info.plist')
    for path in name_list:
        m = pattern.match(path)
        if m is not None:
            return m.group()

def print_ipa_info(plist_root):
    print ('\n' + '源 ipa 包信息:')

    CFBundleDisplayName = ''
    isCFBundleDisplayNameExist = True

    try:
        CFBundleDisplayName = plist_root['CFBundleDisplayName']
    except:
        print ('CFBundleDisplayName 不存在,将获取 CFBundleName')
        isCFBundleDisplayNameExist = False

    
    if isCFBundleDisplayNameExist != True:
        try: 
            CFBundleDisplayName = plist_root['CFBundleName']
        except:
            print ('CFBundleName 不存在')

    print ('Display Name: %s' % CFBundleDisplayName)
    print ('Bundle Identifier: %s' % plist_root['CFBundleIdentifier'])
    print ('Version: %s' % plist_root['CFBundleShortVersionString'] + '\n')
 
def print_plist_info(plist_path, beforeWritten):
  
    dict = plistlib.readPlist(plist_path)

    metadata = dict["items"][0]["metadata"]
    softwarePackageDict = dict["items"][0]["assets"][0]

    state = "前" if beforeWritten else "后"

    print ('写入' + state +'的plist信息')
    print ('bundle-identifier: %s' % metadata['bundle-identifier'])
    print ('bundle-version: %s' % metadata['bundle-version'])
    print ('title: %s' % metadata['title'])
    print ('ipa url: %s' % softwarePackageDict['url'] + '\n')

def writeToPlist(plist_path):
    global plist_root
    # 读取app.plist
    dict = plistlib.readPlist(plist_path)

    CFBundleDisplayName = ''
    isCFBundleDisplayNameExist = True

    try:
        CFBundleDisplayName = plist_root['CFBundleDisplayName']
    except:
        print ('CFBundleDisplayName 不存在,将获取 CFBundleName')
        isCFBundleDisplayNameExist = False

    
    if isCFBundleDisplayNameExist != True:
        try: 
            CFBundleDisplayName = plist_root['CFBundleName']
        except:
            print ('CFBundleName 不存在')

    # 更改包信息
    metadata = dict["items"][0]["metadata"]
    metadata['bundle-identifier'] = plist_root['CFBundleIdentifier']
    metadata['bundle-version'] = plist_root['CFBundleShortVersionString']
    metadata['title'] = CFBundleDisplayName
   
    # 更改ipa地址
    softwarePackageDict = dict["items"][0]["assets"][0]

    # 生成新的 ipa 路径
    global ipa_name
    url = softwarePackageDict['url']
    urlComArr = url.split('/')
    # 删除文件名
    urlComArr.pop() # 删除最后一个
    urlComArr.append(ipa_name) # 拼接新文件名及扩展名
    newUrlComArrStr = '/'.join(urlComArr) # Array to String
    softwarePackageDict['url'] = newUrlComArrStr

    plistlib.writePlist(dict, plist_path) # 写入plist
 
if __name__ == '__main__':
    args = sys.argv[1:]
    if len(args) != 2:
        print ('参数错误,用法: python3 config.py /path/to/ipa /path/to/plist')
        sys.exit(0)

    global ipa_name

    ipa_path = args[0]
    analyze_ipa_with_plistlib(ipa_path)
    ipa_name = os.path.basename(ipa_path)
    plist_path = args[1]
    print_plist_info(plist_path, True)
    writeToPlist (plist_path)
    print_plist_info(plist_path, False)

说明

每次新打包后,需要更新 plist 文件相关信息,直接在服务器或者本地运行该脚本便可更新 plist 信息,然后覆盖服务器上的同名文件即可(亲手实践过 ipa 分发就没困惑),但运行该脚本的系统应该安装了 Python3,否则无法运行。用法如下,选你的终端执行:

python3 config.py appName.ipa路径 app.plist路径