メリークリスマス!TECH:CAMPのエンジニアの中川です。
今日はクリスマスイブで暇なので僕たちの会社がやっているサービス、TECH::CAMPの舞台裏で使われている技術についてご紹介したいと思います。
あまり多くなってしまってもダレてしまうので、今日は次の2点だけ、ご紹介します。
- チャットを使ったデプロイフロー
- RailsにおけるSprockets(Asset Pipeline)を極力使わないJavaScript環境の構築
チャットを使ったデプロイフロー
まずはチャットを使ったデプロイフローについてご紹介します。
僕たちTECH::CAMPプログラミング教育サービスをやっていて、メンターと言う大学生、社会人を含めたプログラミングを教える講師兼エンジニアを含めると40~50名ほどの規模で運営しています。
そのうち全員がRails、iOSアプリ開発を学んでいて、TECH::CAMP自体の開発にもコミットしています。人数もあるので開発速度もあり、日に何度もデプロイをします。
そのため、誰でもDeployでき、かつDeployしたものがちゃんと動くことが保証されていなければいけません。最初はコマンドラインから各々がCapistranoのコマンドでデプロイをしていましたが、公開鍵やログインユーザの管理などが煩雑になってしまっていました。
そこで現在はhubotとwerckerを組み合わせて、チャット上からデプロイできる仕組みを作っています。
図で示すとこんな感じです。
順を追って説明していきます。
① @hubot deploy アプリ名 to デプロイ先
このコマンドでheroku上に起動しているhubotにPull Requestを作るように命令します。
アプリ名にはgithubのリポジトリ名、デプロイ先にはproduction
やstaging
などが入ります。
② hubotを通じてPull Requestを作成
この時どのようにPull Requestを作成するか、ですが
release/デプロイ先
というブランチを作成し、これを最終的にdeployするbranchとします。
普段の開発は全て master
ブランチに向けてPull Requestを送っており、master
が常に最新の状態になるようにしています。そしてdeployの際に、例えばproduction
環境にdeployする際は release/production
というbranchに向けてmasterからPull Requestを送ります。こうすることで環境ごとにデプロイする内容を変えることもできますし、それぞれのbranchへのPull Requestをrevertするだけで前の状態に戻すことができます。
③ PRをマージするとwerckerでCIが走る
hubotによってつくられたPull Requestをマージすることで、wercker上でCIが走ります。 wercker は無料でも使えるCIのSaaSです。privateリポジトリでも無料で使えるので重宝しています。このwercker上で、
- rspec, capybaraを使ってユニットテスト、結合テスト
- rubocopを使ったコーディング規約のチェック
- reekを使ったコードスメルの検知
などを行っています。
④ deploy hookを使いデプロイ
最後に、werckerの便利な機能、deploy hookを使いwercker上からCIが通ったもののみデプロイしています。
こうすることでterminalを一切触ることなく、チャットとGithubのWebを使うだけでデプロイが誰でも簡単に行える仕組みを作っています。
最後に実際のスクリーンショットをお見せしたいと思います。
こんな感じで日々デプロイを行っています。
こんな感じでpull requestを作って...
RailsにおけるSprockets(Asset Pipeline)を極力使わないJavaScript環境の構築
次はRailsにおけるJavaScript環境の構築について、ご紹介します。
僕たちは主にRailsを使ってアプリを開発しているのですが、JS系のgem(angular-railsとか)や、jQueryのプラグインなど、バージョンでしっかり管理できていないJavaScriptのDependencyをいろんな人がどんどん lib/
とかのフォルダを作って入れていってどうもよくない。。という状態に。
そこでSprocketsを極力使わず、
- フロントエンドのライブラリ管理を全てnpmに
- モジュールの依存解決にはCommonJS Modules(webpack)を使う
- ES6のシンタックスを使うようにしていく(babel)
という構成にしました。
今回は、これを実際にどのようにやっているのかをご紹介します。
フロントエンドのライブラリ管理を全てnpmに
まずは -rails
系のgemを全て取り除き、npmでライブラリをインストールする形に変えました。
バージョンはとりあえず -rails
系のgemが内部で使っていたバージョンに固定し、どんどん入れていきます。書いてて思ったんですが shrinkwrap
使うのも良さそうです
次に lib/
などのディレクトリに放り込まれていたjQueryプラグイン(本当は削除したい)もnpmでインストールしていきます。
モジュールの依存解決にはCommonJS Modules(webpack)を使う
モジュールローダーには、ビルドが早かったのでwebpackを使っています。
こんな感じで設定ファイルを書いてます
var path = require('path');
var webpack = require('webpack');
var ngAnnotatePlugin = require('ng-annotate-webpack-plugin');
var configurations = {
entry: {
app: "./app/assets/javascripts/app.js",
},
output: {
path: path.join(__dirname, "app/assets/javascripts/dist/"),
filename: "bundle.[name].js"
},
module: {
loaders: [
{
test: /\.js$/,
loader: 'babel',
exclude: /node_modules/,
query: {
cacheDirectory: true,
presets: ['es2015']
}
},
{ test: /\.html$/, loader: "html" },
{ test: /\.coffee$/, loader: "coffee-loader" },
]
}
};
if(process.env.NODE_ENV==="production") {
configurations.plugins.push(
new ngAnnotatePlugin({
add: true,
})
)
}
module.exports = configurations
最終的なJavaScriptのminifyはassets:precompile
で行います。digestの付与などはJavaScript側でやろうとするのは面倒だと思ったので。
webpackでビルドしたJavaScriptを app/assets/javascripts/dist
以下に吐くようにして、そのファイルを Rails側のconfig/initializers/assets.rb
から読み込むようにします。
こんな感じにassets.rbに追記しています。
Rails.application.config.assets.precompile += %w( dist/* )
こうすることで、JavaScriptの依存解決はwebpackで行いつつ、ビルドはRailsに合わせられるので余計な手間がいらなくなり、楽だと思います。
ES6のシンタックスを使うようにしていく(babel)
また、webpackの設定にもあったのですが、新規で書くファイルはBabelを使い、ES6で書くようにしています。今まではRailsのデフォルトであるcoffeescriptで書いてきていたのですが、これらはwebpackのcoffee-loaderで読み込みつつ、新規には書かないという方針にしています。これらも少しずつ置き換えていこうかなと思っています。
webpackのloaderを使用することで、古いcoffeeのコードと新しいES6のコードを混在させつつ、少しずつ書き換えていけるので既存のプロジェクトにも比較的取り入れやすいと思います。
終わりに
いかがでしたでしょうか。
まだまだ僕も未熟であり改善すべきところは多いですが、少しずつでも新しい技術を取り入れながら、常に前進していける開発環境にできるようにこれからも頑張っていきたいと思います。
こうした方が良いよ、とかこの方が綺麗にできるよ、などあれば是非教えていただきたいです!