Ark's Blog

数学とか競プロとかCTFとか参加記とか備忘録とか

ようこそ

GolangでCLIをつくったので知見メモ

GoでCLIをつくったので、そのとき得た知見をメモします。

github.com

簡単に言うと、docker container cpのaliasで、コンテナ内のパスに対しても補完が効くようにしたコマンドです。

これはなに

ローカルとDockerコンテナ間でファイルやディレクトリをコピーするdocker container cpというコマンドがあります。 使い方はSSH経由でコピーするscpコマンドとほとんど同じです。

ところで、Dockerコマンドを快適に使うために補完機能があります。めっちゃ便利です。

これをインストールするとdocker container cpにも補完が効くようになるのですが、コンテナ内のパスに関しては補完が効きません(2019/09/09 現在)。

$ docker container cp hoge:<TAB><TAB>

とやっても何も補完が出ません。

逆にscpだと、SSHの設定が適切であればリモートのパスに対しても補完が効きます。そこで、docker container cpでもリモート(コンテナ)のパスに対して補完が効くようになると便利〜ということで、このCLIをつくりました。

メモ

Goでまともに何かをつくった経験がなかったので、色々と得た知見を書いていきます。

Go: go1ブランチ

Goのバイナリを

$ go get github.com/user/repo

みたいな感じでインストールすることが多いと思います。

When checking out or updating a package, get looks for a branch or tag that matches the locally installed version of Go. The most important rule is that if the local installation is running version "go1", get searches for a branch or tag named "go1". If no such version exists it retrieves the default branch of the package. get - The Go Programming Language

go getを叩いたとき

  • go1ブランチ(またはタグ)があればそこを見に行く。
  • なければ、デフォルトブランチを見に行く。

という挙動をするようです。

そのため、バージョンを切りたいタイミングでgo1ブランチにデプロイ(?)するような開発フローにしました。

その部分はTravis CIを使って自動デプロイを実現しています。GitHub Pages用のデプロイ機能を流用しています。

Go: Build Constraints

GoにはBuild Constraintsという機能があります。

これはbuild時のメタ的な定数(OSやビルドタグなど)によって、ファイル単位でbuild対象を変更する機能です。static ifみたいな構文レベルの分岐機能はないようです(もしあれば教えてください)。

debug中はlogを取りたいけどrelease版ではlogを出したくなかったので、その分岐のためにBuild Constraintsを利用しました。

こんな感じで実現していて、go build -tags=debugのようにタグを付けたときだけlogをとってくれるようにしました。

Go: バージョン情報の埋め込み

現状の仕様ではgo getでdcpをインストールした後、--versionを付けて実行すると

$ dcp --version
dcp version v1.0.0

と出力されます。これの実現手段について書きます。

git管理しているのでgit tagを使用している場合は

  • git describe --abbrev=0 --tags

で今のバージョンを取得できます。

バージョンが上がるたびにソースコード中にバージョン番号を書き込むのは、面倒なのでしたくないです。build時に

$ go build -ldflags "-X main.VERSION=$(git describe --abbrev=0 --tags)"

とすれば、変数の値を直接書き換えられます。

ただ、go getでインストールさせるときにいちいちldflagsのオプションを付けさせるのは、気持ち悪い。 特にオプションを付けさせることなくバイナリにバージョン情報の入ったものがインストールできる仕組みがほしかったです。

そこで、CI/CD中に直接ソースコードを書き換えてgo1ブランチにデプロイするようにしました。

結構アグレッシブな方法だけど、現状のGoの機能の範疇では割となしではない気がしてきた。

Bash: bash completionのコロンの扱い

これは、bash completionを書いたことある人向けの情報です。

bash completionでは、:$COMP_WORDBREAKSの中に入っていて特殊な扱いになっています。

たとえば、${COMP_WORDS[COMP_CWORD]}: のときに :hoge のような候補を出力すると、変な補完になってしまいます。

これは

  • _get_comp_words_by_ref -n :
  • __ltrim_colon_completions

を使うといい感じにできます。

詳しくは

を見てください。

今回つくるdcpではコンテナ名とパスのdelimiterとしてコロンが使われるので、この辺りの機能を使いました。