Python实现提醒iOS描述文件有效期
背景
公司企业APP描述文件过期,没有提醒,导致当天出现不可用的问题。
为了避免再发生类似的问题,笔者想要写一个Python
脚本,读取描述文件,获取有效期,设置提醒,且自动运行。
实现
首先再来理一下思路,所有的描述文件都在~/Library/MobileDevice/Provisioning Profiles/
目录下,但是里面的内容通常不会自动删除,过期的或者重复的都在这个目录中,而且这个目录下的文件名是uuid
命名的和Xcode中的文件名字也不能直接对应,所以一眼看去,只能用一个字形容:乱。
如果账号是管理员,直接登录在电脑上,项目中用的自动管理描述文件的,还好一些,现在会自动续期。但是如果账号是开发者,发布的描述文件没有权限用自动管理的,就需要注意这个描述文件有效期的问题。
再来理一下思路,想要的是一个读取描述文件夹下所有描述文件,获取描述文件中的内容,根据有效期,设置提醒,且自动运行的脚本。
那这里面最重要的是什么?是获取描述文件的内容,这关系到这个思路是否可行。
获取描述文件的内容
首先,来看下,描述文件的格式是uuid.mobileprovision
,而这个.mobileprovision
格式默认是直接安装到 Xcode 的,通过预览可以看到里面的内容。但是用脚本如何读取里面的内容呢?
首先用VSCode
打开一个这样的描述文件,提示如下:
点击Open Anyway
,然后选择用Text Editor
的方式打开,可以看到,文件的开始和结束都是一堆乱码,中间却是一段plist
格式的内容,如下:
所以猜想可以通过读取文件内容,截取开始和结束字符串,生成Plist文件。然后再通过读取 Plist 文件并解析获取对应的属性的内容。
下面来一步步尝试实现:
首先是读取文件内容,出师不利,在这一步就遇到了困难,设置encoding
为utf-8
,通过 open 读取到的文件内容一直为空,排查了好久。一开始以为是encoding
指定的不对,调试后发现,设置errors='ignore'
即可。
1 | # read mobileprovision file content |
截取字符串,生成新的Plist
格式的文件
获取到文件内容之后,下一步是截取指定字符串之间的内容,生成新的Plist
格式的文件。根据上面的用文本格式查看.mobileprovision
内容的分析,需要截取的内容是<?xml
和</plist>
之间的内容,然后生成新的文件。这里需要注意的是,查找到结束位置时,获取到的Location
,需要加上</plist>
的长度才是完整的内容,详细代码如下:
Ps:这里走了一部分弯路,一开始转为XML
格式的文件,生成后内容的读取并不方便,后来发现直接转为Plist
格式的读取内容更为快捷。
1 | # 获取指定字符串之间的内容 |
接下来是用获取到的内容,生成Plist
文件。在这里需要注意写入的方式,要用覆盖写入的方式,而不是拼接写入,防止多次执行出现问题。具体代码如下:
1 | # 根据字符串内容生成 plist 文件 |
生成 Plist
文件后,接下来是解析 Plist
文件内容,获取到描述文件名字、有效期、UUID 等信息,下面具体来看看:
解析 Plist
文件
在解析Plist
之前,需要思考一下,具体需要获取哪些字段,最终目的是提醒,所以过期日期字段是一定要解析的。然后需要考虑提醒的问题,是添加日历提醒,还是通过生成一个Excel 或者 html 的表格文件,用不同颜色区分不同有效期。这里用第二种生成 Excel 或者 html 的方式。
接下来需要考虑的就是显示哪些字段:
由于描述文件的名字中显示的是 UUID.mobileprovision
,和 Xcode 中配置的不同,Xcode 中显示的是名字,所以名字和UUID都要显示出来,用于一一对应。
然后是描述文件对应的bundleID,用于确认具体的APP。
再然后是有效期相关信信息,CreationDate
和ExpirationDate
,以及计算出来的剩余天数。
Name | UUID | bundleID | CreationDate | ExpirationDate | 剩余过期天数 |
---|---|---|---|---|---|
单元格 | 单元格 | 单元格 | 单元格 | 单元格 | 单元格 |
解析Plist
使用Python
的plistlib
库,日期计算使用datetime
库,都不需要额外安装,直接导入使用,具体代码如下:
Ps:
- 解析出来的
CreationDate
和ExpirationDate
都是 date 类型,而不是 string 类型。 - open file的 mode 需要指定为
rb
,如果指定为r
,则会提示TypeError: startswith first arg must be str or a tuple of str, not bytes
1 |
|
到这一步说明之前的思路是可行的,即读取描述文件xxx.mobileprovision
的内容,生成新的plist
格式的文件,然后再通过读取plist
的content获取对应属性的值,并计算到期日期。
最后需要考虑的是设置提醒的逻辑,起初打算直接写入 Mac 日历,调研后发现能做到的是生成日历格式的文件,然后手动导入。所以改为生成一个 html
或Excel
文件,对快过期和已过期的标红显示,然后自动发送到邮箱(在这里实现为直接打开)。下面来看一下生成html
或Excel
的逻辑。
生成 html
或Excel
文件
在生成之前需要考虑哪些状态是需要标红显示的:如果剩余天数小于 0,说明已过期;如果剩余天数小于 30,说明一个月内过期,这两种可以高亮显示;如果大于 30,则说明有效期大于 1 个月,只需要正常显示即可。
生成html
文件
先来看下生成 html 的代码:
1 | def writeToHtml(infoTurple, filepath): |
运行后显示效果,如下图所示:
生成Excel
文件
再来看一下,如何生成 Excel 格式的文件,毕竟如果要发送给他人,Excel
格式的比html
的更正式点。
生成Excel
格式的文件,需要安装三方库,这里使用的是openpyxl
,首先用如下命令安装:
1 | pip3 install openpyxl |
然后生成Excel
的代码如下:
1 | from openpyxl import Workbook |
运行后效果如下:
自动打开最终生成的文件
最终生成的文件,可以通过脚本发送给相关人,或者直接打开以达到提醒的效果。这里用的是直接打开。具体代码如下:
1 |
|
整体处理
截止到这一步,针对单个描述文件的处理已经完成,即对单个描述文件,解析内容并生成可视化提醒的一整套逻辑都已经实现。下面需要考虑的是另外三个方面:
- 批量处理逻辑:熟悉 iOS 开发的都知道,描述文件是存放在打包机或者自己电脑上的
~/Library/MobileDevice/Provisioning Profiles/
中,里面存放了许多描述文件,所以下一步首先要考虑的是批量扫描处理的逻辑。 - 过期自动删除的逻辑:这个可以说是一个
feature
,因为~/Library/MobileDevice/Provisioning Profiles/
这个目录下,如果没有清理过,可能存在很多已过期的文件,所以既然能获取到这个文件是否已过期,那么就能实现已过期的文件直接删除,但是这一步是可选,取决于自己是否需要。 - 重复文件自动标记的逻辑:因为描述文件所在的目录中可能会存在多个描述文件有同样的名字、同样的bundleID,都有效,但有效期不同的情况;这种情况可能会出现打包的时候不同版本用了不同的描述文件,从而导致 APP 不同版本有不同的有效期。所以针对这种情况,需要把名字重复的也添加高亮标记提醒,然后手动进行确认处理。
批量处理需要注意的是,由于描述文件所在目录~/Library/MobileDevice/Provisioning Profiles/
是相对路径,需要转为绝对路径再打开。脚本所在目录就没有限制,不需要和描述文件放在同一个文件夹也可。
再来思考一下整体处理的思路:
- 打开描述文件所在文件夹
- 遍历读取每个描述文件
- 针对每个描述文件进行如下处理:
- 读取描述文件内容
- 截取开始和结束字符串,生成新的 Plist 文件,放入暂存文件夹中
- 读取 Plist 文件,获取指定字段的值
- 存储读取到的内容到指定数组
- 在写入过程中,存储之前每步写入的文件名;如果发现当前文件名在已写入的数组,说明是重复文件,则把当前文件名放入重复文件数组中。
- 根据剩余有效期,判断文件是否过期,存储已过期的文件UUID到过期数组
- 遍历数组将读取到的内容写入最终生成的文件
- 在写入过程中,判断剩余有效期,针对快过期和已过期的做标记显示
- 如果发现当前文件名在重复文件的数组中,则对当前文件名做标记显示
- 根据需要,遍历过期数组,删除每个过期的描述文件
- 删除暂存文件夹中生成的所有 Plist 文件
- 打开最终生成的
Excel
或html
文件
整体处理的完整代码如下:
1 | import plistlib |
自动运行
还差最后一步,设置脚本自动运行,参考mac 自动执行python项目,根据需求设置脚本每隔多久自动运行即可。
总结
再来回顾一下整体的处理逻辑,由于原有的描述文件分析查看不方便,所以想要通过脚本读取描述文件内容,生成一种便于阅读的格式,并用于提醒。
首先做的是针对单个描述文件验证,这种思路是否可行,通过读取文件、截取文件内容、生成新的便于处理格式,获取想要的信息,最终生成便于阅读的格式。
单个文件的处理通过验证,发现可行后,再来做针对整个描述文件夹的处理:通过扫描文件夹,然后针对文件夹中的每个文件都做如上处理,并添加过期和重复的处理逻辑,把最终的信息拼接到一起,即是对所有文件的处理逻辑。
最后再通过设置定时运行,来达到提醒的目的,还可以通过发送邮件,定时提醒相关人员,感兴趣的可以自己实现。
整体的流程大致如上,流程不太复杂,但处理稍微有点绕,网上并没有类似的处理方案,所以这里记录分享出来,供大家参考。