Commit Graph

880 Commits

Author SHA1 Message Date
Jamie Tanna d4614acc54 test(config/presets/github): cover that preset strings don't include a host
As part of recent changes to harden Host Rules, a test case was noticed
to be missing - we assume that a preset string doesn't include something
that looks like a host, but we didn't previously have a test to enforce
this.

This wasn't exploitable before our recent changes, but this test is here
to ensure we have a test covering this existing functionality.

Co-authored-by: Claude Fable 5 <jamie.tanna+claude-code@mend.io>
Co-authored-by: Claude Opus 5 <jamie.tanna+claude-code@mend.io>
2026-09-10 14:52:01 +01:00
Jamie Tanna acb46bc706 fix(config): surface blocked hosts more clearly when resolving config
With the new hardening on blocking internal hosts with outbound HTTP
calls, we should build on top of this to ensure that the UX is better
when a preset is blocked.

We can do this by making sure that when `HOST_BLOCKED`, we report a more
targeted config validation error.

We can also make sure to pass around our `internalGrants`, similar to
how we currently allow an admin's `headers` are handled.

Co-authored-by: Claude Fable 5 <jamie.tanna+claude-code@mend.io>
Co-authored-by: Claude Opus 5 <jamie.tanna+claude-code@mend.io>
2026-09-10 14:52:01 +01:00
Jamie Tanna 29a7b9c5f5 fix(util/http): allow blocking requests to internal hosts
To harden Renovate from being able to access internal hosts, we can add
a level of enforcement on all outgoing requests.

We can split this into two types of traffic:

- where the response becomes configuration (i.e. `extends`)
- where the response does not become configuration (i.e.
  `customDatasources`)

To harden this, we enforce on every outbound HTTP request:

- check the resolved URL before anything, including cache lookups, can
  proceed
  - catching IP-literal hosts in every spelling the WHATWG URL parser normalizes
  - blocking known Cloud metadata IPs, too
- a `dnsLookup` wrapper validates every address a hostname resolves to,
  on every new connection, which also defeats DNS rebinding
- a `beforeRedirect` hook re-runs the URL check on each redirect
  target, with the new host getting its own grant lookup rather than
  inheriting the original host's verdict

We default this enforcement to `warn`, to not break existing deployments
and instead give them visibility into required configuration before a
future major version (major+1) enables `block` by default.

We'll also remove allow in a future major version (major+2), to give
time for admins to migrate over.

When a request is blocked, this degrades like an unreachable registry,
so a Renovate run isn't aborted by a blocked request.

As part of this, we can also provide a worked example of the different
cases that do/do not need allowing via `hostRules`.

Requests through an admin-configured proxy resolve DNS at the proxy, so
`dnsLookup` never runs for them, so we can resolve the hostname locally
and validate every address before the request instead.

This doesn't catch everything, especially as the proxy may resolve the
name to a different result, and if we can't resolve the name - possible
in locked-down corporate environments - we fail open.

Child processes aren't currently able to route traffic through
Renovate's HTTP layer, so are unrestricted.

Co-authored-by: Claude Fable 5 <jamie.tanna+claude-code@mend.io>
Co-authored-by: Claude Opus 5 <jamie.tanna+claude-code@mend.io>
Co-authored-by: Claude Sonnet 5 <jamie.tanna+claude-code@mend.io>
2026-09-10 14:52:01 +01:00
Will Slattum b5121c7b15 feat(presets): vale package versions custom manager (#45817)
* feat(presets): vale package versions custom manager

Co-Authored-By: Claude Code

* fix(presets): fix lint and type errors in valeVersions tests

Co-Authored-By: Claude Code

* fix(presets): fix vale.ini file pattern to use regex delimiter syntax

Co-Authored-By: Claude Code

* test(presets): update valeVersions examples and add non-pinned case

Co-Authored-By: Claude Code
2026-09-09 17:04:30 +00:00
renovate[bot] 9d74625d93 build(deps): update dependency prettier to v3.9.6 (main) (#45815)
* build(deps): update dependency prettier to v3.9.6

| datasource | package  | from  | to    |
| ---------- | -------- | ----- | ----- |
| npm        | prettier | 3.8.1 | 3.9.6 |

* chore: prettier fixes

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Michael Kriese <michael.kriese@mend.io>
2026-09-09 13:50:03 +00:00
Michael Kriese 7c03a7e4ec feat(presets/workarounds): group commander major update packages (#45801) 2026-09-09 09:31:16 +00:00
Sanjay Doppalapudi ff76622791 test(presets): validate internal presets the way they are consumed (#45712)
The internal preset validation ran `validateConfig()` with `isPreset`,
which allows selectors such as `matchManagers` at the top level of a
preset. That is how #39738 shipped `helpers:goXPackages*` with a bare
`matchManagers` and broke `inheritConfig` users: the real resolution
paths validate the resolved config without that leniency.

Resolve each preset through `extends`, as a repository or inherited
config would, and validate the result without `isPreset`. `packages:`
and `monorepo:` presets are selector-only `packageRules` fragments, so
they are resolved from within a `packageRules` entry instead.
2026-09-07 14:22:27 +00:00
Adam Setch 1bc6686671 feat(presets): remove duplicate octokit preset (#45708)
Signed-off-by: Adam Setch <adam.setch@outlook.com>
2026-09-07 06:52:02 +00:00
Adam Setch 10c5232778 feat(preset): @octokit group preset (#45643)
Signed-off-by: Adam Setch <adam.setch@outlook.com>
2026-09-04 12:36:35 +00:00
Jonah Snider 5f5e58b822 fix(presets): fix javaLTSVersions not preserving partial semver for mise Java versions (#44899)
* fix(presets): fix javaLTSVersions not preserving partial semver for mise Java versions

Signed-off-by: Jonah Snider <jonah@jonahsnider.com>

* test(presets): remove obsolete type assertion

Signed-off-by: Jonah Snider <jonah@jonahsnider.com>

* test(presets): type package rule result

Signed-off-by: Jonah Snider <jonah@jonahsnider.com>

---------

Signed-off-by: Jonah Snider <jonah@jonahsnider.com>
2026-09-04 11:15:21 +00:00
Sebastian Poxhofer 05e1271376 test: use expect().resolves instead of awaiting inside expect (#45672)
Enables the vitest/prefer-expect-resolves oxlint rule and applies its
autofix across the spec suite.
2026-09-04 10:40:46 +00:00
Sebastian Poxhofer df8c4051ff test(core): replace file snapshots with explicit assertions (#45660)
* test(core): replace file snapshots with explicit assertions

* test(core): replace inline snapshots with explicit assertions

* test(core): assert on relevant sections instead of full bodies

* test(core): drop assertions implied by full equality

* test(core): address review feedback

* test(core): use resolves instead of awaiting inside expect
2026-09-04 10:28:55 +00:00
Sebastian Poxhofer 3412de84ce refactor(linters): enable oxlint rule prefer-named-capture-group (#44714)
* refactor(lint): enable oxlint prefer-named-capture-group at error

Numbered capture groups (m[1], $1) break silently when a regex is
edited and group order shifts. Named groups (?<name>...) make intent
explicit and accesses (m.groups.name) resilient to reordering.

This enables the built-in eslint rule prefer-named-capture-group,
surfacing 321 pre-existing unnamed-group violations across ~121
files to be burned down incrementally in follow-up commits.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017RxHFNsSworLcv23xXk9Hk

* refactor(versioning): name capture groups for prefer-named-capture-group

Burns down all prefer-named-capture-group violations in
lib/modules/versioning/. Unused capture groups (only reachable via
.test() or whole-match) become non-capturing (?:...); groups whose
value is read become named (?<name>...), with accesses rewritten to
match.groups!.<name> (or destructured from .groups!) instead of
positional match[n]/$n so a future edit that reorders groups cannot
silently break the reader.

Two cases needed extra care to preserve behavior:
- semver/common.ts and poetry/patterns.ts split() on a regex whose
  capture group is relied upon by String.prototype.split to keep the
  separator in the output array; the group is kept capturing and
  named, not converted to non-capturing.
- hashicorp/convertor.ts's semverRegex is only ever embedded via
  .source into a named `(?<version>...)` group elsewhere and never
  matched directly, so its internal groups are safe to make
  non-capturing.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017RxHFNsSworLcv23xXk9Hk

* refactor(datasource): name capture groups for prefer-named-capture-group

Burns down all prefer-named-capture-group violations under
lib/modules/datasource/. Same treatment as the versioning module:
unused groups become non-capturing, used groups get names, and
positional match[n] reads move to match.groups!.<name>.

docker/ecr.ts and pypi/common.ts keep their positional destructuring
(`const [, region] = ...`, `!!m &&`) working unchanged since a named
capture group is still addressable by its numeric index in
JavaScript — only pypi/common.ts's access was rewritten to
m.groups!.owner for clarity since it read a single group directly.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017RxHFNsSworLcv23xXk9Hk

* refactor(platform): name capture groups for prefer-named-capture-group

Burns down all prefer-named-capture-group violations under
lib/modules/platform/. Uses the repo's established idiom of
`match.groups?.<name>` after an optional-chained exec()/match() (see
bitbucket/index.ts hostnameWithoutApiPrefix, gerrit/utils.ts
extractSourceBranch) and `$<name>` in String.replace replacement
strings. Unused groups (markdown/HTML massaging regexes matched only
to be stripped) become non-capturing.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017RxHFNsSworLcv23xXk9Hk

* refactor(manager): name capture groups in ansible-galaxy for prefer-named-capture-group

Burns down prefer-named-capture-group violations in
lib/modules/manager/ansible-galaxy/. newBlockRegEx and blockLineRegEx
dropped their unused outer wrapping group (only the inner key/value
groups were ever read) and named the key/value groups; nameMatchRegex's
remaining unused groups (protocol/user-info/port alternatives) become
non-capturing since only source/hostname/depName/version are read via
.groups. Accesses in collections.ts and roles.ts move from
lineMatch[2]/lineMatch[3] to lineMatch.groups!.key/value.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017RxHFNsSworLcv23xXk9Hk

* refactor(manager): name capture groups for prefer-named-capture-group (batch 1)

Burns down prefer-named-capture-group violations across the first
alphabetical slice of lib/modules/manager/ (ansible through mix).
Used groups get names with accesses rewritten to match.groups!.<name>
(or $<name> in replacement strings); groups whose value is never read
become non-capturing (?:...). Two String.split() call sites
(git-submodules/extract.ts, meteor/extract.ts) keep the established
"named group still readable positionally from the split() result
array" idiom instead of restructuring the destructuring.

bazel/rules/go.ts and buildpacks/extract.ts dockerRef verified
byte-for-byte equivalent before/after via a quick node script since
several nested groups collapsed to non-capturing at once.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017RxHFNsSworLcv23xXk9Hk

* refactor(manager): name capture groups for prefer-named-capture-group (batch 2)

Burns down prefer-named-capture-group violations across the remaining
alphabetical slice of lib/modules/manager/ (npm through terragrunt).
Same treatment as batch 1: used groups get names with accesses moved
to match.groups!.<name> or $<name> replacement strings; unused groups
(wrapping alternations, quantifier repetition groups only consumed via
.test(), or values never read from a match) become non-capturing.

terraform/extractors/others/modules.ts and terragrunt/modules.ts share
near-identical URL-parsing regexes (github/bitbucket/git/azure-devops
ref matchers) — the same set of always-unused `depth=`/`&depth=`
query-string groups is converted to non-capturing in both files.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017RxHFNsSworLcv23xXk9Hk

* refactor(config): name capture groups for prefer-named-capture-group

Burns down prefer-named-capture-group violations in lib/config/.
Used groups get names with replacement strings switched from $1/$2 to
$<name>; the unused separator group in schedule-migration.ts's
afterBeforeRegex becomes non-capturing. presets/parse.ts keeps
assigning through the already-declared `repo` variable rather than
array-destructuring the exec() result, matching this file's existing
style for the other named-group regexes just above it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017RxHFNsSworLcv23xXk9Hk

* refactor(util): name capture groups for prefer-named-capture-group

Burns down prefer-named-capture-group violations across lib/util/ and
lib/logger/. Same treatment as previous batches: named groups for
values that are read (via $<name> replacement strings, .groups!.<name>,
or destructured from .groups), non-capturing groups where nothing reads
the captured value (e.g. semantic.ts's angular-commit .test()-only
regex). www-authenticate.ts's tokenizer entries each get their own
named group but keep accessing the match generically via match[1],
since a positionally-indexed named group is still readable by index.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017RxHFNsSworLcv23xXk9Hk

* refactor(workers): name capture groups for prefer-named-capture-group

Burns down prefer-named-capture-group violations in lib/workers/.
Used groups get names with $<name> replacement strings; unused
wrapper/alternation groups (e.g. semantic-commit-message.ts's optional
scope/issue wrappers, release-notes.ts's date-heading and
horizontal-rule matchers that are only probed with .search()/replaced
wholesale) become non-capturing.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017RxHFNsSworLcv23xXk9Hk

* refactor(test,tools): name capture groups for prefer-named-capture-group

Finishes the prefer-named-capture-group burndown: test/, tools/, and
vitest.config.mts. Groups whose value is read get names ($<name>
replacement strings or .groups.<name> access); groups only used via
.test()/.search() or that feed a .match(/…/g) call (whose return value
is the array of whole matches, not captured groups) become
non-capturing.

This is the last remaining slice of pre-existing violations, so the
rule (already enabled at error repo-wide) now passes with zero
findings across the whole codebase.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017RxHFNsSworLcv23xXk9Hk

* fix prettier failure

* refactor(lint): use a named capture group in `v8-ignore-no-count`

New rule from main trips the newly enabled `prefer-named-capture-group` oxlint rule.

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-09-03 09:54:40 +00:00
Adam Setch 3206c6ada5 feat(preset): group vite+ (#45631)
* feat(preset): vite plus

Signed-off-by: Adam Setch <adam.setch@outlook.com>

* feat(preset): vite plus

Signed-off-by: Adam Setch <adam.setch@outlook.com>

---------

Signed-off-by: Adam Setch <adam.setch@outlook.com>
2026-09-02 16:05:33 +00:00
Sebastian Poxhofer 97327c3d28 refactor(linters): add prefer-coerce-array rule (#45492)
* refactor(linters): add prefer-coerce-array rule

Refs #45217

`x ?? []` and `coerceArray(x)` say the same thing, but only the second
one is searchable and consistent. The rule flags a `??` whose default is
a bare `[]` literal; a non-empty default or an asserted `([] as Foo[])`
still passes.

Applying it across `lib/` surfaced three spots the helper cannot cover,
which keep `?? []` with a disable comment or a narrower fix:

- `package-files.ts` destructures a tuple, which `coerceArray()` widens
- `generate.spec.ts` defaults `matchAll()`, an iterator rather than an array
- `mise/upgradeable-tooling.ts` declares a `Record` return, so its
  `?? []` fallback was wrong to begin with and becomes `coerceObject()`

* refactor(platform/github): hoist the GHE version header lookup

`Object.entries(...).find()` returns a tuple, and defaulting it with
`?? []` only to destructure the second element reads as an array
default when it is not one. Name the found entry instead, which drops
the `?? []` rather than routing a tuple through `coerceArray()`.
2026-08-28 09:27:58 +00:00
Sebastian Poxhofer 1012b34025 feat(presets): add @oxlint/* packages to packages:linters and monorepo:oxlint (#45466)
* feat(presets): add @oxlint/* packages to packages:linters

Groups oxlint plugin packages together with oxlint itself.

* feat(presets): add @oxlint/plugins to monorepo:oxlint

* test(presets): update linters snapshots for @oxlint/**
2026-08-26 07:14:28 +00:00
Sebastian Poxhofer 69e05233e8 feat(manager/github-actions): add workflow depType for reusable workflow calls (#45443)
* feat(manager/github-actions): add `workflow` depType for reusable workflow calls

A job-level `uses:` pointing at `owner/repo/.github/workflows/<file>.yml@<ref>`
calls a reusable workflow rather than running an action, but both got the
`action` depType, so `matchDepTypes` could not tell them apart.

Give a reference whose sub-path is exactly `.github/workflows/<file>.yml|.yaml`
the new `workflow` depType. Every other sub-path, such as
`github/codeql-action/init`, stays an action.

The existing depType-keyed sites now accept both values, so nothing else
changes behaviour: the `actions.lock` file records reusable workflow calls too,
and `helpers:pinGitHubActionDigests` pinned them before this split.

* Apply suggestions from code review

Co-authored-by: Sebastian Poxhofer <secustor@users.noreply.github.com>
2026-08-25 11:52:05 +00:00
Sebastian Poxhofer 9d0e535934 feat(config): add overrideDescription option (#45400)
* feat(config): add `overrideDescription` option

Presets which extend many other presets used an `ignoreDeps: []` sentinel
to keep their own one-line summary in the onboarding PR instead of one line
per extended preset. Replace that hack with an explicit `overrideDescription`
option, which is consumed and removed while presets are resolved.

The resolved descriptions are unchanged. Repositories which set
`ignoreDeps: []` in their own config no longer lose all preset descriptions
in their onboarding PR.

* fixup! feat(config): add `overrideDescription` option

* fixup! feat(config): add `overrideDescription` option

* fixup! feat(config): add `overrideDescription` option
2026-08-24 11:22:31 +00:00
Sebastian Poxhofer 874f3e3953 feat(presets): support relative paths inside a preset repo (#45211)
* feat(presets): support relative `extends` references inside presets

Presets which are fetched from a repository can now reference sibling
presets relatively, instead of repeating their own source, repository and
tag:

- `./x` resolves against the directory of the preset file with the
  reference
- `../x` resolves against the parent directory of that file
- `/x` resolves against the root of the preset repository

The source, repository and tag of the preset which holds the reference are
inherited, so `./system/registries` inside `github>org/repo#v2.0.0`
resolves to `github>org/repo//system/registries#v2.0.0`. A reference which
resolves to the `default` preset at the repository root is emitted in the
plain repository form, so `github>org/repo#v2.0.0`, which keeps it
deduplicated against the way consumers spell the very same preset.

A reference must stay inside its repository, its final path segment must
name a preset file rather than a directory, and it can carry parameters
but neither its own `#tag` nor a `:subPreset`. Parameters may contain a
Handlebars template, including a sub-expression such as
`./group({{ lower (env.TEAM) }})`, while a templated path cannot be
resolved and is left alone. A reference must also not carry a source
prefix, so strings such as `local>./x` or `github>../x` are rejected as
invalid.

References are canonicalized lazily, while the preset which holds them is
fetched in `getPreset()`, after migration and massaging. Only presets from
a repository hosted source, so `github`, `gitlab`, `gitea`, `forgejo` and
`local`, are walked. A relative reference which has no parent preset, for
example in a repository config, an inherited config or in `globalExtends`,
is reported as a validation error for that entry.

Relative entries of a preset's `ignorePresets` are resolved in the same
way, so a preset can neutralize a preset which it references relatively,
as long as the consuming repository does not set `ignorePresets` itself.
A reference which cannot be resolved is kept raw and logged, which lets
consumers neutralize it with `ignorePresets`, and it only fails per entry.

Because relative references cannot be resolved while a config is
validated, they are stripped from a `packageRules` entry before its
presets are resolved. A rule which is then left without any selector is
reported as a warning rather than as an error, since the stripped preset
may still provide the missing selectors. Rules without relative references
keep failing hard as before.

The `renovate-config` manager reports relative and unparsable `extends`
entries with a skipReason, instead of dropping them, and ignores templated
entries which can only be resolved at runtime.

Behavior change: an `extends` string which starts with `./`, `../` or `/`,
such as `./x`, `../x` or `/x`, is now read as a relative reference. Before
it was parsed as a local repository name, which in rare cases could
accidentally resolve through the dot segment normalization of the request
URL. For the same reason a source prefixed relative looking string, such
as `local>/org/repo`, `github>./x` or `npm>./x`, is now rejected as an
invalid preset.

* fix(presets): parse scoped npm presets with explicit `npm>` prefix

`npm>@scope/name` was parsed as a local preset, because the heuristic for
local presets matched before the `npm>` prefix was stripped.

Every other use of `npm>` keeps its current behavior, so `npm>foo` still
falls back to the npm package `renovate-config-foo`, `npm>owner/repo` is
still resolved as a local preset and nested prefixes such as
`local>npm>foo` keep working.

* refactor: remove duplicated preset source

* docs: streamline new docs

* add more docs for skipReason and add requested changes

* Apply suggestions from code review

Co-authored-by: Jamie Tanna <github@jamietanna.co.uk>

* test(presets): drop object literal type casts in relative preset spec

---------

Co-authored-by: Jamie Tanna <github@jamietanna.co.uk>
2026-08-13 13:54:50 +00:00
Jamie Tanna 23dd5682be test(config/validation): ensure that errors and warnings are always checked and that we don't length check (#45065)
* test(config): ensure that `errors` and `warnings` are always checked

As a follow-up to 6e411730a0, we can make
sure that we have a linter rule to make sure that this is consistently
checked.

Co-authored-by: Claude Sonnet 5 <jamie.tanna+claude-code@mend.io>

* test(config/validation): ensure we don't use length checks

As it hides the actual messages, and makes it harder to know what is
being checked.

Co-authored-by: Claude Sonnet 5 <jamie.tanna+claude-code@mend.io>

---------

Co-authored-by: Claude Sonnet 5 <jamie.tanna+claude-code@mend.io>
2026-08-04 14:46:53 +00:00
Jamie Tanna f89efb2233 feat(presets): add security:minimumReleaseAgePypi (#44992)
Per suggestion in #42610.
2026-08-03 10:04:56 +00:00
Jamie Tanna 8af53eb629 fix(presets): add missing updateTypes to exclusions in security:minimumReleaseAge* (#44990)
As these have been out-of-sync with what is and is not currently
supported per our documentation.

Co-authored-by: Claude Opus 4.8 <jamie.tanna+claude-code@mend.io>
2026-08-03 07:19:35 +00:00
Jamie Tanna 786c2ee5f2 fix(presets): refactor security:minimumReleaseAge* to reduce duplication (#44989)
As a means to make it easier - in the future - to add new
`security:minimumReleaseAge*` presets, we can refactor the 2  mostly
duplicated presets into dynamically generated presets.

Although this is not quite a "rule of 3" scenario, this shape does look
consistent, and so we can perform an early refactor.

Although this should be like-for-like, we can release this as a `fix` to
get early feedback on whether it changes anything.

Co-authored-by: Claude Opus 4.8 <jamie.tanna+claude-code@mend.io>
2026-08-02 12:56:35 +00:00
Tobias Bieniek fa7f6ec002 feat(presets): add security:minimumReleaseAgeCrate preset (#44659)
* feat(presets): add `security:minimumReleaseAgeCrates` preset

This adds a three-day minimum release age for the `crate` datasource and preserves the existing exception for pin updates.

The preset is based on the existing `security:minimumReleaseAgeNpm` preset, with its exceptions limited to update types relevant to crates.io.

It accounts for the initial 72-hour crates.io deletion window and gives scanners time to identify malicious releases.

* fix(presets): scope crate release age by update type

This replaces the pin-specific override with one package rule for `major`, `minor`, and `patch` crate updates. It keeps updates without release timestamps out of the cooldown.

The focused tests cover included and excluded update types.

* fix(presets): warn about unsupported crate update types

This exempts lock file maintenance, replacement, and pin updates from the crate minimum release age policy. It adds PR body notes that explain why each update requires manual age validation.

* fix(presets): use singular crate release age name

This renames `security:minimumReleaseAgeCrates` to `security:minimumReleaseAgeCrate`. It aligns the preset with the `crate` datasource name before release.

* Update lib/config/presets/internal/security.preset.ts

Co-authored-by: Jamie Tanna <github@jamietanna.co.uk>

---------

Co-authored-by: Jamie Tanna <jamie.tanna@mend.io>
Co-authored-by: Jamie Tanna <github@jamietanna.co.uk>
2026-07-31 13:19:53 +00:00
renovate[bot] 196101a6bb chore(deps): update dependency oxlint-tsgolint to v0.25.0 (main) (#44424)
* chore(deps): update dependency oxlint-tsgolint to v0.25.0

| datasource | package         | from   | to     |
| ---------- | --------------- | ------ | ------ |
| npm        | oxlint-tsgolint | 0.23.0 | 0.25.0 |

* chore: fix new no-unnecessary-type-assertion findings

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Sebastian Poxhofer <sebastian.poxhofer@stedi.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-28 07:04:21 +00:00
Felipe Santos ddbbb687f9 feat: add commitTrailers option (#44651)
* feat(config): add commitTrailers option

Renovate's only way to add git trailers today is `commitBody`, but it's
broken on Gerrit: Renovate appends its own `Renovate-Branch`/`Change-Id`
block after the body, so user trailers end up in a non-final message
block and are not real trailers per git-interpret-trailers rules.

This adds a `commitTrailers: string[]` option (each entry a full
`Key: value` line), standardizes Gerrit's own trailers through the same
mechanism so user trailers and platform trailers merge into one final
block, and verifies the result at runtime with `git interpret-trailers`
(commitlint's trailer-exists rule uses the same approach). The
`:gitSignOff` preset is migrated from `commitBody` to `commitTrailers`
so sign-off works correctly on Gerrit too.

* Simplify a bit

* Simplify docs even more

* Simplify another bit

* Add comment about rawExec

* Drop runtime commit trailer verification

Bypass simple-git only to emit a proactive warn log is not worth the
extra surface area. Config validation already enforces Key: value form.

* test(git): cover string commit message with trailers

Codecov patch failed on the partial ternary branch that normalizes a
string message before appending trailers.

* test(gerrit): fix trailers test after scm rework

Align the commitTrailers merge test with the post-rework Gerrit SCM:
use fakeSha helpers and drop the removed force flag.

* Improve wording on gitSignOff docs

* Validate compiled commitTrailers as well

* Improve trailers example

* Use isString instead

* Update lib/util/git/index.ts

Co-authored-by: Sebastian Poxhofer <secustor@users.noreply.github.com>

* Update lib/workers/repository/onboarding/branch/create.ts

Co-authored-by: Sebastian Poxhofer <secustor@users.noreply.github.com>

* Format code

* Fix type-check

* Fix tests

---------

Co-authored-by: Sebastian Poxhofer <secustor@users.noreply.github.com>
2026-07-27 06:49:09 +00:00
Micah Nagel c33364d126 fix(presets/helpers): ensure that pinGitHubActionDigestsToSemver preset only applies to actions (#44830)
fix: ensure that pinGitHubActionDigestsToSemver preset only applies to actions

Signed-off-by: Micah Nagel <micah.nagel@defenseunicorns.com>
2026-07-24 13:58:35 +00:00
Sebastian Poxhofer 58771bb183 refactor(linters): enabled oxlints func-style (#44691)
* refactor(oxlint): enable func-style

Enable the built-in func-style rule as ["warn", "declaration"] so that
named functions are written as function declarations instead of arrow or
function expressions assigned to variables, matching the convention in
docs/development/best-practices.md (Functions section). Inline arrow
callbacks are unaffected; prefer-arrow-callback continues to cover the
callback direction.

Severity is "warn" instead of "error" as a fallback: a repo-wide run
found 196 pre-existing violations (70 in spec files, 126 in production
code), exceeding the 80-violation fix threshold for this change, so no
violations were fixed here. Follow-up work can convert the offenders
(largest clusters: lib/modules/versioning/ruby, lib/util/cache/package,
lib/modules/versioning/maven, lib/modules/versioning/gradle) and then
raise the rule to "error". Note oxlint 1.72 also supports an
allowTypeAnnotation option if typed consts should remain arrows.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* ci(linter): enforce func-style at error

* refactor(func-style): convert const-arrow functions to declarations in with-cache spec and ruby versioning

* refactor(func-style): convert const-arrow functions to declarations in maven/gradle modules

* refactor(func-style): convert const-arrow functions to declarations in remaining versioning modules

* refactor(func-style): convert const-arrow functions to declarations in platform modules

Discriminated-union closures (ensureCommentRemoval byTopic/byContent
helpers in github, gitlab, bitbucket, bitbucket-server) needed the
narrowed field passed explicitly as a parameter, since function
declarations are hoisted and lose the outer if-narrowing that inline
arrow-function closures previously relied on.

* refactor(func-style): convert const-arrow functions to declarations across util/manager/datasource modules

One case (logger/renovate-logger.ts logOnceFn) is kept as an arrow
function with a documented oxlint-disable: it is stored on
this.logger.once[level] and relies on lexically-bound this, which a
hoisted function declaration would not preserve.

* refactor(func-style): convert remaining const-arrow test helpers to declarations

get-updated.spec.ts: hoisted expectPipCompilePackageAndLockFile to
module scope (it only closes over the module-level pipCompile mock)
and added a documented oxlint-disable for vitest/expect-expect on its
caller test, since oxlint's expect-expect check does not trace
assertions through an externally-defined helper function.

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-19 11:14:22 +00:00
Felipe Santos 768fd6c4b1 fix(presets/workarounds): keep major-only Java Docker tags rolling (#44603)
fix(presets/workarounds): don't upgrade major-only Java Docker tags to full-precision tags
2026-07-17 12:29:08 +00:00
Sebastian Poxhofer 7436b80cda test(linter): add renovate/no-redundant-mock-reset rule (#44557)
ci(linter): add renovate/no-redundant-mock-reset rule

Vitest sets mockReset: true globally in vitest.config.mts, which resets
all mocks before each test. This makes vi.resetAllMocks() /
vi.clearAllMocks() and per-mock .mockReset() / .mockClear() calls inside
beforeEach/afterEach hooks redundant. Add a custom oxlint rule (enabled
for **/*.spec.ts and test/**) flagging such calls, and remove the 40
redundant calls it found. restoreAllMocks/mockRestore are not flagged
because restoreMocks is not enabled globally.

Two sites keep an inline disable with a reason: nested beforeEach hooks
in filter-checks.spec.ts and onboarding/pr/index.spec.ts intentionally
call mockReset() to discard once-values queued by an outer beforeEach.

Requested by maintainer review feedback (review comment 3052324877).

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-14 11:26:20 +00:00
Maxime Brunet 00e05db4f9 feat(presets): add security:gomodIndirectSecurityUpdates preset (#43222)
Add a preset to allow users to opt-in to vulnerability-only updates for go.mod
indirect dependencies.
2026-07-11 12:07:14 +00:00
Sebastian Poxhofer ad0b59f8b7 refactor(oxlint): enable vitest/require-to-throw-message (#44421) 2026-07-07 17:47:04 +00:00
Sebastian Poxhofer b106668a95 refactor(oxlint): enable typescript/no-useless-default-assignment (#44419)
- Convert `param: T | undefined = undefined` to optional `param?: T`.
- Drop dead `= false` defaults on the required cache-provider options.
- Make `GraphqlPaginatedContent.nodes/edges` optional so the `= []`
  fallbacks (load-bearing for partial GraphQL connections) are valid.
- Type the unchecked `default_branch` response field as optional so
  the `'HEAD'` fallback is retained.
- Drop `tag = undefined` from the Forgejo/Gitea/GitLab preset configs.

Part of #43845

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 16:38:48 +00:00
Sebastian Poxhofer aac8365dd6 refactor(oxlint): enable vitest/prefer-snapshot-hint (#44420)
Add distinct hints to the multi-snapshot tests flagged by the rule
and regenerate the affected snapshot files, then re-enable the rule.

Part of #43845

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 16:35:22 +00:00
Jan-Otto Kröpke e921d2754b feat(presets/workarounds): Add grafana/grafana-enterprise to grafanaDockerImageVersioning (#44058)
* fix(presets/workarounds): Add grafana/grafana-enterprise to grafanaDockerImageVersioning

* fix lint
2026-06-17 10:31:20 +00:00
Sebastian Poxhofer 7573fa98f2 feat(platform/forgejo): validate http request with schemas (#43900)
* fix(platform/forgejo): replace getJsonUnchecked with validated getJson

Migrate all getJsonUnchecked calls in the Forgejo platform module to use
getJson with Zod schemas for response validation. Add comprehensive Zod
schemas to schema.ts and update test mocks to use proper data formats.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* simplify by using infered types

* first pass on reducing duplications

* move PrStatus and CommitStatus to schema

* remove code handling BranchStatus values now ensured by schema

* remove now unreachable code

* fix coverage

* use forgejo schemas instead gitea for changelog fetching

* feat: do not abort repo on unrecognized default merge style

* refactor: instead of usages of `?? {}` with a default parameter

* tighten required parameters and move contentString generation to schema

* refactor: merge both content schemas and use a discriminated union

* refactor: move patchJson and postJson to schema validation

* Make body field required

* make schema more strict

* use DeepNullish

* simplify catch

* revert the gitea Content type change

* unify User

* log more nicely if accidentally not a file has been referenced

* Use fakeSha and LongCommitSha schema

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-16 14:22:01 +00:00
Jan-Otto Kröpke 7424be0484 feat(presets/workaround): add Grafana Docker image versioning workaround (#44015)
* feat(presets/workaround): add Grafana Docker image versioning workaround

* Update lib/config/presets/internal/workarounds.preset.ts

---------

Co-authored-by: Sebastian Poxhofer <secustor@users.noreply.github.com>
2026-06-16 09:04:12 +00:00
Jan-Otto Kröpke bb147e0414 fix(config/presets): Exclude docker.io/kindest/node from nodejs versioning (#43772)
* fix(lib): Exclude docker.io/kindest/node from nodejs versioning

* Update group.preset.ts
2026-06-08 13:33:38 +00:00
Sullivan S. f60b6930bf feat(presets): stylelint packages group (#43351)
Stylelint come with two official config packages maintained separately.

However, some major release of one of the config packages require the
latest major release of the stylelint tool and vice versa.

On that case, Renovate will propose separated update merge requests that
will never be solvable because of dependencies conflict during npm
install.

Also, as stylelint is not a mono-repo and use a completly different
releases history from its related config package, we MUST ensure
major/minor separation is deactivated to get the group process working
correctly.
2026-06-04 08:46:30 +00:00
Sebastian Poxhofer c908d078ef fix(datasource): replace getJsonUnchecked in npm/Node/PyPI/Go (#43699)
* fix(datasource): replace getJsonUnchecked with validated getJson in npm/Node/PyPI/Go

Replace all getJsonUnchecked calls in npm, Node.js, PyPI, and Go datasources
with Zod-validated getJson calls:

- datasource/npm: add NpmResponseSchema to schema.ts (shared with presets)
- config/presets/npm: migrate to NpmResponseSchema with passthrough version fields
- datasource/node-version: add NodeReleasesSchema using LooseArray
- datasource/pypi: add PypiJSONSchema (nullable+catch for lenient empty-body handling)
- datasource/go: add VersionInfoSchema for both versionInfo and getLatestVersion calls

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* some manual cleaning up

* removing schema level catch

* removing some more types

* refactor: rename schema

* fix linter errors

* remove schema suffix which hasn't been catched by linter

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-03 19:14:50 +00:00
Jamie Tanna 87b4a04a00 feat(presets): link to Octochangelog for updates to Renovate itself (#43712)
* feat(presets): link to Octochangelog for updates to Renovate itself

Related to #41809, and a recommendation I personally have for folks
updating their version of Renovate via Renovate, we can use
Octochangelog to provide a nicer breakdown of Renovate CLI release
notes, grouping them by headers i.e. "Features" and "Bug Fixes" across
all version(s) being updated.

This overrides the default link i.e.

```
https://app.renovatebot.com/package-diff?name=renovate&from=43.198.0&to=43.206.1
```

to:

```
https://octochangelog.com/compare?repo=renovatebot%2Frenovate&from=43.198.0&to=43.206.1
```

As this is a strong recommendation, we add it into `config:recommended`.

* fixup! feat(presets): link to Octochangelog for updates to Renovate itself
2026-06-01 11:13:11 +00:00
Jamie Tanna 0ccf83253f feat(presets): support fetching .jsonc files (#43680)
* test(presets): validate filename construction

Ahead of future changes to add JSONC support, we should cover explicitly
how the filename is constructed.

* feat(presets): support fetching `.jsonc` files

Similar to 6fb540d494, as we now support a
`.jsonc` file for configuration, we should make sure that we can also
use it for a preset.

We can also add additional tests across a few presets to make sure that
we have sufficient coverage.
2026-05-29 15:10:13 +00:00
Sebastian Poxhofer d004b52a91 test: add coverage for lines detected by pnpm check (#43589)
tests: add coverage for lines detected by pnpm check
2026-05-26 07:53:07 +00:00
Jamie Tanna ff6761ed23 fix(config/global): return default values for GlobalConfig.get (#42113)
* fix(config/global): return default values for `GlobalConfig.get`

As noted in #39949, it can be awkward working with in `GlobalConfig.get`, where
we would previously need to specify the default value when calling `.get`,
which could lead to drift with the defaults for a given config option.

To reduce this risk, we can rely on a given `default` for a configuration
option, providing that as a default.

Because this now definitely returns a value that matches the config option, it
now allows us to remove `!` assertions.

* fixup! fix(config/global): return default values for `GlobalConfig.get`

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>

* fixup! fix(config/global): return default values for `GlobalConfig.get`

* test(presets): remove impossible test

As it's now not possible to set a value as `undefined` (as
`GlobalConfig` will then use the default), this test no longer validates
anything.

* test(workers/repository): add mock

Now `platform` defaults to `github`, we need to mock out the call to
fetch config.

Co-authored-by: Claude Sonnet 4.6 <jamie.tanna+claude-code@mend.io>

* test: coverage

* test: coverage

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.6 <jamie.tanna+claude-code@mend.io>
2026-05-15 17:10:38 +00:00
David Murdoch 37364d29e1 feat(groups): add grouping for ghcr.io docker image (#43276)
feat(pnpm): add grouping for ghcr.io docker image

* pnpm has added a docker image to it's GitHub repository starting with v11
2026-05-12 12:53:00 +00:00
Jamie Tanna 032a5cb289 feat(presets): log the resolved configuration (without internal presets) (#39314)
As noted in #15827, it would be useful to log the "shallow"
configuration for a given repository - that is, the resolved
configuration, but without any Renovate internal presets being included.

This will be very useful for debugging, as it shows the final resulting
configuration, but without the - often very - verbose internal presets.

In the case that only this log line is shared, we can make sure to
reference the version of the Renovate CLI in the log line.

This requires building on top of changes in
6a4fa3f245 to be able to return a `merged`
and `unmerged` list of presets, and uses helpers from
c8f42e86b4, and
66b84dd12b to process the internal presets
accordingly.

Since we first worked on this, we've worked on improvements to caching
which should provide a require no additional performance overhead
performing this resolution twice.

This can also lead to being able to understand more clearly how Renovate
configuration is being used[0], as we have the final resolved configuration.

We want to make sure we don't try and merge or visit any internal
presets if we're not going to merge them, so we can avoid verbose
`config` or `visitedPresets` results.

[0]: https://www.jvt.me/posts/2024/04/14/renovate-config-sql/
2026-05-11 10:46:55 +00:00
Jamie Tanna b10905bc1e fix(util/exec): allow setting PNPM_WORKERS/PNPM_MAX_WORKERS in environment (#43131)
As found in #40942, we've been seeing increased memory usage by pnpm and
Yarn in recent months on Mend-hosted apps.

As a way to provide more control to test + validate fixes, as well as
provide self-serve for other environments, we can allowlist
`PNPM_WORKERS`/`PNPM_MAX_WORKERS` in both the "basic env vars" and the
allowed `global:safeEnv`.
2026-05-06 20:55:22 +00:00
Felipe Santos e9e8083223 feat(config-validator): optimize imports to improve speed by 5x (#43048)
Optimize config-validator
2026-05-04 08:48:32 +00:00
RahulGautamSingh 246a5ab9c6 feat(presets): add changelogUrl link for GitLab-based digest updates (#41700)
* feat(presets): add changelogUrl link for GitLab-based digest updates

* Update lib/config/presets/internal/helpers.preset.ts

Co-authored-by: Michael Kriese <michael.kriese@visualon.de>

* Update lib/config/presets/internal/helpers.preset.ts

* prettier

---------

Co-authored-by: Michael Kriese <michael.kriese@visualon.de>
2026-04-30 08:28:07 +00:00
Felipe Santos 3afa38511b refactor(datasource/npm): extract defaultRegistryUrl constant (#42794)
* feat(datasource/npm): extract defaultRegistryUrl constant

* Fix some other places

* Remove useless defaultRegistryUrls export

* Fix another place

* Undo rules.spec.ts changes, which are not related

* Fix npm presets

* Restore line removed by mistake

* Fix another test file

* Restore changes to a file which is not relevant

* Fix another small place
2026-04-27 15:24:01 +00:00