Golangでどこでもtwitterできるbinaryをつくる

2018/02/08 追記

記事中で紹介していたgo-bindataの作者がgithubアカウントを削除し、そのアカウントを別の誰かが取得したそうです。

善意でこの行為を行っているとは考えにくいので、go get -u github.com/jteeuwen/go-bindata/...は行うべきではないです。 記事執筆時点では、有志の方がforkしてくれているので、そちらを参照するのが良さそうです(mattnさんありがとうございます)

github.com

ドリコムアドベントカレンダー16日目

ドリコム Advent Calendar 2017 この記事はドリコムアドベントカレンダー の16日目です。
前日はgremitoさんの「オレオレDockerfileを作って、サーバーサイドの開発をやっている話。」です。

あなたは誰

先月ドリコム社に中途入社したid:miyachik です。サーバーサイド側のコード書いている人です。
主にRailsを書いて、たまにJavaScriptGolangをさわったりするぐらい。

twitterの話

みなさんtwitterしてますよね。昼夜問わず変な人がいっぱいいてとても有意義で無意義です。
そして、みなさん(おそらく)エンジニアだと思うので、terminal上でコマンドを叩いたときに、よくわからんエラーを吐いてエラったときの鬱憤をネットの海に叫びたい気持ちが多々あると思います。僕はあります。

gotwi

そんな衝動を解決するために、miyachik/gotwi というものを先日リリースしました。

github.com

いわゆるGo製のCLItwitterクライアントなんですが、現状だとつぶやことしかできません。(というかユースケースがそれしかありません)
プログラムを書いていて適当に愚痴りたいときに、ブラウザやクライアントに戻って呟いたりしちゃうと、うっかり変なつぶやきが目に止まって時間を取られてしまったりしまいがちですよね。
そんな人のために求められているのが、このgotwiです。つぶやく機能しかありません。(replyのshowぐらいは実装予定)
terminal上で動作できて、クロスコンパイル可能なのでどこにでもbinaryを配布することができます。
以前も似たようなCLIクライアントをThorを使ってRubyで実装していたのですが、アクセストークンの扱いや、成果物のポータビリティがいまいちだったりで、そのコードを書いたPC上ぐらいでしか使用していませんでした。
ところが、Goで書いたことによってアクセストークンをgo-bindataで埋め込みつつ、任意環境で実行可能なbinaryに吐き出すことが可能になったので、個人持ちのPCから任意のEC2、どこでも簡単に動かすことができます。
今回はその中でも便利だったgo-bindataを使った話をしたいと思います。

go-bindata

go-bindataは、任意のdataをビルドして、dataをbinaryに変換しGoのコードに埋め込みアクセスできるようなgoのソースを生成してくれるライブラリです。

Install

% go get -u github.com/mattn/go-bindata/...
% go-bindata -h

で、問題なければgo-bindataコマンドが使えるようになっています。

Build

以下のディレクトリ構成で作業をするとして

├── Makefile
├── README.md
├── main.go
├── settings.local.toml
└── settings.sample.toml

このsettings.local.tomlをbuildするbinaryに含めることによってアクセストークンなどの秘匿情報を実行binaryに含めてしまうことができます。
今回はtwitterAccess Tokenが必要になるので、下記を参考に自分のAccess Tokenを取得するようにしてください。(2017年12月時点)

Access tokens from apps.twitter.com — Twitter Developers

.tomlとは、yamlのような設定記述言語で、yamlより色々とシンプルな点や、Goのサポートが厚いことからよくGo界隈では使われています。

github.com

# settings.local.toml
ConsumerKey="YOUR TWITTER_CONSUMER_KEY"
ConsumerSecret="YOUR TWITTER_CONSUMER_SECRET"
AccessToken="YOUR TWITTER_ACCESS_TOKEN"
AccessSecret="YOUR TWITTER_ACCESS_SECRET"

これを

% go-bindata settings.local.toml

とすることで、settings.local.tomlの中身が圧縮してbinaryに変換され、アクセス可能なメソッドの生えているbindata.goが生成されていると思います。
あとは、以下のように、Asset("#{ファイル名}")とすることで、binaryに埋め込んだ文字列やその他のリソースを読み込むことが可能です。

func main() {
    data, err := Asset("settings.local.toml")
    if err != nil {
        panic(err)
    }
    fmt.Print(string(data))
}

debug時などに注意が必要なのは、data本体はbindata.goに含まれているので、実行時にbindata.goを含める必要があります。

% go run main.go bindata.go
ConsumerKey="YOUR TWITTER_CONSUMER_KEY"
ConsumerSecret="YOUR TWITTER_CONSUMER_SECRET"
AccessToken="YOUR TWITTER_ACCESS_TOKEN"
AccessSecret="YOUR TWITTER_ACCESS_SECRET"

gotwiでは、このようにしてアクセストークンなどをbinaryに埋め込んでいるので、あとは任意の環境にビルドしたbinaryを、実行したいサーバーに配置することで、どこでも実行することが可能になります。

gotwiのクロスコンパイル

様々な環境で動かすためには、build時に$GOOS,$GOARCHを指定する必要があります。
環境変数に指定できるものは下記を参考にしてください。

Installing Go from source - The Go Programming Language

指定なしでbuildをした場合は、build時の環境が参照されるようになります。
例えば、Linuxamd64な環境へのbuildは以下のようになります。

% GOOS=linux GOARCH=arm64 go build main.go bindata.go
=> 直下に gotwi が生成される
% ./gotwi "Hello!"
=> "Hello!"が投稿

その他にも、build時には任意のオプションをつけられるので、公式のドキュメントを参照すると良いです。

go - The Go Programming Language

注意点

  • このbinaryが流出してしまうと、それこそ誰でもアクセストークンの所有者として呟けてしまうので、管理には気をつけてください。
  • go-bindataでビルドしたbindata.goの時点では、Goのソースに圧縮されたbyte列が入っているだけなので、ちょっと頑張ればアクセストークンを復号できるんじゃないかと思います(それこそ無圧縮とかにすると簡単に)。なので.gitignoreしましょう。

まとめ

  • どこでも実行可能な自分が呟けるbinaryを吐き出せる、gotwiを作りました。
  • 使い始めて一ヶ月くらいですが、なかなか快適です。クロスコンパイル可能なbinaryを吐き出せるGoは雑なCLIツールを作るのに非常に良い
  • AWS LambdaのGoサポート の話もありますし、Golangは良い
  • twitterはほどほどに

明日は hayabusa333 さんの「Elixir環境構築にて暗号化でエラーにならないために」です。