こんにちは、なんとかChrome拡張を公開できて、少し安心してる正徳です。
昨日、Forkwell JobsのChrome拡張の記事を公開しましたが、本記事では技術的な話を書いてみたいと思います。
リポジトリ
リポジトリはGitHubのgrooves/forkwell_for_chromeで公開しています。 実際のコードを参考にしたい方はどうぞ。
Haml, Sass, CoffeeScript を使う
社内の他エンジニア・デザイナーも触りやすいように、Forkwell Jobsで使用している技術に合わせました。
それぞれ、gulp.jsを使ってHTML, CSS, JavaScriptに変換しています。
mocha + power-assert を使ったテスト
mochaとpower-assertでテストを実行できるように環境を整えました。
詳細なテストの環境についてはリポジトリを読んで頂くとして、要点だけいくつか書いておきます。
mochaでクライアントサイドJavaScriptのテストをするとエラー
Chrome拡張のJavaScriptはHTML上で動く、普通のクライアントサイドJavaScriptです。 このため、Node.jsで動くmochaを使ってテストしても…
- ReferenceError: window is not defined
- Error: jQuery requires a window with a document
などのエラーが出てしまいます。
また、クライアントサイドのJavaScriptでは global
や module.exports
も使えません。
browserify + jsdom を使う
上述した問題に対応するため、Browserify を使い、クライアントサイドのJavaScriptでもNode.jsっぽく書けるようにしました。 また、jQueryがwindowを使うためtmpvar/jsdomも導入しておきます。
これにより、require
を使ったり、global.$
をテスト側で上書きしたりが出来るようになります。
こんな感じ。
global.$ = require('jquery')(require('jsdom').jsdom().parentWindow) Service = require 'src/coffee/models/service'
プロジェクトルートをNODE_PATHに追加
テストを書く際にrequire '../../src/coffee/models/service'
のように書くと分かりづらいので、プロジェクトルートをNODE_PATHに追加しています。
test/mocha_resolve_test_path.coffee
path = require 'path' process.env.NODE_PATH = [ process.env.NODE_PATH, path.resolve(__dirname, '..') ].join(':') require('module').Module._initPaths()
これをmochaの実行時に引数で渡すと効くようになります。
$ mocha --require test/mocha_resolve_test_path.coffee
Coveralls の設定
カバレッジを出したくて導入しました。少しハマった所があったので、そこだけ書いておきます。
istanbul-jsがCoffeeScriptに対応していない
JavaScriptでカバレッジを計測するためにistanbul-jsを導入したのですが、CoffeeScriptではうまく動きませんでした。 これ解決するためにはbenbria/coffee-coverageを別途インストールする必要があります。
gulpfile.coffeeを無視する
coffee-coverageのデフォルト設定だとgulpfile.coffee
のカバレッジまで測定されるため、これを対象外にします。
coffee-coverage-loader.js
var coffeeCoverage = require('coffee-coverage'); var coverageVar = coffeeCoverage.findIstanbulVariable(); var writeOnExit = coverageVar == null ? true : null; coffeeCoverage.register({ instrumentor: 'istanbul', basePath: process.cwd(), exclude: ['/test', '/node_modules', '/.git', '/gulpfile.coffee'], coverageVar: coverageVar, writeOnExit: writeOnExit ? ((_ref = process.env.COFFEECOV_OUT) != null ? _ref : 'coverage/coverage-coffee.json') : null, initAll: (_ref = process.env.COFFEECOV_INIT_ALL) != null ? (_ref === 'true') : true });
デフォルト設定であるcoffee-coverage/register-istanbul
の代わりに、作成したcoffee-coverage-loader.js
の設定を読み込むようにします。
$ mocha --require test/coffee-coverage-loader.js
Google APIを使い、Chromeウェブストアに自動デプロイ
Chrome拡張のzipファイルを作成し、手動でアップロードするのは非常に面倒なので、Google APIと werckerで自動デプロイの環境を作りました。
Google Developers Consoleの登録
まず、Google APIを使うためにGoogle Developers Consoleでプロジェクトを登録します。
手順はUsing the Chrome Web Store Publish API - Google Chromeに書いてあるので、この手順通りに作業を進めるとOAuth 2.0のCLIENT_ID
、CLIENT_SECRET
, REFRESH_TOKEN
が手に入ります。
アクセストークンの有効期限
Google APIのアクセストークンの有効期限は 40分 です。 このため、毎回のデプロイ時にアクセストークンを再取得する必要があります。
参考にしたAPIのドキュメント一覧
- Using OAuth 2.0 for Installed Applications - Google Identity Platform — Google Developers
- Using the Chrome Web Store Publish API - Google Chrome
- Using the Chrome Web Store Publish API - Google Chrome
wercker.ymlの例
werckerで以下の環境変数を設定してあります。
環境変数の名前 | 内容 |
---|---|
GITHUB_TOKEN | GitHubのpush権限のあるユーザーのtoken |
APP_ID | Chromeストアに公開しているアプリID |
GOOGLE_APP_ID | Google API の CLIENT_ID |
GOOGLE_APP_SECRET | Google API の CLIENT_SECRET |
GOOGLE_REFRESH_TOKEN | Google API の REFRESH_TOKEN |
wercker.yml
deploy: steps: - script: name: get app_version from gulp code: export APP_VERSION=$(npm run app_version --silent) - script: name: configure git code: | git config --global user.email "pleasemailus@wercker.com" git config --global user.name "wercker" git remote set-url origin https://$GITHUB_TOKEN@github.com/$WERCKER_GIT_OWNER/$WERCKER_GIT_REPOSITORY.git - script: name: create tag code: git tag $APP_VERSION -m "$APP_VERSION release" - script: name: push tag code: git push origin $APP_VERSION - github-create-release: token: $GITHUB_TOKEN tag: $APP_VERSION - github-upload-asset: token: $GITHUB_TOKEN file: dist-$APP_VERSION.zip - script: name: Refresh a access token code: | JSON=`curl -d "client_id=$GOOGLE_APP_ID" -d "client_secret=$GOOGLE_APP_SECRET" -d "refresh_token=$GOOGLE_REFRESH_TOKEN" -d "grant_type=refresh_token" https://www.googleapis.com/oauth2/v3/token` export GOOGLE_APP_TOKEN=`node -p "($JSON).access_token;"` - script: name: Upload a package to update an existing store item code: | curl -H "Authorization: Bearer $GOOGLE_APP_TOKEN" -H "x-goog-api-version: 2" -X PUT -T dist-$APP_VERSION.zip -v https://www.googleapis.com/upload/chromewebstore/v1.1/items/$APP_ID - script: name: Publish an item to the public code: | curl -H "Authorization: Bearer $GOOGLE_APP_TOKEN" -H "x-goog-api-version: 2" -H "Content-Length: 0" -X POST -v https://www.googleapis.com/chromewebstore/v1.1/items/$APP_ID/publish
こんな感じのwercker.ymlを作成すると、自動で
- GitHub のTagの作成
- GitHub のReleasesの作成
- Chromeウェブストアにzipのアップロード
- Chromeウェブストアで新しいバージョンを公開
が行われます。便利です。
まとめ
今回のChrome拡張は出来たばかりで、これから少しずつ改良していきたいと思っています。
要望などあれば是非、Twitterで#forkwellのハッシュタグを付けてツイート頂くか、公式アカウントの@Forkwell_jaにご連絡ください。
最後になりましたが、この記事がChrome拡張を作る方、作っている方の参考になれば幸いです。