JekyllでHTMLを動的生成し、Webページの更新を効率化する
IPLのホームページを内部的にリニューアルしました。 レイアウト崩れのテストからJenkinsによる自動更新までを扱います。
久々の技術記事更新です。
IPLのホームページも、前回リニューアル時よりかなり時間が経過し、メンテナンスが難しくなってきました。 HTMLを手書きしていると、どうしてもコピーペーストで作成する部分が発生してしまい、 デザインやサイト構成に手を入れることが難しくなってきます。
そういった時に役立つのが、**「静的サイトジェネレータ」(Static Site Generator)**です。 これは、PHPのようにHTMLをスクリプトによって生成し、生成後のHTMLファイルをサーバにアップロードすることで、 部品やレイアウトの共通化を図ることが出来るツールです。
よく使われる静的サイトジェネレータに、下記のものがあります。
この手のツールにも流行り廃りがありますが、 現状最も古く、多く使われているのはJekyllでしょう。
他にも多数の静的サイトジェネレータが存在します。主なものは下記のサイトにまとめられています。
今回は、デファクトということでJekyllを採用します。 ただ、Ruby製のツールはWindowsでの動作に問題が多い(Windowsのファイルシステム上でテストされていない、ネイティブモジュールを使っている等)ため、 Windows上での開発が見込まれる場合は、hexoの方が適しているかもしれません。
自動テスト
まず、既存のサイトの振る舞いを破壊しないように、テストを整備する必要があります。 しかし、HTMLは一般的なプログラムと異なり、記述ミスによるエラーはほとんど出ませんし、レイアウト崩れなどは目視によってしか確認が出来ません。
そこで、自動でスクリーンショットを撮影し、gifを作成するようにしておくと、 レイアウト崩れやカラム落ちなどのミスに気づくことが出来ます。 手動でサイト内をくまなく見て回る必要もありません。
この手順は、リファクタリング前に行なうことに意義がありますので、最初に着手することをおすすめします。
テストには、Seleniumベースの自動テストツール「webdriverio」を使うのがおすすめです。
WebDriverIOのインストール
まず最新のChromeとNode.jsが入っていることを確認してください。 次に、下記コマンドを実行して、Selenium Serverのスタンドアロン版とWebDriverIOをインストールします。
# Seleniumのインストール
npm install selenium-standalone@latest -g
selenium-standalone install
# WebDriverIOのインストール
npm install webdriverio
自動スクリーンショット
下記のコードは、サイトのスクリーンショットを取って回るサンプルコードです。
var selenium = require('selenium-standalone');
selenium.start({spawnOptions: {stdio: 'inherit'}}, function(err, child) {
if(err){
console.log(err);
return;
}
console.log("selenium-standalone started");
});
var cnt = 0;
function s(){
function zeroPadding(number, length){
return (Array(length).join('0') + number).slice(-length);
}
return "test/" + zeroPadding(cnt++, 3) + ".png"
}
var io = require('webdriverio');
var options = { desiredCapabilities: { browserName: 'chrome' } };
var prefix = "http://localhost:4000/";
client = io.remote(options);
client.init()
.setViewportSize({
width: 1024,
height: 1024
})
.url(prefix + "/")
.saveScreenshot(s())
.url(prefix + "about/")
.saveScreenshot(s())
.url(prefix + "sitemap.html")
.saveScreenshot(s())
.end();
client.end();
以上のコードを、test/e2e.jsとして保存し、下記のように実行すると、gifが生成されます。 (別途imagemagickが必要です)
node test/e2e.js && convert -delay 50 -loop 0 test/*.png test/e2e.gif && rm test/*.png
生成されたgifのファイル名にタイムスタンプを追加して保管しておくと、どのタイミングでレイアウトが崩れたのか追うことが出来ます。
Jekyllプロジェクトの基本構成
Jekyllのファイル構成は、基本的に自由に構成することができます。デフォルトでは下記のような構成になります。
jekyll buildを実行すると、サイト全体が_sites
内に出力されます。また、サイトのタイトル等の設定は_config.yml
に記述します。
また、上記以外のファイルを配置すると、単に_sites
以下にファイルがコピーされます。
画像やCSS、Javascriptファイルは適当なフォルダを作成して格納しておけばOKです。
インストール方法、設定手順などは、日本語公式ドキュメントをご参照下さい。
Front-matter
Jekyllの基本的な動作は、ファイルをそのまま_sites
にコピーするというものですが、
下記のような記法がファイル先頭にある場合は、特別なファイルとして処理を行います。
---
layout: post
title: Blogging Like a Hacker
---
ここでは変数定義や、使用するレイアウト(後述)の指定を行なうことが出来ます。
部品の切り出し
まず、ソースの総量を減らし、共通部品を洗い出すために、ヘッダ、フッタ、サイドバーなどを_includes
に切り出します。
これにより、共通化が容易な部分と、再利用の難しいコンテンツの切り分けも出来ますので、最初に実施することをおすすめします。
読み込み側では、下記のように記述します。
{% raw %}
{% include footer.html %}
{% endraw %}
レイアウトの作成
_layouts
には、コンテンツの外側部分を定義することが出来ます。
_includes
に似ていますが、入れ子構造に出来るのが特徴です。
layoutsを利用した共通化はスマートですが、やり過ぎると、オブジェクト指向における継承のように、柔軟性が失われてしまいます。 まず構造を把握しやすくする意味で、includesでの切り出しを先に行った方がいいでしょう。
プレビュー
jekyll serve
でプレビュー用のサーバが起動します。
ファイルが更新されるたびにブラウザのリロードを行なうのが面倒に感じられるのであれば、wtchを使うと、リロードの自動化を行なうことが出来ます。
ビルド
jekyll build
を実行すると、_site
下にhtmlファイルが生成されます。
Gitでバージョン管理を行なう場合、.gitignore
に_site
を指定しておくべきでしょう。
また、ビルドし忘れを防ぐために、自動ビルドの設定をすることをおすすめします。 弊社では、サイト構成ファイルが更新されると、Jenkinsサーバに通知され、 自動でビルドが走り、アップロードまで完了するようになっています。
また、自動ビルドの設定を行っておくと、環境構築をしていないユーザでも サイトの更新が可能になります。
おわりに
Webの管理をこれで省力化できました。 Jekyllはどのような構成のWebサイトでも生成することができるので、HTMLマニュアルの作成にも知見を活かすことが出来ると考えています。
副次効果として、HTMLではなくMarkDown記法で記事を書くことも可能になりました。 これで、更新が楽になればいいなと思っています。