目次

今回は自分なりのGo言語入門を書いていきたいと思います。初心者なので既存のフレームワークやコードを参考にします。

はじめに

Go言語は、Googleが開発しているプログラミング言語で主にCLIとの親和性、コードのシンプルさ、クロスコンパイル環境、バイナリ配布の簡易さ、実行速度などから人気があります。

ただ、現時点ではRubyやJavaなどに比べ機能面での不足はあると思います。

しかし、プログラムをGoで作成する意義は上記メリットから十分にあると私は思います。

Goのインストール

まず、Go言語のインストールです。Go言語を実行できるまでの手順は、他の言語に比べて複雑です。なぜなら、Shellの知識がある程度なければ難しいからです。しかし、Goの管理はCLIユーザーから見ると非常に明解で、私は好きです。

# まずPATHにGoをインストール
$ brew info go
$ brew install go --HEAD

# 次にGoで定義されている環境変数を設定します。
# これを`~/.${SHELL##*/}rc`に書いておくと便利です
if [ -x "`which go`" ]; then
    export GOPATH=$HOME/go
    export PATH=$PATH:$GOROOT/bin:$GOPATH/bin
fi

ここでは~/goに管理をまとめています。例えば、go getする場合には基本的にここにインストールされます。コードに書くimportもこの場所を参照します。

google/go-github

次に実際にコードを書いてみましょう。それをビルドし、CLIコマンドとしてのバイナリを作成してみることにします。ここではgo-githubを使います。これはGitHub APIのフレームワークみたいなものです。GitHubクライアントを作成する時に便利です。バイナリのファイル名はデフォルトではフォルダ名から設定されます。

main.go

// https://github.com/google/go-github/blob/master/examples/basicauth/main.go
package main

import (
	"bufio"
	"fmt"
	"os"
	"strings"
	"syscall"

	"github.com/google/go-github/github"
	"golang.org/x/crypto/ssh/terminal"
)

func main() {
	r := bufio.NewReader(os.Stdin)
	fmt.Print("GitHub Username: ")
	username, _ := r.ReadString('\n')

	fmt.Print("GitHub Password: ")
	bytePassword, _ := terminal.ReadPassword(int(syscall.Stdin))
	password := string(bytePassword)

	tp := github.BasicAuthTransport{
		Username: strings.TrimSpace(username),
		Password: strings.TrimSpace(password),
	}

	client := github.NewClient(tp.Client())
	user, _, err := client.Users.Get("")

	// Is this a two-factor auth error? If so, prompt for OTP and try again.
	if _, ok := err.(*github.TwoFactorAuthError); err != nil && ok {
		fmt.Print("\nGitHub OTP: ")
		otp, _ := r.ReadString('\n')
		tp.OTP = strings.TrimSpace(otp)
		user, _, err = client.Users.Get("")
	}

	if err != nil {
		fmt.Printf("\nerror: %v\n", err)
		return
	}

	fmt.Printf("\n%v\n", github.Stringify(user))
}

次にmain.goをビルドして、バイナリを作成してみます。

$ pwd
	~/go-github
$ go get github.com/google/go-github
$ go build 
$ ./go-github
	user : syui
	pass : ****
	2FA : ****
		{"api":"xxxx"}

CLIユーザーから見ると、CLIとの親和性があり、とても分かりやすいと思います。コード自体もそうですが、コマンドオプションやその他もそうです。すごく分かりやすいです。

urfave/cli

次にGo言語でコマンドラインツールを作成してみます。これについてはspf13/cobra, tcnksm/gcli, urfave/cliなどがあります。ここで使用するのはurfave/cliです。

$ mkdir test-cli
$ cd test-cli
$ go get github.com/urfave/cli

test-cli/main.go

package main

import (
  "os"
  "fmt"
  "sort"

  "github.com/urfave/cli"
)

func main() {
  app := cli.NewApp()

  app.Flags = []cli.Flag {
    cli.StringFlag{
      Name: "lang, l",
      Value: "english",
      Usage: "Language for the greeting",
    },
    cli.StringFlag{
      Name: "config, c",
      Usage: "Load configuration from `FILE`",
    },
  }

  // オプションコマンドの追加
  app.Commands = []cli.Command{
    {
      Name:    "complete",
      // 短縮の定義で"$ test-cli c"とかで実行できる
      Aliases: []string{"c"},
      // Help
      Usage:   "complete a task on the list",
      // 実行内容
      Action:  func(c *cli.Context) error {
        // 出力
    	fmt.Println("test!")
        return nil
      },
    },
    {
      Name:    "add",
      Aliases: []string{"a"},
      Usage:   "add a task to the list",
      Action:  func(c *cli.Context) error {
    	fmt.Println("test!")
        return nil
      },
    },
  }

  sort.Sort(cli.FlagsByName(app.Flags))
  sort.Sort(cli.CommandsByName(app.Commands))

  app.Run(os.Args)
}
$ go run main.go
	--help
$ go build
$ ./test-cli a
	test!

travis

クロスコンパイルやバイナリ配布(GitHub Releaseなど)については、Travis CIを使うと良いです。WindowsはAppVeyorです。

.travis.yaml

language: go

sudo: false

cache:
  directories:
  - node_modules

go:
- 1.2.x
- 1.3.x
- 1.4.2
- 1.5.x
- 1.6.x
- 1.7.x
- master

matrix:
  allow_failures:
  - go: master
  include:
  - go: 1.6.x
    os: osx
  - go: 1.7.x
    os: osx

before_script:
- go get github.com/urfave/gfmrun/... || true
- go get golang.org/x/tools/... || true
- if [ ! -f node_modules/.bin/markdown-toc ] ; then
    npm install markdown-toc ;
  fi

script:
- ./runtests gen
- ./runtests vet
- ./runtests test
- ./runtests gfmrun
- ./runtests toc

appveyor

appveyor.yml

version: "{build}"

os: Windows Server 2012 R2

clone_folder: c:\gopath\src\github.com\urfave\cli

environment:
  GOPATH: C:\gopath
  GOVERSION: 1.6
  PYTHON: C:\Python27-x64
  PYTHON_VERSION: 2.7.x
  PYTHON_ARCH: 64

install:
- set PATH=%GOPATH%\bin;C:\go\bin;%PATH%
- go version
- go env
- go get github.com/urfave/gfmrun/...
- go get -v -t ./...

build_script:
- python runtests vet
- python runtests test
- python runtests gfmrun

codecov

https://codecov.io/

.travis.yml

language: go

go:
  - 1.7

before_install:
  - go get -t -v ./...

script:
  - go test -race -coverprofile=coverage.txt -covermode=atomic

after_success:
  - bash <(curl -s https://codecov.io/bash)