前線客服傳來消息 — 「用戶反饋一打開咱們的 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 這兩款神器。