Go: module modeでのgo.mod, go.sumファイルを用いた依存ライブラリ管理、及びcontributionの方法&手順

PUBLISHED ON 2019-04-18 — GITHUB, GO, GO MODULES, GOLANG

Summary

Go1.13がリリースされてGo Modulesのmodule modeがデフォルトになってからの、go modコマンドを駆使した依存ライブラリ管理、及び第三者のOSSプロジェクトへのcontributionの方法&手順について

  • Module-awareモードに最適化した手順を把握・実践する
    • ライブラリの追加手順
    • ライブラリのバージョン更新手順(個別 or 全ライブラリ一括)
    • ライブラリの削除手順
  • Happy Contributing!!
  • 参考リンク(参考というか、答えは下記リンク先に全て書いてあります)

前提条件

$ go version
go version go1.12.4 darwin/amd64

# GO111MODULE=on して、常にModule-awareモードとなる状態で検証
$ export GO111MODULE=on

はじめに

Githubの”contributing ドキュメントについて

githubにプロジェクトを公開する際に、

  1. ルート
  2. docs
  3. .github

の3つの内いずれかのディレクトリに contributing* というパターン名(大文字でも可)が付いたファイルを置いておくと、pull-request作成時に「ここにガイドラインがあるよ」と通知してくれるようになります。慣例としてこのファイルは CONTRIBUTING.md という名前で用意されている事が多いです。

このファイルには主に環境構築・テストの実行方法・pull-requestの送り方 などを記載しますが、Goプロジェクトでは「最初に $GOPATH 下にソースを持ってくる」事が前提になっている手順をよく見かけます(自作プロジェクトでもそうしていました)。

GoプロジェクトにおけるCONTRIBUTING.mdはどうあるべきか?

Go ModulesのModule-awareモードがデフォルト挙動になるGo1.13以降では、ソースをcloneしてくるディレクトリは何処でも良い=必ずしも$GOPATH下にソースを持ってくる必要は無くなりますし、依存ライブラリの追加・削除・バージョン変更を行いたい時の流れも変わります。なので、 CONTRIBUTING.mdに書く内容もそれに合わせておくとcontributorに親切なガイドラインを提示できそうです。

引き続き$GOPATHベースでの開発を推奨するプロジェクトもあるかも知れませんし、内容や粒度はプロジェクトの運営方針や想定読者レベルによっても変わるとは思いますが、この記事ではModule-awareモードのレールに乗る前提で、CONTRIBUTING.mdに書くべき内容と、合わせてmodule modeでのGoプロジェクトにおけるライブラリ管理プラクティスを整理してみます。

1. module modeでのGo製プロジェクトの初期環境構築

想定手順

  1. cd 任意のディレクトリ
  2. git clone foo
  3. cd foo
  4. make

CONTRIBUTING.mdの冒頭は「自分のorganizationにforkしてgit clone」と書かれている事が多いですが、clone先のディレクトリは$GOPATHに拘らず何処でも良くなりましたよという事が伝われば、それこそ git clone 任意のディレクトリ と明記しておくだけでも良いと思います。

git clone後にmake(或いは make setup のような名前のターゲット)を叩けば依存ライブラリを取り込んでくれるようなmakeターゲットを用意しておくと初期の敷居を下げられそうです。

$ git clone foo
$ cd foo

# makeを叩く => go.sum の内容に則った依存ライブラリ取得実行
$ make  # 例えば go build
go: finding github.com/urfave/cli v1.20.0
go: finding github.com/aws/aws-sdk-go v1.17.14
:
go: downloading github.com/urfave/cli v1.20.0
go: downloading github.com/aws/aws-sdk-go v1.17.14
:
go: extracting github.com/urfave/cli v1.20.0
go: extracting github.com/aws/aws-sdk-go v1.17.14

例えば go build でもgo.modファイルの内容を元にした依存ライブラリの取り込みが動いてくれるので、go build をmakeのデフォルトターゲットとして用意しておくのも良さそうです。

2. module modeで依存ライブラリを追加したい時の手順

想定手順

  1. コードを編集・import文を追加
  2. go build or go run or go test を実行する(これでgo.mod, go.sumも最新化される)

Go Modulesでは、import文を追加・保存してgo buildすればそのタイミングで必要なライブラリ取得&go.modの更新が実行されます。「コードを書いてbuildを動かすという流れの中で(lazyに)依存ライブラリ取得が走ってくれる」というGo Modulesの配慮が伺える設計になっています。

// importを追加
import "github.com/k0kubun/pp"

  // 使用コードを追加
  pp.Println(c)
# go.mod, go.sum もこのタイミングで更新される
$ go build

# moduleの更新内容を確認
$ vim go.mod
module foo

require (
  :
  github.com/k0kubun/pp v3.0.1+incompatible
  :
)

+incompatible と出力されるのはどういう時か?

たまたま今回使わせてもらったppで +incompatible というsuffixがgo.modに出力されましたが、これは 「ライブラリがGo Modulesに未対応で、且つMAJORバージョンが2以上」の場合に発生します(が、開発への悪影響がある訳ではありません)

3. module modeでライブラリのMINORバージョン又はPATCHバージョン(或いはその両方)を更新する手順

想定手順

  1. go list -u -m PACKAGE - PACKAGEで利用可能な最新バージョンが存在するか確認
    • go list -u -m -versions PACKAGE | tr ' ' '\n' ← 利用可能な全バージョンを確認したい場合
  2. go get PACKAGE@VERSION - 指定バージョンを取得
    • go get foo@latest - 現在取得可能な最新バージョン
    • go get foo@v1.6.2 - 指定したバージョン
    • go get foo@e3702bed2 - 指定したcommit hash(のリビジョン)
    • go get foo@'<v1.6.2' - 指定した条件に合致する最新バージョン
    • go get foo@master - masterブランチの内容
  3. go build or go run or go test を実行する(これでgo.mod, go.sumも最新化される)

go getに代わるもう1つの手段として「go.mod のバージョン指定を直接編集する」事も可能なのですが、go.modに記述できるバージョン形式はPseudo-versions形式であり、go get foo@'<v1.6.2' のようなモジュールクエリの形式で書く事は出来ません。

モジュールクエリ形式でバージョンを指定してgo getを実行すると、条件にマッチして選択されたバージョンそのものがgo.modに反映されます。

## 実施例

# v1.19.12 という最新バージョンが存在している事が分かる
$ go list -u -m github.com/aws/aws-sdk-go
github.com/aws/aws-sdk-go v1.17.14 [v1.19.12]

# v1.18.x にマッチする最新バージョン、に更新してみる
$ go get github.com/aws/aws-sdk-go@'<v1.19'
go: finding github.com/aws/aws-sdk-go v1.18.6
go: downloading github.com/aws/aws-sdk-go v1.18.6
go: extracting github.com/aws/aws-sdk-go v1.18.6

# go.modの更新内容を確認
$ cat go.mod | grep aws-sdk-go
        github.com/aws/aws-sdk-go v1.18.6

4. 全てのライブラリを最新versionに更新する手順

想定手順

  1. go list -u -m all - 全依存ライブラリに更新可能バージョン(があればそれ)を付けて一覧表示・確認
  2. go get -u
  3. go build or go run or go test を実行する(これでgo.mod, go.sumも最新化される)

contributorがこれを行うケースはあまり無さそうではありますが、公式Wikiの Daily Workflow にも書かれている通りパッケージ指定なしでgo get -uを実行すると全依存ライブラリの最新のminor versionが、go get -u=patchを実行すると全依存ライブラリの最新のpatch versionが取得されます。

$ go get -u
# 既に最新状態のライブラリは latest と表示される
go: finding golang.org/x/sys latest
# 最新バージョンが別途存在したら、そのバージョンが表示される
go: finding github.com/aws/aws-sdk-go v1.19.11
:
:

# go get -u が完了すると、go.mod, go.sum がそれぞれ更新される(が、ライブラリの取得はまだ未実施状態)
# 取得はその後の go build や go test の際に行われる
$ go build

# 最新バージョンが別途存在したライブラリの取得処理が走る
go: downloading github.com/aws/aws-sdk-go v1.19.11
go: extracting github.com/aws/aws-sdk-go v1.19.11
:
:

5. 使わなくなったライブラリを削除する手順を書く

想定手順

  1. コードを編集・import文を削除
  2. go mod tidy
  3. go build or go run or go test を実行する(これでgo.mod, go.sumも最新化される)

go buildgo test はまだ取得していないライブラリの取得は自動でやってくれますが、未使用ライブラリの削除は自動ではやってくれません削除も含めてライブラリ依存関係を整理するのにgo mod tidyコマンドを実行します。

公式Wikiでは、依存関係を整理しても互換性が担保されている事を(直接・間接いずれの依存に関わらず)テストしたいなら go test all を実行すると良い、と紹介されていますが、全依存ライブラリのテストが実行されるのでもしやるなら開発PCのリソースに余裕がある時が良いです。

Thanks to