* 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>
* feat(gomod): add `gomodTidyAll` option for indirect dependency updates in Go monorepos
In Go monorepos it is common for one module to depend on another in the
same repo via a local `replace` directive (e.g. `replace example.com/shared
=> ../shared`). When Renovate updates the shared module, today `go mod
tidy` runs only in that module, leaving dependent `go.sum` files stale.
Add a new `gomodTidyAll` post-update option that:
- Discovers every `go.mod` in the repo and parses its local `replace`
directives (both inline and block form).
- Builds a dependency graph with `graph-data-structure`, with edges from
dependency → dependent, matching the existing NuGet convention.
- Runs `go mod tidy` on every transitive dependent in topological order,
propagating any `gomodTidy1.17` / `gomodTidyE` flags to the dependent
commands.
- Collects updated `go.mod` and `go.sum` files from each dependent
directory into the artifact result.
- Implies `gomodTidy` so users only need to enable one option.
Supporting changes:
- Extract `getMatchingFiles` and `resolveRelativePathToRoot` into
`lib/util/fs/util.ts` so NuGet and gomod can share them instead of
each having its own glob/resolve helper.
- Refactor `lib/modules/manager/nuget/package-tree.ts` to use the
shared `getMatchingFiles` helper and rename local variables for
clarity (`deps` → `visited`, `dep` → `dependent`) per review feedback.
Docs and config:
- Register `gomodTidyAll` in `postUpdateOptions` `allowedValues`.
- Add a row for `gomodTidyAll` to the `postUpdateOptions` table in
`docs/usage/configuration-options.md`.
- Add a "Monorepo tidying for local `replace` directives" subsection
to `docs/usage/golang.md` describing when to use the option.
Tests and coverage:
- `lib/modules/manager/gomod/package-tree.spec.ts` — parse/graph/topo
order, 100% line/branch/function coverage.
- `lib/modules/manager/gomod/artifacts-gomodtidyall.spec.ts` — six
integration cases covering subshell commands, `tidyOpts` propagation,
result collection, no-op with no dependents, error handling, and
the disabled path.
- Full `lib/modules/manager/gomod` suite (151 tests) and NuGet
package-tree suite (9 tests) pass; new code is fully covered.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refactor(gomod): use named capture groups and direct fs util imports
* fix(gomod): use `go -C` for dependent module tidy commands
A subshell only works in Docker mode, where all commands are concatenated
into a single `bash -l -c`. With `binarySource=install` or `global` each
command is split with shlex and run without a shell, so `(cd dir && ...)`
is treated as the name of a binary.
* refactor(gomod): simplify parsing of local `replace` directives
A single regex now covers both the single line form and the lines inside
a `replace (...)` block. The previous block pattern stopped at the first
`)`, so a block containing a comment with a bracket was skipped, and the
single line pattern did not allow a trailing comment.
Also keeps the NuGet package tree unchanged apart from the two helpers it
now shares.
* docs(gomod): warn against combining `gomodTidyAll` with `gomodMassage`
Massaging comments out the relative `replace` directives that
`gomodTidyAll` follows, so the two options work against each other.
* refactor(fs): use `matchRegexOrGlob` in `getMatchingFiles`
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* refactor(manager/github-actions): make `extractPackageFile` async
As part of follow-up changes we'll need to read `.github/workflows/actions.lock`
while extracting, which requires we are an async function.
To reduce the diff, we can prefactor this change first.
Co-authored-by: Claude Opus 5 <jamie.tanna+claude-code@mend.io>
* feat(manager/github-actions): add support for Actions Lockfiles
In June, GitHub announced a public preview for the GitHub Actions
lockfile functionality[0].
To add support for this, we can extend our support for GitHub Actions to
handle the presence of an `actions.lock` file, and if so, update it
after updating other manager files.
Although our ideal case would be to have a pure Typescript-based parsing
and updating of the `actions.lock`, due to the early nature of the
Actions Lockfile syntax, and its lack of stability, we can invoke the
`gh actions-lock` extension when updating.
After the schema has reached a v1 release, we can then migrate to
Typescript.
To make sure that the version of the `actions-lock` extension is always
version pinned, we can expose a `constraints` to control the version,
but allow Renovate to keep the pinned default updated as needed.
This doesn't imply that we have support for `lockFileMaintenance`, as we
are only updating the lockfile when there is a dependency update, not
periodically recreating the lockfile from the current ranges.
`gh actions-lock` re-reads every workflow from disk and rewrites the
whole lockfile, so it does not fit `updateArtifacts`, which is called
once per updated package file.
We can instead follow the shape the `npm` manager uses for its
lock files, and have `processBranch` invoke this once per branch, after
every updated package file has been written to disk.
We skip workflows that aren't onboarded to the lockfile, but still run
for any other package file, as a local composite action can be a
transitive dependency of a workflow which is.
`gh actions-lock` rewrites the workflows it manages back to plain refs
when it regenerates them, which strips out any inline digest pin:
```diff
- uses: actions/setup-java@1bcf9fb12c # v5.4.0
+ uses: actions/setup-java@v5.7.0
```
This would lead to a repository with `pinDigests` set "flip flopping",
and would cause a lot of erroneous noise for repository owners.
Instead, we can make sure that:
- we don't suggest a `pinDigest` for workflows that are managed by the
lockfile
- digest pinning is still enabled for `docker://` references
- already-committed digests are updated when they're present - we only
avoid suggesting /new/ digests to be pinned
Co-authored-by: Sebastian Poxhofer <secustor@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <jamie.tanna+claude-code@mend.io>
Co-authored-by: Claude Fable 5 <jamie.tanna+claude-code@mend.io>
[0]: https://github.com/github/actions-lockfile
* coverage
Co-authored-by: Claude Sonnet 5 <jamie.tanna+claude-code@mend.io>
* fixup! feat(manager/github-actions): add support for Actions Lockfiles
Co-authored-by: Claude Opus 5 <jamie.tanna+claude-code@mend.io>
---------
Co-authored-by: Claude Opus 5 <jamie.tanna+claude-code@mend.io>
* feat: add regex support for gitIgnoredAuthors configuration
* feat: add tests for gitIgnoredAuthors regex handling in isBranchModified
* refactor: simplify ignored authors matching in isBranchModified function
* refactor: use for-of loop for ignored author filtering
* fix(git): treat non-regex gitIgnoredAuthors as exact strings
* fix(git): remove duplicate import of matchRegexOrGlobList
* fix(git): reorder import statements for consistency
* feat(git): enhance gitIgnoredAuthors handling with regex validation and logging
* feat(git): enhance gitIgnoredAuthors support for glob patterns and improve regex handling
* fix(git): add support for escaped glob patterns in matchRegexOrGlobList
* feat(git): update gitIgnoredAuthors documentation and improve regex handling for glob patterns
* fix(git): improve escaping of brackets and backslashes in glob pattern handling
* feat(git): normalize GitHub bot email addresses in ignored authors handling
* feat(tests): add custom author with brackets to file list in index.spec.ts
* feat(git): refactor ignored author handling to support regex normalization
* feat(git): enhance ignored authors handling to support regex patterns
* chore: retrigger ci
As part of future changes, we'll add options that do not require the
Global option to take precedence and/or be used as an upper limit.
To make this clearer, we can split out the options into Global and Repo
variants.
* feat(config-validator): validate object entries in the `repositories` array
* Allow global configs in the repository object entry
* Simplify tests by combining them
* Allow `enabled` inside the repository entry
* Address review feedback
* Align type with #43295
* Skip validation for 'repositories' key in global config arrays
* refactor: flatten repositories array validation nesting
Address review feedback by combining the isArray and key checks
to avoid an extra nesting level.
* fix(schema): allow object entries in repositories array
Align renovate-global-schema with runtime: entries may be a string or
an object with required `repository` plus full global/repo config.
* Revert "fix(schema): allow object entries in repositories array"
This reverts commit 9aca8dcde8.
* fixups
* fix(config/validation): handle `allowedEnv` and `allowedHeaders` in `repositories`
Co-authored-by: Claude Opus 4.8 <jamie.tanna+claude-code@mend.io>
---------
Co-authored-by: Jamie Tanna <jamie.tanna@mend.io>
Co-authored-by: Claude Opus 4.8 <jamie.tanna+claude-code@mend.io>
* 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>
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>
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>
* 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>