Fuzoku実践入門ブログ

Amazon で好評発売中の『Fuzoku実践入門』に関するエピソードなどを紹介するブログです。

CircleCIによる電子書籍の継続的デプロイメント

先日、CircleCIを導入して、CIによるEPUBビルド環境が整ったわけですが、人間とは欲望が尽きないもので、こうなってくると、今度は継続的デプロイメント環境が欲しくなってきました。

私が考える電子書籍の継続的デプロイメントがどのようなものか整理してみると、EPUBビルド(テスト)が完了した後に、Kindleへファイルを自動送信するのが自然だろうという回答に至りました。

そこで、CircleCIに設定を追加して、継続的デプロイメント環境を整えてみるとことにしました。

kindlemail を改造して環境変数に対応させる

KindleへのMOBIファイルの転送は、以前記事に書いたとおり、kindlemailを利用しているのですが、今回、CircleCIで動かすにあたり、OAuthトークンなどを設定ファイルからではなく、環境変数から読み込むようにした方が良いと思いましたので、改良をほどこしたkindlemailを作ることにしました。

とりあえず、簡単に実現したかったので、lib/Configuration.rbファイルを次のように変更することにしました。

@@ -61,23 +61,33 @@ class Configuration
   def get_email_credentials
-    raise ArgumentError, "Cannot find email credentials file #{EMAIL_CONF_FILE}." if !File.exists?(EMAIL_CONF_FILE)
-    begin
-      load_yaml(EMAIL_CONF_FILE)
-    rescue
-      raise StandardError, "Error parsing #{EMAIL_CONF_FILE}"
+    if (ENV['SMTP_OAUTH_TOKEN']  &&
+      ENV['SMTP_OAUTH_TOKEN_SECRET'] &&
+      ENV['KINDLE_USER_EMAIL'])
+
+      return {
+        :smtp_oauth_token => ENV['SMTP_OAUTH_TOKEN'],
+        :smtp_oauth_token_secret => ENV['SMTP_OAUTH_TOKEN_SECRET'],
+        :email => ENV['KINDLE_USER_EMAIL']}
+    else
+      raise ArgumentError, "Cannot find email credentials file #{EMAIL_CONF_FILE}." if !File.exists?(EMAIL_CONF_FILE)
+      begin
+        load_yaml(EMAIL_CONF_FILE)
+      rescue
+        raise StandardError, "Error parsing #{EMAIL_CONF_FILE}"
+      end
   end
 end

SMTP_OAUTH_TOKEN」と「SMTP_OAUTH_TOKEN_SECRET」と「 KINDLE_USER_EMAIL」という3つの環境変数が全て存在すれば、設定ファイルではなく、環境変数を優先して利用するという形です。

フォークしたリポジトリに上記の変更を加えて、新規作成したブランチ(use-env-first)が下記になります。

GithubリポジトリからGemをインストールする

次は、新たに作成したリポジトリから、kindlemailをインストールするようにGemfileを書き換えます。

@@ -2,5 +2,5 @@ source "https://rubygems.org"

 gem "review"
 gem "md2review"
-gem "kindlemail"
+gem 'kindlemail', github: 'akinomurasame/kindlemail', branch: 'use-env-first'
 gem "dropbox-sdk"

bundle updateを実行すると、指定した自分のリポジトリのkindlemailに更新されました。

CircleCI の設定を変更する

CircleCI で行なうことは、kindlegenを使ってEPUBからMOBIを作成して、kindlemailによってMOBIをKindleへ送ることですので、作業内容は主に次のようになります。

  1. kindlemailの環境変数を追加する
  2. kindlegenをインストールする
  3. デプロイ設定を追加する

順番に作業を追っていきましょう。

kindlemailの環境変数を追加する

これはCicleCIの設定パネルから、先ほどの3つの環境変数を追加するだけですので、とても簡単です。

環境変数の追加

kindlegen をインストールする

kindleへMOBIファイルを送るためには、CircleCIでkindlegenコマンドが使える必要があるため、これをインストールする設定をcircle.ymlに追加します。

@@ -1,3 +1,9 @@
+dependencies:
+  pre:
+    - mkdir tmp && cd ./tmp
+    - wget http://kindlegen.s3.amazonaws.com/kindlegen_linux_2.6_i386_v2_9.tar.gz
+    - tar zxvf kindlegen_linux_2.6_i386_v2_9.tar.gz
+    - cp kindlegen /usr/local/bin
 test:
   override:
     - rake md2review epub send_to_dropbox

kindlegenはMakefileがないので、べたにインストールしました。そして、ブランチをpushして、CircleCIのビルドを実行してみると、無事にインストールされることを確認しました。

kindlegenインストール確認

さて、毎回kindlegenをダウンロードするのは少し無駄な処理なため、キャッシュを使うように書き換えることにしました。

少し設定が複雑になるため、ついでにインストール処理をスクリプトファイルにしました。

dependencies:
  cache_directories:
    - tmp
  pre:
    - bash ./install-kindlegen-2.9.sh
test:
  override:
    - rake md2review epub send_to_dropbox
#!/bin/sh -xe

if [ ! -e tmp/kindlegen ]; then
mkdir tmp && cd ./tmp
wget http://kindlegen.s3.amazonaws.com/kindlegen_linux_2.6_i386_v2_9.tar.gz
tar zxvf kindlegen_linux_2.6_i386_v2_9.tar.gz
sudo cp kindlegen /usr/local/bin;
fi

pushして、ビルドが完了した後に、リビルドしてキャッシュが利用されていることを確認しました。

キャッシュ利用の確認

デプロイ設定を追加する

circle.ymlにデプロイ設定を追加します。masterブランチが変更されたタイミングで、デプロイすることにしたので、次のような設定になりました。

@@ -7,3 +7,8 @@ dependencies:
 test:
   override:
     - rake md2review epub send_to_dropbox
+deployment:
+  staging:
+    branch: master
+    commands:
+      - rake mobi send_to_kindle

これで、masterブランチにマージされると、kindlemailが実行されるようになりました。最後に、送信先のKindleデバイスを柔軟に追加できるようにします。

デバイスリストからKindleへ送る

kindle_addresses.ymlというデバイスリストのファイルを作成して、こちらに追加されているアドレス全てにMOBIを送るようにRakefileを修正します。

- kindle1@example.com
- kindle2@example.com
desc 'send to kindle'
task :send_to_kindle do
  if File.exist?("kindle_addresses.yml")
    YAML.load_file('kindle_addresses.yml').each do |kindle_address|
      sh "bundle exec kindlemail -f #{bookname}.mobi -k #{kindle_address}"
    end
  else
    sh "bundle exec kindlemail -f #{bookname}.mobi"
  end
end

この変更により、kindle_addresses.ymlに書かれている、全てのsend-to-kindleアドレスに対して、MOBIファイルが自動的に送られるようになりました。

masterへマージ、デプロイ実行

様々な変更をしましたが、masterブランチにマージすると、無事にCircleCIによる自動デプロイが実行され、継続的デプロイメント環境が完成しました。

bundle exec kindlemail -f example.mobi -k kindle1@example.com
kindlemail 0.2.8. Written by djhworld. https://github.com/djhworld/kindlemail

Preparing example.mobi to be sent to kindle1@example.com
Adding attachment: /home/ubuntu/.kindlemail/.staging/c78f78d87f0de292b516a7af051f0d40_209895.mobi (290.9131 kb)
Sending message....sent!
example.mobi was successfully sent to kindle1@example.com
bundle exec kindlemail -f example.mobi -k kindle2@example.com
kindlemail 0.2.8. Written by djhworld. https://github.com/djhworld/kindlemail

Preparing example.mobi to be sent to kindle2@example.com
Adding attachment: /home/ubuntu/.kindlemail/.staging/c78f78d87f0de292b516a7af051f0d40_970393.mobi (290.9131 kb)
Sending message....sent!
example.mobi was successfully sent to kindle2@example.com

デプロイ確認

まとめ

まだまだ電子書籍の執筆環境は改善できる点があるかと思いますが、ひとまずCircleCIによる継続的デプロイメントを試すことができたのは、私にとって大きな進歩でした。

あと簡単に思いつく改善としては、PDFの自動作成もCircleCI上で行なえれば便利かなと考えているのですが、CircleCI上でTeX環境を整えるのは、なかなか骨な気がするため、やるかどうか悩んでいます。

やるとすれば、TeX環境構築済みdockerコンテナを用意して利用するという方法が良いのかなと思うのですが、dockerを利用したことのない一介のライターの私にとっては、なかなか大変そうな作業です。

しかしながら、こういった改善を続けつつ環境を整えて、その情報を共有していくことで、他の電子書籍執筆者の助けになれば幸いですので、これからも少しづつ改善を続けていきたいと思う次第です。