monoの開発ブログ

Jekyllでgzipを生成してrsyncでデプロイする

以前、Jekyllを便利に使うためのRakefileと称して、Jekyllで生成したHTMLなどのgzipファイルを生成してrsyncでデプロイするコードを紹介しました。このコードで確かにデプロイできることはできるのですが、何も変更されていないファイルも含めて毎回転送されてしまうという問題があったため、対策しました。

まずは完成品をどうぞ。

require 'bundler'
Bundler.require

task :new, :title do |t, args|
  title = args.title or exit
  config = Jekyll.configuration({})

  open(File.join(config['source'], '_posts', "#{Time.now.strftime('%Y-%m-%d')}-#{title.to_url}.markdown"), 'w') do |f|
    f << <<-EOS
---
layout: post
title: #{title}
date: #{Time.now.strftime('%Y-%m-%d %H:%M')}
comments: true
categories:
published: false
---
EOS
  end
end

task :build do
  config = Jekyll.configuration({})
  Jekyll::Commands::Build.process(config)
  Parallel.each(Dir.glob(File.join(config['destination'], '**', '*.{css,html,js,xml}'))) do |f|
    system "gzip --rsyncable --keep --no-name #{f}"
  end
end

task :rsync do
  config = Jekyll.configuration({})
  system "rsync -avzr --compress --checksum --delete #{config['destination']}/ #{config['rsync']['destination']}"
end

task :deploy => [ :build, :rsync ] do
end

前回のコードの最大の問題は、Jekyllでビルドするとすべてのファイルが再生成されてタイムスタンプが変化することを考慮していない点です。これにより、ファイルの内容が変わっていないにも関わらず、ファイルが変更されたと見なされていました。今回のコードでは、--checksumオプションを追加することで、ファイルの内容によって変更をチェックするようにしています。

しかし、これだけでは十分ではありません。gzip圧縮したファイルは、--checksumオプションを追加しても毎回転送されてしまいます。これは、gzipファイルに圧縮前のファイルのタイムスタンプの情報が圧縮後のファイルに格納されていることに起因します。圧縮時に--no-nameオプションを追加することで、圧縮前のファイルのタイムスタンプ (とファイル名) が含められることを回避できます。

なお、上の例では--rsyncableオプションを追加してrsyncでの変更の有無の確認を高速に行えるgzipファイルを生成しています。このオプションはGNUのgzipにしか含まれていないようなので、OS Xなどの環境ではオプションを削除するか、GNUのgzipをインストールしてください。