Migrate apm, bun, cargo, cocoapods, composer, conan, copier, deno,
devbox, gomod, helmv3, jsonnet-bundler, kustomize, mix, nuget and vendir
onto `fileAddition()`, `artifactError()` and `artifactErrorResult()`.
copier's private `artifactError()` is replaced by the shared one.
Behaviour change: artifact error text now comes from
`artifactErrorMessageFromExecError` everywhere, i.e. a non-blank
`err.stderr`, else a non-blank `err.stdout`, else `err.message`.
Previously apm used `${err}` ("Error: " prefix), cocoapods used a
nullish coalesce that kept an empty stderr, nuget preferred stdout over
stderr, and jsonnet-bundler emitted an undefined stderr with no
fallback. The rest used `err.message` only and are unchanged for errors
without output.
Add `collectFileChanges()` next to the other repository helpers, plus a
thin `fileChangesToArtifactResults()` in `lib/modules/manager/util.ts`,
and migrate the managers which re-implemented the same status loops:
apm, cocoapods, composer, copier, gomod, helmv3, jsonnet-bundler,
kustomize and vendir.
Each site keeps its own status bucket selection and path filter, so the
emitted artifacts are unchanged. Two dead pieces of logic are dropped:
vendir's `vendorDir || isFileInVendorDir` condition, which was always
true, and kustomize's optional chaining on a non-nullable status, which
only ever mattered for an unset test mock (now set explicitly).
* feat(manager): pass extracted constraints to updateArtifacts
`extractedConstraints` were collected during extraction but only ever read
by `fetch.ts` for the lookup. At artifact time every manager had to derive
the same tool constraints a second time, and when the updated package file
no longer carried the constraint there was nothing to fall back to.
`generateBranchConfig()` now merges the `extractedConstraints` of all
upgrades of a branch onto the branch config, which reaches `updateArtifacts()`
through `patchConfigForArtifactsUpdate()`. `UpdateArtifactsConfig` declares
the field and documents what it contains.
The new `resolveToolConstraint()` helper implements the ladder once:
user `constraints` > an optional artifact-time derivation > the extracted
value. The derivation is ranked above the extracted value on purpose: it
reads the package files as the branch changed them, so poetry (python is an
updatable dependency there), composer, pipenv (`pipenv` can be a dev
package) and pep621 (`requires-python` is an updatable dependency) would
otherwise regress to a stale constraint. pipenv, pep621 uv/pdm, poetry and
composer are migrated to the helper and keep their derivation.
Behaviour change: these managers now use the extract-time constraint as a
last resort instead of running unconstrained, for example on lock file
maintenance where pipenv deletes `Pipfile.lock` before deriving.
`execute-post-upgrade-commands.ts` is unaffected: it reads
`upgrade.constraints` per upgrade, and the extracted values are kept in a
separate field rather than folded into `constraints`, so post-upgrade task
tool constraints still come from user config only.
Not migrated, because their constraints are never extracted, so the
fallback would be dead code: bundler (`ruby`/`bundler`), pixi (`pixi`),
pep621 uv (`required-version`) and npm's node ladder, whose last step
already reads `package.json` engines.
* refactor(manager): resolve tool constraints through resolveToolConstraint()
Every manager now obtains the constraint of a tool it runs through
`resolveToolConstraint()` instead of reading `config.constraints` itself, so
the precedence "user config, then a value derived from the updated files, then
the constraints collected during extraction" applies everywhere. The helper
treats an empty string as not set at every step and accepts the
`PostUpdateConfig` shape, whose `constraints` may be `null`.
Behaviour changes that go beyond the new fallback to extracted constraints:
- `mix` applies its hardcoded erlang default only after the extracted
constraint, so an erlang version collected at extraction can win over it.
- `kustomize` decides `HELM_EXPERIMENTAL_OCI` from the fully resolved helm
constraint; `generateHelmEnvs()` takes that value instead of the config.
- `npm` (yarn): an upgrade of yarn without a new value now consults the user
constraint before deriving one from package.json or the lock file.
- The "Using <tool> constraint from config" debug lines in bundler, composer,
copier and pip-compile are gone, as the helper is the only reader of the
config now.
Helpers that read the config synchronously became async: bundler
`getBundlerConstraint()`, copier `getPythonVersionConstraint()` and
`getCopierVersionConstraint()`, pip-compile `get*VersionConstraint()` and
`getToolVersionConstraint()`, gomod `deriveGoToolchainConstraints()` and
`getUpdateImportPathCmds()`, mise `getMiseLockToolConstraints()`. composer
`getPhpConstraint()` is removed.
Each converted manager has a test showing the extracted constraint reaching
the executed tool, and, where a derive callback exists, that the derived value
still wins over it.
* refactor(linters): add prefer-resolve-tool-constraint rule
Reports member reads and destructuring of `constraints` and
`extractedConstraints` from a manager config in `lib/modules/manager/**`, so
that tool constraints are resolved through `resolveToolConstraint()` in
`lib/modules/manager/util.ts` only. Writes and the same properties on package
files, which extraction has to populate, are not reported. The helper itself
and spec files are exempt in `.oxlintrc.json`.
* refactor(manager): inline thin resolveToolConstraint wrappers
Drop copier/utils.ts and the pip-compile getPipToolsVersionConstraint /
getUvVersionConstraint helpers, which only forwarded to
resolveToolConstraint(). Callers now call it directly.
* docs(development): note lock file maintenance in tool constraints section
* refactor(manager): address review on tool constraint resolution
composer only runs extractConstraints() when a user constraint is missing,
via Lazy. UpdateArtifactsConfig no longer re-declares constraints and
extractedConstraints, which ToolConstraintsConfig already provides.
We dropped the "bot" from our terminology long ago, but it looks like
we've got some outdated docs + code that reference it.
We should remove this to keep things consistent.
A `fix`, as we're changing the commit author's name, and log lines.
Co-authored-by: Claude Sonnet 5 <jamie.tanna+claude-code@mend.io>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
fix(manager/copier): set isExecutable on added and modified files
Detect the owner execute bit on files written by copier update and
propagate it as isExecutable on the returned file additions, so
committed files keep their executable flag.
Convert SSH-style template URLs to their HTTPS equivalent when
constructing the packageName for the git-tags datasource lookup,
mirroring what the git-submodules manager does. This allows the
lookup to be authenticated via hostRules credentials on setups
which access the Git host over HTTPS only.
The answers file itself keeps the original URL, so local
'copier update' runs by developers continue to use SSH.
Discussed in #44528
* refactor(types): document `localDir` isn't a `globalOnly` option
To make sure that it's clear when reviewing `GlobalConfig`, as well as
working with our types, we can move the `localDir` definition into a
separate type.
* fixup! refactor(types): document `localDir` isn't a `globalOnly` option
* fixup! refactor(types): document `localDir` isn't a `globalOnly` option
Co-authored-by: Claude Sonnet 5 <jamie.tanna+claude-code@mend.io>
* fixup! refactor(types): document `localDir` isn't a `globalOnly` option
Co-authored-by: Claude Sonnet 5 <jamie.tanna+claude-code@mend.io>
* fixup! refactor(types): document `localDir` isn't a `globalOnly` option
Co-authored-by: Claude Sonnet 5 <jamie.tanna+claude-code@mend.io>
---------
Co-authored-by: Claude Sonnet 5 <jamie.tanna+claude-code@mend.io>
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>
* chore: migrate from zod/v3 to zod/v4
This commit contains the logic changes
* chore: migrate imports from zod/v3 to zod/v4
This contains import updates and adds explicit record keys as zod/v4 requires these now
* ci: enforce usage of zod/v4
* test: ensure we set `binarySource=global`
As part of future changes, we'll correctly return the default value from
`GlobalConfig.get`, instead of `undefined` if no default is provided in
the `get` call.
As `binarySource=install` is the default, this then leads to these tests
failing, as they're intended to run with `binarySource=global`.
Co-authored-by: Claude Sonnet 4.6 <jamie.tanna+claude-code@mend.io>
* fixup! test: ensure we set `binarySource=global`
* fixup! fixup! test: ensure we set `binarySource=global`
---------
Co-authored-by: Claude Sonnet 4.6 <jamie.tanna+claude-code@mend.io>
* docs: explicitly document `depTypes` each manager supports
A long-standing question we receive is "what `depTypes` can I match
against in my project".
Although the best recommendation we usually have is "check the
`packageFiles with updates" log line (as a very key log line for users),
we can go one step further and try and create a stronger contract.
To do this, I've used Claude Sonnet 4.6 to work through the codebase,
determine areas that a `depType` is set in a manager (statically or
dynamically) and generate a structured, typed, means to document how
they're used.
Through a few iterations of the process, and attempts at human-written
documentation, we've settled on:
- prioritising autogenerated documentation (for humans to read) to note
what `depTypes` are known, and what they are
- `knownDepTypes`, to describe any known, static, `depType`s and their
human-readable meaning
- with the `prettyDepType`, if used
- `supportsDynamicDepTypesNote`, to add a Markdown note to the generated
documentation
While here, we also:
- make sure to note this in the "adding a new package manager"
documentation
- remove any hand-written references to `depTypes` in each manager's
documentation page
- correct the Gleam `depTypes`
- make sure to use the `knownDepTypes`'s `prettyDepType` definition if
we know it, for a given `depType`
The descriptions have been generated by Claude Sonnet, with some
human-written tweaks where appropriate.
[0]: https://github.com/renovatebot/renovate/discussions/15176
Co-authored-by: Claude Sonnet 4.6 <jamie.tanna+claude-code@mend.io>
* fixup! docs: explicitly document `depTypes` each manager supports
* docs(manager): display `depTypes` documentation if none present
For consistency, and to make it more explicit.
---------
Co-authored-by: Claude Sonnet 4.6 <jamie.tanna+claude-code@mend.io>
As `fileName` makes more sense (as it's not always a lock file we're
updating).
Also, if you set `fileName`, it would never actually output the
filename.
This renames the usages of `lockFile` to `fileName`.
Co-authored-by: Claude Sonnet 4.5 <jamie.tanna+github-copilot@mend.io>
* Fix git+ and ssh+ for copier
* Address PR review: use codeBlock and it.each in copier extract tests
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>