Ansibleのベストプラクティスに従うとmain.ymlだらけになって、エディタで開くとどれがどれだかわからなくなるんだが

`ansible-galaxy init --offline <role名>`を実行するとベストプラクティスに従ったroleのスケルトンを作ってくれて、それを編集するようにしていけば良いのだが、とにかくmain.ymlばっかりなのであっちこっちのファイルを編集しながらの作業になると、エディタ上にファイル名しか表示されない場合どれが編集したいファイルなのか探すのに時間が掛かったりする。
あとファイル数の割にディレクトリが多くて、開くのにファイラーであっちこっち行かないと行かないのも面倒。

なので1つのディレクトリ内にルールに従ったファイル名でファイルを作って、自作のbashスクリプトを通すと、ベストプラクティスに従ったディレクトリ構成になるようにリンクしてくれるようにしてみた。

元ファイル名のルールはこんな感じ。

* '%'で始まるファイルだけをリンクの対象にする。
* ファイル名が'%foo'か'%foo.yml'ならば'roles/foo/tasks/main.yml'にリンクを作る。
* ファイル名が'%foo%bar.yml'ならば'roles/foo/tasks/bar.yml'にリンクを作る。
* ファイル名が'%foo%baz%'か'%foo%baz%.yml'ならば'roles/foo/baz/main.yml'にリンクを作る。
* ファイル名が'%foo%baz%qux'ならば'roles/foo/baz/qux'にリンクを作る。

つまり、このようにリンクが作られる。

* %common.yml → roles/common/tasks/main.yml
* %common%subtask.yml → roles/common/tasks/subtask.yml
* %common%defaults%.yml → roles/common/defaults/main.yml
* %common%vars%server.yml → roles/common/vars/server.yml
* %common%templates%server.conf.j2 → roles/common/templates/server.conf.j2

編集する機会が多くなる各ディレクトリのmain.ymlやtasksディレクトリにリンクされる各ファイルは、元ファイル名をかなり省略できるようにしてある。

また、ver.20150420にて1つの.ymlファイル内に複数の.ymlファイルの内容を記述することができるようになった。
つまり次のような内容で%common.ymlを作ったとすると、
（commonはrole名の一例であり、%ssh.ymlだの%nginx.ymlだの任意である）

```
--- # tasks/main.yml
(YAMLで記述した内容1)
--- # defaults/main.yml
(YAMLで記述した内容2)
--- # vars/main.yml
(YAMLで記述した内容3)
```

* 中身が(YAMLで記述した内容1)である roles/common/tasks/main.yml
* 中身が(YAMLで記述した内容2)である roles/common/defaults/main.yml
* 中身が(YAMLで記述した内容3)である roles/common/vars/main.yml

の3ファイルが生成される。

# 更新履歴

* ver.20150412
 * 最初のバージョン

* ver.20150420
 * （バグ修正）ファイルパスにスペースなどを含む場合に正しく処理されないのを修正
 * （バグ修正）cygwin環境下で新規に作ったディレクトリのパーミッションに絡んでファイルのコピーなどが失敗することがあるのを修正
 * （機能追加）.ymlファイルの中に、区切り記号の後にコメントで各roleからのファイルパスを入れておくと、.ymlファイルを分割してそのファイルパスにファイルを作る機能の追加（-Dオプションで抑止）
 * （機能追加）ベストプラクティス構成から逆にリンク元ファイルを作る機能の追加（-gオプション）
 * （仕様変更）-aオプション指定時に、リンク対象のファイル以外が-s, -H, -cどのオプションを指定しているかでそれぞれシンボリックリンク、ハードリンク、コピーされるようにした。ただしディレクトリはハードリンクできないので-Hの時はコピーを行い、新設した-Sではシンボリックリンクにする

* ver.20150630
 * （バグ修正）ゴミが入っていたせいでヘルプを表示しようとすると終了しないのを修正
 * （機能追加）-gオプション指定時はfromディレクトリ直下にある.で始まるディレクトリを対象にしないようにした。代わりに-Gオプションを新設し、fromディレクトリ直下にある.で始まるディレクトリを対象にするようにした

* ver.20150907
 * （バグ修正）-gあるいは-Gオプション指定時に、改行で終わらないファイルを対象にした際に改行を追加するように修正
 * （バグ修正）.ymlファイルをまとめる、あるいは分割する機能が働く際に、出力先ファイルに対して追記になっていたのを一度空にしてから追記を行うように修正
 * （仕様変更）-tオプションは省略できないようにした（オプションなしで実行した場合にヘルプが表示されるように）
 * （仕様変更）%(role名)%(ファイル名)のリンク先を、ファイル名が.yml以外の場合は(role名)/tasksではなく(role名)に

# 実行の仕方とオプションの説明

[Gistに上げた](https://gist.github.com/yunano/e9aaaddaf2f33b25ae76)のでダウンロードして利用できる。
Linux以外にもOS XやWindowsのcygwinでも使えるように作っているはずだ。
ただし、私はOS Xには詳しくないので挙動について突っ込まれてもあまりわからないかもしれない。

このbashスクリプトを ansible_best_practice.sh という名前で保存したとすると、例えば以下で実行できる。

```
$ bash ansible_best_practice.sh -t .
```

上記の通り実行した場合、暗黙的に次のオプションで実行されたのと同じになる。

```
$ bash ansible_best_practice.sh -m % -f . -t . -s -p
```

各オプションの意味は以下の通りである。


* -m <区切り文字>
リンク対象のファイルであることを示す先頭の文字、およびディレクトリの区切り文字を指定する。
デフォルト値は % である。

* -f <リンク元ディレクトリ>
リンク元となるファイルの置かれたディレクトリを指定する。
デフォルト値は . （カレントディレクトリ）である。

* -t <リンク先ディレクトリ>
リンク先のディレクトリを指定する。
（ver.20150907より）
デフォルト値はなくなったので必ず指定する必要がある。

* -s
リンク先のディレクトリにシンボリックリンクを作成する。

* -p
リンク元/先ディレクトリはプロジェクトのルートディレクトリ（rolesなどより1つ上のディレクトリ）であるものとして扱う。

## その他のオプション

その他にも以下のオプションがある。

* -H
シンボリックリンクではなく、ハードリンクを作成する。（ver.20150420より）-aオプションとともに指定した場合、リンク対象のファイル以外のすべてのディレクトリはコピーする。
Windows上だとシンボリックリンクを作ってもあまり幸せにはならないだろうから（cygwin外ではエディタでテキストとして開けないっぽい）、cygwinで実行する場合は-Hや-cを使った方が良いと思われる。

* -S
（ver.20150420より）
シンボリックリンクではなく、ハードリンクを作成する。-aオプションとともに指定した場合、リンク対象のファイル以外のすべてのディレクトリはシンボリックリンクにする。

* -c
シンボリックリンクを作るのではなく、コピーを行う。

* -r
リンク元/先ディレクトリはrolesディレクトリであるものとして扱う。
つまり、リンクはこのように作られる。
 * %common.yml → common/tasks/main.yml
 * %common%subtask.yml → common/tasks/subtask.yml
 * %common%defaults%.yml → common/defaults/main.yml
 * %common%vars%server.yml → common/vars/server.yml
 * %common%templates%server.conf.j2 → common/templates/server.conf.j2

* -i
リンク元/先ディレクトリは個別のroleのディレクトリであるものとして扱う。
つまり、リンクはこのように作られる。
 * %.yml → tasks/main.yml
 * %subtask.yml → tasks/subtask.yml
 * %defaults%.yml → defaults/main.yml
 * %vars%server.yml → vars/server.yml
 * %templates%server.conf.j2 → templates/server.conf.j2

* -a
リンク対象のファイル以外のすべてのディレクトリやファイルもリンク先ディレクトリにコピーする。
リンク元ディレクトリとリンク先ディレクトリが同じ場合はこのオプションは無視される。

* -d
リンク先ディレクトリ内を掃除してからリンク作成を始める。
-aとともに指定すればリンク先ディレクトリ内の全ファイルと全ディレクトリ、
-rか-iとともに指定すればリンク先ディレクトリ内の全ディレクトリ、
いずれでもなければリンク先ディレクトリ内のrolesディレクトリを削除してからリンク作成を始めることになる。

* -g
（ver.20150420より）
通常とは逆処理を行う。つまり、逆にベストプラクティス構成からroleごと等にまとめられたファイルを作る。
-Dが指定されていない場合は、拡張子が.ymlのファイルはroleごとに1つのファイルに集められる。
そのファイル名は「<区切り文字><role名>.yml」（-iオプションが指定されている場合は「<区切り文字>.yml」）となり、-gオプションが指定されていない時に分割できるように「--- # <元の各roleからのファイルパス>」でファイル内が区切られる。
なお、-gが指定されている時は-dも暗黙的に指定されているものとし、toディレクトリ内にある<区切り文字>で始まるファイルがすべて削除される。
（ver.20150630より）fromディレクトリ直下の.で始まるディレクトリは対象としない。

* -G
（ver.20150630より）
-gとほぼ同じだが、fromディレクトリ直下の.で始まるディレクトリは対象とする。

* -D
（ver.20150420より）
拡張子が.ymlのファイル内に「--- # <各roleからのファイルパス>」がある場合に、.ymlファイルを分割してそのファイルパスにファイルを作る機能を抑止する。

* -v
詳細な出力を行う。
ちなみにこのオプションを指定しない場合、処理が成功した時には全く何も表示されない。

* -h
ヘルプを表示する。

# 利用例

ロールを作る。

```
$ ansible-galaxy init --offline roles/role1
$ ansible-galaxy init --offline roles/role2
```

-gオプションを使ってロールごとにファイルをまとめる。
README.mdは.ymlではないので別ファイルになる。

```
$ bash ansible_best_practice.sh -grc -f roles -t gather
$ ls gather/
%role1%README.md  %role1.yml  %role2%README.md  %role2.yml
```

まとめたファイルを編集後、元のファイルに反映する。

```
$ bash ansible_best_practice.sh -rc -t roles -f gather
```

# 注意事項

このスクリプトはMITライセンスの元に自由に使用して良く、同時に無保証である。
このスクリプトを実行して発生したいかなる損害も、私は一切の責任を負うことはない。

例えば-tオプションでシステム上重要なディレクトリを指定し、さらに-aオプションと-dオプションを指定してrootユーザで実行すれば消したらまずいファイルがごっそり削除されるだろうが、それについて私は責任を取ったりはしない。