はじめに
こんにちは。DMM.comラボ プラットフォーム開発部エンジニアの松下 (@_kentaro_m) です。DMMのサービス全体で利用される基盤システムの開発を担当しています。
最近、個人的に興味を持ったGo言語の学習の一環として、社内情報共有ツールになっているConfluenceを便利に使うためのCLIツール開発に挑戦したので、本記事ではその学習の流れを紹介したいと思います。
これからGoを学びたいと考えているエンジニアの参考になれば幸いです。
目次
Goの学習をはじめた動機
普段はNode.jsをメインで開発しており、Goはまったく触る機会がありませんでした。社内で開催されたGoの勉強会のレポートを見たり、ブラウザプッシュ通知の開発チームでプロダクトに採用された話を聞いたりして興味を持ち、個人で勉強するようになりました。
Goの学習の流れ
Goの学習は以下のように進めました。
- A Tour of Goを行う
- ローカル開発環境を構築する
- CLIツール開発に挑戦する
1. A Tour of Goを行う
A Tour of Goはブラウザ上でGoの文法が学べる公式のチュートリアルです。CLIツールを開発するにあたって、事前にひととおり行いました。
基本的にはスライドを見つつ、サンプルコードを動かして学んでいきます。各章の終わりには演習問題があり、それを解いて理解度を測れるようになっていました。私は演習問題で何度かハマったので、もう何周かして理解を深めたいです。
2. ローカル開発環境を構築する
ローカル開発環境の構築には次の3つを行いました。
- homebrewでのGoのインストール
-
GOPATH
を設定 - エディタに開発補助パッケージをインストール
homebrewでのGoのインストール
Macをお使いであれば、homebrewでGoをインストールするのが一番早いと思います。
$ brew install go
GOPATHを設定
~/.bash_profile
にGoのワークスペース(GOPATH
)の指定と、GOPATH配下のbin
ディレクトリをPATHに追加します。
export GOPATH=$HOME/go export PATH=$PATH:$GOPATH/bin
設定ファイルを読み込み直します。
$ source ~/.bash_profile
エディタに開発補助パッケージをインストール
お使いのエディタに合わせて、必要なものを入れてください。
3. CLIツール開発に挑戦する
Goの学習でCLIツール開発に挑戦した理由は以下の3つです。
- Goではクロスコンパイルをサポートしており、各種プラットフォーム向けにCLIツールを配布しやすいこと
- Go向けのCLIツール開発を支援するフレームワークが存在すること
- GoでCLIツール開発に挑戦する人が多く、情報量が多いこと
CLIツールの開発を始める
何を開発するか
DMMでは社内情報共有のためにAtlassianのConfluenceが多くの部署で利用されています。ドキュメントをリッチテキストで書けるため、誰でも簡単に扱えるのが特徴です。
一方でドキュメントを書きながら、装飾もしなくてはいけないため、Markdownを好むエンジニアにとっては使いづらい一面1もありました。
そこでMarkdownをConfluenceの独自記法であるWikiマークアップに変換する仕組みを作り、Markdown文書をConfluenceへ容易に投稿できる環境を実現することにしました。
作ったもの
MarkdownをConfluence Wikiマークアップに変換するmd2conflというCLIツールを作りました。
ファイルまたは標準入力から読み込んだMarkdownの文書をConfluenceのWikiマークアップに変換して、標準出力に出力します。
$ md2confl sample.md h1. Section Some _Markdown_ text. * list1 * list2 * list3 *strong text* -strikethrough text- [Example Domain|http://www.example.com/] !https://blog.golang.org/gopher/header.jpg! $ echo "# Hello, world" | md2confl h1. Hello, world
Macをお使いであれば、homebrewからインストールが可能です。その他のプラットフォーム向けにもGitHub Releaseにバイナリが公開されています。
$ brew tap kentaro-m/md2confl $ brew install md2confl
CLIツールをリリースするまでの流れ
次にCLIツールをリリースするまでに行った3つの作業を紹介したいと思います。
- CLIツールのフレームワークで雛形を作成
- Markdownパーサーのカスタムレンダラの開発
- Circle CIでリリースの仕組みを準備
CLIツールのフレームワークで雛形を作成
GoではCLIツールを簡単に素早く開発するためのフレームワークがあります。今回はKubernetesやDockerでも採用されているspf13/cobraを使用しました。
数ステップでCLIツールの雛形を作成することができます。
cobraをインストールします($GOPATH/bin
にインストールされる)。
$ go get github.com/spf13/cobra/cobra
cobra init <アプリケーション名>
で雛形が作成されます。
$ cobra init github.com/kentaro-m/sampleApp $ cd $GOPATH/src/github.com/kentaro-m/sampleApp $ go run main.go Your Cobra application is ready at ~/go/src/github.com/kentaro-m/sampleApp Give it a try by going there and running `go run main.go`. Add commands to it by running `cobra add [cmdname]`.
cobra add
コマンドでサブコマンドが追加されます。
$ cobra add show show created at ~/go/src/github.com/kentaro-m/sampleApp/cmd/show.go $ go run main.go A longer description that spans multiple lines and likely contains examples and usage of using your application. For example: Cobra is a CLI library for Go that empowers applications. This application is a tool to generate the needed files to quickly create a Cobra application. Usage: sampleApp [command] Available Commands: help Help about any command show A brief description of your command Flags: --config string config file (default is $HOME/.sampleApp.yaml) -h, --help help for sampleApp -t, --toggle Help message for toggle Use "sampleApp [command] --help" for more information about a command. $ go run main.go show show called
この時点でのディレクトリは以下のとおりです。
. ├── LICENSE ├── cmd │ ├── root.go │ └── show.go // 追加したサブコマンド └── main.go
あとはcmd/show.go
に必要な処理を追加すれば、オリジナルのCLIツールが完成します。
今回作成したCLIツールは、この雛形をベースに開発していきました。
package cmd import ( "fmt" "github.com/spf13/cobra" ) // showCmd represents the show command var showCmd = &cobra.Command{ Use: "show", Short: "A brief description of your command", Long: `A longer description that spans multiple lines and likely contains examples and usage of using your command. For example: Cobra is a CLI library for Go that empowers applications. This application is a tool to generate the needed files to quickly create a Cobra application.`, Run: func(cmd *cobra.Command, args []string) { // ここに処理を書く fmt.Println("show called") }, } func init() { RootCmd.AddCommand(showCmd) }
Markdownパーサーのカスタムレンダラの開発
Go製のマークダウンパーサー github.com
Confluence Wikiマークアップを出力するBlackfridayカスタムレンダラ github.com
MarkdownのパースとConfluence Wikiマークアップへの変換はrussross/blackfridayを使用しました。このライブラリはデフォルトではMarkdownをHTMLに変換するようになっていますが、カスタムレンダラを使用することでLaTeXなどの他言語の出力が可能になります。
md2conflを開発するにあたり、Confluence Wikiマークアップを出力するレンダラを作成しました。正しい出力を得るためにマークダウンの各要素ごとにユニットテストを作成し、出力を確認しながら開発を進める非常に愚直な作業でした。
// マークダウンの各要素ごとにユニットテストを作成 func TestHeading(t *testing.T) { // マークダウン形式のテキスト (入力値) input := ` # Section hello, world. ` // Confluece Wikiマークアップのテキスト (期待値) expected := `h1. Section hello, world. ` // Confluece Wikiマークアップ変換後のテキスト (出力結果) output := string(bfconfluence.Run([]byte(input))) // 出力結果と期待値を比較 if output != expected { t.Errorf("got:%v\nwant:%v", output, expected) } }
Circle CIでリリースの仕組みを準備
クロスコンパイルを実施してバイナリを生成 github.com
GitHub Releaseのページ作成及びバイナリをアップロード github.com
バイナリを配布するためにCircle CIとgox、ghrでリリースの仕組みを準備しました。最終的に以下の構成のconfig.yml
ができあがりました。
Gitでv.X.Y.Z
のバージョンタグを付与して、リモートにプッシュしたタイミングでCircle CIがクロスコンパイルを行い、リリースページにバイナリをアップロードします。
これでCLIツールを配布できるようになりました。
version: 2 jobs: build: docker: - image: circleci/golang:1.10 working_directory: /go/src/github.com/kentaro-m/md2confl steps: - checkout - run: name: Install dependencies command: go get -v -t -d ./... - run: name: Run linters command: | go get github.com/golang/lint/golint golint ./... go vet ./... deploy: docker: - image: circleci/golang:1.10 working_directory: /go/src/github.com/kentaro-m/md2confl steps: - checkout - run: name: Deploy the project command: | go get -v -t -d ./... go get github.com/inconshreveable/mousetrap go get github.com/mitchellh/gox go get github.com/tcnksm/ghr # クロスコンパイル・バイナリ生成 gox -output "pkg/{{.OS}}_{{.Arch}}_{{.Dir}}" # GitHub Releaseにバイナリ公開 # repo権限を付与したトークンを環境変数に登録しておく ghr -t $GITHUB_TOKEN -u $CIRCLE_PROJECT_USERNAME -r $CIRCLE_PROJECT_REPONAME --replace `git describe --abbrev=0 --tags` pkg/ workflows: version: 2 build: jobs: - build: filters: branches: only: /.*/ deploy: jobs: - deploy: filters: branches: ignore: /.*/ tags: # タグが付与されたときのみジョブが実行される only: /v[0-9]+\.[0-9]+\.[0-9]+/
まとめ
普段の業務で使うプログラミング言語やツールは、比較検討を重ねて選定していきます。Goの学習に関しては、個人的な勉強なので純粋に楽しそうという理由で触りました。結果的にCLIツールを開発してリリースするところまででき、今はさらに深くGoを学びたいという気持ちになっています。
概算になりますが、学習時間を簡単に紹介したいと思います。
平日帰宅後に1時間くらい時間をとってコツコツと進めて、Goの学習開始からリリースまでおよそ30時間でした。 最初に作るものを決めて、それを作るためには何を勉強すべきかという点を考えながらやったので、学習効率は良かったと思います。
今回はCLIツールを完成させてリリースすることに重きを置いていたため、エラーハンドリングやテストの書き方などGoの作法が分からないまま、おざなりに進めてしまった部分がありました。今後はGoらしいコードを書くために勉強を続けていきたいと思います。
皆さんも何か作るものを1つ決めて、Goの学習を始めてみてはいかがでしょうか。
私たちと一緒にDMMサービスを支えるプラットフォームを作りませんか
プラットフォーム開発部で一緒に働くエンジニアを募集しています。興味がある方は下記リンクより詳細をご確認ください。
-
プラグインを使用することでMarkdownの文書を投稿することは可能↩