2021/11/21

[golang] go.mod の pseudo-version の調べ方がわからん

わからんのだ。。。

 

golang で go.mod を作って require にて自分の GitHub リポジトリにあるコードを使う場合、 pseudo-version を書くのだろうと思っている。

https://golang.org/ref/mod#pseudo-versions

フォーマットはこんな感じ。

v0.0.0-<YYYYMMDDhhmmss>-<commit-id>

私が書くときはこういう順番でやっている。

  1. GitHub などで使いたい commit-id を調べる
  2. “YYYYMMDDhhmmss”のところは適当な日時にしておく
  3. “commit-id”のところに調べたcommit-idを書く
  4. go mod tidy
  5. commit-id の桁が長すぎると怒られるので、エラーメッセージに出力された文字列に置き換える
  6. go mod tidy
  7. タイムスタンプが違うといわれるので、エラーメッセージに出力された文字列に置き換える

commit-id は先頭から12文字分とってくればよいので、まだなんとかなると思うのだがタイムスタンプが分からんのだ。
コミットした日時をUTC変換すれば良いのだけど、GitHub だと秒数が出てこない。
clone してくればよいのだけど、めんどくさい。clone するくらいだったら↑でやってしまえばいいや、ということで今に至っているのであった。

go mod edit だとうまいことやってくれないかと期待したのだが、-require は少なくとも書いたとおりに go.mod に書くだけだった。
んー、大した作業ではないのだけど、なんかきれいにできないものか。

[golang][grpc]protocの準備

golang で gRPC をやりたい。

ということは、protobuf がいるので、protoc がいる。
準備しよう。


インストール手順があった。

Protocol Buffer Compiler Installation | gRPC
https://grpc.io/docs/protoc-installation/

これを書いている時点での最新版は v3.19.1。

https://github.com/protocolbuffers/protobuf/releases/tag/v3.19.1

私が動かしている Ubuntu にも protoc はインストールされていたのだが、あんまり新しくない。

$ protoc --version
libprotoc 3.6.1

GitHub の方からコンパイル済みの zipファイルをダウンロードして、$HOME/.local に展開する。

$ protoc --version
libprotoc 3.19.1

新しくなった。


golang で使うには protobuf compiler だけでは足りない。
というか、protoc はプラグインで各言語向けのソースを吐き出してくれるので、その準備がいる。

golang の場合は楽だ。

Quick start | Go | gRPC
https://grpc.io/docs/languages/go/quickstart/

今見ると、 protoc-gen-go@v1.26protoc-gen-go-grpc@v1.1 になっているが、protoc-gen-go の latestは v1.27.1 だった(protoc-gen-go-gprc の latestはv1.1.0だった。)。
v1.27.1 にした方がよいのかな? メジャーバージョンアップでもないし新しい方がよいかもしれん。困るまで v1.27.1 にしてみよう。
GOPATH は好きに設定すれば良いが、プロジェクトごとに GOPATH を設定しているならどこかにまとめておいた方がよいだろう。

 

Quick Start の順番にやっているので、次は example だ。
今は grpc-go の v1.41.0 を clone するよう書かれているが、最新は v1.42.0 だったのでそちらにした。

まずサーバ側を起動。

$ go run greeter_server/main.go
go: downloading github.com/golang/protobuf v1.4.3
go: downloading google.golang.org/protobuf v1.25.0
go: downloading golang.org/x/net v0.0.0-20200822124328-c89045814202
go: downloading google.golang.org/genproto v0.0.0-20200806141610-86f49bd18e98
go: downloading golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd
go: downloading golang.org/x/text v0.3.0
2021/11/21 13:21:11 server listening at [::]:50051

別のターミナルからクライアントを起動すると、たぶんサーバ側と通信している。

$ go run greeter_client/main.go
2021/11/21 13:22:03 Greeting: Hello world

こちらはサーバ側の出力。

2021/11/21 13:22:03 Received: world

うん、なんというか、protoc などを自分でやっていないので、gRPC したって気分になれないね。
というわけで、その次の gRPCサービスの更新をやろう。


さて、ファイルの構成はこんな感じ。

image

サーバとクライアントにそれぞれ main.go があって、あとは protoファイルとコンパイルされた pb.go と _grpc.pb.go がある。
ただの pb.go の方は protobuf のシリアライズ/デシリアライズの処理、_grpc.pb.go の方は gRPCのサーバ/クライアントの処理になっているそうだ(basics tutorial)。

あまり深く考えず、proto ファイルに SayHelloAgain()を追加しよう。
そして protoc でコンパイル。
--go_ が pb.go で --go-grpc_ が _grpc.pb.go になる。paths=source_relative は相対パスの場所にファイル生成する。

protoc でコンパイルできたら、それぞれの main.go を変更して同じように実行させると SayHelloAgain が動いていることが確認できるだろう。


ツールとしてはこのくらいあれば golang で gRPC できることがわかった。
あとは・・・知識がいるだけである。

[golang, github]go.modのmoduleを実物と違うのにしてよいのか

go.mod に慣れていないせいかもしれないが(golang自体にも慣れていないが)、非常に難しく感じる。

文句を言っても仕方ない(か、golangのプルリクを出すとか)ので、理解に努めよう。

$ go version
go version go1.17.3 linux/amd64


golang でオリジナルのものを作るときは気にならないかもしれないが、GitHub で fork したり mirror したりして一部を書き換えたいとなると思考が止まってしまう。

 

よくあるのが、git cloneする中でも改造して使いたいのはその一部だけ、というパターンだろう。
例として、fmt.Printf するだけのリポジトリを用意した。

https://github.com/hirokuma/gogo-test1/tree/v1

main.go と gogo/gogo1.go だけだ。

ここの gogoパッケージにある Gogo() だけ使うリポジトリがこちら。

https://github.com/hirokuma/gogo-test2/tree/v1

特に変な動作はしない。

go.mod は自分で記述したわけではなく、 go tidy で勝手に追加されたように思う。

それはよいのだが、gogo-test2 の方を go build . でビルドすると gogo-test2 というバイナリファイルができあがる。
これはなぜかというと、go.mod の module がそういう名前になっているからだ。
もしここで、オリジナルと同じ gogo-test1 という名前にしたくて go.mod の module を github.com/hirokuma/gogo-test1 にすると go build . は失敗する。

$ go build .
main.go:6:2: no required module provides package github.com/hirokuma/gogo-test1/gogo; to add it:
        go get github.com/hirokuma/gogo-test1/gogo

main.go:6 は import "github.com/hirokuma/gogo-test1/gogo" している箇所だ。

ではオリジナルと同じ名前のバイナリファイルを作るのには go build だけで無理なのかというとそうでもない。
あまり理解できていないのだが module path とか module directory という考え方があるようで、うーん、後ろに v2 とか書けばいけるのかな?

https://github.com/hirokuma/gogo-test2/blob/v2/go.mod#L1

いけた。

自分より前に v2 を使っている人がいたら私が v3 にしないといけないかというと、たぶんそういうことではないはずだ。調べるのは無理だろうし。
もし v2 でダメになるのはオリジナルが v2 などにした場合だろう。

 

さて、 gogo-test2 から gogo-test1/gogo にある Gogo() を呼び出していたが、自分で gogo.Gogo() を定義し直したいとしよう。

https://github.com/hirokuma/gogo-test2/blob/v3/gogo/gogo1.go

これを実行すると、gogo-test1 の Gogo() が呼び出される。

$ go run .
gogo-test2!
gogo!

なぜかというと、gogo-test2/main.go で import しているのが gogo-test1/gogo だからだ。
では、go.mod で置き換えてみよう。

https://github.com/hirokuma/gogo-test2/blob/v4/go.mod

うまくいくかと思ったが、これではダメだった。

$ go run .
gogo-test2!
gogo!

main.go の import を変更すればよいと思うのだが、本体の変更をせずに go.mod だけで管理できるようになっていると思うのだよ、私は。

難しいねぇ。続きは明日だ。


明日になった。続けよう。
ここまでを整理しておこう。

gogo-test2/v3 から追加した gogo/gogo1.go を main.go で参照させたいのだが、うまくいっていない。
gogo-test2/v4 で replace を追加したのだが、やっぱり参照してくれない。
もちろん、main.go の import を gogo-test2 に変更すれば参照する。

あれ、もしかしたらそれで問題ないのか?
そもそも gogo を自分で実装したものを使う時点で gogo-test1 とは何の関係もなくなっているのだ。
なので、関係をなくした v5を作った。

https://github.com/hirokuma/gogo-test2/compare/v4...v5

これを gogo-test1 の人が見て、あら私もこっちの gogo を使ってみたいわ、と思ったらこうなる。

https://github.com/hirokuma/gogo-test1/compare/v1...v2

うーん、これでよいような気もする。

 

 

では、gogo-test2 の module を yoshio に変更してみよう。

https://github.com/hirokuma/gogo-test2/compare/v5...v6

go.mod を変更すると main.go の import も変更しないといけなかった。
しかし、これだと git clone しないといけないのだったら gogo-test2.git だとわからないんじゃなかろうか?

gogo-test1/v2 の go.mod を gogo-test2/v6 に変更したらやはりエラーになった。

$ go get
go: github.com/hirokuma/gogo-test2@v0.0.0-20211121012830-b239fb1fd1ae: parsing go.mod:
        module declares its path as: github.com/hirokuma/yoshio
                but was required as: github.com/hirokuma/gogo-test2

こういう場合も replace で対応するようだ。

https://github.com/hirokuma/gogo-test1/compare/v2...v3

なので、module と実物のパスが異なってもなんとかなりそうだ。

2021/11/20

[git, golang]GitHubのorganizationのprivateリポジトリをgo getする

golang で作業している。
お仕事なのでプライベートリポジトリなのだが、場所が個人ではなくorganizationにある。

さて go getしたいのだが、organization には Personal Access Token (PAT)はない。
どうしたらよかろうか?

$ go version
go version go1.17.3 linux/amd64


検索して出てきたのは2つの方式だった(他にもあると思うけど)。

  • PATでアクセス
  • SSHにする

PAT方式で出てきたのはこちら。

https://zenn.dev/shootacean/articles/go-get-from-github-private-repository

organizationだからPATはないと書いたが、ここでは個人アカウントのPATを使う。
つまり、個人アカウントだろうとorganizationだろうと気にするなということだ。

これでできた。
GOPRIVATEは設定しなくても特にエラーは出なかった。

 

SSH方式で出てきたのはこちら。

https://gist.github.com/magnetikonline/3ceec9ece11375393ab5781d9370ee99

こちらはダメだった。
git clone で https://github.com で始まるリポジトリ名を打ち込むと SSHのパスフレーズを要求されたのでできるかと思ったのだけど、go getの方は「git@github.com: Permission denied (publickey)」としばしば見かけるエラーになった。

表示しているコマンドもcloneしているだけだし、そのまま打ち込んでもやっぱりパスフレーズ要求になったから、stdinを止められてるとかでダメなんだろうか。

~/.netrc で入力を回避するやり方も出ていたので、それと併用するのかもしれんが、試してはいない。

 

というわけで、「GitHubのorganization のプライベートリポジトリを go get するとき、個人アカウントのプライベートリポジトリをPATを使って go getするやり方と同じで取得できた」ということにしておく。

 

GOPRIVATEがいるのは、おそらく go.mod の中にプライベートリポジトリがある場合だと思う。
go get してエラーになるようだったら、GOPRIVATEに github.com/xxx みたいなのを設定すればできた。
全部同じ organizationのプライベートリポジトリだったのだけど、複数の organization にまたがってかつプライベートリポジトリとかだとわからんね。そんなことをするなってことだろうけど。