運用効率化のためにツール開発＆自動アップデートの仕組みを作った話

# この記事を読む前に

この記事の内容と、過去に書いた記事「[ひとりDevOpsのすすめ](https://qiita.com/happy_packet/items/98e63879a3baed3c8a76)」の内容を整理して、社内で自動化に対するプロパガンダのために作ったスライドをGithubで公開しました。

[行き詰まらないための運用自動化.pdf](https://github.com/iranika/Documents/blob/master/docs/%E8%A1%8C%E3%81%8D%E8%A9%B0%E3%81%BE%E3%82%89%E3%81%AA%E3%81%84%E3%81%9F%E3%82%81%E3%81%AE%E9%81%8B%E7%94%A8%E8%87%AA%E5%8B%95%E5%8C%96.pdf)
※最後のスライド「DevOps文化を作る」はまだ書けてません。そのうち書きます。

スライドは非エンジニアな偉い人にもわかるようざっくりした感じになっています。
より詳しいことが知りたい場合に、この記事を参考にしてください。

# 注記
半年くらいかけたプロジェクトなので長文です。
たぶん、下記のような人向けの記事だと思います。

- ツール開発(または保守)してるけど、バージョンアップ時の再配布が手作業なので、自動で配布するようにしたい(本記事のメインディッシュ)
- 現在の運用の作業を自動化していきたいけど、アプローチ方法がよくわからないので実例を探している(開発スタイルの話)
- Bashスクリプトなら書き慣れてるけど、ツールのユーザーがほとんどWindows(8以降)だし匙を投げたい(なぜPowerShell?とGitbashの話)

運用改善に興味のある暇な人や、要約を読んで面白いと思った人は本編を読んでも面白いと思うかもしれません。

# やったことの要約
独自コマンドレットを作成＆自動配布する仕組みを作った。
結果として個人的にすごく便利なDevOps環境が作れた。

# ナレッジの要約

こんな感じの知見を得られた

- PowerShellのプロファイルに一日一回だけgit pullするのスクリプトを書いて、ツールのデイリーオートアップデート機能を実装。ツールの配布問題が解消される。
- PowerShellのモジュール暗黙インポートを利用して、独自コマンドレットをツールとして配布。PowerShellはHelpの記述やコマンドラインパーサ(param)が強力で、フロント(CLI)の実装が簡単なので開発が捗る。特にHelpにサンプルコードを書いておけば、
  ``` ps
  man command-let
  ```
  で実行例やら色々確認できるので、ユーザからどう使うかの質問に「manに使い方書いてあるから」と一蹴できてしまう。サポートの負荷が減らせる。
- Windowsでgitを使うために入れたgitbashが、opensslやscpなどのLinuxライクなコマンドが使えるようにしてくれたおかげでシェル芸が捗った。開発速度Up。

# 本編
## はじめに
協業していた別部署のサーバエンジニアの同僚とランチに行ったときのこと。

同僚「最近、WEB申請業務の案件が増えて忙しいんだよね」
私「そういえば、Pythonで申請業務の半自動化ツールのプロトタイプ作ってたけど、動作不安定だし保守が面倒なので配布はしてなかったなぁ。いる？」
同僚「なにそれ、ほしい」
私「でも君のPCにPython入ってなかったでしょ。自分で環境構築できる？環境変数のPathとかの設定が必要な感じだよ？一応、検証サーバに立てたGitlabでツール管理しているから、バグフィックスした最新ツールはgit pullすれば同期できるけど、gitの使い方わかる？」
同僚「なんかよくわからんけど面倒だなぁ。Windows PCで環境構築とかツール配布の手間がないやつ作れないの？」
私「そんな無茶……待てよ……できるかもしれない」
私「とりあえずプロトタイプ作ってみるから、試験導入という名のデバッグには付き合ってもらうぞ」
同僚「私にできるのは、バグ報告くらいだけどな」
--貴重なデバッガーを手に入れた--

これが事の発端でした。
当時、「Unixという考え方」を読み返していたことと「PowerShell実践ガイドブック　クロスプラットフォーム対応の次世代シェルを徹底解説」という本を読んでいたおかげで、仕組みを思いつくことができました。

## 思いついた仕組み
### gitリポジトリの構成
百聞は一見にしかず。コードも実際に動いているのを確認するほうが理解しやすいです。
ということでGithubに解説用のテンプレートを作りました。

https://github.com/iranika/WindowsPowerShell

導入の仕方は、Readmeの通りです。

### 仕組みの解説
まずはPowerShellのモジュールとプロファイルについての知識が必要になります。
詳細は[guitarrapcさんのこの記事](http://tech.guitarrapc.com/entry/2013/12/03/014013#3-%E3%83%A2%E3%82%B8%E3%83%A5%E3%83%BC%E3%83%AB%E3%83%A1%E3%83%B3%E3%83%90%E3%83%BC%E3%81%AE%E5%AE%9F%E8%A1%8C-%E3%81%A8-%E6%9A%97%E9%BB%99%E7%9A%84%E3%81%AA%E3%83%A2%E3%82%B8%E3%83%A5%E3%83%BC%E3%83%AB%E3%81%AE%E8%AA%AD%E3%81%BF%E8%BE%BC%E3%81%BF)がとても参考になります。
※guitarrapcさんのPowerShell実践ガイドブックはPowershell界隈では数少ない良書なので、初学者におすすめです。
（6.0ベースで書かれているのが、個人的にポイント高い）

今回の仕組みで肝になるのは、以下の２点。

- プロファイルに記述したスクリプトはPowerShellを起動するたびに実行される
- PowerShell 3.0以降のモジュールは明示的なモジュールのインポートをしなくても適切なパスに配置さえすれば暗黙的に読み込まれる

プロファイルに各モジュールのリモートリポジトリをpullする処理を書いておけば、Powershell起動時(≒ツールを使う時)にツールを最新にしてくれます。
プロファイル自体もリモートリポジトリからpullする処理を書いておくと、例えば「〇〇モジュールがない場合はgit cloneする」処理をプロファイルに追記してリモートリポジトリにアップすることで、ユーザーがPowershellを起動した時に追記した処理(git clone)が実行されます。
こうすることで、ツール配布がすごく簡単になります。

ただ、起動時に毎回PullするとPowerShellの起動が遅くなってしまうので、今回は１日１回だけPullするように実装しています。
また、モジュール毎にリポジトリを用意する方がユーザー別に配布するモジュールを決めたりと自由度が高くて便利ですが、新規モジュール追加時にプロファイルに必ずgit clone書かないといけないのが面倒なので、今回はプロファイルのリポジトリで全てのモジュールを管理しています。
これでプロファイルは、プロファイルのリポジトリをpullするだけで新規モジュールも追加されます。

##開発のスタイル
既にPythonで書いていたコードをPowerShellで書き直す際に、開発スタイルについても考えはじめました。

###大まかなイメージ
だいたいこんな感じ。

![iEdit1.png](https://qiita-image-store.s3.amazonaws.com/0/223008/e10ce836-cf2c-3acd-dd15-2b11948cae80.png)


ひとつのことを上手くやる感じの関数を沢山作っていく短期プロジェクト。
それらを組み合わせてユーザーが簡単に使えるようにするコマンドレットを作る中期プロジェクト。
最終的に全行程を自動化するための長期プロジェクト。
この3つのストリームで開発を進めます。

例えば業務の一連の処理が書かれたフローチャートがあったとして、
- 短期プロジェクトで小さな機能を沢山作る
- 中期プロジェクトで小さな機能を組み合わせて、フローチャートでいうところの処理(プロセス)を作る。
- 長期プロジェクトで処理を組み合わせて、フロー全体を作る

というように各プロジェクトは位置づけがされています。

短期プロジェクトで作るのは小さな機能なので開発速度が非常に速いです。一部の機能は先行導入して実運用の処理で試験的に使ってもらえば、直ぐに効率化に貢献できるのもいいメリットだなと思いました。既に現場で動いているコードは、成果を偉い人にアピールしやすくていい武器になります。

中期プロジェクトは、既にある小さな機能を組み合わせて行くだけなので、開発が結構早いです。
ここでは、小さな機能群をラップしてユーザーが使いやすいコマンドとして実装することで、UXを向上させて「いいね！」を貰うのが目的です。
今までの処理を置き換えるツールとして完成して、フローの一部がだんだんと自動化されていくので達成感がありモチベーションも上がります。
自動化は楽しい！！(動けば)

長期プロジェクトはフロー全体という最終的な成果物を作るためなので、長い道のりです。
中期の成果物を組み合わせて巨大なシステムを作る感じになるので、Hinemosやkompiraなどジョブフロー管理のシステム導入も検討すべきかなと思います。
ただ、コストパフォーマンスや変更リスクなどを考慮して、フロー全体を置き換えるのが本当に望ましいのかよく考える必要があります。
無理をして全自動化すると運用が破綻することもあり得るので、「コンピュータが得意なこと、人間が得意なこと」をよく考えて判断しましょう。
今回は、おいおい考えるということで全自動化の判断を先送りにしました。

といった具合に手探りながら、開発を進めて行きました。
多少泥臭い感じの開発ですが、結果はいい感じなので良かったなと思います。

### なぜPowershell？
個人的には、Pythonとかのほうが書き慣れていますが、PowerShellを選んだ理由は下記です。

- コマンドラインパーサー(param)が簡素でインターフェースの設計がしやすい←これが一番の理由
- コメントベースでヘルプが作成でき、man(Get-Help)で確認できる。関数内でドキュメント保守ができるのは非常に楽
- .NET Frameworkの資産が使える(やろうと思えばML.NETで機械学習もできるかも！？)
- BDDのテストフレームワークがある(pester)
- 変数にマルチバイト文字が使える

paramは非常に簡素に書けますが、型指定などもできて非常に便利です。
コメントベースのヘルプは、ユーザーからコマンドレットの操作方法を聞かれたときに

**「ヘルプにサンプルと仕様が書いてあるから、`man <コマンドレット> `で確認して」**

と一蹴できて便利です。
ユーザーもわからなければ、manする習慣がついてお互いにWin-Winです。ドキュメントを参照する文化は素晴らしい。

変数にマルチバイト文字が使えることについては、賛否両論あると思います。
ただ、CSV等の実データのカラム名とスクリプト内での変数名は可読性を良くするために一致させておきたいので、
CSVのヘッダで日本語を使われているときなどは、`$csv."カラムA"`みたいに使うことがあります。

### gitbashについて
Windowsでgitを使えるようにしようと思うと、もれなくGitbashがついてくると思います。
これが意外と便利で、場合によってはbashスクリプトが流用できたりします。
個人的には、PowerShellを採用しましたがgitbashを使って同様の仕組みを作るのもありだと思います。

# 余談
本当に余談なので飛ばして結構です。
## 本当はやりたかったけど、泣く泣く諦めたこと
本当は、PowerShell Core 6.0基準でモジュールとか作りたかったです。
というのもPowerShell Core 6.0から、内部の文字コードがUTF-8 **BOM付き**からUTF-8 BOM無しになったので、文字コードの処理で手を焼くことが減る(Go製の自作ツール等をUTF-8 BOM無しで統一しているので)。
それにMacやLinux向けに配布する際に、全てのファイルをBOM無しに書き換える必要もないですし。

ただ、今回は導入のスムーズさを考えてWindowsPowershell5.xをサポートすることにしました。
Windows8以降ならすぐさま導入できますし、Win7はもうすぐ滅びるので…



## FAQ
**Q. PowerShell依存なのでMacとかで使えない仕組みですよね？**
A. PowerShell Core6.0はMacでも使えるので、WindowsPowerShell依存なコードでない限り動きます。
まぁBash等にもProfileは存在するので、好きなシェルでPowerShellで実装されている機能を置き換えるのも一つの手です。
 個人的にはPowerShellのparamやヘルプが便利なので、可能ならpwshをインストールします。