【Go】promptuiでCLIツールを作ってbrewにリリースする
はじめに
SREとして日常的いろんなコマンドを実行することが多く、ターミナルとの付き合い時間が多いです。
自分がよくいろんなCLIツールを探したり、作業効率化を上がったりすることに楽しんでいます。
せっかくなので、今回は自分でもCLIツールを作ってみようと思いました。
本記事では、Go製のCLIツールを作るために、promptuiを使う予定です。
本記事のスコープ
- promptui基本の使い方を紹介する
- AWS各リージョンにあるEC2の情報、起動ステータス、インスタンスタイプ、想定の利用料金を表示するCLIツールを作る
- 作ったCLIツールをbrewにリリースする
- GitHub ActionsとGoreleaserを使って、リリースの自動化を行う
また、文字数が長くなるため、詳細なソースコードは割愛します。
promptuiの基本
promptuiは、Go製のコマンドラインツールを作るためのライブラリです。
主な機能は下記のようにとてもシンプルです:
- Prompt: ユーザーの入力を受け付ける
- Select: ユーザーの選択肢を受け付ける
- Confirm: ユーザーの確認を受け付ける
Prompt
公式の例を見てみましょう:
package main
import (
"errors"
"fmt"
"strconv"
"github.com/manifoldco/promptui"
)
func main() {
validate := func(input string) error {
_, err := strconv.ParseFloat(input, 64)
if err != nil {
return errors.New("Invalid number")
}
return nil
}
prompt := promptui.Prompt{
Label: "Number",
Validate: validate,
// パスワードの場合は、Mask: '*'を指定する
// Mask: '*',
}
result, err := prompt.Run()
if err != nil {
fmt.Printf("Prompt failed %v\n", err)
return
}
fmt.Printf("You choose %q\n", result)
}
実行結果
❯ go run main.go
Number: 111
You choose "111"
// 数字以外の入力をした場合
✗ Number: 111aa█
>> Invalid number
主な特徴としては、カスタムのバリデーションを行うことができる点です。
そしてリアルタイムで検証できるためユーザー体験が良いです。
Select
公式の例を見てみましょう:
package main
import (
"fmt"
"github.com/manifoldco/promptui"
)
func main() {
prompt := promptui.Select{
Label: "Select Day",
Items: []string{"Monday", "Tuesday", "Wednesday", "Thursday", "Friday",
"Saturday", "Sunday"},
}
_, result, err := prompt.Run()
if err != nil {
fmt.Printf("Prompt failed %v\n", err)
return
}
fmt.Printf("You choose %q\n", result)
}
実行結果
❯ go run main.go
Use the arrow keys to navigate: ↓ ↑ → ←
? Select Day:
▸ Monday
Tuesday
Wednesday
Thursday
Friday
// 選択した結果
✔ Sunday
You choose "Monday"
また、template機能も用意されていて、選択する度にリアルタイムで結果をプレイビューするなど複雑な実装も可能です。
package main
import (
"fmt"
"strings"
"github.com/manifoldco/promptui"
)
type sushi struct {
Name string
Ingredient string
Price int
}
func main() {
sushiList := []sushi{
{Name: "マグロ", Ingredient: "赤身マグロ", Price: 300},
{Name: "サーモン", Ingredient: "生サーモン", Price: 250},
{Name: "エビ", Ingredient: "茹でエビ", Price: 200},
{Name: "イカ", Ingredient: "新鮮なイカ", Price: 180},
{Name: "ウナギ", Ingredient: "蒲焼きウナギ", Price: 400},
{Name: "タマゴ", Ingredient: "玉子焼き", Price: 150},
{Name: "イクラ", Ingredient: "鮭の卵", Price: 350},
}
templates := &promptui.SelectTemplates{
Label: "{{ . }}?",
Active: "\U0001F363 {{ .Name | cyan }} ({{ .Price }}円)",
Inactive: " {{ .Name | cyan }} ({{ .Price }}円)",
Selected: "\U0001F363 {{ .Name | red | cyan }}",
Details: `
--------- 寿司の詳細 ----------
{{ "名前:" | faint }} {{ .Name }}
{{ "主な具材:" | faint }} {{ .Ingredient }}
{{ "価格:" | faint }} {{ .Price }}円`,
}
searcher := func(input string, index int) bool {
sushi := sushiList[index]
name := strings.Replace(strings.ToLower(sushi.Name), " ", "", -1)
input = strings.Replace(strings.ToLower(input), " ", "", -1)
return strings.Contains(name, input)
}
prompt := promptui.Select{
Label: "寿司を選んでください",
Items: sushiList,
Templates: templates,
Size: 4,
Searcher: searcher,
}
i, _, err := prompt.Run()
if err != nil {
fmt.Printf("プロンプトが失敗しました: %v\n", err)
return
}
fmt.Printf("あなたが選んだのは %s です。価格は %d円です。\n", sushiList[i].Name, sushiList[i].Price)
}
実行結果
❯ go run main.go
Use the arrow keys to navigate: ↓ ↑ → ← and / toggles search
寿司を選んでください?
🍣 マグロ (300円)
サーモン (250円)
エビ (200円)
↓ イカ (180円)
--------- 寿司の詳細 ----------
名前: マグロ
主な具材: 赤身マグロ
価格: 300円
// 選択した結果
🍣 マグロ
あなたが選んだのは マグロ です。価格は 300円です。
Confirm
Confirmは、ユーザーの確認を受け付けるための機能です。
一番シンプルなので、公式の例を見てみましょう:
package main
import (
"fmt"
"github.com/manifoldco/promptui"
)
func main() {
prompt := promptui.Prompt{
Label: "Delete Resource",
IsConfirm: true,
}
result, err := prompt.Run()
if err != nil {
fmt.Printf("Prompt failed %v\n", err)
return
}
fmt.Printf("You choose %q\n", result)
}
実行結果
❯ go run main.go
? Delete Resource? [y/N] █
// 選択した結果
Delete Resource: y
You choose "y"
自分のCLIツールを作る
今回はec2cat
というAWS各リージョンにあるEC2の情報、起動ステータス、インスタンスタイプ、想定の利用料金を表示するCLIツールを作ります。
実際に作ったものはこちらです。
Brewにリリースする
せっかく開発したCLIツールをbrewにリリースして、他の人にも使ってもらいましょう。
リポジトリの作成
まずは専用のリポジトリを作成しましょう。
命名規則はhomebrew-${CLIツール名}
とします。
今回はhomebrew-ec2cat
にしました。
CREDITSの作成
CREDITS
とは、ツールを配布する際に、そのツールを作成するために使用したライブラリの作者を記載するためのファイルです。
しかし一個ずつ記載するのは大変なので、gocredits
を使って自動生成するのが良いです。
brew install Songmu/tap/gocredits
gocredits . > CREDITS
GitHub Actionsの設定
続いて、リリースの自動化を行います。
goreleaser CLIのインストール
まずはgoreleaser CLIをインストールしましょう。
goreleaserは、Go製のCLIツールをリリースするためのツールです。
GitHub Actionsと合わせて使うことで、リリースの自動化を行うことができます。
brew install goreleaser
release.ymlの設定
続いて、.github/workflows/release.ymlを作成しましょう。
name: release
on:
push:
tags:
- "v[0-9]+.[0-9]+.[0-9]+"
jobs:
goreleaser:
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: '1.22'
- name: Run GoReleaser
uses: goreleaser/goreleaser-action@v6
with:
distribution: goreleaser
version: latest
args: release --clean
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAP_GITHUB_TOKEN: ${{ secrets.TAP_GITHUB_TOKEN }}
.goreleaser.ymlの設定
続いて、リポジトリのルートパスに.goreleaser.ymlを作成しましょう。
version: 2
project_name: ec2cat
env:
- GO111MODULE=on
before:
hooks:
- go mod tidy
builds:
- main: .
binary: ec2cat
ldflags: -s -w -X main.version={{.Version}} -X main.revision={{.ShortCommit}} -X main.date={{.Date}}
archives:
- format: tar.gz
name_template: >-
{{ .ProjectName }}_
{{- title .Os }}_
{{- if eq .Arch "amd64" }}x86_64
{{- else if eq .Arch "386" }}i386
{{- else }}{{ .Arch }}{{ end }}
files:
- LICENSE
- CREDITS
release:
prerelease: auto
changelog:
sort: asc
filters:
exclude:
- '^docs:'
- '^test:'
- Merge pull request
- Merge branch
brews:
- repository:
owner: the-exile-110 # GitHubのユーザー名
name: homebrew-ec2cat # リポジトリ名
token: "{{ .Env.TAP_GITHUB_TOKEN }}"
homepage: 'https://github.com/the-exile-110/homebrew-ec2cat' # リポジトリのホームページ
description: 'ec2cat is a command line tool to list and filter EC2 instances' # ポジトリの説明
license: "MIT"
トークンの発行
yaml編集の際に、TAP_GITHUB_TOKEN
を書いてあるため、実際にトークンを発行してGitHub Actionsのシークレットとして設定します。
こちらから発行できます。
- Token name:
ec2cat
- Expiration:
今回は30日
- Repository access:
Only select repositories
->homebrew-ec2cat
- Repository permissions
- Contents:
Read and write
- Metadata:
Read-only
- Contents:
シークレットの設定
発行したトークンをGitHub Actionsのシークレットとして設定します。
https://github.com/<ユーザー名>/<リポジトリ名>/settings/secrets/actions/new
にアクセスして、TAP_GITHUB_TOKEN
を設定します。
リリース・動作確認
全ての設定が完了したので、実際にタグを発行してリリースします。
git tag v0.0.1
git push origin v0.0.1
リリースが成功したら、brew
でインストールして動作確認します。
brew tap the-exile-110/ec2cat
brew install ec2cat
❯ ec2cat
✔ gogo
Selected AWS profile: gogo
✓ Retrieving all AWS regions...
Retrieved 17 regions
✓ Checking EC2 instances in each region...
Time taken to check regions: 2.084923s
Found 2 regions with EC2 instances
✔ View all regions
Retrieving EC2 instances from all regions...
+----------------+---------------+---------------------+---------------+---------+---------------------+---------------+-----------------------+------------+
| REGION | INSTANCE NAME | INSTANCE ID | INSTANCE TYPE | STATE | LAUNCH TIME | TOTAL RUNTIME | ESTIMATED HOURLY COST | TOTAL COST |
+----------------+---------------+---------------------+---------------+---------+---------------------+---------------+-----------------------+------------+
| us-east-1 | test-2 | i-062a75268f29f648d | t3.micro | running | 2024-10-06 08:06:42 | 0d 7h 7m | $0.0104 | $0.0741 |
+ +---------------+---------------------+---------------+---------+---------------------+---------------+-----------------------+------------+
| | test | i-0be81ad68d76a071e | t3.small | running | 2024-10-06 08:02:30 | 0d 7h 11m | $0.0884 | $0.6359 |
+----------------+---------------+---------------------+---------------+---------+---------------------+---------------+-----------------------+------------+
| ap-northeast-1 | test | i-08be43355bb37b8fb | t3.micro | running | 2024-10-06 07:04:01 | 0d 8h 10m | $0.0136 | $0.1111 |
+----------------+---------------+---------------------+---------------+---------+---------------------+---------------+-----------------------+------------+
Total:
Estimated Total Hourly Cost: $0.1124
Total Cost: $0.8211
おわりに
今回はpromptuiを使ってCLIツールを作ってbrewにリリースするまでの一連の流れを紹介しました。
promptuiは、シンプルながらも柔軟なカスタマイズが可能なので、ぜひ使ってみてください。
Discussion