前线客服传来消息 — “用户反馈一打开咱们的 App,就直接闪退了”,刚听到这个消息,我很吃惊,上一期发的新版本 QA 都有验证过。难道是由于功能权限的问题致使的,赶忙跟客服确认具体状况。原来是客户前几天都能正常使用 App,今天一打开就莫名闪退了。刚了解清楚具体状况,一会儿闪退的消息,就如滔滔江水一涌而来,随后也就开始了 iOS 证书过时填坑之旅。javascript
咱们公司的产品有几十个客户,但并非每一家客户都有反馈,而只是其中的几家。反馈闪退的几家客户中,都是同时使用 Android 和 iOS 两个平台,但反馈闪退问题的都是使用 iOS 平台的用户,Android 平台并无出现闪退问题。html
梳理完思路后,咱们就想到了是否是反馈闪退的客户使用的 App 证书或描述文件出问题了,所以立马登录苹果开发者后台,登录后发现果真是几个客户使用的证书,今天就过时了。那是否是证书过时致使闪退的呢?原生开发人员,立刻更新一下证书,打了个包进行验证。果真,用新的证书打出来的包,就能正常使用,不会出现闪退了。网上找了相关的资料,也不少小伙伴遇到一样的问题 —— “企业版证书过时,App 出现闪退”。问题是已经定位了,但客户那边怎么解决呢?客户一打开咱们的 App 就立马闪退了,没有办法进行强制更新。此后,在网上绕了一大圈,看了不少文章,发现咱们最终的方案,只能从新打包让用户重装。java
苍天啊!大地啊!为何苹果企业证书即将过时,没有发邮件通知,这真是一个大坑!!!事情居然已经发生,只能咽下苦水,乖乖地接受外部的 “轰炸” 了。接下来咱们当即针对闪退的客户从新打包,而后让公司客服与客户沟通,说明状况...git
这个问题之后要如何避免?难道要安排专人,天天定时检查证书的有效性?最初的这个想法,其实我是拒绝的。这种脏活累活,确定要请咱们吃饭的家伙 —— ?(Computer)来帮咱们处理咯。前阵子恰好偶遇谷歌出品的一个神器 —— GoogleChrome/puppeteer (Headless Chrome Node API),接下来咱们就先来介绍这款神器。github
puppeteer 是一个 Node.js 的库,支持调用 Chrome 的 API 来操纵 Web,相比较 Selenium 或是 PhantomJS,它最大的特色就是它的操做 DOM 能够彻底在内存中进行模拟既在 V8 引擎中处理而不打开浏览器,并且关键是这个是 Chrome 团队在维护,会拥有更好的兼容性和前景。json
puppeteer 的神技:api
是否是感受 puppeteer 棒棒哒。其实还有其它一些无头浏览器,好比:浏览器
简单介绍完 puppeteer,接下来咱们就来稍微介绍一下思路。其实实现思路很简单,只须要使用 puppeteer 模拟登陆?开发者网站,进入证书管理的页面,获取全部证书的有效期,而后设置计算出即将过时的天数。微信
最终的流程以下:app
基于 puppeteer API 的版本为: 0.11.0
const puppeteer = require('puppeteer'); (async() => { const browser = await puppeteer.launch({ headless: false // 开发调试阶段,设置为false }); const page = await browser.newPage(); page.setViewport({ width: 1376, height: 768, }); page.on('response', async(response) => { if (response && response.status == 200) { // 判断是否加载完概览视图,而后再次进入证书页面 if (response.url.indexOf('tpl.overview-view.html') != -1) { await getCertsInfo(); } // 判断是否为生产环境证书列表请求 if (response.url.indexOf('status=4&certificateStatus=0&type=distribution') != -1) { const res = await response.json(); if (res && res.certRequests) { console.dir(calcCertsDays(res.certRequests)); await browser.close(); } } } }); // 跳转到苹果官网并等待页面资源加载完成 await page.goto('https://developer.apple.com/cn/', { waitUntil: 'load' }); // 跳转到登陆页面 await page.click('.ac-gn-account > a'); await page.waitForSelector('#accountname', { timeout: 50000 }); await login(); // 执行登陆操做 async function login() { await page.focus('#accountname'); await page.type('开发者帐号', { // 此处替换为真实帐号 delay: 100 }); await page.focus('#accountpassword'); await page.type('开发者帐号密码', { // 此处替换为真实密码 delay: 100 }); await page.click('#submitButton2'); } // 获取证书信息(等待模板加载完成后,才进入证书管理页面) async function getCertsInfo() { const CERT_ITEM_SELECTOR = '#main section.getting-started > a:nth-child(2)'; await page.waitForSelector(CERT_ITEM_SELECTOR); await page.click(CERT_ITEM_SELECTOR); const PROD_CERT_SELECTOR = 'li.subitem > a[href*="certificate/distribution"]'; await page.waitForSelector(PROD_CERT_SELECTOR); await page.click(PROD_CERT_SELECTOR); await page.waitForSelector(PROD_CERT_SELECTOR); } // 计算每一个证书的天数 function calcCertsDays(certs) { if (Array.isArray(certs)) { const today = new Date(); return certs.map(cert => { return { name: cert.name, type: cert.typeString,// Apple Push Services || iOS Distribution expirationDay: dateDiff(today, new Date(cert.expirationDate)) }; }); } } // 计算两个日期的间隔天数 function dateDiff(today, expirationDate) { return parseInt((Math.abs(expirationDate.getTime() - today.getTime())) / 1000 / 60 / 60 / 24); } })();
经过 puppeteer 这款神器提供强大的 API,咱们只是实现了基本功能,后面还有一些功能须要优化和开发,好比异常处理、帐号信息灵活配置和预警机制等,目前初步打算优先使用 wechaty 微信我的号?开发框架,来实现消息通知。我的感受 puppeteer 在往后的工做中,还有不少用武之地,好比此前本人使用 puppeteer 实现了简单的页面功能测试。
此外在填坑过程当中,偶遇了另外一款神器 —— fastlane (The easiest way to automate building and releasing your iOS and Android apps) ,感受真是相见恨晚啊(前阵子部门刚花大力气,实现App自动打包)。有兴趣的小伙伴,能够试试 puppeteer 和 fastlane 这两款神器。