Goで自作コマンドを作成し、homebrewで公開する

## 概要
今回はコマンドラインツールを作成し、homebrewでインストールできるように公開した時の手順をまとめました。

本記事では

- コマンドラインツールの実装、アップロード
- `Formula`を作成し、専用repositoryにアップロードする
- 実際にリモートからコマンドをインストールする
 
の三手順で書いていきます。


ちなみに作成したものはこちらです。goで実装しました。
https://github.com/kcwebapply/imemo

(注)本記事は[homebrew本家](https://github.com/Homebrew)への登録申請手順についてではなく、「個人レポジトリにて `brew tap` 経由で公開する手順」についての記事となります。


## Goでコマンドラインツールを作成する
コマンドアプリを実装するに当たって、以下のライブラリを使用させていただきました。
https://github.com/urfave/cli
`main()`に、例えば以下のような感じで実装します。

```go
package main

import(
   "github.com/codegangsta/cli"
)

func main(){
    app := cli.NewApp()
    app.Name = appName
	app.Usage = "github.com/kcwebapply/imemo"
	app.Version = version

	// コマンドの引数に対して、Usageやエイリアス、対応する処理を記述します。 .
	app.Commands = []cli.Command{
		{
			Name:    "all", // コマンド名
			Aliases: []string{"a"}, // エイリアス一覧
			Usage:   "View saved memo.", //Usage
			Action:  commands.GetAllMemo, // コマンド実施時に実行されるメソッド
		}
    }

   app.Run(os.Args)

}

```

実際にアプリケーションをビルドして、helpを表示します。

```terminal
> ./imemo --help
NAME:
   imemo - github.com/kcwebapply/imemo

USAGE:
   imemo [global options] command [command options] [arguments...]

VERSION:
   1.0.1

COMMANDS:
     all, a     View saved memo.
     save, s    Save memo.
     delete, d  Delete memo that specified by id.
     edit, e    Edit memo that specified by id.
     clear, c   clear (delete) all memo data.
     help, h    Shows a list of commands or help for one command

GLOBAL OPTIONS:
   --dryrun, -d   for dry run
   --help, -h     show help
   --version, -v  print the version
```
このように、自動で `--help` optionのような出力をしてくれるようになります。
後は`Action` で指定したメソッドの中に実装を行なっていくだけです。
一旦、簡単なコマンドが作成できました。

## releaseタグを作成する
作成したコマンドをgithubにアップロードして、`release`タグを作成します。

`release`タグを作成するとソースコードを圧縮したtarballが生成されます。
この後のFormula作成時に使用します。


## Formulaを作成する
Formulaを作成します。
Fomula用のファイルには、ユーザーが実際にコマンドをインストールする際のビルド手順を記述します。
`brew create　${ソースコードのurl}` でテンプレートを作成できます。

```terminal
$ brew create https://github.com/kcwebapply/imemo/archive/1.0.1.tar.gz
```


```ruby:imemo.rb
require "formula"

class Imemo < Formula
  desc "Save memo easily on terminal"
  # ホームページ情報。なんでも大丈夫です。
  homepage "https://github.com/kcwebapply/imemo"
  # ソースコード(tarball)の置き場所。
  url "https://github.com/kcwebapply/imemo/archive/1.0.1.tar.gz"
  # shasum コマンド　または brew fetch ${formula名} --build-from-source で算出される値を記述する。
  sha256 "3fba4ce91b8e2c46bcd499846862984feea17a4d23a30c08376fa66389302e33"
  # 依存する他のFormulaを記述します。goと、パッケージ管理ツールのdepを記述します。
  depends_on "dep" => :build
  depends_on "go" => :build

  # install手順を記述する。
  def install
    print buildpath
    ENV["GOPATH"] = buildpath
    imemo_path = buildpath/"src/github.com/kcwebapply/imemo/"
    imemo_path.install buildpath.children

    # ソースコードのbuildPathに移動した後、depコマンドで依存パッケージを取得し、buildを行う。
    cd imemo_path do
      system "dep", "ensure", "-vendor-only"
      system "go", "build"
      bin.install "imemo"
    end
  end
  # 任意。テスト処理を記述する。
  test do
    assert_match "memo saved!", shell_output("#{bin}/imemo save \"learning math\" ")
    assert_match "1 learning math", shell_output("#{bin}/imemo all ")
    assert_match "", shell_output("#{bin}/imemo delete 1")
    assert_match "", shell_output("#{bin}/imemo all ")
  end
end
```

#### sha256の値

```ruby
sha256 "3fba4ce91b8e2c46bcd499846862984feea17a4d23a30c08376fa66389302e33"
```
こちらのsha256の値についてですが、releaseタグのversionを変更した際に、再度発行する必要があります。

圧縮ファイルから生成する場合は

```terminal
$ shasum -a 256 imemo-${version}.tar.gz
```
すでにformulaを上げた状態から新たに生成する場合は

```terminal
$ brew fetch ${repository名} --build-from-source 
```
で生成できます。

## Formulaを公開する
Formula用のrepositoryを作成します。
名前は、`homebrew-${パッケージのrepository名}` で作成します。

https://github.com/kcwebapply/homebrew-imemo

ここまでで公開手順は完了です。

## インストール
実際に外部からインストールできるようになっているか確認します。
自分のrepositoryをtapし、インストールします。

```terminal
$ brew tap kcwebapply/imemo
$ brew install imemo
```

こちらで、無事にインストールできれば無事に公開できたことになります。


## まとめ
今回はサクッとhomebrewのコマンドを公開する方法を書きました。
基本的にやることは、
- ソースコードを上げて`release`する
- `Formula` を記述する
の２点です。


## [宣伝]今回作ったもの
最後にちょっとだけ紹介させてくださいmm 
ターミナルでメモができるコマンドを作りました。
ちょっとしたことをメモしたい時向けです。


- __メモ一覧を表示する__

```terminal
$ imemo all 
----------------------------------------------------------------------------------
| 1| playing tennis with Mike on Next  Tuesday                                   |
| 2| meeting at 13:30                                                            |
----------------------------------------------------------------------------------
```
- __メモを追加する__

```
$ imemo save "meeting at 13:30"
----------------------------------------------------------------------------------
| 2| meeting at 13:30                                                            |
----------------------------------------------------------------------------------
memo saved!
```

- __メモを消す__

```
$ imemo d 2
----------------------------------------------------------------------------------
| 2| meeting at 13:30                                                            |
----------------------------------------------------------------------------------
memo deleted!
```

もう少し修正を入れたら、また他の記事で紹介させていただこうと思いますmm