👻

Goで開発途中のプライベートモジュールを参照する方法

2023/05/05に公開

💡 はじめに

先日インターンの業務で開発途中のプライベートモジュールを参照したいという場面に出会した。具体的には以下のようなことを実現したかった。

  • モジュールAとモジュールBがある
  • モジュールA内部でモジュールBを呼び出している
  • モジュールBで開発ブランチ(develop)を切って新しい機能を追加した
    • mainブランチにはマージしていない
  • モジュールAで開発ブランチ(develop)を切って、モジュールBの新機能を呼び出して動作を確認したい←やりたいこと

以下では、上記の実現方法と解説を行う。

🛠️ 方法

モジュールB: github.com/yagikota/private-repo-B
モジュールBのコミットハッシュ: 6f10de1
プライベートレポジトリ: github.com/yagikota
とした時

GOPRIVATE=github.com/yagikota  go get github.com/yagikota/private-repo-B@6f10de1

でモジュールAのgo.modにモジュールBを追加。

repo-A/go.mod
module github.com/yagikota/repo-A

go 1.20

+ require github.com/yagikota/private-repo-B v0.0.0-20230504111014-6f10de185850 // indirect
repo-A/go.sum
+ github.com/yagikota/private-repo-B v0.0.0-20230504111014-6f10de185850 h1:W8MOZqBFfCfX4KSuxiPC9meARqHASZC+18eJ5qNzfSY=
+ github.com/yagikota/private-repo-B v0.0.0-20230504111014-6f10de185850/go.mod h1:MzW6pqC7RzhR/iH8/MFBtoAn9FIlEEbo3fLYLXeqRAY=

さらに、go mod tidyでモジュールBが依存しているモジュールをgo.modgo.sumの整理。

repo-A/go.mod
module github.com/yagikota/repo-A

go 1.20

- require github.com/yagikota/private-repo-B v0.0.0-20230504111014-6f10de185850 // indirect
+ require github.com/yagikota/private-repo-B v0.0.0-20230504111014-6f10de185850
repo-A/go.sum
+ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+ github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
github.com/yagikota/private-repo-B v0.0.0-20230504111014-6f10de185850 h1:W8MOZqBFfCfX4KSuxiPC9meARqHASZC+18eJ5qNzfSY=
github.com/yagikota/private-repo-B v0.0.0-20230504111014-6f10de185850/go.mod h1:MzW6pqC7RzhR/iH8/MFBtoAn9FIlEEbo3fLYLXeqRAY=
+ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=

以上で、開発途中のプライベートモジュール(github.com/yagikota/private-repo-B)を別モジュール(github.com/yagikota/repo-A)参照することができるようになる。

🎓 解説

go getについて

そもそも、go getって何をしているのか?これを理解するには、以下の2つのサービスについて理解する必要がある。

proxy.golang.org(Module Mirror)

Goモジュールのダウンロード要求に対して、GitHubなどのVCSからではなくキャッシュされたモジュールを返す。

  • ダウンロードの高速化
  • 可用性の保証(GitHubが落ちていてもダウンロード可能)

といったメリットがある
環境変数はGOPROXY。デフォルトではGOPROXY="https://proxy.golang.org,direct"となっている。

sum.golang.org(Checksum Database)

モジュールの検証情報(checksum)を提供するためのサーバー。

  • モジュールの整合性の担保
  • 不変性の担保(追記型DBなので改竄されない)

といったメリットがある。
環境変数: GOSUMDB。デフォルトではGOSUMDB="sum.golang.org"となっている。

これらのサービスが、以下のように連携してgo getが動作している。

ref: https://jfrog.com/blog/why-goproxy-matters-and-which-to-pick/
これらのサービスは、一般に公開されているソースコードにしかアクセスでない。非公開モジュールに依存する場合は後述するGOPRIVATEまたはGONOPROXY環境変数を設定する必要がある。
すべてのモジュールをソースリポジトリから直接ダウンロードするようにgoコマンドを構成するには、GOPROXYdirectに設定する。

GOPRIVATEについて

https://go.dev/ref/mod#private-module-proxy-direct

プライベートモジュールのインストールの際には、環境変数GOPRIVATEの指定が必要。指定しない場合、以下のようなエラーが出る。

go get github.com/yagikota/private-repo-B@6f10de1
github.com/yagikota/private-repo-B@v0.0.0-20230504111014-6f10de185850: verifying module: github.com/yagikota/private-repo-B@v0.0.0-20230504111014-6f10de185850: reading https://sum.golang.org/lookup/github.com/yagikota/private-repo-!b@v0.0.0-20230504111014-6f10de185850: 404 Not Found
	server response:
	not found: github.com/yagikota/private-repo-B@v0.0.0-20230504111014-6f10de185850: invalid version: git ls-remote -q origin in /tmp/gopath/pkg/mod/cache/vcs/4463a39359f6875ac8c075bfecd48694efdb7232d6f021b58d8c47fb9d8af8d2: exit status 128:
		fatal: could not read Username for 'https://github.com': terminal prompts disabled
	Confirm the import path was entered correctly.
	If this is a private repository, see https://golang.org/doc/faq#git_https for additional information.

開発途中のモジュール参照方法について

方法としては以下の2つ。

  • コミットハッシュを指定して参照
    • チーム開発などでGitHubでの共有をしたい場合
  • replace directiveの使用
    • 開発途中のモジュールをGitHubに上げたくない場合
      • 例えば、GitHubに上げたくないけど新しいバージョンを触って欲しいときとかにzipで渡して手元で展開して…みたいなので使ったりするらしい

コミットハッシュを指定して参照

今回は、こちらの方法を採用。
検証レポジトリはこちら(Publicにしている)
https://github.com/yagikota/repo-A/pull/1

replace directiveの使用

https://go.dev/ref/mod#go-mod-file-replace

A replace directive replaces the contents of a specific version of a module, or all versions of a module, with contents found elsewhere. The replacement may be specified with either another module path and version, or a platform-specific file path.

今回の場合だと、レポジトリrepo-Aと同じディレクトリにprivate-repo-Bをcloseしておき、開発ブランチ(develop)にcheckoutした後、repo-A/go.modを以下のように変更。

repo-A/go.mod
module github.com/yagikota/repo-A

go 1.20

+ replace github.com/yagikota/private-repo-B => ../private-repo-B

その後、go mod tidyを実行。

repo-A/go.mod
module github.com/yagikota/repo-A

go 1.20

replace github.com/yagikota/private-repo-B => ../private-repo-B

+ require github.com/yagikota/private-repo-B v0.0.0-00010101000000-000000000000
repo-A/go.sum
+ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+ github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
+ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=

検証レポジトリはこちら(Publicにしている)
https://github.com/yagikota/repo-A/pull/2

📚 参考文献

Discussion