窮人的持續集成與持續交付(下)
客戶端程序的的持續交付
上篇文章介紹了如何使用一些免費的服務來實現服務器端API的持續集成、持續交付環境的搭建。有了服務端,自然需要有消費者,在本文中我們將使用另外一個工具來實現純前端的站點的部署。
其中包括:
- 持續集成(單元測試,集成測試等)
- 持續部署/持續交付
- 靜態站點托管
除此之外,我們還會涉及到:
- 自動化UI測試site_prism
- 靜態站點的發布腳本
- aws的 命令行工具
我們的應用最后看起來是這樣子的。
技術選型
我們在本文中,將采取另外一套免費服務來完成環境的搭建
- ThoughtWorks 出品的 Snap CI 作為持續集成/持續交付環境
- AWS的S3 作為應用發布的地方
Snap CI 是一個非常易于使用的持續交付環境,由于很多關于持續集成,持續交付的概念和實踐都跟 ThoughtWorks 有關,所以這個產品對于構建,流水線,部署等等的支持也都做的非常好。
S3 是亞馬遜的云存儲平臺,我們可以將靜態資源完全托管在其上。 S3 的另一個好處是它可以將你的文件變成一個Web Site,比如你的目錄中有 index.html ,這個文件就可以作為你的站點首頁被其他人訪問。這個對于我們這個前后端分離項目來說非常有用,我們的 css , js , font 文件,還有入口文件 index.html 都可以托管于其上。
實例
在本文的例子中,我們將定義3個 stage 。 Snap CI 將一次發布分為若干個 stage ,每個 stage 只做一件事情,如果一個 stage 失敗了,后邊的就不會接著執行。
我們的3個 stage 分別為:
- 單元測試
- 集成測試
- 部署
準備工作
bookmarks-frontend 是一個純前端的應用,它會消費后端提供的API,但是其實它并不知道(也不應該知道)后端的API部署在什么地方:
$(function() { var feeds = $.get(config.backend+'/api/feeds'); var favorite = $.get(config.backend+'/api/fav-feeds/1'); $.when(feeds, favorite).then(function(feeds, favorite) { //... }); });
由于我們在本地開發時,需要 backend 指向本地的服務器,而發布之后,則希望它指向上一篇文章中提到的服務器,因此我們需要編寫一點構建腳本來完成這件事兒:
var backend = 'http://quiet-atoll-8237.herokuapp.com'; gulp.task('prepareConfig', function() { gulp.src(['assets/templates/config.js']) .pipe(replace(/#backend#/g, 'http://localhost:8100')) .pipe(gulp.dest('assets/script/')); }); gulp.task('prepareRelease', function() { gulp.src(['assets/templates/config.js']) .pipe(replace(/#backend#/g, backend)) .pipe(gulp.dest('assets/script/')); });
我們定義了兩個 gulp 的task,本地開發時,使用 prepareConfig ,要發布時,使用 prepareRelease ,然后定義一個簡單的模板文件 config.js :
module.exports = { backend: '#backend#' }
然后可以很簡單的包裝一下,方便本地開發和發布:
gulp.task('dev', ['prepareConfig', 'browserify', 'concatcss']); gulp.task('build', ['prepareConfig', 'script', 'css']); gulp.task('release', ['prepareRelease', 'script', 'css']);
這樣,我們在本地開發時,只需要簡單的執行:
$ gulp
即可。而在發布階段,只需要執行:
$ gulp release
單元測試
我們在 Snap CI 上將 github 上的代碼庫關聯起來,然后添加一個名叫 unit-test 的 stage ,指定這個 stage 對應的命令為:
npm install gulp
這樣,每當我們有新的提交之后, Snap CI 都會拿到新代碼,并執行上述命令,如果執行成功,則本地構建成功。
集成測試
由于采取的是 前后端分離 的策略,我們的應用可以完全獨立與后端進行開發,因此我們設置了一個 fake server ,具體細節可以參考我之前的博客,也可以看源碼。不過這里我們要為集成測試編寫一個腳本,并在 Snap CI 上執行。
#!/bin/bash export PORT=8100 bundle install # launch the application echo "launch the application" ruby app.rb 2>&1 & PID=$! # wait for it to start up sleep 3 # run the rspec tests and record the status rspec RES=$? # terminate after rspec echo "terminate the application" kill -9 $PID # now we know whether the rspec success or not exit $RES
這個腳本中,首先安裝所有的 gems ,然后啟動 fake server 并將這個server放置在后臺運行,然后執行 rspec 。當 rspec 測試執行完成之后,我們終止服務進行,然后返回結果狀態碼。
這里使用了 capybara 和 poltergeist 來做UI測試, capybara 會驅動 phantomjs 來在內存中運行瀏覽器,并執行定義好的 UI 測試,比如此處,我們的UI測試:
require 'spec_helper' describe 'Feeds List Page' do let(:list_page) {FeedListPage.new} before do list_page.load end it 'user can see a banner and some feeds' do expect(list_page).to have_banner expect(list_page).to have_feeds end ##... end
部署
首先需要在 S3 上創建一個 bucket ,命名為 bookmarks-frontend 。然后為其設置 static website hosting ,這時候 AWS 會assign一個新的域名給你,比如 http://bookmarks-frontend.s3-website-us-west-2.amazonaws.com/ 。
然后你需要將這個 bucket 設置成 public ,這樣其他人才可以訪問你的 bucket 。
有了這個之后,我們來編寫一個小腳本,這個腳本可以將本地的文件上傳至S3。
#!/bin/bash # install gulp and its dependencies npm install # package stuff, and point the server to the right place gulp release # upload the whold folder aws s3 cp public/ s3://bookmarks-frontend \ --recursive \ --region us-west-2 \ --acl public-read
aws 命令是 aws command line 提供的,另外我們需要在環境變量中設置AWS提供給你的token:
AWS_ACCESS_KEY_ID=xxxxxxxxxx AWS_SECRET_ACCESS_KEY=xxxxxxxxxx
然后我們就可以將本地的 public 目錄遞歸的上傳到S3的對應目錄了!
完整的代碼可以在 此處下載 。
總結
我們前端的持續交付也介紹完了。現在前后端應用完全獨立,發布也互不影響。不論是服務器端新增加了API,還是添加了新數據,客戶端的發布都不受影響;同樣,修改樣式,添加新的 JavaScript 也完全不會影響后端。更重要的是,所有的發布都是一鍵式的,開發者只需要一個 git push 就可以享受這些免費服務提供的自動構建,自動化測試以及自動部署的功能。