Hugoの設定ファイルを分割しfswatchを使って結合してみる



Nプログラマです。

今回は、Hugoの設定ファイルconfig.tomlを分割・結合するという内容です。

config.tomlの分割は公式でサポート(params, languages, menusなど)されていますが、任意のパラメータの場合はこれができないようです。

一つの設定ファイルにモリモリ書いてもいいのですが、1000行を超えてくると編集がツラくなってきます。

そこでfswatchを使ってファイル監視を行い、編集を検知したらconfig.tomlに結合するようにしてみました。

ファイル単位で分割しているため、設定の場所や編集のしやすがラクになるかもしれません。

環境

  • Mac OS X High Sierra 10.13.6
  • fswatch 1.14.0

準備

まずは検証のため空のプロジェクトを作成します。

\$ hugo new site sample

sampleディレクトリの階層構造はこうなっています。

./
├── archetypes
│   └── default.md
├── config.toml
├── content
├── data
├── layouts
├── static
└── themes

HugoのConfiguration Directoryを参考にconfigディレクトリを作成します。 その下に_defaultディレクトリを作成して、config.tomlをそこに移動させます。

変更した結果、以下のようなディレクトリ階層になりました。

./
├── archetypes
│   └── default.md
├── config
│   └── _default
│       └── config.toml
├── content
├── data
├── layouts
├── static
└── themes

これで準備完了です。

設定ファイルを分割していく

分割ファイル用のディレクトリを作成

先に分割したファイルを配置するためのディレクトリを作成します。 ディレクトリ名は任意なのでtmpという名前にしました。 このディレクトリがfswatchの監視対象になります。

またこのディレクトリはhugoのサーバ起動時のビルド対象にならないので、必要以上にビルドがされないのもいいところかなと思っています。
(おそらくConfiguration Directoryのルールから外れているため、ビルドされないのだろうと思っています)

ファイルの分割

ここからは先程移動させたconfig.tomlを分割していきます。
分割前の内容はこんな感じです。

config.toml コードを開く
config.toml

baseURL = "サイトのURL"
title = "サイトのタイトル"
themes = "使っているテーマ"

[MyParams.Hoge]
  name = "hoge"
  age  = "20"
  link = "hoge link"

[MyParams.Fuga]
  name = "fuga"
  age  = "30"
  link = "fuga link"

[MyParams.Piyo]
  name = "piyo"
  age  = "40"
  link = "piyo link"

これらを4つのファイルに分割してみます。

  • 00.base.toml
  • myparams-hoge.toml
  • myparams-fuga.toml
  • myparams-piyo.toml

分割したファイルを先程作成したtmpディレクトリに配置します。

./
├── _default
│   └── config.toml
└── tmp
    ├── 00.base.toml
    ├── myparams-fuga.toml
    ├── myparams-hoge.toml
    └── myparams-piyo.toml

baseだけ数字がついているのは、結合時にソートしてconfig.tomlで一番上に記述したいからです。

config.tomlはfswatchの検知により常に自動生成されるため、削除しなくても問題ありません。

config.tomlは常に上書き

この仕組みを導入するとconfig.tomlは常に分割したファイルを結合した結果の上書き更新になります。
導入する前にはバックアップを取るようにお願いします。
またconfig.tomlを編集する場合は、必ず分割ファイルを編集するようにしてください。

分割したファイルのコード

00.base.toml コードを開く
00.base.toml

baseURL = "サイトのURL"
title = "サイトのタイトル"
themes = "使っているテーマ"

myparams-hoge.toml コードを開く
myparams-hoge.toml

[MyParams.Hoge]
  name = "hoge"
  age  = "20"
  link = "hoge link"

myparams-fuga.toml コードを開く
myparams-fuga.toml

[MyParams.Fuga]
  name = "fuga"
  age  = "30"
  link = "fuga link"

myparams-piyo.toml コードを開く
myparams-piyo.toml

[MyParams.Piyo]
  name = "piyo"
  age  = "40"
  link = "piyo link"

ファイルの監視・結合

ファイルの分割ができたので、次はfswatch(公式サイト)を使った監視をします。
コマンドがない場合はbrewコマンドでインストールします。

brew install fswatch

ファイル変更を検知したらconfig.tomlに結合する処理が必要なので、そのシェルスクリプトを作成しました。

update-config.sh コードを開く
update-config.sh

#!/usr/bin/env bash

# (1) 一時ファイル
tmpfile=$(mktemp)

# (2) 1ファイルに対して1度だけ処理: PHPStorm用
echo $1 # ファイル変更の通知内容の確認
if [[ $(echo $1 | grep -e 'Removed') ]]; then
  echo '分割したファイルの結合処理をする'
else
  echo '何もせず終了する'
  exit
fi

# (3) 分割ファイルの結合処理
find ./sample/config/tmp -type f | sort | xargs -I{} cat {} >> $tmpfile

# (4) 一時ファイルからconfig.tomlへ書き出し
cat $tmpfile > ./sample/config/_default/config.toml

# (5) 一時ファイルの削除
function rm_tmpfile {
  rm -f "$tmpfile"
  echo 'tmpfile remove!'
}
trap rm_tmpfile EXIT

参考サイト

スクリプト内で一時ファイルの作成と削除について、こちらのサイト様の記事を参考にしました。
ありがとうございます。

使い方

作成したスクリプトをhugoで作ったディレクトリと同じ階層に配置します。

./
├── sample
└── update-config.sh

この状態でfswatchコマンドを実行します。

fswatch -xr ./sample/config/tmp | xargs -I{} ./update-config.sh {}

オプションの-rをつけることで、分割した設定ファイルが入ったtmpを再帰的に監視します。
また-xをつけることでどのようなイベントなのかが表示されるようになります。(これを使ってスクリプト内で条件判定をしています)

ファイルの変更を検知したら、その情報をupdate-config.shに渡します。
渡された内容はupdate-config.shの$1に入ってきます。

後述しますが、1ファイルの変更で複数の通知がくるのでスクリプト側で1回だけの処理になるようにしています。

スクリプトの配置場所について

スクリプト内でファイルパスを指定している部分があるので、そこを書き換えれば任意の場所に配置することができます。

コードの説明

(1) 一時ファイルについて

mktempを使うと一時ファイルの生成し、削除し忘れても一定期間立つと自動的に削除もされるみたいです。
これは便利で勉強になりました。

(2) 1ファイルに対して1度だけ処理について

fswatchでファイル更新をするとその通知内容が飛んでくるのですが、編集に使用したツールによって通知内容が異なるみたいです。

ツール名 通知の内容
PHPStorm 1ファイルにつき3つの通知
Vim v8.1 1ファイルにつき2つの通知

自分はPHPStormを使っていたので、fswatchからの通知内容はこんな感じでした。

変更したファイルパス___jb_tmp___ Created Renamed Updated IsFile}
変更したファイルパス Renamed OwnerModified IsFile}
変更したファイルパス___jb_old___ Removed Renamed IsFile}

一番最後の通知に対して一度だけ分割ファイルの結合処理をするようにしました。

他のツールの場合はfswatchの実行画面で通知内容を確認して、条件分岐内のRemovedのところを変更すればうまく動作すると思います。

(3) 分割ファイルの結合処理

ここがメインの処理です。

findコマンドでtmpディレクトリ内のファイル一覧を取得します。

find ./sample/config/tmp -type f

設定ファイルの中身は、昇順ソートのファイル単位で記述したいのでソートします。
findで出力したファイル一覧はソートされていないので、パイプで出力をsortに渡してソートします。

sort

xargsコマンドを使い、ソートされたファイル一覧を一つずつcatで出力したものを一時ファイルの変数へ書き込んでいきます。
出力結果を追記していきたいので>>を使っています。

xargs -I{} cat {} >> $tmpfile

これが終わるとtmpディレクトリ内のファイルの内容全てが、一時ファイルの変数へ書き込まれます。

(4) 一時ファイルからconfig.tomlへ書き出し

先程の一時ファイルの内容をconfig.tomlへ上書きします。

hugo serveで起動している場合は、ここでconfig.tomlが更新されたのでページのリロード処理が走ります。

使ってみる

先程作ったスクリプトを使ってみます。

sampleとupdate-config.shを配置してあるディレクトリに移動後、使い方で書いたコマンドを実行します。

fswatch -xr ./sample/config/tmp | xargs -I{} ./update-config.sh {}

これで実行中になるので別のterminalを起動させて、myparams-hoge.tomlを編集します。

次のように変更して保存します。

変更前 コードを開く
変更前

[MyParams.Hoge]
  name = "hoge"
  age  = "20"
  link = "hoge link"

変更後 コードを開く
変更後

[MyParams.Hoge]
  name = "hogehoge"
  age  = "2020"
  link = "hoge link"

保存した後、結合されて作成された./sample/config/_default/config.tomlの中身を確認すると、myparams-hoge.tomlの変更が反映されていることが確認できました。

config.toml コードを開く
config.toml

baseURL = "サイトのURL"
title = "サイトのタイトル"
themes = "使っているテーマ"

[MyParams.Fuga]
  name = "fuga"
  age  = "30"
  link = "fuga link"

[MyParams.Hoge]
  name = "hogehoge"
  age  = "2020"
  link = "hoge link"

[MyParams.Piyo]
  name = "piyo"
  age  = "40"
  link = "piyo link"

00.base.tomlなど他のファイルを変更しても同じように変更内容が反映されます。

おわりに

今回は、Hugoの設定ファイル(config.toml)を分割・結合するという内容でした。

ちょっと準備が大変でしたがこれを導入してみたら、かなり設定ファイルがスッキリとしました。
設定ファイルが肥大化してきたらお試しください。

それでは、このへんで。
バイナリー!



関連した記事