シェルスクリプトのパッケージ管理ツール「shelp」をGo言語で作った
これです。
https://github.com/progrhyme/shelp/
前回の記事でもちらっと触れましたが、あれから2週間近く経って、自分が欲しい機能はもう一通り揃ったところです。
何ができるツールなの?
だいたいbasherと同じようなことができます。 basherの機能概要については以前にQiitaに書きましたが、多少の相違点もあるので、改めて書き下します。
shelpの主な機能:
- 任意のGitリポジトリをgit cloneして
$SHELP_ROOT
下に配置し、「パッケージ」として扱います include
というシェル関数で任意のパッケージの任意のシェルスクリプトを読み込むことができます- YAMLの設定ファイルに取得したいパッケージリストを書いてまとめてインストールしたり、リストにないパッケージをまとめて削除できます
- Bash, Zsh, fish shellをサポート。他のPOSIXコンパチなシェルでもたぶん動く
basherとの差異:
- shelpにあってbasherにない:
- basherにあってshelpにない:
- シェルのcompletion(コマンド補完)機能
- インストール時にマニュアルがあればsymlink作成
- インストール時に依存パッケージがあれば追加インストール(再帰的にできるっぽい)
利用イメージ
シェルスクリプトなツールを試したい
GitHubに上がっているツールであれば、下のコマンドで取得できます:
shelp install <account>/<repository>
source
して使うタイプのシェルスクリプトなら、 include <repository> path/to/script
とやれば使えます。
リポジトリの直下か、 bin/
以下に実行ファイルが置かれていたなら、もうPATHが通っているはずです。
要らなくなったら、次のコマンドでパッケージを削除できます:
shelp remove <repository>
もちろん、実行ファイルへのsymlinkがあれば一緒に削除してくれます。
自分で作った実行ファイルにさっとPATHを通す
そんなときにも便利です。
$EDITOR new-tool.sh # エディタでファイルを新規作成 chmod +x new-tool.sh shelp link .
などとやると、カレントディレクトリを仮初めのパッケージと見なして、 $SHELP_ROOT/packages/
にsymlinkを作って、new-tool.shにPATHを通してくれます。
shelp link
の対象ディレクトリはGitリポジトリでなくても大丈夫です。
自分が使うシェルスクリプトツールを管理したい
がっつり管理したい人向けのガイドです。
ここでは、私の利用例を紹介します。
今の私の環境の ~/.shelp/config.yml
はこんな風になっています:
packages: - from: progrhyme/git-wraps - from: progrhyme/bash-git-push-carefully - from: progrhyme/toolbox - from: progrhyme/gcloud-prompt - from: progrhyme/sh-pathctl - from: b4b4r07/enhancd
このファイルをdotfilesリポジトリに入れて、 ~/.shelp/config.yml
にsymlinkするようにしたので、環境が変わっても shelp bundle
ですぐに必要なパッケージ一式をインストールできます。
上のリストの内、 gcloud-prompt
, sh-pathctl
, enhancd
については、シェル起動時に ~/.bashrc
や ~/.zshrc
の中でshelpの include
関数によってパッケージ内のシェルスクリプトを読み込むようにしています。*1
あと、シェル起動時は1日1回 shelp bundle
と shelp upgrade
(パッケージをまとめて更新)を実行するようにしました。
インストール方法
3つ方法がありますので、お好みでどうぞ。
- Homebrew (Linuxbrew) でインストール
- GitHub Releasesからバイナリ取得
go get github.com/progrhyme/shelp
brewの場合のコマンドは下の通り:
brew tap progrhyme/tap brew install shelp
シェルでshelpを有効にするには、以下を ~/.bashrc
, ~/.zshrc
, ~/.config/fish/config.fish
等に追記して下さい:
# fish以外 eval "$(shelp init -)" # fishの場合 shelp init - | source
このコマンドでやっていることは下の3つです:
$SHELP_ROOT
環境変数の設定$SHELP_ROOT/bin
を$PATH
に追加include
関数の定義
ドキュメント
Material for MkDocsを使って、Netlifyで公開してみました。
拙い英語ですが、細かいユースケースや設定ファイルの書式も載せていますので、shelpを使う場合は役立つと思います。
shelp自体の紹介は以上です。
以下では、開発に至った経緯を述べます。
やや内省的な、ともすればポエムっぽい内容になりますので、興味のない方は読み飛ばしてください。
開発の経緯
どうしてまたシェルスクリプトのパッケージ管理ツールを、しかもGo言語で作ったのか、というところを述べます。
前回の記事でも触れたように、basherを使い出す前はclenvという自作ツールを使っていました。
こちらはbasherと同じくシェルスクリプト製で、実行ファイルはBashで書いています。
前回の記事に書いたように、basherに出会う前からclenvのイケてないところは色々と気になっていました。
作り直すことも考えていたのですが、その際には別にシェルスクリプトで書く必要はないよなぁと思っていました。
むしろ、シェルスクリプトで書くことに限界を感じていたので、これ以上はシェルスクリプトで書くべきでない、とまで思っていました。
シェルスクリプトの管理ツールをシェルスクリプトで書く必要はない
basher然りclenv然りですが、色んなシェルに対応しているのに、ほとんどBashで書かれています。
rbenvやanyenvもそうですね。
各々のシェル環境で解釈される rbenv init -
のようなコマンドの出力結果は各種シェルに対応させる必要がありますが、コマンド実行するものであれば、POSIX準拠じゃなくてもよいわけですね。(もちろんその環境で動くものでないといけませんが)
…であれば、別にシェルスクリプトである必要さえないわけです。
シェルスクリプトによる開発でつらいところ
POSIXシェルよりBashは幾分楽だとはいえ、そもそも高級プログラミング言語に比べて機能が貧弱なので、ぶっちゃけ最初からつらいです。
特に自分がつらみや限界を感じていた点は以下です:
- マルチプラットフォーム対応。シェルスクリプトならポータブルかというと全然そんなことはない。特にmacOSでのコマンドの差異がつらい
- ライブラリがない
たぶん、自分のためだけの OR 特定の環境だけで動けばいいツールを書くなら、もっとずっと楽だと思います。
シェルスクリプト以外で書かれたツールの例
けっこう色々あります。
シェルスクリプトのパッケージ管理ツールっぽいものだと、最近Rust製のrossmacarthur/sheldonというのを見つけました。
READMEをざっと見たところ、Zshのプラグインマネージャーであるzplugに機能がよく似ていることに気づきました。
他の例としては、 https://dotfiles.github.io/ で紹介されているdotfilesの管理ツールが挙げられます。
シェルスクリプト製のものもありますが、PythonやGo, Rustで書かれたものもあります。
直近で見つけたものだと、 https://starship.rs/ というRust製のプロンプト設定ツールもありますね。
basherへの不満(?)
特に実用上で不便はなかったのですが、強いて挙げれば shelp bundle
みたいな機能がないことと、macOSで basher link
が動かなかったことぐらいでしょうか。
まあ、できそうだから作ってみようか、ぐらいのノリもありました。
そしてGoに至る
プログラム言語を選ぶにあたっては、ランタイムのことを考えたくないので、クロスプラットフォームでバイナリを作れるものがいいだろうなと思いました。
Rustも考えたのですが、それなりに思い通りにコードが書けるようになるまでけっこう長く修行しないといけなさそうだったので、諦めてGoにスイッチしました。
Goも2〜3年ブランクがあり、簡単なCLIを作ったぐらいの経験しかありませんでしたが、言語仕様が小さいのでRustよりはすぐにコードが書けるようになりそうな見込みがありました。
2年ぶりぐらいにRustに再入門してたけど、言語仕様と標準ライブラリの海に溺れかけた。
— progrhyme (@progrhyme) 2020年5月31日
ちょっとさくっと動くツールを作りたいだけなので、Goでいいのではという気持ちになっている。
学習コストには大きな差がある気がする。
遡ってみたところ、このツイートの数時間後に、shelpに1st commitしていました。
Go言語で開発したことによって、上に挙げたようなシェルスクリプトでつらかったところは全て解消されました。
Goの標準ライブラリを使っていれば、プラットフォーム間のコマンド実装の差異など気にしなくて済みます。
また、サードパーティー製も含めれば無数といっていいほど多くの外部ライブラリを利用できるので、コマンドライン引数の解釈やYAML対応などの機能も比較的簡単に実装していくことができました。
その内、Rustもまた再入門したい気もしますが、当面はshelpのメンテや追加開発を含めてGoを使っていくことになる気がします。
まとめ
以上、シェルスクリプトのパッケージ管理ツール「shelp」の紹介と、Go言語で開発した経緯について述べました。
よろしければお試しください。
気に入ったらスターを押してもらえると、励みになります。
脚注
*1:1つのURLで上手く抜き出せませんでしたが、設定例としては次の2コミットが参考になるかもしれません: progrhyme/dotfiles@f4a6b30, progrhyme/dotfiles@881af91