🤖

go.mod で管理されていないライブラリを Renovate で更新する

に公開

これまでに Renovate を使って Go のライブラリ更新や、Go 本体のバージョン更新を行なってきました。

Renovate は go.mod を監視しており、go.mod で管理しているライブラリは Renovate で更新することができます。
しかし逆に、go.mod で管理されていなければ Renovate で更新できないのか?と言われるとそうでもありません。
今回は go.mod で管理されていないライブラリを Renovate で更新するための方法を紹介します。

やり方としては2つあります。

  1. Go 1.24 で導入された tool directive を使う
  2. Renovate の Custom Manager を使う

tool directive を使う

Go 1.24 では、go.mod 内の tool directive を使用してライブラリの依存関係を追跡できるようになりました(Go 1.24 Release Notes)。

これまで go.mod で管理されていなかったものも、この仕組みに乗せることで Renovate の更新対象に含めることができます。

次のように Dockerfile 内で go install を使ってライブラリをインストールしていると仮定し、これを tool directive に乗せ替えてみます。

Dockerfile
RUN go install go.uber.org/mock/mockgen@v0.4.0
RUN go install github.com/air-verse/air@v1.60.0

上記2つのライブラリで go get -tool <tool_path> を実行します。

go get -tool go.uber.org/mock/mockgen@v0.4.0
go get -tool github.com/air-verse/air@v1.60.0

すると、go.mod に次のように記述されます。

require (
~省略~
+    github.com/air-verse/air v1.60.0 // indirect
+    go.uber.org/mock v0.4.0 // indirect
~省略~
)

+ tool (
+ 	github.com/air-verse/air
+ 	go.uber.org/mock/mockgen
+ )

続いて、Dockerfile に記述していた go install <tool_path>go install tool に変更します。

- RUN go install go.uber.org/mock/mockgen@v0.4.0
- RUN go install github.com/air-verse/air@v1.60.0

+ RUN go install tool

これにより tool directive に記述された全ライブラリのバイナリを $GOBIN (デフォルトで $GOPATH/bin) にインストールします。

RUN go install tool を実行せずとも、go tool mockgen のようにして指定のツールを実行することもできます。
しかし、その場合はモックの生成コマンドが mockgen から go tool mockgen に変わってしまうためモックを生成しているコードに影響を及ぼしてしまいます。
そのため、ここではバイナリを $GOBIN にインストールすることでコマンドインターフェースを従来通りに保つようにしています。
上記の変更を main ブランチにマージすると、Renovate によって更新する準備が整いました。

あとは、Go のライブラリ更新で紹介したような renovate.json を構成すると、tool directive に記述されたライブラリのバージョンを最新化するPRが Renovate によって作成されました。

Custom Manager を使う

tool directive を使う方法一本で解決できれば良いですが、それができないケースもあります。
例えば golangci-lint では、 go get コマンドを使ったインストールの動作保証をしておらず、代わりにバイナリインストールを推奨しています。

このようなケースでは Custom Manager を使うことで、他の組み込みマネージャーでは検出されない依存関係を検出するように Renovate を構成できます。
Custom Manager には regexjsonata の2種類がありますが、今回は regex を使います。

次のように Dockerfile で golangci-lint のバイナリをインストールしていると仮定し、これを Renovate で自動更新できるようにしていきます。

Dockerfile
RUN curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v2.1.1

まず、バージョン値を正規表現で抽出しやすい形に変えるべく環境変数化します。

Dockerfile
ENV GOLANGCI_LINT_VERSION=v2.1.1
RUN curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin ${GOLANGCI_LINT_VERSION}

次に、renovate.json に customManagers の設定を行います。

renovate.json
{
  "customManagers": [
    {
      "customType": "regex",
      "fileMatch": ["^Dockerfile$"],
      "matchStrings": [
        "ENV GOLANGCI_LINT_VERSION=(?<currentValue>\\S+)"
      ],
      "depNameTemplate": "github.com/golangci/golangci-lint",
      "datasourceTemplate": "go",
      "versioningTemplate": "semver-coerced"
    }
  ]
}
  • customType:regex custom manager を使用するため regex を指定します。
  • fileMatch:Renovate がリポジトリ内のどのファイルを解析・抽出するかを判断するための正規表現パターンを記述します。ここでは、golangci-lint のバージョンを記述したファイル名を Dockerfile としているので、そのファイル名を検出する正規表現にしています。
  • matchStrings:名前付きキャプチャグループの正規表現パターンを記述します。GOLANGCI_LINT_VERSIONに渡す値を currentValue として抽出し、Renovate に現在のバージョンを認識させます。ちなみに上記例では、v1.64.5 # comment のようにコメントが含まれる場合でもバージョン番号だけが抽出されるようにしています。
  • depNameTemplate:depName を指定します。具体的にはバージョンを検索するライブラリのパスを指定します。この文字列がPRタイトルやコミットメッセージにも含まれます。類似項目として packageNameTemplate がありますが、こちらは検索用途でのみ使用されます(参考)。
  • datasourceTemplatedatasource を指定します。ここでは Go ライブラリの検索を行うため go を指定します。
  • versioningTemplateversioning を指定します。ここでは regex Custom Manager のデフォルト値である semver-coerced を指定します。

最後に、renovate.json で enabledManagers を使用している場合、そこに custom.regex を追加して Custom Manager を有効化します。

renovate.json
{
  "enabledManagers": [
    "gomod",
    "dockerfile",
    // 追加
    "custom.regex"
  ],
}

最終的な renovate.json の設定は次のようになります。今回追記した箇所以外については Go 本体のバージョン更新をご参考ください。

renovate.json
{
  "$schema": "https://docs.renovatebot.com/renovate-schema.json",
  "extends": [
    "config:recommended",
    ":label(renovate)",
    ":timezone(Asia/Tokyo)"
  ],
  "schedule": ["* * * * 2"],
  "rangeStrategy": "bump",
  "prHourlyLimit": 10,
  "reviewers": [
    "atushi-koga"
  ],
  "enabledManagers": [
    "gomod",
    "dockerfile",
    // 今回追記箇所
    "custom"
  ],
  // 今回追記箇所
  "customManagers": [
    {
      "customType": "regex",
      "fileMatch": ["^Dockerfile$"],
      "matchStrings": [
        "ENV GOLANGCI_LINT_VERSION=(?<currentValue>\\S+)"
      ],
      "depNameTemplate": "github.com/golangci/golangci-lint",
      "datasourceTemplate": "go"
    }
  ],
  "postUpdateOptions": [
    "gomodTidy"
  ],
  "packageRules": [
    {
      "matchManagers": [
        "gomod"
      ],
      "matchDatasources": [
        "golang-version"
      ],
      "groupName": "golang-version-updates"
    },
    {
      "matchManagers": [
        "dockerfile"
      ],
      "groupName": "golang-version-updates"
    },
    {
      "matchManagers": [
        "gomod"
      ],
      "matchDatasources": [
        "go"
      ],
      "matchUpdateTypes": [
        "minor",
        "patch"
      ],
      "groupName": "golang-modules-minor-patch-updates"
    }
  ]
}

上記設定を main ブランチに反映すると、Renovate によって golangci-lint を更新するPRが作成されました。

Files changed を見てみると、ENV GOLANGCI_LINT_VERSIONv2.1.1 から v2.1.2 に変更されています。

これで Custom Manager を使ったバージョン管理ができるようになりました!🎉

Discussion