大家都知道iOS的企业证书开发的APP,证书都是一年有效期,满一年得新建证书重新打包,否则无法继续使用。
我们一个企业账号下有几十个APP,一个个去看也很麻烦~~搞个监控呗!!!
写个脚本放Jenkins上定时跑就行,跑完发布邮件:
1.邮件包含信息:APP名称,APP相关的bundle id、证书名称、签名时间、团队id,过期时间,以及剩余有效的月数。
common_mail.py: 发布邮件,邮件发送网上多,自己找,不累述
copy_ipa_monitor.py: 把打好的ipa复制到指定的目录,这个目录给监控的扫描用
date_month.py: 专门计算月份差,Python没有直接计算月份差的模块,自己写,考虑下各种条件计算
ipa_monitor.py: 获取ipa的有效时间以及ipa包里描述文件里的有效时间
ipa_monitor_html.py: 生成邮件发送的html模版
贴代码:
1 ## ipa_monitor.py 2 # coding = utf-8 3 # 读取配置证书 4 5 import os 6 import time 7 import shutil 8 import datetime 9 from pkg_common.cmd import common_run_cmd as cmd 10 from pkg_common.handle_file import find_file as find 11 from pkg_common.ipa_monitor import common_mail as mail 12 from pkg_common.ipa_monitor import ipa_monitor_html as html 13 14 15 to_find_path = ‘/Users/Work/package/ipa_monitor‘ 16 17 18 # 获取证书时间一些命令 19 codesign_cmd = ‘codesign -d --extract-certificates Payload/*.app‘ 20 openssl_cmd = ‘openssl x509 -inform DER -in codesign0 -noout -nameopt -oneline -dates‘ 21 codesign_get_cmd = ‘codesign -dv --verbose=4 Payload/*.app‘ 22 mobile_provision_cmd = ‘more Payload/Runner.app/embedded.mobileprovision‘ 23 24 25 def get_ipa_time(): 26 f, f_l = find.find_file("‘*.ipa‘", to_find_path) 27 all_dic = [] 28 for i in f_l: 29 dic = {} 30 dir_name = os.path.dirname(i) 31 app_name = dir_name.split(‘/‘)[-1] 32 dic[‘app_name‘] = app_name 33 base_name = os.path.basename(i) 34 un_zip_cmd = ‘unzip -q %s‘ % base_name 35 # 解压ipa文件 36 cmd.run_cmd(un_zip_cmd, dir_name) 37 time.sleep(2) 38 cmd.run_commands(codesign_cmd, dir_name) 39 time.sleep(2) 40 not_time = cmd.run_commands(openssl_cmd, dir_name)[1] 41 not_after, not_stamp = deal_gmt(not_time.split(‘\n‘)[1].split(‘=‘)[1]) 42 f, f_l = find.find_file("‘*.mobileprovision‘", dir_name) 43 ipa_no_time = ‘‘ 44 ipa_no_stamp = 0 45 # 获取描述文件里的过期时间,这里解码格式一定要用ISO-8859-1 46 with open(f_l[0], ‘r‘, encoding=‘ISO-8859-1‘) as f_w: 47 lines = f_w.readlines() 48 for index, l in enumerate(lines): 49 if ‘ExpirationDate‘ in l: 50 ipa_no_time = lines[index+1].split(‘>‘)[1].split(‘<‘)[0] 51 ipa_no_time, ipa_no_stamp = deal_utc(ipa_no_time) 52 break 53 if not_stamp <= ipa_no_stamp: 54 expired_time = not_after 55 else: 56 expired_time = ipa_no_time 57 dic[‘expired_time‘] = expired_time 58 pro_info = cmd.run_commands(codesign_get_cmd, dir_name)[1].split(‘\n‘) 59 delete_file(dir_name) 60 61 for x in pro_info: 62 if ‘Identifier‘ == x.split(‘=‘)[0]: 63 dic[‘Identifier‘] = x.split(‘=‘)[1] 64 if ‘Authority‘ in x: 65 if ‘Authority‘ not in dic: 66 dic[‘Authority‘] = x.split(‘=‘)[1] 67 if ‘Signed Time‘ in x: 68 dic[‘Signed_time‘] = x.split(‘=‘)[1] 69 if ‘TeamIdentifier‘ in x: 70 dic[‘TeamIdentifier‘] = x.split(‘=‘)[1] 71 all_dic.append(dic) 72 return all_dic 73 74 75 def deal_gmt(gmt_time): 76 """ 77 GMT时间转换 78 :param gmt_time: 79 :return: 80 """ 81 gmt_format = ‘%b %d %H:%M:%S %Y GMT‘ 82 # GMT时间与北京时间相差8小时 83 sta_time = datetime.datetime.strptime(gmt_time, gmt_format) + datetime.timedelta(hours=8) 84 time_array = time.strptime(str(sta_time), "%Y-%m-%d %H:%M:%S") 85 time_stamp = int(time.mktime(time_array)) 86 return sta_time, time_stamp 87 88 89 def deal_utc(utc_time): 90 utc_format = "%Y-%m-%dT%H:%M:%SZ" 91 sta_time = datetime.datetime.strptime(utc_time, utc_format) + datetime.timedelta(hours=8) 92 time_array = time.strptime(str(sta_time), "%Y-%m-%d %H:%M:%S") 93 time_stamp = int(time.mktime(time_array)) 94 return sta_time, time_stamp 95 96 97 def delete_file(dir_name): 98 """ 99 删除多余文件 100 :param dir_name: 101 :return: 102 """ 103 os.remove(dir_name + ‘/codesign0‘) 104 os.remove(dir_name + ‘/codesign1‘) 105 os.remove(dir_name + ‘/codesign2‘) 106 shutil.rmtree(dir_name + ‘/Payload‘) 107 108 109 if __name__ == ‘__main__‘: 110 ipa_info = get_ipa_time() 111 subject, html = html.deal_html(ipa_info) 112 try: 113 if mail.cs_mail_send(subject, html, ‘c‘): 114 print(‘Send success‘) 115 else: 116 print(‘Send failure‘) 117 except Exception as e: 118 raise e 119
## date_month.py import datetime import calendar as c def cal_months(start_date, end_date): # 计算两个日期相隔月差 try: same_month_date = datetime.date(end_date.year, end_date.month, start_date.day) except Exception as e: print(e) same_month_date = datetime.date(end_date.year, end_date.month, c.monthrange(end_date.year, end_date.month)[1]) decimal_month = 0.0 if same_month_date > end_date: try: pre_date = datetime.date(end_date.year, end_date.month - 1, start_date.day) except Exception as e: print(e) pre_date = datetime.date(end_date.year, end_date.month - 1, c.monthrange(end_date.year, end_date.month - 1)[1]) curr_month_days = (same_month_date - pre_date).days hold_months = (pre_date.year - start_date.year) * 12 + pre_date.month - start_date.month decimal_month = round((end_date - pre_date).days / curr_month_days, 1) elif same_month_date < end_date: try: next_month_date = datetime.date(end_date.year, end_date.month + 1, start_date.day) except Exception as e: print(e) next_month_date = datetime.date(end_date.year, end_date.month + 1, c.monthrange(end_date.year, end_date.month + 1)[1]) curr_month_days = (next_month_date - same_month_date).days hold_months = (same_month_date.year - start_date.year) * 12 + same_month_date.month - start_date.month decimal_month = round((end_date - same_month_date).days / curr_month_days, 1) else: hold_months = (end_date.year - start_date.year) * 12 + end_date.month - start_date.month return hold_months + decimal_month
## ipa_monitor_html.py # coding = utf-8 # ipa过期时间监控 import datetime from pkg_common.ipa_monitor import date_month as dm # 处理html def deal_html(data): tl = deal_tl(data) subject = ‘企业证书iOS应用过期时间监控‘ html = """ <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>企业证书iOS应用过期时间监控</title> <body> <div id="container"> <center> <strong>汇总时间: """ + str(datetime.datetime.now().strftime(‘%Y-%m-%d %H:%M:%S‘)) + """</strong> <p><strong></strong></p> <div id="content"> <table border="1" cellpadding="2" style="border-collapse:collapse;margin-top:10px"> <tr> <td colspan="4" align="center" height="35px"><font size="4"><strong>只针对企业证书打包的应用</strong>[<font color="red">有效期小于3月的红色加粗显示</font>]</font></td> </tr> <tr height="28px" bgcolor="#f0f8ff"> <td width="120" align="center"><font size="3"><strong>应用名称</strong></font></td> <td width="520" align="center"><font size="3"><strong>APP参数</strong></font></td> <td width="180" align="center"><font size="3"><strong>过期时间</strong></font></td> <td width="80" align="center"><font size="3"><strong>剩余月数</strong></font></td> </tr> """ + tl + """ </tr> </table> <p><font size="3" ><center>--------------------<strong><a href="http://chandao.thecover.cn/">汇总数据源于测试监控系统</a></strong>--------------------</center></font> </center> </div> </div> </div> </body> </html> """ return subject, html def deal_tl(data): tl_a = ‘‘ for i in data: dt1 = i[‘expired_time‘].date() dt2 = datetime.date.today() dt = dm.cal_months(dt2, dt1) a = """ <tr> <td rowspan="4" align="center"> %s </td> <td align="left"> Identifier:%s</td> <td rowspan="4" align="center"> %s</td> """ % (i[‘app_name‘], i[‘Identifier‘], i[‘expired_time‘]) if dt > 3: b = """ <td rowspan="4" align="center"> %s</td> """ % dt else: b = """ <td rowspan="4" align="center"> <strong><font color="red"> %s </font></strong></td> """ % dt c = """ </tr> <tr> <td align="left"> Authority:%s</td> </tr> <tr> <td align="left"> Signed_time:%s </td> </tr> <tr> <td align="left"> TeamIdentifier:%s </td> </tr> <tr> <td colspan="4" align="center" height="0px" bgcolor="#a9a9a9"><font size="4"> </td> </tr> """ % (i[‘Authority‘], i[‘Signed_time‘], i[‘TeamIdentifier‘]) tl_a = tl_a + a + b + c return tl_a
其他py文件很简单,就不贴了,若遇到问题,可留言交流~~