前言

身為一個前端工程師,在工作上遇到一些需要反覆操作的流程時,總會想要用一些方法來優化它。像我自己也在公司開發了一些 CLI 及 Chrome Extension 來加速日常的開發。然而在開發了這些工具的同時,我卻也不經意的為自己引入了新的反覆操作,如果可以把測試、過版、壓檔、上傳、發佈……等步驟全部自動化,我就可以更專注的在新功能上的開發上。

底下將以開發一個新的 Chrome Extension 為例,利用 Travis CIChrome Web Store Publish API 做到完整的 CI/CD 整合。

開發環境

1
2
3
4
$ node -v
v6.3.1
$ yarn -v
yarn install v0.24.5

流程

  1. 建一個新的 React 專案
  2. Chrome Extension 設定
  3. 基本 CI 設定
  4. 自動化打包及發布 CD 設定
  5. 發布 Chrome Extension

建一個新的 React 專案

這個步驟很簡單,只有一行

1
$ yarn create react-app <EXTENSION_NAME>

本篇教學所用的EXTENSION_NAMEmy-ext,所以我用

1
$ yarn create react-app my-ext

它會幫你開一個新的 React 專案my-ext

Chrome Extension 設定

這個新的 React 專案現在還不是一個合法的 Chrome Extension,我們需要補上正確的設定。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// public/manifest.json
{
"short_name": "React App",
"name": "Create React App Sample",
"icons": [
{
"src": "favicon.ico",
"sizes": "192x192",
"type": "image/png"
}
],
"start_url": "./index.html",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
}

目前的manifest.json是 PWA 的設定,而不是我們所想要的 Chrome Extension 設定,所以將其改寫為

1
2
3
4
5
6
7
8
9
10
// public/manifest.json
{
"manifest_version": 2,
"name": "my-ext",
"description": "...",
"version": "0.1.0",
"browser_action": {
"default_popup": "index.html"
}
}

再來,manifest.json中的 version 應該要與package.json中的 version 互相對應。為了不要每次過版都要手動改兩邊的版本,我用version-everything來幫我處理版號問題

1
$ yarn add -D version-everything

首先安裝它,再更改package.json

1
2
3
4
5
6
7
8
9
10
11
// package.json
{
...
"scripts" : {
...
"version": "version-everything && git add -u"
},
"version_files": [
"public/manifest.json"
]
}

如此一來,我們在用yarn version的指令跳版號時,會觸發 version 這個 hook 以達到版號同步的效果。

1
$ yarn build

接下來我們在 Chrome 上看看my-ext能不能正確地被載入。yarn build這個指令在打包時會把在./public底下的檔案也一併複製到./build底下,如此一來,我們即可以在 chrome 中用載入未封裝擴充功能,裝載./build

載入後可以看到瀏覽器右上角出現一個按鈕,按下去之後會彈現一個視窗如下圖
http://i.imgur.com/viruNna.png

恭喜,我們的 Chrome Extension 看起來設定對了。

基本 CI 設定

這步驟主要是設定好 Travis CI 的串接,首先我們必須要 push 我們的專案到 github repo,再來在 Travis 中打開此 repo 的開關 (在這裡是 kaddopur/my-ext),最後設置.travis.yml如下

1
2
3
4
5
# .travis.yml
language: node_js
node_js:
- "6"
cache: yarn

這邊我們用的是 node@6,再來我們也打開了 yarn caching。將.travis.yml commit 之後 push 到 github,你會看到 Travis 開始跑起來了。

自動化打包及發布 CD 設定

我們將會利用 Travis 的 after_success hook 來自動觸發上傳及發布,但在上傳之前,我們需要準備好 Extension 的 zip 檔。在yarn build之後,我用zip-folder來幫我產生壓縮檔

1
$ yarn add -D zip-folder
1
2
3
4
5
6
7
8
9
10
// scripts/zip.js
const zipFolder = require('zip-folder');
zipFolder(`${process.cwd()}/build`, `${process.cwd()}/bundle.zip`, err => {
if (err) {
console.log('oh no!', err);
} else {
console.log('EXCELLENT');
}
});

它會把/build資料夾壓成bundle.zip

再來我會使用webstore-upload來上傳壓縮檔與發布

1
$ yarn add -D webstore-upload
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// scripts/upload.js
const webstore_upload = require('webstore-upload');
const uploadOptions = {
accounts: {
default: {
publish: true,
client_id: process.env.CLIENT_ID,
client_secret: process.env.CLIENT_SECRET,
refresh_token: process.env.REFRESH_TOKEN
}
},
extensions: {
my_ext: {
appID: process.env.APP_ID,
zip: `${process.cwd()}/bundle.zip`
}
},
uploadExtensions: ['my_ext']
};
webstore_upload(uploadOptions, 'default')
.then(function(result) {
console.log(result);
// do somethings nice
return 'yay';
})
.catch(function(err) {
console.error(err);
});

我們要提供client_id,client_secret,refresh_token及 Extension 的appID方可使用。特別提醒一下,我把這些值設為 Travis 的環境變數並在執行時取得,而非 commit 在程式碼中。

這些值怎麼取得的

最後,我們把after_success hook 設定好

1
2
3
4
5
6
7
8
9
// package.json
{
...
"scripts": {
...
"zip": "node scripts/zip.js",
"upload": "node scripts/upload.js"
}
}

1
2
3
4
5
# .travis.yml
after_success:
- yarn build
- yarn zip
- yarn upload

就大功告成啦!

發布 Chrome Extension

只要 repo 上的 master branch 有新的 commit,Travis 都會幫我們跑 CI/CD 流程。需要特別注意的是,Chrome Web Store 無法上傳和同版本的 Extension,所以 merge 時請記得跳個版。

1
2
3
4
5
6
7
8
9
10
$ yarn version
yarn version v0.24.5
info Current version: 0.1.0
question New version: 0.1.1 // 輸入你的新版號
info New version: 0.1.1
$ version-everything && git add -u // 更新 manifest.json 中的 version
Loading package.json
Current version is "0.1.1"
Updated public/manifest.json from 0.1.0 to 0.1.1
✨ Done in 5.21s.

直接git push或是你要先發 PR 再 merge 也行。之後在你的開發人員資訊主頁就可以看到版本更新了!

後記

程式碼放在 https://github.com/kaddopur/my-ext ,如果過程中有任何問題或討論也歡迎在底下留言。

修訂

2017-06-03 - 改用zip-folder產生壓縮檔