データサイエンス - TECH PLAY - TECH PLAY

TECH PLAY

データサイエンス

イベント

マガジン

技術ブログ

こんにちは!LINEヤフー 分析ユニット LINE Service Analytics Divisionの柳です。8月20日にデータサイエンス関連の事例を共有するイベントを開催しました。本記事では、イ...
こんにちは。タイミーのデータエンジニアリング部DSグループでMLOpsを担当しているYukitomoです。 前回の記事では、Python + uvのモノレポ環境において、Renovateを用いて依存関係更新のプルリクエストを制御するための基本設計を紹介しました。 その後、実際に運用してみると、Renovateの管理対象はPythonの依存パッケージだけでは収まりませんでした。 devcontainer.json やCloud Runのマニフェストなどへ対象を広げるにつれて設定が複雑になり、Mend.io経由で動かして結果を待つだけでは、意図通りに動かない設定の原因を追いにくくなりました。 そこで今回は、前回からの更新として、以下の4点を紹介します。 Renovate設定をローカルやCI/CDで検証できるようにしたこと 検証手順をAIエージェント用スキルとして整備したこと devcontainer.json やCloud Runのマニフェストをアプリケーション単位のプルリクエストにまとめる設定 グルーピングされた更新で、対象となるすべてのファイルを bumpVersions で更新する設定 この記事の想定読者 Renovate設定を継続的に改善・デバッグしたい方 Mend.io経由でRenovateを運用しており、設定変更の検証に課題を感じている方 プライベートなGoogle Cloud Artifact Registryを使うモノレポでRenovateを運用している方 AIエージェントにRenovate設定の調査・修正・dry runを任せたい方 要約(TL;DR) Mend.ioへ反映する前に、ローカルやCI/CDでRenovateのconfig validationとdry runを実行できるようにした 検証手順をAIエージェント用スキルにまとめ、調査手順と実行環境を揃えた managerごとに異なる packageFileDir を正規化し、アプリケーション単位で更新をまとめた グルーピングされたプルリクエストでは、 upgrades に含まれるディレクトリを列挙し、すべての pyproject.toml / uv.lock を bumpVersions で更新した 前提とするモノレポ構成 今回の内容を説明するため、次のような簡略化したモノレポを想定します。 . ├── .renovaterc.json5 ├── group-x │ ├── project-a │ │ ├── .devcontainer │ │ │ └── devcontainer.json │ │ ├── kustomize │ │ │ ├── base │ │ │ └── overlays │ │ ├── pyproject.toml │ │ └── uv.lock │ └── project-b │ ├── pyproject.toml │ └── uv.lock └── group-y └── ... 記事中で扱う設定の全体像は次の通りです。説明を簡潔にするため、実際の設定から関係する部分だけを抜き出しています。 { extends : [ "config:recommended" ] , devcontainer : { managerFilePatterns : [ "/(?:^|/)\\.devcontainer/(?:[^/]+/)?devcontainer\\.json$/" , "/(?:^|/)\\.devcontainer\\.json$/" , ] , } , packageRules : [ { matchFileNames : [ "group-x/**" ] , groupName : "group-x" , groupSlug : "group-x" , additionalBranchPrefix : "{{packageFileDir}}/" , } , { matchFileNames : [ "**/devcontainer.json" ] , additionalBranchPrefix : "{{replace \"/\\.devcontainer\" \"\" packageFileDir}}/" , } , { matchFileNames : [ "**/kustomize/**" ] , additionalBranchPrefix : "{{{replace '/kustomize/.*' '' packageFileDir}}}/" , } , { matchUpdateTypes : [ "major" , "minor" , "patch" ] , bumpVersions : [ { bumpType : "patch" , filePatterns : [ "/^({{#each (distinct (lookupArray upgrades \"packageFileDir\"))}}{{{.}}}{{#unless @last}}|{{/unless}}{{/each}})\\/pyproject\\.toml$/" , ] , matchStrings : [ "version\\s*=\\s*\"(?<version>[^\"]+)\"" ] , } , { bumpType : "patch" , filePatterns : [ "/^({{#each (distinct (lookupArray upgrades \"packageFileDir\"))}}{{{.}}}{{#unless @last}}|{{/unless}}{{/each}})\\/uv\\.lock$/" , ] , matchStrings : [ "name = \"[^\"]+\"\\nversion = \"(?<version>[^\"]+)\"\\nsource = \\{ (?:editable|virtual) = \"\\.\" \\}" , ] , } , ] , } , ] , } AIエージェント用スキルは独立したリポジトリで管理しているため、そのファイル構成は後述の2章で説明します。 1. Renovate設定はローカルやCI/CDで検証してからデプロイする 一般的なソフトウェア開発では、変更をメインブランチに入れる前にローカルやCI/CDで検証します。Renovateの設定も同じように扱えるよう、ローカルでconfig validationとdry runを実行するスクリプトを用意しました。このスクリプトは、2章で説明するAIエージェント用スキルに含めています。 実際のスクリプトはもう少し複雑ですが、Google Cloud Artifact RegistryやGitHubへの認証情報を環境変数へ設定した上で、固定バージョンのRenovateを実行するのがポイントです。 RENOVATE_HOST_RULES="$(gcloud auth print-access-token | jq -Rc '[ {matchHost:"asia-northeast1-docker.pkg.dev",hostType:"docker",username:"oauth2accesstoken",password:.}, {matchHost:"asia-northeast1-python.pkg.dev",username:"oauth2accesstoken",password:.} ]')" \ GITHUB_COM_TOKEN="$(gh auth token)" \ RENOVATE_CONFIG_FILE=.renovaterc.json5 \ npx --yes --package "renovate@${RENOVATE_VERSION}" renovate-config-validator --strict # または、同様に環境変数を設定してdry runを実行 # TIER = extract | lookup | full npx --yes --package "renovate@${RENOVATE_VERSION}" renovate --platform=local --dry-run="$TIER" Renovateの設定項目は多岐にわたります。Mend.io経由で実行していると、一見エラーなく終了していても、一部の設定が意図通りに適用されていないことがありました。 Mend.io側の環境変数を変えてメインブランチ以外で動作させ、詳細ログを確認する方法でもデバッグできます。ただし、Gitリポジトリの管理者権限が必要になる場合があり、誰でも簡単に試せるわけではありません。また、設定変更をプルリクエスト化し、承認後にメインブランチへ反映してから実行結果を確認する方法では、フィードバックループが長くなります。 そのため、現在はRenovate設定を「ローカルで検証してからデプロイする対象」として扱うようにしました。 2. AIエージェント用スキルとして準備する Renovateのdebug logは非常に大きく、リポジトリが大きくなるとdry runにも時間がかかります。人が毎回同じ前提を確認しながらログを読むのではなく、AIエージェントが一定の手順で設定を調査・修正できるよう、検証手順をスキルとして整備しました。 構成は以下です。 plugins/renovate-config-tester/ ├── assets │ ├── sample.gha.yml # サンプルのGitHub Actions │ └── sample.renovaterc.json5 # Renovateのサンプル初期設定 ├── scripts │ └── renovate-local.sh # 1章で説明したRenovate実行スクリプト └── SKILL.md SKILL.md には、主に次の前提と調査手順を記載しています。 Renovateのバージョン Renovateは必ず固定バージョンで実行します。Renovateの管理対象となるファイル(例:GitHub Actionsのworkflow)にバージョンを記述し、その更新はRenovate自身に任せます。スキルは、このファイルから実行するバージョンを取得します。 # CI用GitHub Actions jobs : validate-and-dry-run : runs-on : ubuntu-latest env : # renovate: datasource=npm depName=renovate RENOVATE_VERSION : 44.29.4 # Renovate自身が更新し、スキルはここから取得する steps : - name : Install Renovate run : npm install -g "renovate@${RENOVATE_VERSION}" - name : Renovate dry run run : renovate --platform=local --dry-run=lookup Node.jsの必要バージョン RenovateはNode.js applicationであるため、Renovateのバージョンに対応したNode.jsが必要です。 本記事で扱っているRenovate 44系は、Node.js 24の環境で検証しました。手元ではNode.js 26環境で正常に実行できなかったため、スキルにはRenovateが対応するNode.jsバージョンを確認し、検証済みのruntimeを使うよう明記しています。 gcloudやghの認証情報が必要であること Private Artifact RegistryやGitHubにアクセスする必要がある場合は、事前に gcloud や gh の認証を済ませる必要があります。 ただし、AIエージェントにtokenを直接扱わせるのではなく、スクリプト( renovate-local.sh )の中で RENOVATE_HOST_RULES やGitHub tokenを組み立てるようにしています。AIエージェントはスクリプトを実行するだけで、credentialの組み立て方を毎回判断する必要はありません。 必要なツール類 RenovateのログはJSONL形式で出力されるため、調査には jq などが必要です。リポジトリや設定によっては yq も利用します。こうした周辺ツールもスキルの前提に含めることで、AIエージェントが調査できない理由を切り分けやすくしています。 実行ログは一旦ファイルに記録してから読む 対象リポジトリが大きい場合、debug logをそのまま読み込ませるとLLMのtokenを多く消費します。そこで、ログを一旦ファイルへ保存し、対象パッケージやbranchに関係する行だけを grep / jq で抽出します。 "${CLAUDE_SKILL_DIR}/scripts/renovate-local.sh" > /tmp/renovate.log 2>&1 スキルとして独立させたことで、自分が担当するリポジトリ以外にも同じ調査手順を展開できます。Renovate設定の調査方法を人へ説明する代わりに、AIエージェントが同じ前提と手順で調査できる状態にしたことが、今回の大きな変更です。 3. devcontainer.jsonやCloud Runのマニフェストもアプリケーション単位のプルリクエストにまとめる Renovateは当初、Pythonの依存パッケージ更新を中心に導入しました。その後、 devcontainer.json やCloud Runのマニフェストから参照するコンテナイメージなどにも対象を広げています。 前提とするモノレポで単純に対象managerを増やすと、同じアプリケーションに関する更新であっても、ファイルの場所によって別のプルリクエストになります。 pyproject.toml / uv.lock の更新 .devcontainer/devcontainer.json のimage/feature更新 kustomize配下にあるCloud Runマニフェストのコンテナイメージ更新 これらは「同じアプリケーションの実行・開発環境に関する更新」としてまとめて確認したいケースが多く、プルリクエストが細かく分かれるとレビューの負担が増えます。そこで、managerごとに異なる packageFileDir を正規化し、アプリケーション単位のbranch prefixへ揃えます。 A. モノレポ内のdevcontainer.jsonを検出する 各アプリケーション配下の .devcontainer/devcontainer.json もRenovateの対象へ含めるため、devcontainer managerの managerFilePatterns を追加します。 devcontainer : { managerFilePatterns : [ "/(?:^|/)\\.devcontainer/(?:[^/]+/)?devcontainer\\.json$/" , "/(?:^|/)\\.devcontainer\\.json$/" , ] , } , B. packageFileDirをアプリケーションのディレクトリへ正規化する 前回の記事で紹介した共通ルールでは、 packageFileDir を additionalBranchPrefix に使っています。しかし、devcontainer managerの packageFileDir には /.devcontainer が含まれるため、そのままでは pyproject.toml / uv.lock の更新と別のbranchになります。 そこで、devcontainer managerから抽出したdependencyについては、 packageFileDir 末尾の /.devcontainer を取り除きます。 // 前回の記事で紹介した共通ルール { matchFileNames : [ "group-x/**" ] , groupName : "group-x" , groupSlug : "group-x" , additionalBranchPrefix : "{{packageFileDir}}/" , } , // devcontainer.json用のルール { matchFileNames : [ "**/devcontainer.json" ] , additionalBranchPrefix : "{{replace \"/\\.devcontainer\" \"\" packageFileDir}}/" , } , これにより、たとえば group-x/project-a/.devcontainer は group-x/project-a へ正規化されます。 Cloud Runのマニフェストが kustomize/base や kustomize/overlays/env に分かれている場合も、同じ考え方で /kustomize/** を取り除きます。 { matchFileNames : [ "**/kustomize/**" ] , additionalBranchPrefix : "{{{replace '/kustomize/.*' '' packageFileDir}}}/" , } , この設定により、Python dependency、devcontainerのimage/feature、Cloud Runマニフェストのコンテナイメージを、アプリケーション単位のプルリクエストとして確認しやすくなります。 ただし、複数アプリケーションの更新をさらに1つのプルリクエストへまとめると、別の問題が発生しました。 bumpVersions.filePatterns で {{packageFileDir}} だけを参照すると、グループ内に複数ある pyproject.toml と uv.lock のバージョンを更新しようとしても、一部しか更新されなかったのです。4章ではこの対処方法を紹介します。 4. グルーピング時のbumpVersionsで対象ファイルを漏れなく更新する 前回記事は bumpVersions をtop-levelに置く例を紹介しました。現在はpathを条件に加えているため、 packageRules の中に移しています。 通常であれば、 filePatterns は {{packageFileDir}}/pyproject.toml のように書きたくなります。しかし、複数のアプリケーションを1つのプルリクエストへグルーピングした場合、この記述ではいずれか1つの pyproject.toml / uv.lock しか更新されませんでした。 RenovateのDiscussion #35770を参考に、グループ内の upgrades からすべての packageFileDir を取り出し、正規表現の選択肢として組み立てるよう変更しました。 { matchUpdateTypes : [ "major" , "minor" , "patch" ] , bumpVersions : [ { bumpType : "patch" , filePatterns : [ "/^({{#each (distinct (lookupArray upgrades \"packageFileDir\"))}}{{{.}}}{{#unless @last}}|{{/unless}}{{/each}})\\/pyproject\\.toml$/" , ] , matchStrings : [ "version\\s*=\\s*\"(?<version>[^\"]+)\"" ] , } , { bumpType : "patch" , filePatterns : [ "/^({{#each (distinct (lookupArray upgrades \"packageFileDir\"))}}{{{.}}}{{#unless @last}}|{{/unless}}{{/each}})\\/uv\\.lock$/" , ] , matchStrings : [ "name = \"[^\"]+\"\\nversion = \"(?<version>[^\"]+)\"\\nsource = \\{ (?:editable|virtual) = \"\\.\" \\}" , ] , } , ] , } , lookupArray で各upgradeの packageFileDir を取り出し、 distinct で重複を除き、正規表現の選択肢として連結しているのがポイントです。 設定 グループ内の対象 更新結果 {{packageFileDir}} のみを参照 複数ディレクトリ いずれか1組だけ更新 upgrades からディレクトリを列挙 複数ディレクトリ 対象となる全組を更新 この記述はRenovateのtemplate contextに含まれる upgrades を利用します。導入時には、使用するRenovateバージョンでconfig validationとdry runを実行し、想定するすべてのファイルが更新対象になっていることを確認してください。 まとめ 今回、Renovateの設定をMend.ioへ反映する前に検証できるようにし、その手順をAIエージェント用スキルとして整備しました。configだけでなく、実行環境やcredentialの渡し方、ログの調べ方まで含めて管理することで、同じ前提と手順で継続的に設定を改善できます。 また、 packageFileDir の正規化によって更新をアプリケーション単位にまとめ、複数アプリケーションをグルーピングする場合は、 bumpVersions の対象もグループ全体へ広げました。モノレポのRenovate設定は、検証と運用を繰り返しながら育てることが重要だと感じています。 このようなMLOps基盤や開発環境の改善に、一緒に取り組むメンバーを募集しています。 We’re Hiring! 現在、タイミーでは、データサイエンスやエンジニアリングの分野で、共に成長し、革新を推し進めてくれる新たなチームメンバーを積極的に探しています! 現在募集中のポジションは こちら です。 また、気軽な雰囲気での カジュアル面談 も随時行っています。「話を聞きたい」と思われた方は、ぜひお気軽にエントリーしてください。 References 前回記事: Renovateの設定 - Timee Product Team Blog Renovate configuration options Renovate package rules Feedback thread: bumpVersions · renovatebot/renovate · Discussion #35770
はじめに こんにちは!データ推進室 2026年度新卒の島田・山尾です。 株式会社リクルートのデータ推進室では、配属前に約2ヶ月

動画

書籍