Travis CIでdotfilesのテストを自動化する
概要
自作のdotfilesが一定の品質であることを保証するために、4つのテスト項目を定義し、テストの再設計・再実装した。
また、上記のテストとTravis CIを連携させることにより、テストの自動化を図った。
はじめに
皆様は設定ファイルをどのように管理しているだろうか。 外部記憶装置に保存する。ブログにアップする。いろいろな管理方法があるだろう。
私はdotfilesの一つのリポジトリで管理している。実際に私が使用しているdotfilesは下記の通りとなっている。
dotfilesで管理することで、新しい開発環境でもmake installのコマンド一つで自分の環境に設定ファイルを展開できる。
dotfilesについて興味のある方は有識者のサイトを参考にしてほしい。
一方で、dotfilesは常に使用しているものではなく、新しい開発環境を用意するとき (半年に一回程度くらい) などにしか使用されない。 そういったタイミングで用意していたdotfilesが動作しなかったら目も当てられない。
そこで、dotfilesが意図した通りに動作することをテストするためにCIを導入することにした。 今回は、CIツールとしてTravis CIを利用する。

dotfilesの構成
CIの導入に入る前に、私のdotfilesの現状構成について紹介する。

私はdotfilesで以下の6つの設定ファイルを管理している。
利用者がmake installコマンドを実行することで、これらの設定ファイルがローカルの開発環境に展開されるようになっている。
またmake install は、先駆者様に倣ってmake deployとmake initを実行するコマンドとなっている。
make deployは、dotfiles内の設定ファイルをローカル開発環境に展開する(リンクを張る)作業をする。
make initは、開発環境の初期化処理をする。
自分のdotfilesでは初期化処理で、tmuxとvimのプラグインをダウンロードする。
テスト環境
- Ubuntu Bionic 18.04
本来であれば複数の環境でテストすべきであるが、「dotfilesはあくまで自分用のリポジトリであること」「自分がUbuntuの最新LTSしか使用していないこと」から上記の一つのみ実施する。
テスト項目
これらの環境を考慮したうえで、dotfilesで満たすべき項目を考える。
設定ファイルのデプロイに成功している
単純かつ明解であるが、重要なテスト項目の一つである。
確認方法は、make installコマンドを実行したときのリターンコードが0であるかとした。
設定ファイルが正しい場所に展開されている
「正しい場所」とは、開発者の意図した場所のことを指す。
このテスト項目は、dotfilesの性質とし優先して満たすべきと考えられる。 確認方法は、展開した設定ファイルが存在しているか、設定ファイルのリンク先が正しいか、この2つの手順で実施する。
設定ファイルの存在は、シェルスクリプトの「-e」演算子で簡単に確認することができる。
「-h」演算子(シンボリックリンクであるか)の場合のis_link関数については後述。
# $1: source file # $2: destination file function is_exist() { DOTFILE=`basename $2` if [[ ! -e $2 ]]; then echo " × \"$2\" is failed" echo " Not found \"${DOTFILE}\"...NG" exit 1 elif [[ -h $2 ]]; then is_link $1 $2 elif [[ -e $2 ]]; then echo " ? \"$2\" is failed...OK" echo " deploying \"${DOTFILE}\"\. but, file is unsupported...UNKHOWN" exit 2 else echo " ERROR: script was broken." exit -1 fi }
リンク先が正しい場所を指しているかについては、「readlink」コマンドで得られたパス名を比較することで確認できる。
# $1: source file # $2: destination file function is_link() { LINKPATH=`readlink -f $2` DOTFILE=`basename $1` if [ $1 = ${LINKPATH} ]; then echo " ○ \"${DOTFILE}\" is succeed" else echo " × \"${DOTFILE}\" is failed" echo " deploying dot file...OK" echo " invalid paths ($2 -> ${LINKPATH})...NG" fi }
上記のスクリプトを実行する「make test」を用意したので、結果が0か非0かどうかをチェックする。
vimプラグインのインストールが成功している
このテスト項目は、展開した開発環境でVimが正しく使用できるか確認するものである。 そのために、インストール時のリターンコードと、プラグインが使用できること、確認するの二つを実施する。
私のdotfilesでは、vim起動時にプラグインをインストールしていなければプラグインをインストールするようにしている。 そこで、下記のスクリプト(一部) を実行して、vimの起動とリターンコードを確認する。
またプラグインのインストール時に、続けるにはENTERを押すかコマンドを入力してくださいが表示されるので、yesコマンドでごまかしている。
# install success? yes | timeout -sKILL 60 vim +:q > /dev/null 2>&1 if [ $? != 0 ]; then echo "Failed install dein, Please install again." exit 1 fi
インストールしたプラグインが実行できるかの確認は、vimプラグインを実行するフェイズとエラーメッセージを確認するフェイズで実施する。
Vimプラグイン実行するフェイズでは、プラグインが実行できるかどうか判定する変数○○○_enabledを参照する。
ただし一部のプラグインは提供されていないので、適当のコマンドを実行することにした)
vim -V0easymotion.log +"echo g:EasyMotion_keys" +:q vim -V0webdevicons.log +"echo g:webdevicons_enable" +:q vim -V0ale.log +"echo ale_enabled" +:q vim -V0gitgutter.log +"echo gitgutter_enabled" +:q vim -V0lightline.log +"echo lightline" +:q
エラーメッセージを確認するフェイズでは、前工程で得られたログファイルからエラーメッセージが存在するか判定する。
VimのエラーメッセージはEと任意の数字で構成されるので、正規表現でパターンマッチさせる。
# $1: logfile name # $2: return code function check_plugin() { LOGFILE=$1 ERRCODE=$2 FAILED=0 while read LINE do if [[ $LINE =~ :E[0-9]*: ]]; then echo $LINE FAILED=1 fi done < $1 if [ $FAILED -ne 0 ]; then ret=`expr $ret + $2` fi }
tmuxプラグインのインストールが成功している
このテスト項目は、展開した開発環境でtmuxが正しく使用できるか確認するものである。 確認方法は、インストールされたtmuxプラグインが正しいかどうか確認することを実施する。
そのために、ローカル環境に展開されたプラグインを取得するフェイズと、設定ファイルに記述したプラグイン名と取得するフェイズで実施する。
ローカル環境に展開されたプラグインを取得するフェイズでは、プラグインマネージャがtmuxプラグインを展開する先の~/.tmux/pluginのディレクトリエントリから取得する。
# installed tmux plugin for LINE in `ls -l ~/.tmux/plugins/ | awk '$1 ~ /d/ {print $9 }'` do acutual=(${actual[@]} ${LINE}) done
設定ファイルに記述したプラグイン名と取得するフェイズでは、設定ファイルからプラグイン記述部から取得する。
プラグイン記述部は、set -g @plugin 'tmux-plugins/でで始まるので、正規表現でパターンマッチさせる。
COMMAND="set -g @plugin 'tmux-plugins/" # expect to install tmux plugin while read LINE do if [[ $LINE =~ ${COMMAND}(.*)\' ]]; then expect=(${BASH_REMATCH[1]} ${expect[@]}) fi done < ~/.tmux.conf
テスト項目と実施概要について下記にまとめる。
| テスト項目 | 実施概要 | 参考 |
|---|---|---|
| 設定ファイルのデプロイに成功している | make install の返り値が0である |
make install |
| 設定ファイルが正しい場所に展開されている | 「-e」演算子とreadlinkコマンドで場所を確認する | make test |
| vimプラグインのインストールが成功している | Vimプラグインのコマンドを実行してエラーメッセージがでないこと | check_vim_plugin |
| tmuxプラグインのインストールが成功していること | インストールされたTmuxプラグインが正しいかどうか | check_tmux_plugin |
CIサービスとの連携
Travis CIにアクセスして、リポジトリの連携を有効化する。 Travis CIの使い方については、他の方がわかりやすくまとめてあるのでそちらを参照。
作成した.travis.ymlは下記の通りである。
language: bash os: linux dist: bionic sudo: required before_install: - sudo apt-get update - sudo apt-get install git - sudo apt-get install make - sudo apt-get install vim - sudo apt-get install zsh script: - "make clean" - "make install" - "make test" - "tests/check_tmux_plugin" - "tests/check_vim_plugin"
上記の設定ファイルをdotfilesのトップディレクトリに格納し、GitHubに何かしらをプッシュすると自動でテストが走る。

おわりに
下記のテスト項目をTravis CIと連携させることで、作成したdotfilesが壊れていたということを減らせるだろう。
しかし、テスト項目もテスト環境もまだまだ甘く、見つけることのできないバグなどもある。 そのためにも、テスト項目自体も定期的にメンテナンスしていく必要があるだろう。
変更履歴
- 2019/12/22: 記事公開
- 2022/06/06: デザイン修正