0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Golangで、自動生成ファイルがある場合のパッケージ自動更新

Last updated at Posted at 2023-08-27

Golangでパッケージを自動更新させたい場合、dependabotやrenovateが選択肢になるかと思います。

ただgRPCなどの自動生成ファイルがあるときは、いろいろと厄介だったのでやったことを残しておきます

課題

dependabotはパッケージアップデートを自動で行ってくれるGithub純正のBotです。

Dependabot は、セキュリティアップデートプログラムを使用してプルリクエストを発行することにより、脆弱性のある依存関係を修正できます。

とてもありがたいのですが、一つ困ったことがあってdependabotが走ってプルリクが出された際、go mod tidyが自動で走ります。これによってgo.modに必要なパッケージの追加と不要なパッケージの削除をしてくれるのですが、自動生成ファイルがあるときには悪さをすることがあります。

具体的にいうと、自動生成ファイルのみが呼び出しているパッケージがgo.modから削除されます。そのため自動生成ファイル部分のコードが動かなくなり、Lintなどが落ちます。

対応方法

Renovateの検討(うまくいかなかった)

ではgo mod tidyを自動で走らせなければいいのでは?という気持ちになります。

Renovateでは、dependabotよりもカスタム性のある自動パッケージアップデートができます。また指定しない限りはgo mod tidyが走らないので今回のケースでも良い選択肢に見えます。

そこで実際に試してみたのですが、自動テスト実行時に

go: updates to go.mod needed; to update it:
	go mod tidy

と怒られてしまいました。。

Renovateではrenovate.jsonに

    "postUpdateOptions": [
        "gomodTidy"
    ],

を追記することでgo mod tidyを実行させることが可能ですが、Renovateのgo mod tidyはgo.sumからパッケージのsumが抜け落ちることがあるようです。(そしてこの問題は対応しない方針のようです)

そのため、今回の場合Renovateを使う選択肢はなさそうです。

dependabot.goの追加(うまくいった)

そもそも今回の問題が報告されていないのか調べてみると、dependabotにissueがありました。

この手法は、自動生成ファイルがつくられるディレクトリにdependabot.goを用意し、自動生成ファイルに用いるパッケージをすべてimportしておく、というものです。

dependabot.goの生成

すべての生成ファイルを目で確認していくのは大変なので、(なぜかPythonでつくりましたが)dependabot.go生成コードを置いておきます。

Install

import部分を認識させるため、ASTを利用します。ASTのjsonをdumpしてくれるgoastというパッケージがあるので、インストールしておきます。

go install github.com/m-mizutani/goast/cmd/goast@latest

次にPython3をインストールしておきます。必要な外部パッケージは特にありません。

generate_dependabotgo.pyの作成

generate_dependabotgo.py
import subprocess
import json
import glob
import os


def get_package_from_dic(dic):
    if 'Kind' in dic.keys() and dic['Kind']=='ImportSpec':
        return dic['Node']['Path']['Value']
    else:
        return None


def get_imported_packages(go_file):
    cmd = f"goast dump '{go_file}'"

    ast_result = subprocess.run(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True).stdout

    dics = [json.loads(l) for l in ast_result.replace('\n}\n{\n', '}\t{').replace('\n', '').replace('\t', '\n').splitlines()]
    
    return set([get_package_from_dic(d) for d in dics if get_package_from_dic(d)])   


def gen_dependabotgofile(dir_path):
    all_packages = []

    if dir_path[-1] == '/':
        dir_path = dir_path[:-1]

    basename = os.path.basename(dir_path)
    go_paths = glob.glob(f'{dir_path}/*.go', recursive=True)
    
    if len(go_paths) == 0:
        Exception(f'dir_path: {dir_path} が適切ではありません')

    for go_path in go_paths:
        all_packages.extend(get_imported_packages(go_path))

    import_txt = ''

    import_txt += f'package {basename}\n'
    import_txt += '\n'
    import_txt += 'import (\n'

    for package in sorted(set(all_packages)):
        import_txt += f'\t_ {package}\n'

    import_txt += ')\n'
    print(import_txt)
    
    with open(f'{dir_path}/dependabot.go', mode='w') as f:
        f.write(import_txt)

def goformat(dir_path):
    subprocess.run(f'goimports -w "{dir_path}/dependabot.go"', shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
        

auto_generated_gofile_dirs = [
    # 自動生成ファイルの作られるディレクトリ達
]

for dir_path in auto_generated_gofile_dirs:
    print(dir_path)
    gen_dependabotgofile(dir_path)
    goformat(dir_path)

生成手順

ローカルで実行

  1. gRPCなど自動生成ファイルを生成
  2. generate_dependabotgo.pyのauto_generated_gofile_dirsを書き換えて、python generate_dependabotgo.pyを実行

出力されたdependabot.goはこんな感じになると思います。dependabot.goがgitの追跡対象になるように.gitignoreの設定も忘れずに。

dependabot.go
package account

import (
	_ "bytes"
	_ "context"
	_ "errors"
	_ "fmt"
	_ "net"
	_ "net/mail"
	_ "net/url"
	_ "reflect"
	_ "regexp"
	_ "sort"
	_ "strings"
	_ "sync"
	_ "time"
	_ "unicode/utf8"

	_ "google.golang.org/grpc"
	_ "google.golang.org/grpc/codes"
	_ "google.golang.org/grpc/status"
	_ "google.golang.org/protobuf/reflect/protoreflect"
	_ "google.golang.org/protobuf/runtime/protoimpl"
	_ "google.golang.org/protobuf/types/known/anypb"
)

dependabot.goがちゃんと機能しているか確認

  1. 自動生成ファイルをすべて削除
  2. go mod tidyを実行
  3. 自動生成ファイルを再度生成
  4. TestやLintを実行してエラーがでないか確認

以上で、dependabot.go配置による自動パッケージアッブデート問題は解消します。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?