Docker Machine の環境変数を rc ファイル内で自動で設定する

Linux 以外で Docker を使うには、Linux のホストマシンを VM で用意する必要があります。もっともメジャーなのは Boot2Docker でしょうが、個人的には Docker Machine を使うのがおすすめです。まだベータ版ですが、基本的な機能は特に問題なく使えています。

Docker Machine は複数のホスト VM を切り替えることができるのが1つの利点ですが、これにより逆に不便になってしまったことがあります。 Boot2Docker を使う場合、ホスト VM 起動後

1
2
3
4
$ boot2docker shellinit
export DOCKER_HOST=tcp://192.168.59.103:2376
export DOCKER_CERT_PATH=/Users/skatsuta/.boot2docker/certs/boot2docker-vm
export DOCKER_TLS_VERIFY=1

により生成される環境変数を設定するために、 .bashrc.zshrc などに

1
eval "$(boot2docker shellinit)"

と書いておけばいいのですが、 Docker Machine の場合はホストを複数作れるために、上記のように一筋縄ではいきません。

Docker Machine で同じように環境変数を設定する場合、以下のようにホスト VM の名前の指定が必要です。たとえば dev というホスト VM を作った場合、

1
2
3
4
5
6
7
$ docker-machine env dev
export DOCKER_TLS_VERIFY="1"
export DOCKER_HOST="tcp://192.168.99.101:2376"
export DOCKER_CERT_PATH="/Users/skatsuta/.docker/machine/machines/dev"
export DOCKER_MACHINE_NAME="dev"
# Run this command to configure your shell:
# eval "$(docker-machine env dev)"

で環境変数が取得できます。このホスト VM 名は存在し、かつ動いているものでなければなりません。

いろいろ模索と妥協をした結果、 docker-machine ls コマンドと組み合わせることにより、現状動いているホスト VM を1つ取り出して、その環境変数を設定するシェルスクリプトを .zshrc に書くことにしました。

.zshrc
1
2
3
4
5
6
7
8
9
# check if `docker-machine` command exists
if command -v docker-machine > /dev/null; then
# fetch the first running machine name
local machine=$(docker-machine ls | grep Running | head -n 1 | awk '{ print $1 }')
if [ "$machine" != "" ]; then
eval "$(docker-machine env $machine)"
export DOCKER_IP=$(docker-machine ip $machine)
fi
fi

IP アドレスだけを使いたいときもあるので、同時に DOCKER_IP という変数も設定しています。

これでちょっとだけ手間が省けます。

Go 1.5 の変更点

間もなく Go 1.5 がリリースされるようなので、 release notes の原稿から気になる変更点をピックアップしてみました。

言語仕様

構造体をキーとした map リテラルの簡略化

構造体をキーとした map のリテラルを少し簡単に書けるようになります。たとえば、これまでは

1
2
3
4
5
m := map[Point]string{
Point{29.935523, 52.891566}: "Persepolis",
Point{-25.352594, 131.034361}: "Uluru",
Point{37.422455, -122.084306}: "Googleplex",
}

と書いていたものが、

1
2
3
4
5
m := map[Point]string{
{29.935523, 52.891566}: "Persepolis",
{-25.352594, 131.034361}: "Uluru",
{37.422455, -122.084306}: "Googleplex",
}

と書けるようになります。

実装

C のソースコードの削除

コンパイラとランタイムがすべて Go で書き換えられ、C のコードは排除されました。

ガベージコレクタ

GC 処理のアルゴリズム改善・スケジューリング改善・並列化により、レイテンシが劇的に小さくなります。アプリケーションの動きが完全に停止する “stop the world” の時間はほとんどの場合 10 ミリ秒以下に抑えられるようです。

ランタイム

GOMAXPROCS のデフォルト値が 1 からコア数になります。シングルスレッド前提で書かれているプログラムにはもしかすると影響が出るかもしれません。

ポート

darwin/arm, darwin/arm64 (iOS) と linux/arm64 がサポートされました。

ツール

コンパイラ・リンカ

6g, 8g などのコンパイラ・リンカはなくなり、go tool compile, go tool link などに置き換えられました。

新たなデフォルトコマンドの追加

go vet, go cover, go doc コマンドが標準で使えるようになりました。

  • vet はソースコードのバリデーションや静的解析をおこなうツールです。
  • cover はカバレッジを計算してくれるツールです。

vetcover はよく使っているので、標準で同梱されるようになると嬉しいですね。

Go コマンド

Internal package

Go でのアクセス制御はこれまで public か private しか指定できませんでした。 Go 1.4 で internal package という、パッケージプライベートを実現する機能が試験的に導入されましたが、この段階ではまだコアライブラリでしか利用できるようになっていませんでした。 Go 1.5 でこの機能がユーザライブラリにも開放されます。

具体的には、パッケージ名に internal が含まれている場合、同一のルートパッケージからのみ import できます。たとえば、 $GOPATH/src/mypkg/internal/foo というパッケージは、 $GOPATH/src/mypkg からのみ import できます。

細かい仕様はデザインドキュメントに記載されています。go1.4で追加されたinternal packageについて - Qiita の記事なども参考になります。

Verdoring

Verdoring 機能が実験的にサポートされました。これはどういうものかというと、たとえば

1
2
3
4
5
6
7
8
9
10
$GOPATH
| src/
| | github.com/constabulary/example-gsftp/
| | | cmd/
| | | | gsftp/
| | | | | main.go
| | | vendor/
| | | | github.com/pkg/sftp/
| | | | golang.org/x/crypto/ssh/
| | | | | agent/

というリポジトリ構成にしておくと、 github.com/constabulary/example-gsftp/cmd/gsftp/main.go において

1
2
3
4
5
6
import (
...
"golang.org/x/crypto/ssh"
"golang.org/x/crypto/ssh/agent"
"github.com/pkg/sftp"
)

と書けば verdor 以下の依存ライブラリを import できるようになります。 これまでは

1
2
3
4
5
6
import (
...
"github.com/constabulary/example-gsftp/vendor/golang.org/x/crypto/ssh"
"github.com/constabulary/example-gsftp/vendor/golang.org/x/crypto/ssh/agent"
"github.com/constabulary/example-gsftp/vendor/github.com/pkg/sftp"
)

というふうにフルパスを書かなければならなかったので、だいぶ楽になります。

ただしこの機能を有効にするには環境変数で GO15VENDOREXPERIMENT=1 と設定する必要があります。

これまで Go にはライブラリのバージョン指定機能がなかったため、たとえば依存関係解決ツールである Godep では Godep/_workspace/ で同じような機構を実現していました。 Go 1.5 からはこの標準機能を使っていくことになるでしょう。

標準ライブラリ

flag パッケージ

flag パッケージの help の表示が見やすくなりました。たとえば

1
cpuFlag = flag.Int("cpu", 1, "run `N` processes in parallel")

というフラグを定義すると、 help メッセージは

1
2
-cpu N
run N processes in parallel (default 1)

と表示されるようになります。 Go 1.4 までは

1
-cpu=1  run N processes in parallel

という表示だったので、少し見やすくなります。

math/big パッケージ

math/big パッケージに、任意精度の浮動小数点型である Float が追加されます。

reflect パッケージ

reflect パッケージに ArrayOfFuncOf 関数が追加されます。 SliceOf と同様に、実行時に配列や関数を表す型をつくることができるようになります。

感想

個人的な見どころはやはり GC の改善と internal package ですね。正式リリースが待ち遠しいです。

wercker 上で Aerospike を使ったテストをする

wercker が最近 Docker に対応しました。 これにより、 Docker を利用している場合にはテストからデプロイまでを共通したコンテナでおこなえるほか、既存のコンテナを再利用し、柔軟に組み合わせて利用できるようになりました。

最近 Aerospike という超優秀な KVS を利用しているのですが、 Docker ベースになった werkcer 上でこのクライアントライブラリのユニットテストを実行したいと思ったので試してみました。

werker には services という、 複数のコンテナを外部サービスとして利用する仕組みがあります。この機構により、使用するミドルウェアのコンテナイメージを用意すれば、 CI 時にそれを利用したテストを走らせることができます。素晴らしい仕組みです。たとえば、 Redis を使用したければ、wercker.ymlservices セクションに以下のように redis を追記します。

1
2
services:
- redis

これにより、 CI 時に Docker Hub の Redis イメージが pull されてきて、 アプリケーションから利用できるようになります。 Aerospike を使う場合も同様で、 wercker.yml に以下のように書きます。

1
2
services:
- aerospike

こうすると、ビルド時に Docker Hub の aerospike リポジトリからイメージを取得して、利用できるようにしてくれます。

では、アプリケーション側からこれらのサービスにアクセスするにはどうすればよいのでしょうか?
wercker では Docker のコンテナ間リンク機構 を利用して、 環境変数が自動で準備されるようになってます。 Aerospike を使用した場合には、以下のような環境変数が用意されることになります。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
AEROSPIKE_ENV_AEROSPIKE_SHA256=df810e67d363291f6f40c046564bbc7ab775fcdb45ebfb878368361705063015
AEROSPIKE_ENV_AEROSPIKE_VERSION=3.5.14
AEROSPIKE_NAME=/wercker-pipeline-55ae303ff32b86a92907041f/aerospike
AEROSPIKE_PORT=tcp://172.17.6.203:3000
AEROSPIKE_PORT_3000_TCP=tcp://172.17.6.203:3000
AEROSPIKE_PORT_3000_TCP_ADDR=172.17.6.203
AEROSPIKE_PORT_3000_TCP_END=tcp://172.17.6.203:3003
AEROSPIKE_PORT_3000_TCP_PORT=3000
AEROSPIKE_PORT_3000_TCP_PORT_END=3003
AEROSPIKE_PORT_3000_TCP_PORT_START=3000
AEROSPIKE_PORT_3000_TCP_PROTO=tcp
AEROSPIKE_PORT_3000_TCP_START=tcp://172.17.6.203:3000
AEROSPIKE_PORT_3001_TCP=tcp://172.17.6.203:3001
AEROSPIKE_PORT_3001_TCP_ADDR=172.17.6.203
AEROSPIKE_PORT_3001_TCP_PORT=3001
AEROSPIKE_PORT_3001_TCP_PROTO=tcp
AEROSPIKE_PORT_3002_TCP=tcp://172.17.6.203:3002
AEROSPIKE_PORT_3002_TCP_ADDR=172.17.6.203
AEROSPIKE_PORT_3002_TCP_PORT=3002
AEROSPIKE_PORT_3002_TCP_PROTO=tcp
AEROSPIKE_PORT_3003_TCP=tcp://172.17.6.203:3003
AEROSPIKE_PORT_3003_TCP_ADDR=172.17.6.203
AEROSPIKE_PORT_3003_TCP_PORT=3003
AEROSPIKE_PORT_3003_TCP_PROTO=tcp

ドキュメント にある通り、環境変数の命名規則は

1
<name>_PORT_<port>_<protocol>

をプレフィックスとして、 prefix_ADDR なら IP アドレス、 prefix_PORT ならポート番号、 prefix_PROTO ならプロトコル名になっています。なおかつ、Aerospike の Dockerfile が実行されるので、 3000 ~ 3003 番のポートに対応する環境変数がそれぞれ用意されていますね。

したがって、あとはアプリケーション側が環境変数から必要な値を取得するような実装になっていればよいことになります。たとえば Go 言語であれば、 以下のように os.Getenv() を使えばよいでしょう。

1
addr := os.Getenv("AEROSPIKE_PORT_3000_TCP_ADDR")

これで、 DB などの外部サービスに依存したテストも wercker 上で実行できるようになりました!

GitHub アカウントを使っている場合に wercker CLI で認証を通す方法

先進的な CI サービスである wercker では、 CLI が提供されています。これを使うと、 wercker でのビルドプロセスをローカルで再現できたり、 ビルド済みのコンテナを pull してきて、中の状態を確認できたりします。 OS X 用と Linux 用が提供されており、 OS X の場合には、以下のコマンドで一発です。

1
$ curl -L https://s3.amazonaws.com/downloads.wercker.com/cli/stable/darwin_amd64/wercker -o /usr/local/bin/wercker

この wercker コマンドを使ってコンテナイメージを pull してくるには、あらかじめ wercker login で wercker のアカウントでログインしておく必要があります。そうでないと、以下のようなメッセージが表示されてしまいます。

1
2
3
$ wercker pull skatsuta/aerospike-sample --branch master --load
Fetching build information for application skatsuta/aerospike-sample
ERROR wercker-api: You are not authorized to access the resource (status code: 401)

ところが僕は GitHub のアカウントを使っており、このログイン情報では次のように認証が通りません。

1
2
3
4
5
6
$ wercker login
########### Logging into wercker! #############
Username: skatsuta
Password:
ERROR Unable to log into wercker Error=Invalid credentials
ERROR Invalid credentials

どうなってるのかと思い wercker/wercker-cli リポジトリの issues を見てみると、以下のような issue が挙げられていました。

案の定 GitHub アカウントでのログインには未対応のようです。しかしながら、 Issue #15@captn3m0 のコメントに興味深いことが書かれていました。

Alternatively, you can add instructions to directly generate a token and putting it in the ~/.werkcer/token file

なんと、トークンファイルを置いておけばいけると!?
ということで、早速試してみました。

wercker のアカウントページの右上のアイコンから Settings > Personal tokens をたどり、 wercker-cli という名前でトークンを生成し、コピーします。そしてそれを ~/.wercker/token ファイルに貼り付けて保存します。

さて、もう一度 pull してみましょう。

1
2
3
4
5
6
7
$ wercker pull skatsuta/aerospike-sample --branch master --load
Fetching build information for application skatsuta/aerospike-sample
Downloading Docker repository for build 55a3aaad50dc24ba7b102720
Downloading: 100%
Download complete
Importing into Docker
Finished importing into Docker

本当に成功しました! これで wercker CLI を活用できますね。

wercker 上で Java / Scala プロジェクトの依存ライブラリをキャッシュする

wercker は今ある CI サービスの中ではもっともイケてるものだと思います。 box と step という仕組みにより、ビルドをおこなう環境とビルド処理を分離し、再利用性を高めています。最近 Docker にも対応したようです。まだ Beta 期間中のため、プライベートリポジトリも無料で利用できます。

最近、 Play Framework を使ったプロジェクトの開発でこの wercker を使用してみたのですが、試しにビルドしてみたら何と2回め以降のビルドでも毎回依存ライブラリをすべてダウンロードしているではないですか!!!

これは困った…どうにかならないもんか…と思ってやってみたのがこの記事です。

僕が今回取り組んだのが Play Framework を使ったプロジェクトだったため、それを題材に書きますが、本質的には Ant だろうと Maven だろうと Gradle だろうと sbt だろうと、もっと言えば Java / Scala に限らずどの言語のプロジェクトでも同じようにできるはずです。ぶっちゃけた話、単に rsync を使って自力でキャッシュを保存/復元しているだけですので。

キャッシュのためのディレクティブがない wercker

メジャーな CI サービスにはキャッシュを制御するディレクティブが用意されているものもあります。
たとえば Travis CI では

.travis.yml
1
2
3
cache:
directories:
- vendor/bundle

と書いておけば vendor/bundle がキャッシュされます。
CircleCI は至れり尽くせりで、 Java / Scala のプロジェクトの場合には

  • ~/.m2
  • ~/.ivy2
  • ~/.gradle
  • ~/.play

を自動で(暗黙的に)キャッシュしてくれますし、明示的にキャッシュしたいものは

circle.yml
1
2
3
dependencies:
cache_directories:
- ~/.sbt

のように書いておけば ~/.sbt がキャッシュされます。

一方 wercker はまだ若いサービスのためか、上記のようなキャッシュ機構がきちんと整っておらず、今のところ用意されているのは環境変数 $WERCKER_CACHE_DIR のみのようです (少なくとも僕が探した限りは見つかりませんでした)。

ビルドのたびに依存ライブラリを1からダウンロードされてはたまったもんじゃないので、何とかキャッシュの仕組みを作れないかとやってみました。

参考サイト

以下の wercker の公式ブログの記事を参考にさせていただきました。

上記記事では Go のプロジェクトにおいて、 go get してきた依存ライブラリを rsync を使って $WERCKER_CACHE_DIR に保存したりそこから復元したりして、 ビルド時間を短縮しています。
Java や Scala のプロジェクトでも考え方は同じなので、上記記事を参考にして自分なりのやり方を組み立てました。

要は何をすればよいか

  1. ビルドステップの最初に $WERCKER_CACHE_DIR からキャッシュを復元する
  2. ビルドステップの最後に $WERCKER_CACHE_DIR にキャッシュを保存する

要はこれだけなのですが、

  • キャッシュの保存に rsync を使うために、 rsync をインストールした box を作った
  • キャッシュの保存/復元作業をまとめたシェルスクリプトを書いた

ということをしたので、ちょっと手間暇をかけました。

rsync をインストールした box をつくる

キャッシュの保存/復元は cp コマンドとかでもいいのでしょうが、先の記事でも rsync を使っていましたし、確かに効率を考えると rsync がよさそうです。しかしビルドのたびに rsync をインストールするのも無駄です。そこで、 wercker にはせっかく box という素晴らしい仕組みがあるので、あらかじめ rsync をインストールした box をつくり、それを利用することにしました。

ありがたいことに、 Java 8 と Typesafe Activator をインストールした mitsuse/wercker-box-activator という box がすでにありましたので、 fork させていただき、 rsync をインストールする処理を追記しました。

キャッシュ処理用のシェルスクリプトを書く

簡単な処理であれば wercker.yml に直接書いてしまえばよいのですが、キャッシュ処理が長くなりそうだったので、シェルスクリプトにまとめることにしました。
以下は Play Framework の例です。 Play Framework ではキャッシュディレクトリは ~/.ivy2~/.sbt なので、その2つを指定しています。

wercker_cache.sh
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
30
31
32
33
34
35
#!/bin/bash

#=========================================
# Script for storing and restoring cache
# in Java / Scala project on werkcer
#=========================================

# cache directories
CACHES=( "ivy2" "sbt" )
CMD="$1"

# check if rsync exists
if ! type rsync > /dev/null; then
echo "[ERROR] rsync needs to be installed."
exit 1
fi

# set source and destination directories
case $CMD in
"store")
SRC=$HOME
DST=$WERCKER_CACHE_DIR
;;
"restore")
SRC=$WERCKER_CACHE_DIR
DST=$HOME
;;
esac

# run rsync
for cache in "${CACHES[@]}"; do
if test -d $SRC/.$cache; then
rsync -avz $SRC/.$cache $DST/
fi
done

Gist を貼り付けるとレイアウトが崩れてしまうので、 Gist 版はこちら

単に rsync で $WERCKER_CACHE_DIR へ保存したり、そこから復元したりしているだけです。
このシェルスクリプトをプロジェクトのリポジトリに入れておきます (たとえば sh/wercker_cache.sh など)。

wercker.yml にキャッシュの保存/復元処理を書く

最後に、 wercker.yml にキャッシュの保存/復元処理を書きます。
以下はサンプルで、 sh/wercker_cache.sh に先のスクリプトが入っているとします。

wercker.yml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
box: skatsuta/activator@0.0.3

build:
steps:
- script:
name: restore cache
code: |
sh/wercker_cache.sh restore

- script:
name: run activator test
code: |
activator test

- script:
name: store cache
code: |
sh/wercker_cache.sh store

これで wercker 上でビルドしてみると、2回目以降は restore cache というステップで各キャッシュがホームディレクトリに復元され、 store cache というステップで前回のキャッシュディレクトリとの差分が保存されていることが確認できると思います。

ビルド時間の短縮に成功!

僕のプロジェクトではこのおかげで wercker 上でのビルド時間を2分弱短縮することができました。めでたしめでたし。

AngularJS のフィルタを日本語表記に合わせる

AngularJS で currency フィルタや date フィルタを日本語の表記に合わせるには?

AngularJS のフィルタは便利な機能です。その中で currency フィルタや date フィルタといった、フォーマットを整えてくれるフィルタがありますが、デフォルトでは US 表記になっています。これを日本語の表記に合わせるには、angular-i18n モジュールを利用します。

以下の方法は grunt-wiredep使用の環境でangular-i18nをbowerでインストールしたら注入できないとメッセージ - Qiita を参考にさせていただきました。

angular-i18n モジュールをインストールする

Bower を利用してインストールします。

1
2
3
4
5
6
7
8
$ bower install angular-i18n --save
bower not-cached git://github.com/angular/bower-angular-i18n.git#*
bower resolve git://github.com/angular/bower-angular-i18n.git#*
bower download https://github.com/angular/bower-angular-i18n/archive/v1.2.24.tar.gz
bower extract angular-i18n#* archive.tar.gz
bower invalid-meta angular-i18n is missing "main" entry in bower.json
bower resolved git://github.com/angular/bower-angular-i18n.git#1.2.24
bower install angular-i18n#1.2.24

ん? bower invalid-meta angular-i18n is missing "main" entry in bower.json という警告が出ていますね。
同時に gulp serve などを走らせていた場合、以下のような警告が出てしまいます。

1
2
angular-i18n was not injected in your file.
Please go take a look in "app/bower_components/angular-i18n" for the file you need, the manually include it in your file.

Bower が angular-i18n モジュールのうち、どのファイルをインクルードしていいのかわからないようです。

angular-i18n の main を指定する

bower.json で明示的に使用するロケールファイルを指定してあげましょう。

bower.json
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{
"name": "myApp",
"version": "0.0.0",
... 省略 ....
"resolutions": {
"angular": "1.3.x",
"jquery": "2.x.x"
},
//=== ここから追記 ===
"overrides": {
"angular-i18n": {
"main": "angular-locale_ja-jp.js"
}
}
//=== ここまで追記 ===
}

これで index.html に自動的に追記されるようになります。

index.html の抜粋
1
2
3
4
5
<!-- bower:js -->
<script src="bower_components/jquery/dist/jquery.js"></script>
<script src="bower_components/angular/angular.js"></script>
<script src="bower_components/angular-i18n/angular-locale_ja-jp.js"></script>
<!-- endbower -->

日本語表記に変わったか確かめてみる

以下のような HTML と JS を用意し、レンダリング結果を見てみます。

index.html
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
<!DOCTYPE html>
<html lang="ja" ng-app="app">
<head>
<meta charset="UTF-8">
<title>Format test</title>
</head>
<body>
<div ng-controller="FormatCtrl">
<h1>日付</h1>
<p>{{today}}</p>
<p>{{today | date: 'fullDate'}}</p>

<h1>通貨</h1>
<p>{{yen}}</p>
<p>{{yen | currency}}</p>
</div>

<!-- bower:js -->
<script src="bower_components/jquery/dist/jquery.js"></script>
<script src="bower_components/angular/angular.js"></script>
<script src="bower_components/angular-i18n/angular-locale_ja-jp.js"></script>
<!-- endbower -->

<script src="scripts/main.js"></script>
</body>
</html>
main.js
1
2
3
4
5
6
(function() { 'use strict';
angular.module('app', []).controller('FormatCtrl', ['$scope', function($scope) {
$scope.today = new Date();
$scope.yen = 1000000;
}]);
})();

結果

日付

“2014-09-15T11:38:50.158Z”

2014年9月15日月曜日

通貨

1000000

\1,000,000

きちんと日本語の表記にフォーマットされていますね。

Hexo + GitHub Pages で簡単ブログ作成

いまや巷には溢れるほどの静的サイトジェネレータがありますが、このブログは Hexo を使って書き、 GitHub Pages で公開しています。 個人的に今 Go 言語が好きなこともあって Hugo とどちらを使おうかかなり悩んだのですが、僕は(デザインセンスに自信がないので)格好いい出来合いのテーマを使いたかったので、テーマの種類が多い Hexo を選びました。

Hexo の導入自体は非常に簡単なのですが、 GitHub Pages と連携させる際のやり方が最近ちょっと変わったらしく、検索して出てきた情報がどれも古かったので、覚え書きも兼ねて書いてみます。

参考サイト

以下のサイトを参考にさせていただきました。

ただし注意点として、これらの記事はどれも Github Pages で公開する手順の設定情報が古いです。現在の正しい設定は下記「GitHub Pages で公開する」のセクションに書きました。

Hexo を導入する

まず Git と Node がインストールされていることが前提です。これらの導入方法については省略します。

Hexo の導入自体は公式サイトのトップページに載っている通りにコマンドを実行するだけです。が、 Hexo のリソース自体も GitHub などで管理したほうがいいので、先にリポジトリを作った上で、それをクローンしてきて、その中で Hexo の初期化を実行したほうが良いと思います。

以下は僕の例です。 skatsuta/blog-hexo という GitHub リポジトリを作った上で、以下を実行します。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# Hexo をインストールする
$ npm install -g hexo-cli

# 作っておいた Hexo 用のリポジトリをクローンしてくる
$ git clone git@github.com:skatsuta/blog-hexo.git

# Hexo の初期化を実行する
# (必要なファイル/フォルダが生成されるとともに、.gitignore が上書きされます)
$ hexo init blog-hexo

# リポジトリに移動する
$ cd blog-hexo

# npm モジュールをインストールする
$ npm install

# Hexo サーバを動かしてプレビューを確認する
$ hexo server
INFO Hexo is running at http://0.0.0.0:4000/. Press Ctrl+C to stop.

メッセージの通り http://localhost:4000/ にアクセスしてみて、無事にプレビューが表示されればとりあえずの構築は完了です。

ファイルの変更をブラウザに即時反映する

デフォルトでは記事を書き換えてもプレビューには即時反映されず、毎回サーバを再起動しなければなりません。 Markdown エディタで書けばだいたいの仕上がり具合はわかりますが、やはり実際に適用されるテーマとは違ってしまいます。

そこで BrowserSync を使い、プレビューへの即時反映の設定をしてみたいと思います。

まず Get started in 5 minutes2. に従い、 BrowserSync をインストールします。

1
$ npm install -g browser-sync

続いて、 hexo-browsersync プラグインをインストールします。 package.json にも加えたいので、 --save オプションを付けておきます。

1
$ npm install hexo-browsersync --save

これで導入は終わりです。再び Hexo サーバを立ち上げてから設定や記事を書き換えて保存すると、そのたびに以下のようなメッセージが出てブラウザがリロードされ、変更点がプレビューに即時に反映されるはずです。

1
2
3
4
5
$ hexo server
INFO Hexo is running at http://0.0.0.0:4000/. Press Ctrl+C to stop.
[BS] Reloading Browsers...
[BS] Reloading Browsers...
[BS] Reloading Browsers...

記事を書く

快適な執筆環境が整ったところで、早速記事を書いていきます。 Hexo で新しく記事を作るには以下のコマンドを実行します。たとえば Hello, World というタイトルの記事を書く場合です。

1
2
$ hexo new "Hello, World"
INFO Created: ~/repos/blog-hexo/source/_posts/Hello-World.md

生成された Markdown ファイルの中は次のようになっています。

source/_posts/Hello-World.md
1
2
3
4
title: "Hello, World"
date: 2015-04-30 17:03:08
tags:
---

あとは Markdown で好きなように記事を書いていきます。 BrowserSync を入れているとそのありがたみがわかりますよね?

注意点

日本語で書く場合たいていタイトルも日本語になると思うので、 hexo new "日本語タイトル" というようにコマンド発行したいところです。しかしそうするとファイル名も日本語になってしまい、 Git での管理時にちょっと面倒なことが生じる場合が多いです。なので、ファイル名は英語で生成しておいて、 title: 部分を日本語に書き換えるほうが吉です。このあたりがちょっとした不満点ですね。

GitHub Pages で公開する

最後に GitHub Pages で公開します。

Hexo の Deployment のページにある通り、まず hexo-deployer-git プラグインをインストールします。これも package.json に加えたいので、 --save オプションを付けておきます。

1
$ npm install hexo-deployer-git --save

続いて _config.ymldeploy の項目に必要な設定を追記します。以下は僕の例です。

_config.yml
1
2
3
4
deploy:
type: git
repo: git@github.com:skatsuta/skatsuta.github.io.git
branch: master

GitHub Pages で公開する場合には、GitHub Pages のドキュメントにもある通り、 <username>.github.io というリポジトリを作ります。そこにプッシュされたコンテンツが自動的に http://<username>.github.io/ で公開されるようになります。

最後に、 Hexo で以下のコマンドを使ってデプロイします。

1
$ hexo deploy --generate

上記のコマンドは以下と同じ意味になります。

1
2
3
4
5
# 静的コンテンツを生成する
$ hexo generate

# 静的コンテンツを設定されたデプロイ先にデプロイする
$ hexo deploy

実際には、デプロイの際にいったん既存の生成データをすべて削除した上で再生成したほうが良さそうなため、僕は次のようなシェルスクリプトを実行しています。 hexo clean は生成コンテンツとキャッシュをすべて削除するコマンドです。

deploy.sh
1
2
3
#!/bin/bash

hexo clean && hexo deploy --generate

以上が完了すると、 http://skatsuta.github.io/ にこのブログが公開されます。

ね、簡単でしょう?

Hexo は記事をすべて Markdown で書けるので簡単にバージョン管理ができますし、他の Markdown 対応静的サイトジェネレータに移行するのもハードルが低いです。

ちなみにこのブログのテーマには Landscape plus を少しカスタムして使っています。デフォルトで結構格好いいので気に入っています。

当分の間は Hexo をいろいろいじりながら使ってみようと思います。 Hugo がもう少し成熟したらそちらも使ってみたいですね。

シェルスクリプトの覚え書き

たまにしか書かないのでいつも忘れてしまい、書く必要があるときになって毎度調べ直しているので、覚え書きとしてきちんとまとめてみます。

内容は非常に初歩的なことばかりです。

shebang

シェルスクリプトの先頭に書く宣言文。
どのシェルを利用して実行するかを指定する。

1
#!/bin/bash

変数

シェル変数

実行しているシェルの内部でのみ有効な変数。

1
2
# 変数abcに123を代入する
abc=123

環境変数

シェルから実行されたコマンド内でも有効な変数。

1
2
# 環境変数abcを設定する
export abc=123

read コマンド

標準入力からデータを読み込む。

1
2
# abc に標準出力からのデータを代入する
read abc

引用符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 変数 abc に文字列 xyz をセットする
$ abc=xyz

# シングルクォーテーションは文字列として認識する
$ echo 'The value of abc is $abc.'
The value of abc is $abc.

# ダブルクォーテーションは変数として展開する
$ echo "The value of abc is $abc."
The value of abc is xyz.

# バッククオートはコマンドとして実行する
$ abc=`date`
$ echo $abc
2014年 7月 15日 火曜日 17:56:34 JST

引数

$0は実行コマンド名、$#は引数の数を表す。

args.sh

1
2
3
4
5
6
#!/bin/bash

echo '$1: ' $1;
echo '$2: ' $2;
echo '$0: ' $0;
echo '$#: ' $#;

実行結果

1
2
3
4
5
$ ./args.sh aaa bbb
$1: aaa
$2: bbb
$0: ./args.sh
$#: 2

エスケープシーケンス

行末にバックスラッシュをつけると、改行コードがエスケープされて文字列を途中で折り返すことができる。

1
2
3
$ echo "Hi! \
Today is the day!"
Hi! Today is the day!

条件分岐

構文

1
2
3
4
5
6
7
if [ condition1 ]; then
# ...
elif [ condition2 ]; then
# ...
else
# ...
fi

演算子

文字列比較

演算子 比較内容
a == b a と b が等しければ真
a != b a と b が等しくなければ真

数値比較

演算子 比較内容
a -eq b a と b が等しければ真
a -ne b a と b が等しくなければ真
a -ge b a が b 以上なら真
a -le b a が b 以下なら真
a -gt b a が b より大きいなら真
a -lt b a が b より小さいなら真

ファイル属性の確認

パスがディレクトリであれば真

1
if [ -d path ]; then ...

test コマンドの書き方2通り

1
2
if test 条件節; then ...
if [ 条件節 ]; then ...

ファイル属性を確認する演算子

演算子 内容
-f ファイル名 通常ファイルなら真
-d ファイル名 ディレクトリなら真
-e ファイル名 ファイルが存在すれば真
-L ファイル名 シンボリックリンクなら真
-r ファイル名 読み取り可能なら真
-w ファイル名 書き込み可能なら真
-x ファイル名 ファイルに実行権限があれば真
-s ファイル名 サイズが0より大きければ真