GoでCLIを作る時のテンプレートリポジトリを作った
初めに
普段Goを使ってCLIなどを作ることが多く、毎度CI/CDをセットアップしたり、go get
でライブラリを導入するが面倒なのでテンプレートリポジトリgo-cli-templateを用意しました。
基本的に自分用ですが、ちょっと手を入れればみなさんも使えるので軽く紹介していきます。
デフォルトセット
GitHub Actions(以降GA)を使って以下を設定してあります。
- golangci-lint
- test
- binary release
golangci-lint
はGAがあるので、それをそのまま使っています。
使う人の好みもあるので.golangci.yaml
はあえて設置していないです。各自好きに置いてくださいという感じです。
binary releaseはgoreleaser
のGAを使っています。タグをつけてpushするとReleases
にCHANGELOG、ハッシュ、各OSのバイナリファイルがアップロードされます。便利ですね。
上記に加えて、spf13/cobra
のベース実装(version
コマンドのみ)も入っています。
個人的にこれまでflag
パッケージを使っていましたが、より簡単にオプションやサブコマンドを実装できるようにcobra
を選びました。
そのためパッケージ構成もcobra
推奨の形(サブコマンドはcmd
配下に置く)になっています。
依存パッケージがかなり多いライブラリですが、docker
といった有名なOSSでの採用実績があるため、そちら重視しました。
使い方
- リポジトリの
Use this tempalte
から新しいリポジトリを作成します。
- 新しいリポジトリを
git clone
してmake init
を実行して、リポジトリ名を変更します。diff --git a/.goreleaser.yml b/.goreleaser.yml index 856c0f0..a993558 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -1,4 +1,4 @@ -project_name: go-cli-template +project_name: test-cli env: - GO111MODULE=on before: @@ -8,5 +8,5 @@ builds: - main: . ldflags: - -s -w - - -X github.com/skanehira/go-cli-template/cmd.Version={{.Version}} - - -X github.com/skanehira/go-cli-template/cmd.Revision={{.ShortCommit}} + - -X github.com/skanehira/test-cli/cmd.Version={{.Version}} + - -X github.com/skanehira/test-cli/cmd.Revision={{.ShortCommit}} diff --git a/Makefile b/Makefile index dbfc09c..12d08c5 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ .PHONY: init init: ifeq ($(shell uname -s),Darwin) - @grep -r -l go-cli-template * .goreleaser.yml | xargs sed -i "" "s/go-cli-template/$$(basename `git rev-parse --show-toplevel`)/" + @grep -r -l test-cli * .goreleaser.yml | xargs sed -i "" "s/go-cli-template/$$(basename `git rev-parse --show-toplevel`)/" else - @grep -r -l go-cli-template * .goreleaser.yml | xargs sed -i "s/go-cli-template/$$(basename `git rev-parse --show-toplevel`)/" + @grep -r -l test-cli * .goreleaser.yml | xargs sed -i "s/go-cli-template/$$(basename `git rev-parse --show-toplevel`)/" endif diff --git a/cmd/root.go b/cmd/root.go index 27e0f98..b9feb58 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -8,7 +8,7 @@ import ( ) var rootCmd = &cobra.Command{ - Use: "go-cli-template", + Use: "test-cli", } func exitError(msg interface{}) { diff --git a/go.mod b/go.mod index c586f1b..2598da5 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module github.com/skanehira/go-cli-template +module github.com/skanehira/test-cli go 1.16 diff --git a/main.go b/main.go index 900b7d9..ee06357 100644 --- a/main.go +++ b/main.go @@ -1,6 +1,6 @@ package main -import "github.com/skanehira/go-cli-template/cmd" +import "github.com/skanehira/test-cli/cmd" func main() { cmd.Execute()
- 試しにサブコマンドを1つ実装してみます。
diff --git a/cmd/hello.go b/cmd/hello.go new file mode 100644 index 0000000..fa5e640 --- /dev/null +++ b/cmd/hello.go @@ -0,0 +1,24 @@ +package cmd + +import ( + "fmt" + + "github.com/spf13/cobra" +) + +func Hello(name string) string { + return "hello " + name +} + +var helloCmd = &cobra.Command{ + Use: "hello", + Run: func(cmd *cobra.Command, args []string) { + if len(args) > 0 { + fmt.Println(Hello(args[0])) + } + }, +} + +func init() { + rootCmd.AddCommand(helloCmd) +}
- テストも書いてみます。
diff --git a/cmd/hello_test.go b/cmd/hello_test.go new file mode 100644 index 0000000..8842509 --- /dev/null +++ b/cmd/hello_test.go @@ -0,0 +1,26 @@ +package cmd + +import "testing" + +func TestHello(t *testing.T) { + tests := []struct { + in string + want string + }{ + { + in: "gorilla", + want: "hello gorilla", + }, + { + in: "cat", + want: "hello cat", + }, + } + + for _, tt := range tests { + got := Hello(tt.in) + if tt.want != got { + t.Errorf("unexpected result. want=%s, got=%s", tt.want, got) + } + } +}
- コミットしてpushするとCIが走ります。ちゃんとテストとlinterが通ることを確認します。
-
v0.0.1
タグをつけてバイナリをリリースします。
- 試しにバイナリをダウンロードして使ってみます。
MacbookPro13% curl -LO https://github.com/skanehira/test-cli/releases/download/v0.0.1/test-cli_0.0.1_Darwin_arm64.tar.gz % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 642 100 642 0 0 2051 0 --:--:-- --:--:-- --:--:-- 2051 100 1152k 100 1152k 0 0 1776k 0 --:--:-- --:--:-- --:--:-- 9852k MacbookPro13% tar xvf test-cli_0.0.1_Darwin_arm64.tar.gz x LICENSE x test-cli MacbookPro13% ./test-cli version Version: 0.0.1 Revision: 3c302bf OS: darwin Arch: arm64 MacbookPro13% ./test-cli hello gorilla hello gorilla MacbookPro13% ./test-cli hello cat hello cat MacbookPro13%
簡単ではありますが、以上がサブコマンド追加からリリースまでの一連の流れの説明となります。
これでだいぶ手間が省けると思います。
forkして使う時
sed
などを使ってskanehira
を自分のユーザー名に置換してください。
あとは使い方に書いてあるとおりになります。お試しください。
最後に
CI/CDについて説明の記事は多くありますが、テンプレートを用意してさぁ使ってみてという記事が観測範囲内ではなかったので書いてみました。
個人的にいったんこれで満足していますが、使っていくうちに不満が出てきて改善するかもしれません。
こうするともっとよいよ、というのがあればぜひコメントください。お待ちしています。
Discussion
とても参考になる記事ありがとうございます!
GitHub ActionsのGoReleaserの設定を参考にさせていただいたのですが、ローカルでの実行だとちゃんと作成されるChagelogが、GitHub Actions経由だと最新コミットしか登録されませんでした。
調べたところ
actions/checkout@v2
でfetch-depth: 0
を指定していないのが原因のようです。fetch-depth: 0
を指定したところ、前回のTagより後のコミットで作成されるようになりました。なんと…
これは知りませんでした
ありがとうございます
修正しておきます