diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 1454ca3b..2f06b99e 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -14,6 +14,15 @@ # allowlist policy, asserted by test/unit/test_install_pins.jl). Dependabot # advances those pins to the current SHA of the tracked version, so the # pinning policy itself survives updates. +# +# The #124 rule: minor and patch updates are batched, because they are +# interchangeable in kind; a MAJOR (React 18->19, vite 6->8, TypeScript +# 5->7 -- which #124 landed as one grouped PR, with the build already red +# until #140 repaired it) is a migration, not an update. Every group below +# therefore names its update-types as minor and patch ONLY: an update that +# matches no group opens its own PR, carrying its own review and its own CI +# run. test/unit/test_install_pins.jl asserts this shape, so a new group +# cannot silently go back to batching majors. version: 2 updates: # The frontend is Bun-managed: package.json plus bun.lock, no npm/yarn/pnpm @@ -25,8 +34,10 @@ updates: commit-message: prefix: "chore(deps)" groups: - frontend: + # Minor and patch in one weekly PR; majors arrive one PR each. + frontend-minor-and-patch: patterns: ["*"] + update-types: ["minor", "patch"] - package-ecosystem: "github-actions" directory: "/" @@ -35,5 +46,8 @@ updates: commit-message: prefix: "chore(deps)" groups: - actions: + # Same rule as the frontend: a new major of an action (checkout + # v4.4.0 -> v7.0.1 came through #123) gets a PR of its own. + actions-minor-and-patch: patterns: ["*"] + update-types: ["minor", "patch"] diff --git a/test/unit/test_install_pins.jl b/test/unit/test_install_pins.jl index 3d84086d..abf9e6df 100644 --- a/test/unit/test_install_pins.jl +++ b/test/unit/test_install_pins.jl @@ -346,3 +346,33 @@ end end @test isempty(extras) end + +@testset "dependabot never batches a major" begin + # #124 grouped every frontend update, and with nothing requiring the + # checks it merged five framework majors (React 18->19, vite 6->8, + # TypeScript 5->7, react-router-dom 6->7, plotly 2->4) in one PR, with + # tsc already red until #140 repaired it. A major is a migration, not an + # update, so it must open its own PR: a Dependabot group only claims an + # update when its patterns match AND its update-types admit it -- a + # dependency matching no group gets its own PR. Every group must + # therefore NAME its update-types, and never "major". The frontend must + # also stay on the "bun" ecosystem: the 2026-09 security-update runs + # proved this account's npm_and_yarn updater refuses a Bun-managed + # project (see the header of .github/dependabot.yml). + dependabot = YAML.load_file(joinpath(REPO_ROOT, ".github", "dependabot.yml")) + @test dependabot["version"] == 2 + ecosystems = Dict{String,Any}() + for u in dependabot["updates"] + @test !haskey(ecosystems, u["package-ecosystem"]) + ecosystems[u["package-ecosystem"]] = u + end + @test sort(collect(keys(ecosystems))) == ["bun", "github-actions"] + @test ecosystems["bun"]["directory"] == "/frontend" + for u in dependabot["updates"] + for (_, group) in get(u, "groups", Dict{String,Any}()) + update_types = get(group, "update-types", nothing) + @test update_types !== nothing + @test !("major" in update_types) + end + end +end