fix(renovate): constrain erlang to ^27 so mix.lock updates can run - #141
Conversation
Renovate's mix manager treats its two tools asymmetrically: erlang falls
back to a hardcoded `^26`, while elixir has no default and floats to the
newest release satisfying the `elixir:` requirement in mix.exs. Every
repository on this preset allows `~> 1.17` or wider, so Renovate picks
Elixir 1.20.x -- which requires OTP 27+ and refuses to install against
OTP 26.
Reproduced with the renovate/renovate image (44.23.3):
install-tool erlang 26.2.5.21 -> succeeded
install-tool elixir 1.20.3 -> ERROR! Unsupported Erlang/OTP
version, expected Erlang/OTP 27+
Renovate surfaces that as `Command failed: install-tool elixir v1.20.3`,
leaves mix.lock untouched, and drops the resulting empty commit
(`Detected empty commit - aborting git push`). No mix lock file update
has succeeded since Elixir 1.20.0 was released on 2026-06-03.
Swapping only the erlang version fixes it:
install-tool erlang 27.3.4.15 -> succeeded
install-tool elixir 1.20.3 -> succeeded (Mix 1.20.3)
This affects only the toolchain Renovate installs to regenerate lock
files; each repository's CI matrix is configured separately.
| "Raise the Erlang/OTP floor used to regenerate mix.lock. Renovate's mix manager treats its two tools asymmetrically: erlang falls back to a hardcoded `^26` (lib/modules/manager/mix/artifacts.ts), while elixir has no default and floats to the newest release satisfying the `elixir:` requirement in mix.exs. Every repository here allows `~> 1.17` or wider, so Renovate picks Elixir 1.20.x -- which requires OTP 27+ and refuses to install against OTP 26. The install step then aborts with `ERROR! Unsupported Erlang/OTP version, expected Erlang/OTP 27+`, Renovate reports `Command failed: install-tool elixir vX.Y.Z`, mix.lock is left untouched, and the resulting empty commit is dropped (`Detected empty commit - aborting git push`). Every mix lock file update has failed this way since Elixir 1.20.0 was released on 2026-06-03.", | ||
| "Constrain erlang rather than elixir. Pinning elixir low would only avoid the mismatch by accident, and would freeze lock file generation on an Elixir line the projects no longer target. This affects only the toolchain Renovate installs to regenerate lock files; the OTP/Elixir matrix in each repository's CI is configured separately and is unchanged.", | ||
| "Do not narrow this to an exact version. Renovate resolves the constraint against containerbase's erlang prebuilds, which are published as four-component versions (27.3.4.15, not 27.3.4); a range keeps picking up their patch releases." | ||
| ], |
There was a problem hiding this comment.
description フィールドに非常に長い技術的背景説明が含まれています。Renovate の description はダッシュボードや PR コメントに表示される設定の概要を示すためのものであり、このような詳細な根拠・経緯の記述は通常 PR 本文や CHANGELOG に置くべきです。
設定ファイル内に長文の説明を埋め込むと、将来の保守担当者が設定の全体像を把握しにくくなります。description は簡潔な1〜2文程度に留め、詳細は PR 本文や別ドキュメントを参照するよう案内する形が望ましいです。
例:
"description": [
"Constrain Erlang to ^27 so Renovate can regenerate mix.lock with Elixir 1.20+, which requires OTP 27+."
]There was a problem hiding this comment.
裏取りしたうえで、据え置きます。根拠を 2 点挙げます。
1. 指摘の前提が事実と異なります
「description はダッシュボードや PR コメントに表示される設定の概要を示すためのもの」とありますが、Renovate 本体の定義(lib/config/options/index.ts:451)はこうなっています。
{
name: 'description',
description: 'Plain text description for a config or preset.',
type: 'array',
subType: 'string',
stage: 'repository',
...
}「config または preset のプレーンテキスト説明」とあるだけで、Dependency Dashboard や PR コメントへの表示については記述がありません。表示先が限られるという前提が成り立たないため、「簡潔にすべき」という結論もそこからは導けません。
2. 長文の description はこのリポジトリの確立した慣行です
本 PR の書き方は既存プリセットと同じで、むしろ揃えにいったものです。
| ファイル | top-level description |
最長 |
|---|---|---|
default.json |
3 項目 | 734 文字 |
latex.json |
3 項目 | 634 文字 |
template.json |
2 項目 | 397 文字 |
default.json の packageRules[1] |
— | 771 文字 |
elixir.json の既存 packageRules[0] |
— | 535 文字 |
本 PR の elixir.json |
3 項目 | 825 文字 |
default.json は platformAutomerge: false を選んだ理由、automergeStrategy: "squash" の理由、osvVulnerabilityAlerts を使う理由をそれぞれ数百文字で書いています。ご提案の 1〜2 文形式に本 PR だけ合わせると、かえってこのリポジトリの中で浮きます。
なぜこの内容をファイル内に置くのか
constraints.erlang は、背景を知らない人が「不要な固定に見える」として消しやすい設定です。実際これは 2026-05-23 の Renovate 導入以降 mix.lock 更新が一度も成功していなかった原因そのもので、消せば再び全リポジトリで静かに壊れます。PR 本文はファイルを開いた人の視界に入りませんが、description は入ります。再発しやすさに対して説明の置き場所を選んでいる、という判断です。
なお挙動には影響しない項目で、renovate-config-validator --strict も通過しています。以上より、コード変更なしで据え置きます。
レビュー対応まとめ指摘 1 件、いずれも根拠を確認したうえで据え置きとしました。コード変更はありません。
据え置きの理由は 2 点です。
CI
merge 後に必要な作業このプリセットは各リポジトリから移動タグ
3 まで終えて初めて完了です。 |
概要
elixir.jsonにconstraints.erlang: "^27"を追加し、Renovate による mix.lock 更新が一度も成功していない状態を解消します。症状
このプリセットを使う 5 リポジトリ(tenbin_dns / tdig / tenbin_ex / tenbin_cache / elixir_dnstap)で、mix の依存更新 PR が以下の 3 点セットで止まっていました。
renovate/artifacts = failure : Artifact file update failureFile name: mix.lock/Command failed: install-tool elixir v1.20.3⚠️ WARN: Detected empty commit - aborting git pushgit log -- mix.lockを 5 リポジトリすべてで確認したところ、2026-05-23 の Renovate 導入以降、Renovate が mix.lock の更新に成功した記録は一件もありません(それ以前のgithub-actions[bot]コミットは置換前の別ワークフローによるもの)。原因
Renovate の mix マネージャ(
lib/modules/manager/mix/artifacts.ts)は Erlang と Elixir を非対称に扱います。^26にハードコードされた既定を持つmix.exsのelixir:要件を満たす最新版まで浮く対象リポジトリはすべて
~> 1.17(elixir_dnstap は~> 1.14)なので Elixir 1.20.x が選ばれます。Elixir 1.20 は OTP 27 以上が必須(公開される precompiled zip が otp-27/28/29 のみ)で、^26の Erlang とは両立しません。破綻の起点は 2026-06-03 の Elixir 1.20.0 リリースです。それ以前の 1.19.x は OTP 26 に対応していたため成立していました。エラー中の Elixir 版が時期とともに動いていた(7月
v1.20.2→ 8月v1.20.3)のも、最新を追っている裏付けです。再現と検証
renovate/renovateイメージ(44.23.3)で対照実験を行いました。変えた変数は Erlang のバージョンのみです。A: Renovate の既定と同じ組み合わせ
B: 本 PR の設定
再現手順:
設計上の判断
erlang を制約し、elixir は制約しない。 elixir を低く固定しても、Erlang が
^26のままなので偶然噛み合うだけで、しかも各プロジェクトが対象としていない Elixir 系列に lockfile 生成を固定してしまいます。範囲指定(
^27)にして特定版に固定しない。 Renovate は containerbase の erlang prebuild に対して制約を解決しますが、これは 4 桁バージョン(27.3.4.15、27.3.4ではない)で公開されています。範囲にしておけば patch リリースを拾い続けます。CI には影響しません。 本設定は Renovate が lockfile 再生成のために導入する toolchain にのみ効きます。各リポジトリの OTP/Elixir マトリクスは別途 workflow で設定されており、変更されません。
紛らわしい先行事例との切り分け
containerbase #6924(2026-06-16)は
elixir.shの zip 名対応表を直したもので(既定 otp-26 → otp-27)、renovate discussion #38668 の Elixir 1.19 × otp-25 と同型です。症状も時期も似ていますが本件とは別問題で、現行イメージでは既に修正済みであることを確認しています(zip 名は正しく解決されており、落ちているのは Erlang 側)。この混同のため、当初はconstraints.elixirでの回避を検討していましたが、それでは直りません。検証済み
renovate-config-validator --strict: エラーなしmerge 後の作業
このプリセットは各リポジトリから移動タグ
v1経由で参照されているため、merge しただけでは反映されません。v1.37.0→v1.38.0)。公開済みタグの張り替えは行わないv1を新しい main コミットへ張り直すrebase-branchチェックボックスを ON にし、mix.lock が実際に更新されることを確認する(Renovate は失敗した artifact を自動再試行しないため、既存 PR の赤は失敗時点で凍結しています)