Engineering

RenovateのOnboarding PRを毎回手作業で書き換えるのをやめた

  • Renovate
  • GitHub
  • Mend
  • DevOps

新規リポジトリにRenovateを入れるたびにOnboarding PRを開いて、extendsに組織共通プリセットへの参照を1行足す運用にうんざりして、Mend-hostedのorg-inherited-config.jsonでOnboarding PRの中身そのものを組織側から差し替えるように切り替えました。

// LabeeHive/renovate-config/org-inherited-config.json
{
  "$schema": "https://docs.renovatebot.com/renovate-inherited-schema.json",
  "onboarding": true,
  "onboardingConfigFileName": ".github/renovate.json5",
  "onboardingConfig": {
    "$schema": "https://docs.renovatebot.com/renovate-schema.json",
    "extends": [
      "github>LabeeHive/renovate-config:default.json5"
    ]
  }
}

この記事では、Renovateの自動Onboarding PRに何が入っていないか、Renovate本体に組み込まれた<org>/renovate-config/default.jsonの自動検出、default.json5を選んだ瞬間にその自動検出が効かなくなる理由、そしてorg-inherited-config.jsonで書き換える方針までを順に書きます。

GitHubGitHub - LabeeHive/renovate-configContribute to LabeeHive/renovate-config development by creating an account on GitHub.

Onboarding PRはデフォルトで出る、中身が違うだけ

Renovateを新しいリポジトリで動かすと、初回にOnboarding PRが自動で立ちます。Renovate本体の挙動で、組織継承の設定があってもなくても出てきます。

問題は中身です。デフォルトのOnboarding PRに入っているのはRenovate標準の雛形で、ラビー合同会社で使いたいプリセットへの参照ではありません。新規リポジトリができるたびに、extendsに組織共通プリセットへの参照を1行足して、ファイルパスを.github/renovate.json5に直して、マージする。薄い作業ですが、触る回数だけはあるので、ファイル名のtypoや参照先の付け忘れが少しずつ積もっていきます。「Renovateが回っていない」と思って覗いたら、デフォルトのまま放置されたrenovate.jsonがリポジトリ直下にあった、というのを何度かやってからは、Onboarding PRの中身を組織側で固めたくなりました。

Renovate本体には<org>/renovate-config/default.jsonを拾う仕組みがある

ソースコードを覗くと、Renovateの初回Onboarding処理が<org>/renovate-configを探しに行く分岐があります。lib/workers/repository/onboarding/branch/config.tssearchDefaultOnboardingPresetが該当箇所で、新規リポジトリのパスを上から辿りながら<group>/renovate-configの存在を確認し、見つかればその参照をOnboarding PRのextendsとして埋め込みます。

// lib/workers/repository/onboarding/branch/config.ts より抜粋
// Check for group/renovate-config
const repo = `${groupName}/renovate-config`;
const preset = `local>${repo}`;
if (await getPreset({ repo })) {
  foundPreset = preset;
}

getPreset({ repo })presetName省略時に'default'を使う実装で、内部のfetchPreset()default.jsonを直接読みに行きます。

// lib/config/presets/util.ts より抜粋
if (fileName === 'default') {
  try {
    jsonContent = await fetch(
      repo, buildFilePath('default.json'), endpoint, tag,
    );
  } catch (err) {
    // ... fallback to deprecated renovate.json ...
  }
}

つまり<org>/renovate-configのリポジトリ直下に default.json が置いてあれば、新規Onboarding PRにはextends: ["local><org>/renovate-config"]が自動で差し込まれます。org-inherited-config.jsonを書かなくても、Onboarding PRのextendsを毎回手作業で足す必要がなくなります。

default.json5を選ぶと自動検出は効かない

問題はここからです。組織共通プリセットには「なぜこの設定を入れているか」をコメントで残したい背景があります。JSON5ならコメントを書けるため、ファイルをdefault.json5にしたい。

ところが上のsearchDefaultOnboardingPresetgetPreset({ repo })経由でdefault.jsonを直接探しに行く実装なので、default.json5しか置いていないリポジトリはマッチしません。fetchPreset()fileName === 'default'の分岐はbuildFilePath('default.json')に決め打ちで降りていきます。自動検出が空振りすると、Onboarding PRには組織プリセットへの参照が入らず、Renovate標準の雛形がそのまま書き込まれてしまいます。

回避するには、Onboarding PRの中身を上書きするためのorg-inherited-config.jsonを別途用意し、onboardingConfig.extendsファイル名つきの参照github>LabeeHive/renovate-config:default.json5)を明示的に書きます。fetchPreset()は拡張子に.json5が付いたfilePresetが渡された場合はその名前で取りに行く分岐に入るため、こちらは問題なく解決します。

ラビー合同会社では、default.json5でコメントを残すことを優先して、org-inherited-config.jsonを併設する方式を取りました。「Onboarding PRの中身を制御したい」「プリセットにコメントを書きたい」という2つの目的が、結果として同じファイル(org-inherited-config.json)で実現できる構造です。

// LabeeHive/renovate-config/org-inherited-config.json
{
  "$schema": "https://docs.renovatebot.com/renovate-inherited-schema.json",
  "onboarding": true,
  "onboardingConfigFileName": ".github/renovate.json5",
  "onboardingConfig": {
    "$schema": "https://docs.renovatebot.com/renovate-schema.json",
    "extends": [
      "github>LabeeHive/renovate-config:default.json5"
    ]
  }
}

Mend-hostedはリポジトリ名とファイル名を固定で見る

org-inherited-config.jsonが読まれる側の仕組みはMend-hosted Renovate Appに書かれています。Mend-hostedのバックエンドは毎回のRenovate実行のたびに<org>/renovate-config/org-inherited-config.jsonを自動で読みに行き、対象リポジトリの設定にマージしてから動きます。

有効化に必要なのは3点です。

  • リポジトリ名がrenovate-configであること
  • ファイル名がorg-inherited-config.jsonであること
  • Renovate Appがこのリポジトリにもインストールされていること

リポジトリ名もファイル名もハードコードされていて、.github/org-inherited-config.jsonのような別配置にしても見てもらえません。renovate-configを独立したリポジトリとして切ったのはこの規約に従うためで、Renovate関連だけを抱えるリポジトリにすると、.githubリポジトリと役割を切り分けられます。

org-inherited-config.jsononboardingConfigextendsを書いておくと、Renovateが新規リポジトリのOnboarding PRを開くときに、その内容がそのままrenovate.json5として書き込まれます。onboardingConfigFileName.github/renovate.json5に向けているのはリポジトリのルートを汚さないためで、Renovate関連の設定を.github/配下に集めておくと、ファイルツリーやlsの結果がすっきりします。

既存リポジトリの参照は手動で揃える

org-inherited-config.jsonを置いた後も、すでにOnboarding済みのリポジトリのrenovate.json5は古い参照先を指したままです。onboardingConfig.extendsを新しい参照に切り替えても、これは新規リポジトリ向けの雛形を上書きするだけで、既存ファイルには触れません。

既存リポジトリでgithub>LabeeHive/.github:default.json5のような古い参照を使っているものは、新しいrenovate-config側に向け直すPRを別途出して回します。対象の洗い出しはgh search codeで済みます。

gh search code --owner LabeeHive 'extends "github>LabeeHive/.github:default.json5"' --json repository,path

返ってきたパスごとにextendsの参照先を書き換えるPRを出し、マージし切ってから古い.github/default.json5本体を削除します。切り替えが完了するまでは新旧両方のdefault.json5が並走するため、中身を一時的に同じ内容に揃えておけばRenovateの挙動は変わりません。

ブログRenovateでCloudflare Workers SDKのパッケージをグルーピングするCloudflare Workers SDKはモノレポで管理されていますが、Renovateのデフォルト設定ではパッケージごとに個別のPRが作られます。wrangler、@cloudflare/vite-plugin、miniflareを1つのPRにまとめるpackageRulesの設定と、monorepo presetへの提案で浮上したimmortal PRs問題を紹介します。