Skip to content

fix: split external dev dependencies into their own Renovate group - #1257

Merged
trieloff merged 1 commit into
fix-renovate-stability-daysfrom
split-renovate-dev-deps
Aug 31, 2026
Merged

fix: split external dev dependencies into their own Renovate group#1257
trieloff merged 1 commit into
fix-renovate-stability-daysfrom
split-renovate-dev-deps

Conversation

@trieloff

Copy link
Copy Markdown
Contributor

Summary

Splits dev dependencies out of the two external Renovate groups, so each group is
smaller and the renovate/stability-days check can actually reach green.

Stacked on #1256base is fix-renovate-stability-days, not main. Please
merge #1256 first; this PR's diff will then rebase down to just the two new rules.

The 14-day window is unchanged, and the two existing rules are left
byte-for-byte untouched.

Why

renovate/stability-days is computed per branch, not per package. Any single
member still inside its minimum release age turns the entire grouped branch
yellow (lib/workers/repository/update/branch/index.ts):

config.stabilityStatus = 'green';
// Default to 'success' but set 'pending' if any update is pending
if (timeElapsed < minimumReleaseAgeMs) {
  config.stabilityStatus = 'yellow';
  continue;
}

With every external dependency in one group, the odds of all of them being
simultaneously aged are poor, so the check is yellow most of the time and
automerge never fires. Smaller groups go quiet more often.

This is the sibling of #1256. That PR fixes members that could never age
(pin, digest); this one reduces how often a group contains a member that
merely hasn't aged yet.

Why matchDepTypes and not package names

matchDepTypes reads npm's own dependencies / devDependencies manifest
sections, so the split needs no maintenance as dependencies change — nothing to
update when a package is added. It is
Renovate's documented grouping idiom:

{
  "packageRules": [
    {
      "matchDepTypes": ["devDependencies"],
      "matchUpdateTypes": ["patch", "minor"],
      "groupName": "devDependencies (non-major)"
    }
  ]
}

Verified that the npm manager emits the plural manifest key as depType
(devDependencies, not devDependency — the singular is only prettyDepType,
used for display), so these matchers hit:
lib/modules/manager/npm/extract/common/package-file.ts.

Expected effect

On adobe/helix-cli, the root manifest has 44 runtime vs 16 dev dependencies.
Of the 14 packages currently batched in
helix-cli#2771, the split is
close to even:

group packages
external dev fixes eslint, mocha, sinon, lint-staged, semantic-release, @semantic-release/git, @eslint/config-helpers
external fixes jose, isomorphic-git, fs-extra, express-rate-limit, open, yargs

Roughly halving group size means each branch is materially more likely to be
fully aged on any given Monday.

Runtime dependencies are where a supply-chain quarantine matters most, and they
keep the existing group name — so the group that ships code to production is the
one that stays conservative.

Nothing loses its quarantine

This is the part worth reviewing closely. The two catch-all rules deliberately
keep no matchDepTypes, and the new rules are appended after them.
Renovate evaluates all packageRules and merges matches, where
later rules override earlier ones:

packageRules is a collection of rules, that are all evaluated. If
multiple rules match a dependency, configurations from matching rules will be
merged together. The order of rules matters, because later rules may override
configuration options from earlier ones, if they both specify the same option.

So a devDependency matches both the catch-all and the new rule, and the new rule
wins on groupName only. It sets no automerge, schedule or
minimumReleaseAge, so those are inherited unchanged from the rule above.

Everything that is not a devDependency still matches only the catch-all and is
therefore unaffected — including cases an enumerated matchDepTypes list would
have silently dropped:

  • runtime, optionalDependencies and peerDependencies
  • npm engines (helix-cli has "engines": {"node": ">=18"})
  • npm volta, resolutions, packageManager, overrides, pnpm
  • every non-npm managergithub-actions, dockerfile, circleci and so
    on, which have no npm depType at all

An earlier draft of this change put matchDepTypes: ["dependencies", "optionalDependencies", "peerDependencies"] on the runtime group. That would
have dropped all of the above out of both groups, losing their 14-day quarantine
and their grouping. Using an unrestricted catch-all plus a dev override avoids
that entirely.

Test plan

  • default.json is valid JSON
  • Validates against renovate-schema.json
    via ajv — before and after, so no schema regression
  • Confirmed npm's depType values are the plural manifest keys, against
    Renovate's extractor source
  • Confirmed rule ordering semantics (later rules override) against the
    packageRules docs
  • Existing external fixes / external major rules unchanged (diff is
    purely additive, +24/-0)
  • After merge: confirm four groups appear — external fixes,
    external dev fixes, external major, external dev major
  • After merge: confirm dev PRs carry automerge and the 14-day age
    inherited from the catch-all, and that engines/github-actions updates
    still land in the non-dev groups

The `renovate/stability-days` status check is computed per branch, not per
package, so a single member still inside its minimum release age turns the
whole grouped branch yellow and blocks automerge. With every external
dependency in one group, that check is yellow most of the time.

Split dev dependencies out of `external fixes` and `external major` into
`external dev fixes` and `external dev major`. On helix-cli this halves the
group size (44 runtime vs 16 dev at the root manifest), so each group reaches
a fully-aged state far more often.

Uses `matchDepTypes` rather than package names, so the split reads npm's own
manifest sections and needs no maintenance as dependencies change. This is
Renovate's documented grouping idiom.

The two existing rules are left byte-for-byte unchanged. The new rules are
appended after them, so they override only `groupName` and inherit
`automerge`, `schedule` and `minimumReleaseAge` from the rule above. The
catch-all rules deliberately keep no `matchDepTypes`, so anything that is not
a devDependency -- runtime, optional and peer deps, npm `engines`, and every
non-npm manager such as github-actions or dockerfile -- keeps its grouping and
its 14-day minimum release age.

The 14-day window itself is unchanged.
Copilot AI lite review requested due to automatic review settings August 28, 2026 09:58

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@trieloff
trieloff merged commit ffafb04 into fix-renovate-stability-days Aug 31, 2026
5 checks passed
@trieloff
trieloff deleted the split-renovate-dev-deps branch August 31, 2026 16:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants