Go の Web フレームワーク Gin にちょっとコミットしました

6 min read読了の目安(約5800字

はじめに

この記事は Go Advent Calendar 2020 18 日目の記事です。

普段 Gin をよく使用しているのですが、ふとコミットしたくなり issue を漁っていたら運良くコミットできました。

修正内容はちょっとしたものですが、私の PR がマージされるまでにどんなことをしたのかまとめたいと思います。

Gin とは

Gin は最もスター数の多い Go の Web フレームワークです。

参考:Top Go Web Frameworks

Django や Ruby on Rails のようなリッチなものではなく、必要最低限の機能が備わったシンプルなフレームワークとなっています。

package main

import "github.com/gin-gonic/gin"

func main() {
	r := gin.Default()
	
	r.GET("/nyan", func(c *gin.Context) {
		c.JSON(200, gin.H{
			"message": "にゃーん",
		})
	})
	r.Run(":8080")
}

OSS へのコミット

OSS に貢献するという点で言えば、issue を立てる・ブログを書く・勉強会を開催するなど幅広くあげられます。

最近いいなと思ったのは企業が OSS 開発者のスポンサーになるというものです。
こういったものが増えていくといいですね。

時雨堂は Sphinx の開発者である小宮健氏のプラチナスポンサーになりました

コミットする場合は以下に絞られるかなと思います。

  • ドキュメント周り
    • 追加
    • 修正
    • 翻訳
  • 新機能開発
  • 不具合・バグ修正

新機能開発はハードルが高かったのと、ドキュメント周りは今まで何度かコミットしたこともある(他の OSS)ということで、今回は不具合・バグの修正を行うことにしました。

issue 探し

普段 Gin を使っていて特に不具合やバグはなかったので、とりあえず何かないか issue を漁りました。

OSS によっては「good first issue」というラベルをつけてくれていて、初めての方がコミットしやすいようにしてくれています。

Gin にはなかったので自分で探しました。

探すときに重要視したのは以下 2 点です。

  • 最近できたもの
    • 昔のものだとすぐには解決できない厄介なものが多そうと思ったため
  • コメントがついていない
    • コメントがついていると他の方がすでに対応中(or 済)の可能性が高いため

issue の概要

今回対応したのはこちらの issue になります。

Gin では構造体に binding を記述すると、リクエストパラメータのバインド時にバリデーションをかけてくれます。

以下のプログラムはユーザを登録する API([POST] /users)です。

Schoolbindingrequired_if=isStudent true タグを付与することで、ユーザが学生のときのみ学校の入力を必須にしています。

package main

import (
	"net/http"

	"github.com/gin-gonic/gin"
)

type User struct {
	Name      string `json:"name" binding:"required"`
	IsStudent bool   `form:"isStudent" binding:"required"`
	School    string `form:"school" binding:"required_if=isStudent true"`
}

func main() {
	r := gin.Default()

	r.POST("/users", func(c *gin.Context) {
		var u User
		if err := c.BindJSON(&u); err != nil {
			c.JSON(http.StatusBadRequest, gin.H{"message": "validate error"})
			return
		}
		c.JSON(http.StatusOK, "ok")
	})
	r.Run(":8080")
}

issue の内容としては bindingrequired_if タグを指定すると、required_if が定義されていないとのエラーになるというものです。

curl -X POST -H "Content-Type: application/json" -d '{"name":"john", "isStudent":true, "school": "gopher academy"}' localhost:8080/users
2020/12/13 21:12:31 [Recovery] 2020/12/13 - 21:12:31 panic recovered:
POST /users HTTP/1.1
Host: localhost:8080
Accept: */*
Content-Length: 34
Content-Type: application/json
User-Agent: curl/7.64.1


Undefined validation function 'required_if' on field 'School'
...

OSS にコミットする前に

issue に手をつける前に以下を読みました。

  • README.md
    • Gin はパッケージのドキュメントのようになっていたので流し見しただけ
  • CODE_OF_ONDUCT.md
    • その OSS に関わる上での行動規範
  • CONTRIBUTING.md
    • issue をたてるときや PR をあげるときの約束事が書いてあります
  • マージされた PR(and 元 issue)
    • 雰囲気を掴みます
    • どういう流れで担当者が決まるのか
    • PR にどんなことを書けばいいのか

ルールが厳格な OSS もあるのでしっかり読みます。

エラーの原因調査

1. 自分の環境でも再現することを確認

issue を立てた方のローカル依存の問題でないか確認します。

curl -i http://localhost:9000/list
2020/12/13 22:20:20 [Recovery] 2020/12/13 - 22:20:20 panic recovered:
GET /list HTTP/1.1
Host: localhost:9000
Accept: */*
User-Agent: curl/7.64.1


Undefined validation function 'required_if' on field 'BranchTag'
...

再現、よし!

Gin のリポジトリには issue テンプレートが導入されていて、エラーを再現しやすいような情報が記載されていたので非常に助かりました。

参考: Gin issue template

2. Gin のドキュメントを確認

Gin uses go-playground/validator/v10 for validation. Check the full docs on tags usage here.

Gin のドキュメントを読むと、バリデーションは外部パッケージの go-playground/validator/v10 を使用していることがわかりました。

参考: Gin: Model binding and validation

3. validator パッケージの issue やドキュメント等を確認

Gin の問題なのか外部パッケージの問題なのかを切り分けるために、validator パッケージの issue にバグ報告が上がっていないか確認します。

また、validator パッケージに required_if タグがリリースされたバージョンを調べました。

リリースされたバージョンの確認手順は以下になります。

  1. Blame で required_if が実装された PR を特定
  2. Gin のリリースノートから特定した PR がリリースされたバージョンを探す

もっと良い方法あるような気がします、というかあって欲しい。

validator パッケージ v10.4.0 でリリースされたことがわかりました。

4. Gin の validator パッケージのバージョンを確認

go.mod から使用されている validator パッケージのバージョンを確認します。

module github.com/gin-gonic/gin

go 1.13

require (
	github.com/gin-contrib/sse v0.1.0
	github.com/go-playground/validator/v10 v10.2.0
	github.com/golang/protobuf v1.3.3
	github.com/json-iterator/go v1.1.9
	github.com/mattn/go-isatty v0.0.12
	github.com/stretchr/testify v1.4.0
	github.com/ugorji/go/codec v1.1.7
	gopkg.in/yaml.v2 v2.2.8
)

v10.2.0!

Gin が使用している validator パッケージのバージョンでは required_if タグが追加されていないことが原因であることがわかりました。

原因特定、よし!

修正

  1. Gin をフォーク
  2. フォークしたリポジトリをローカルに落とす
  3. バージョンアップ!
go get -u github.com/go-playground/validator/v10
  1. テスト実行 and 動作確認
  2. コミット and プッシュ

PR

フォークしたリポジトリの master から本家の master に向けて PR を作成します。

PR の説明をどんな感じで書けばいいかはマージされた他の PR を参考にしました。

やったー!

修正内容が go.modgo.sum の変更だけだったこともあり、PR を上げたその日のうちにマージされました。

この修正はバージョン 1.7(現在 1.6.3)でリリース予定です。

おわりに

go.modgo.sum を更新するという簡単なコミットですが、今までドキュメント周りの修正しかしたことがなかったのでマージされたときはとてもうれしかったです。

これだけ大きな OSS でも、運が良ければコミットできることを知れたのは大きな収穫でした。

来年も隙さえあればコミット狙っていこうと思います。