GitHub に git push するだけでウェブサイトを作成・更新できる GitHub Pages 。便利です。
ちょっとめんどくさいのが、 gh-pages ブランチか master ブランチのトップディレクトリに index.html がないといけないところ。
( master ブランチの場合は docs と名付けたフォルダをトップディレクトリにすることも可能)
gulp とか webpack とかで最終的な出力を build フォルダに向けてしている……なんて場合は、この仕様がけっこう厄介です。
デプロイ先のフォルダ名を docs とするのは一つの解決方法ですが、最終出力先のフォルダ名としてどうなの? って気持ちになります。
そこで、GitHub ユーザーにとっての CIツールとしておなじみ、 CircleCI の出番です!
CircleCI に gh-pages ブランチへの git push をお願いしちゃいましょう!
【追記(2019/5/24)】
この記事の内容は古いです。現在は CircleCI 2.0 ですので、以下の記事をご覧ください。
nekonenene.hatenablog.com
0. 前置きの雑談
さっきから出力とかビルドとかデプロイとか言葉が安定しませんが、全部同じ意味で使ってると思ってください。
Sass が CSS ファイルになったり webpack で JS が出力されるのとかをコンパイルって呼んで、minify 系は圧縮と呼んで、それら一連のことがおこなわれるのをビルドとかデプロイとか呼んでる感じです。
正確にはそういう一連のコンパイルの流れを「ビルド」と呼んで、
人がさわれる状態になる(今回CircleCIが担当する、GitHub Pages への公開・更新)のを「デプロイ」と呼び、
サービス開始を「リリース」と言うらしいです。
私はビルドのことデプロイって言ったり、デプロイのことリリースって言ったりするので、
「あ〜この人あたまがわるいんだ〜」と笑いながら読んでください。
ディレクトリとフォルダも同じ意味くらいの気持ちで使ってますけど気にしないでね!
1. circle.yml の設定
答えから見せたほうがわかりやすいと思うので、まず出来上がりを見せます。
細かい注意点はそのあとに書いていきます。
src ディレクトリの中にある .scss ファイルや .pug ファイルを、タスクランナー gulp によってビルドし、
build ディレクトリに出力している形です。
1-1. CircleCI を働かさない branch の指定
general:
branches:
ignore:
- gh-pages
まずはこの部分で、 gh-pages ブランチへの push があっても CircleCI が走らないよう設定されています。
今回は gh-pages ブランチに直接 push する予定はないので、この設定(もしくは以下に述べる [ci skip]
の設定のいずれか)は本当はいりません。
上の写真のように、CI の実行がスキップされたログは残ります。
すこし邪魔ですが、ログがないと CircleCI の挙動がおかしいのか SKIPPED されたかわかりませんから必要ですね。
1-2. npm パッケージのインストール
dependencies:
pre:
- rm -rf node_modules
override:
- npm install
まず node_modules フォルダを削除しています。
CircleCI はキャッシュを持つのですが、 node_modules ディレクトリ内のキャッシュが悪さして変なところで CI が止まるということはよくあって、
しばらく原因解明に悩む時間がたびたび発生するので潔く削除しています。
(CircleCI には「Rebuild without cache」というボタンもあるのですが、そもそもキャッシュが原因の可能性を見落としがち)
1-3. ビルド
deployment:
deploy:
branch: master
commands:
- npm run build
branch: master
と指定することで、 master ブランチへの push による CircleCI タスクのときのみ、この deploy 部分は働きます。
他のブランチへの push でも gh-pages に反映させたいという場合は、この記述は無くすといいでしょう。
その後に、 npm run build
でビルドします。
以下のように package.json に書かれているために、npm run build
とコマンドすると gulp build
がおこなわれます。
# package.json より一部抜粋
"scripts": {
"build": "gulp build"
}
gulp build
の中身は gulpfile.js に記述しています。
大ざっぱに言うと、src ディレクトリにあるファイルをビルドして build ディレクトリに出力する処理が書かれています。
1-4. build ディレクトリの中身をトップディレクトリに
commands:
- npm run build
- rm -rf src
- cp -Rf build/. ./
ビルドし終わったら src フォルダは用済みなので消しています。
特に深い意味はありません。なんとなく消しています。
大事なのは cp -Rf build/. ./
の方です。
build フォルダの中身をトップディレクトリにコピーしてきます。
R オプションでフォルダの下を潜ってファイルを探します。
f オプションで、コピー先に同名のファイルがたとえあっても容赦なく上書きします。
個人的にハマったのは、build
でも build/
でもなく、build/.
が正解なことです。そっかー。
.gitignore によって build ディレクトリを含めてないから考えていませんでしたが、
ここ cp
コマンドでなく mv
コマンドでもよさそうですね。
1-5. git push で gh-pages へデプロイ
- git config --global user.name "CircleCI"
- git config --global user.email "circleci@gh-pages.com"
- git add .
- git commit -m "Publish [ci skip]"
- git checkout -B gh-pages
- git push -u origin gh-pages --force
さて、ビルドが終わりました。
あとはデプロイ! git push までの流れを見ましょう。
1-5-1. git config
まずは git config で username と email の設定を入れます。
値はなんでもいいですが、この設定をしておかないとコミット git commit
ができません。
1-5-2. git commit
コミットメッセージが「Publish [ci skip]」となっていますね。
この [ci skip] の部分が特別で、コミットメッセージにこの文字が含まれているコミットに対して、CircleCI は処理をおこないません。
通常は 1-1. で書いた特定のブランチを無視する方法だけでいいと思いますが、
おもしろい仕組みだなと思いましたので書いておきました。
1-5-3. add branch & checkout
続いて git checkout -B gh-pages
です。
新しいブランチを作りつつチェックアウトする b オプションは知っていたのですが、
すでに同名のブランチがある場合はエラーを発生させてしまうオプションなので「どうしよう」と悩んでいたのですが、
この B オプションならば、基本は b オプションと同様ながら「すでに同名のブランチがある場合はブランチの作成をせずチェックアウトする」ようです。助かりました。
1-5-4. git push
そして最後に gh-pages ブランチをリモートリポジトリに git push
します。
pull を要求されてはエラーになりますので、force オプションを付けます。
2. git push だ! の前に……
CircleCI が git push
できるようにするためには下準備が必要です。
CircleCI のプロジェクトページ(対象リポジトリ)に飛び、PROJECT SETTINGS を開き、
PERMISSIONS の「Checkout SSH keys」をクリックします。
すでに deploy key はあるかと思いますが、これには git pull
の方の権限しかありませんので、user key を作成します。
Add user key のところにある「Authorize with GitHub」ボタンをクリックして user key を発行してください。
それが終わりますと下準備は完了です。
あとは push し無事にビルドが終了すれば、
https://YOUR_USENAME.github.io/REPOSITORY_NAME
にページが作成されます。
3. Failed にならないために
test:
override:
- npm run test
CircleCI には test の部分があります。
ここを省略することは可能ですが、書かないと、必ず結果が Failed になってしまいます。
精神的に Failed ラベルが並ぶのは嫌ですし、
すべてが Failed となってしまうのでは、ビルドに失敗しての Failed などと見分けがつきません。
とはいえ、作りたてのプロジェクトなど、
テストコードがない場合もあるかと思います。
その場合は、test の部分は cd ./
や echo 'にんじんたべたい'
などと、
当たり障りのないコマンドを書いておくといいかと思います。
4. 余談 : yarn
公開した circle.yml では npm install
としていますが、実際は
dependencies:
pre:
- rm -rf node_modules
- npm install -g yarn
override:
- yarn install
test:
override:
- yarn test
deployment:
deploy:
branch: master
commands:
- yarn build
のように、npm から yarn に乗り換えつつあります。
(rm -rf node_modules
のあとに yarn cache clear
を書いたほうが安全かも)
今のところは特に問題が起きていません。
……が、yarn をインストールさせることなく npm だけで終わらせたほうが CircleCI のタスクにかける時間が 1分近く少なくて、複雑な気持ちになっています。
yarn.lock による安定さが優れてるのではって勝手に期待してるんだけど、どうでしょう?