mirror of
https://github.com/renovatebot/renovate.git
synced 2026-10-27 07:50:47 +00:00
fix/maven-wrapper-java-version
2054 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
e144d8dbc1 |
fix(http): validate the cached response shape in the http cache schema (#46417)
refactor(http): validate the cached response shape in the http cache schema Co-authored-by: Claude Fable 5.1 <michael.kriese+claude-code@mend.io> |
||
|
|
ac2fc77641 |
refactor(http-mock): Remove request tracing (#44482)
* refactor(http-mock): remove request tracing * test: replace remaining HTTP trace assertions |
||
|
|
1e6ffa8171 |
refactor(datasource): add cached() helper to the Datasource base class (#46102)
Every datasource that caches its releases hand-wrote the same
`withCache({ namespace: `datasource-${id}`, ... }, () => this._getReleases(config))`
wrapper, repeating the namespace literal and using underscore-prefixed
method names that `docs/development/best-practices.md` discourages.
Add `Datasource.cacheNamespace` (derived from the datasource id) and a
protected `Datasource.cached()` helper that defaults `namespace` to it,
exporting `CachedOptions` from `with-cache.ts`. Migrate 29 datasources
onto the helper, dropping their namespace literals, the static
`cacheNamespace` field in `github-digest`, and renaming the wrapped
`_getX` methods to `fetchX`.
Behaviour change: `golang-version` used the constant cache key `'all'`
while declaring `customRegistrySupport = true`, so a custom registryUrl
shared the cache entry with the default one. The key is now
`${config.registryUrl}`, which invalidates existing entries once.
|
||
|
|
3b70d5ffe2 |
test(utill/git): add 30s timeout to beforeAll (#46386)
So we don't have tests exiting early when on i.e. a slower GitHub Actions machine. |
||
|
|
853290db9c |
refactor(http): read platform families instead of the host type aliases (#46347)
`lib/util/http/auth.ts` and `lib/util/http/host-rules.ts` were the last readers of the `*_API_USING_HOST_TYPES` constants, which the previous commits left as aliases of `PLATFORM_FAMILIES`. Both now read the table directly and the aliases are deleted, so each family's host types are declared in exactly one place. Also covers the `forgejo` fallback in `findMatchingRule()`, which had no test: the shared setup only registers a `gitea` host rule, so the forgejo branch was never entered. Behaviour changes: none. The aliases pointed at the same arrays. |
||
|
|
0583e54718 |
refactor(constants): add a platform family capability table (#46191)
* refactor(constants): add a platform family capability table Every consumer of `detectPlatform` carried its own mapping from the detected platform to a tags datasource or a web UI path, and the seven `*_API_USING_HOST_TYPES` arrays were checked one by one to produce that platform in the first place. Introduce `PLATFORM_FAMILIES`, one record keyed by family holding the host types, the hostnames which identify the family outright, the tags datasource, the API base URL derivation and the web UI path to a directory at the default branch. A family is a group of platforms, so the ids are checked against `PlatformId`: a family which is not a platform is a compile error. Not every platform is a family, because codecommit, gerrit, local and scm-manager are never recognized from a URL. `detectPlatform` loops the table for both its hostname and its hostType matching, and the consumers look their answers up: - `github-actions` and `pre-commit` read `tagsDatasource` instead of importing the tags datasource classes. - The PR body reads `webDirPath` instead of special-casing Bitbucket and Bitbucket Server. `apiBaseUrl` has no consumer yet; the changelog sources pick it up in the next commit. `lib/util/http/auth.ts` and `lib/util/http/host-rules.ts` still read six of the old arrays by name, so those stay as aliases of the table and are migrated in a follow-up. `AZURE_API_USING_HOST_TYPES` had no other consumer and is dropped. Behaviour changes: none. `detectPlatform` now tests every known hostname before its name heuristics instead of interleaving the two, which is equivalent because the hostname lists are pairwise disjoint and no known hostname contains the name of another family. * Apply suggestion from @secustor |
||
|
|
8f5f238b8f |
fix(datasource/crate): skip the web API of registries that do not implement it (#46329)
* fix(datasource/crate): only query the crates.io web API for crates.io Private sparse registries such as Artifactory or CodeArtifact advertise an `api` URL in their `config.json` for publishing, but do not implement the crates.io-only read endpoints `api/v1/crates/<name>` and `api/v1/crates/<name>/<version>`. Since #41536 Renovate called both for every registry with an `api` field, producing 404s and a "Release interceptor failed" warning on each run. Restore the crates.io flavor guard for the crate metadata lookup and the release timestamp lookup. Reading `config.json` from a cloned registry is now unreachable, so drop that branch. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * docs(datasource/crate): cite the registry web API specification in the guards * fix(datasource/crate): probe the registry web API once instead of guarding on flavor Keep the crates.io read endpoints for registries that implement them. Registries whose config.json points `api` at crates.io, such as index mirrors, are used without probing. Any other registry is probed on the first crate; a 404 for a crate that the index just returned means the endpoint does not exist, so the registry is remembered as unsupported in the memory cache and, for a day, in the package cache. The release timestamp lookup handles a 404 the same way instead of surfacing the generic "Release interceptor failed" warning. Restores the cloned config.json branch, since cloned registries can now advertise an API again. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix(datasource/crate): key the unsupported web API marker by the api URL --------- Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com> |
||
|
|
8dc06640a7 |
feat(workers/repository): allow users to bypass registryUrl when unknown-registry (#46323)
As part of follow-up changes in #45706, we want the `apk` / `apt` parsing in the `dockerfile` manager to attempt to determine which distribution - and therefore registry - to look up dependencies for. When there is an image that we don't know how to resolve, we want to treat them as a `skipReason: unknown-registry`. However, this doesn't provide any way for the user to say "I know which registry you should look up updates for". To handle this, we can treat `unknown-registry` differently, allowing it to un-skip itself when there are any `registryUrls`s defined. This allows for general usage, where if Renovate doesn't know how to interact with a given registry, but a user does, then the user can override the registry with a `packageRules` entry. We continue skipping a dependency when `unknown-registry` is set alongside a dependency's `registryUrl`. We add this un-skipping logic into `applyPackageRules()`, but need to make sure that we also gate it inside `lookup()`, so a dependency that's passed through `applyPackageRules()` is re-checked to ensure that the check is enforced. Co-authored-by: Claude Opus 5 <jamie.tanna+claude-code@mend.io> |
||
|
|
17466ceb32 |
fix(host-rules): ignore URL credentials when matching matchHost (#46128)
feat(host-rules): ignore URL credentials when matching matchHost
`matchesHost()` compared the raw href of the searched URL against
`matchHost`, so a URL carrying `user:pass@` userinfo (often a literal
placeholder such as `${USER}:${PASS}@`) never matched a `matchHost`
without credentials, including path-scoped ones. Callers worked around
this by stripping `username`/`password` off the URL by hand before
calling `hostRules.find()`.
- Add `stripUrlCredentials()` to `lib/util/url.ts`, returning the URL's
`href` without userinfo (or `null` if unparseable), covered by
`lib/util/url.spec.ts`.
- `matchesHost()` in `lib/util/host-rules.ts` now strips credentials
from the searched URL before comparing it against an HTTP(S)
`matchHost`, so `hostRules.find()` ignores userinfo on every caller's
URL. Covered by new cases in `lib/util/host-rules.spec.ts` (a
`matchHost` rule matches a URL carrying `user:pass@`, including a
path-scoped `matchHost`).
- Remove the hand-rolled strip in
`lib/modules/datasource/pypi/host-rules.ts`'s
`findPypiIndexCredentials()`, now redundant since `find()` does it.
`lib/modules/datasource/pypi/host-rules.spec.ts` already covers this
behaviour and needed no changes.
This is strictly additive: more URLs match `matchHost` than before,
and nothing that matched before stops matching.
|
||
|
|
654431d69f |
test(util/git): share one clone across the read-only tests (#46287)
* test(util/git): skip the per-test clone for tests that need no repo The spec's `beforeEach` rebuilds a bare clone and calls `initRepo()` before every test, which costs about 0.7s each. Eight tests never touch a repository at all -- `createSimpleGit`, `gitRetry`, `validateGitVersion` and `getUrl` -- yet each paid for that clone. They now sit in a second root describe with no clone in it, so the file drops from about 129s to about 121s with the same 144 tests. The two blocks that are pure functions are marked `describe.concurrent`. The other two stay sequential: `createSimpleGit` spies on a prototype and sets the custom env, and `gitRetry` stubs `NODE_ENV`. This was the outcome of trying `it.concurrent` across the file. It cannot help there, for two reasons stacked on each other. Vitest still runs `beforeEach` for every concurrent test, so concurrency does not remove the clone, it only interleaves several of them. And those interleaved clones all write the same module singleton (`config`, `git`, `gitInitialized`) and `GlobalConfig.localDir`, so one test's `initRepo()` is overwritten by another's before its body runs. The only lever that works is not running the clone for tests that do not need it. Co-Authored-By: Claude Fable 5.1 <michael.kriese+claude-code@mend.io> * test(util/git): share one clone across the read-only tests The spec now has one root describe with three nested groups, split by how much git setup a test needs: - `without a repository`: pure functions, no setup at all. - `shared clone`: 32 read-only tests. One bare clone of `base` and one `initRepo()` in `beforeAll`, so each test costs only the git commands it runs itself. - `clone per test`: the tests that commit, push, check out or re-init. They keep the fresh clone and `initRepo()` per test. The `base` repository moves up to the root describe so both clone groups can use it. Groups run in file order, and some of the writing tests change `base` without restoring it, so the shared-clone group has to stay ahead. `isBranchModified()` stays with the writing tests although it never touches the repository: it caches per-branch results in `config.branchIsModified`, which only `initRepo()` clears, and its tests query the same branch with different ignored authors. Three describes are split by test: `getFileList`, `getBranchUpdateDate` and `diffCommitTree` each have one test that writes, which stays behind under the same describe name with a pointer to the read-only half. Same 144 tests; the file drops from about 121s to about 100s. Co-Authored-By: Claude Fable 5.1 <michael.kriese+claude-code@mend.io> * test(util/git): keep the 30s test timeout for every test The `{ timeout: 30000 }` option used to sit on the single root describe and so covered every test in the file. The split left it on the `clone per test` group only, which put the 32 shared-clone tests on the 5s default even though they still run real git commands. CI is a lot slower than a local machine, so the option moves up to the root describe again; nested describes inherit it. Hooks are unaffected either way: they run under `hookTimeout`, not the suite option, and the shared `beforeAll` does the same work the old `beforeEach` did. Co-Authored-By: Claude Fable 5.1 <michael.kriese+claude-code@mend.io> * test(util/git): reuse one clone for the local-only writers A fourth nested group, `reused clone`, holds the 36 tests that write to the local checkout but never to `origin` or `base`: `checkoutBranch`, `isBranchModified`, `mergeToLocal`, `isFileModeEnabled`, `getCommitMessages`, `setGitAuthor`, `getRepoStatus`, `fetchRevSpec` and single tests of `getBranchUpdateDate`, `mergeBranch`, `deleteBranch` and `diffCommitTree`. The group clones once, but `initRepo()` and `syncGit()` run again before every test. On an existing checkout `syncGit()` takes its fetch path: `fetch --prune`, `reset --hard`, `checkout` of the default branch, `clean -fd` and deletion of every other local branch. With the fresh `config` from `initRepo()` that undoes everything such a test can do to the repository or the module singleton, including the per-branch `branchIsModified` cache that kept `isBranchModified()` out of the shared-clone group. Local git config is the one thing it leaves alone, so the `beforeEach` re-sets the author that `setGitAuthor()` writes. That reset costs about 0.35s per test against about 0.87s for a clone, so the file drops from about 100s to about 87s. Same 144 tests. Co-Authored-By: Claude Fable 5.1 <michael.kriese+claude-code@mend.io> * test(util/git): trim the describe comments to the essentials Review feedback: the block comments read more like a narration of how the layout came to be than like documentation. Each now states only what a group's setup is, what a test may or may not do to belong there, and the ordering constraint about `base`. The explanation of the `isBranchModified()` cache is gone too, since that test now runs with a fresh `initRepo()` in the reused-clone group. Comments only, no test changes. Co-Authored-By: Claude Fable 5.1 <michael.kriese+claude-code@mend.io> * test(util/git): move isBranchModified() into the shared-clone group `isBranchModified()` only reads the repository, but it stores results in `config.branchIsModified`, which only `initRepo()` cleared. That kept its 16 tests in the reused-clone group at about 0.35s each. A new `clearBranchIsModifiedCache()` helper, marked `@internal`, clears that store. The shared-clone `beforeEach` calls it along with the cheap config setters (`setUserRepoConfig`, `setGitAuthor`, `setPlatformIgnoredAuthors`), so those tests now share one `initRepo()` and cost a few milliseconds each. The file drops from about 87s to about 83s. A dynamic import with `vi.resetModules()` was considered instead: a fresh module instance has no `config` and `gitInitialized = false`, so every test would need its own `initRepo()` and `syncGit()` on it, which is the reused-clone cost again plus re-importing the module graph. Co-Authored-By: Claude Fable 5.1 <michael.kriese+claude-code@mend.io> --------- Co-authored-by: Claude Fable 5.1 <michael.kriese+claude-code@mend.io> |
||
|
|
4215f35059 |
feat(config): add minimumReleaseAgeBuffer option (#45577)
* feat(config): add minimumReleaseAgeBuffer option Adds a new config option which extends the time Renovate waits before suggesting an update by a percentage of minimumReleaseAge. This gives companion packages that are published after the main package (e.g. platform-specific binaries) time to pass the package manager's own release age cooldown, avoiding lock file artifact errors. * feat(config): make minimumReleaseAgeBuffer a duration with 10 minutes default * feat(config): raise minimumReleaseAgeBuffer default to 30 minutes |
||
|
|
3abfaacabc |
feat(util/template): Add uppercase helper (#46205)
Add uppercase to template Co-authored-by: Menco Bolt <133383+macmenco@users.noreply.github.com> |
||
|
|
9defb36404 |
refactor(manager): share repo status to file change conversion (#46090)
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). |
||
|
|
312c380926 |
test(manager): cover the pre-commit warning switch and drop unreachable fallbacks (#46221)
* fix(util/http): make the legacy `headers` getter configurable Every other property defined on `HttpError.prototype` in this file is `configurable`, but `headers` was not. That makes the module fail with `TypeError: Cannot redefine property: headers` whenever a test resets the module registry and re-imports anything that pulls in `got.ts`. Co-authored-by: Claude Opus 5 <michael.kriese+claude-code@mend.io> * test(manager/pre-commit): cover the suppress-warning env var `defaultConfig.prBodyNotes` drops the unsupported-manager warning when `RENOVATE_X_SUPPRESS_PRE_COMMIT_WARNING` is set. Since the value is computed at module load, the new spec resets the module registry and re-imports the manager with the variable stubbed. The `istanbul ignore next` on that arm is dropped because the branch is now exercised. Co-authored-by: Claude Opus 5 <michael.kriese+claude-code@mend.io> * refactor(manager): remove unreachable fallbacks Drop three defensive fallbacks that can never be taken: - `docker-compose`: `config.services ?? config` always yields an object, so the `|| {}` guard behind it is dead - `git-submodules`: `git.raw()` resolves to a string, so the `?? ''` never applies - `maven`: the only site that builds a `MavenProp` always passes a `packageFile` string, so narrow the type and drop the `?? undefined` Co-authored-by: Claude Opus 5 <michael.kriese+claude-code@mend.io> --------- Co-authored-by: Claude Opus 5 <michael.kriese+claude-code@mend.io> |
||
|
|
aae889a285 |
fix(datasource/go): cache the publication time of each version (#45951)
* fix(datasource/go): cache the publication time of each version The Go proxy defines that the `.info` API call is for a specific release version of a module, this file should never be modified after it's been created. However, because we previously had a 30m TTL on these entries, a repository that runs infrequently can lead to re-fetching data that doesn't ever update. We can fix this by introducing a separate cache namespace for the module's creation timestamp (`Time` in the `.info` API call). We can make sure that: - we always merge, not replace, the entries, as the `major < 2` filter in `_getReleases` means a run may only see a subset of a module's versions, and writing back only what we saw would evict the rest - we re-read immediately before writing, as `foo` and `foo/v2` are separate lookups which both walk the `v2` module, so one can overwrite the other's timestamps - the key is the proxy which served the data, minus any credentials, since a private Artifactory `GOPROXY` is a different universe of timestamps to `proxy.golang.org` - only the timestamps we successfully fetched are stored, so a proxy which serves no `Time` doesn't have that recorded for 100 days - it uses its own namespace, so that a `cacheTtlOverride` on the release cache doesn't drag the immutable data down with it Co-authored-by: Claude Opus 5 <jamie.tanna+claude-code@mend.io> * fixup! fix(datasource/go): cache the publication time of each version Co-authored-by: Claude Sonnet 5 <jamie.tanna+claude-code@mend.io> * fixup! fixup! fix(datasource/go): cache the publication time of each version Co-Authored-By: Claude Sonnet 5 <jamie.tanna+claude-code@mend.io> --------- Co-authored-by: Claude Opus 5 <jamie.tanna+claude-code@mend.io> |
||
|
|
b2135e41b7 |
fix(exec): memoise which install-tools we've run (#46084)
When running Renovate with `binarySource=install` on a repository with many branches, the amount of time running `install-tool` - on already installed tools - can take a significant percentage of the overall run. For instance, on one repository we have instrumented with OpenTelemetry, we see: - initial `install-tool golang`: 8.7s - follow-up `install-tool golang`: median 635ms, max 2.4s However, because we run `install-tool golang` 640 times, this adds up to 24.8/29.2m (85%) of the repo's run time, which is an overwhelming percentage when we're not even re-installing the tool. To handle this more effectively, we can add an in-memory cache of all the tools (and resolved versions) that we've installed in a given run. We can't do this with `binarySource=docker`, as it has a fresh container each time. Co-authored-by: Claude Sonnet 5 <jamie.tanna+claude-code@mend.io> |
||
|
|
2e951d2254 |
feat(mise): support selector lockfile updates (#44615)
* mise: support selector lockfile updates * fix(mise): handle empty lockfile AST * test(mise): use valid empty TOML AST fixture * fix(mise): support current regex utility * fix: clarify lockfile-only lookup handling * fix(mise): handle selector lockfile updates correctly * test(mise): cover selector lockfile review fixes * fix(mise): scope selector artifact refresh * chore: retrigger CI * fix(mise): remove unreachable lockfile guard * refactor(lookup): clarify lockfile-only value handling * refactor(mise): use shared TOML utilities * fix(mise): handle selector lockfile edge cases * fix(mise): satisfy selector CI coverage * fix(mise): require dependency names * fix(mise): preserve configured dependency names * fix(mise): address selector lockfile review feedback * test(mise): cover major selector lockfile updates * chore(static-data): auto-update Java LTS versions So we don't have a hardcoded set of data in the mise manager. Co-authored-by: Claude Sonnet 5 <jamie.tanna+claude-code@mend.io> * fix(mise): address selector lockfile feedback * test(manager/mise): cover nonnumeric current version prefix formatLockedVersion()'s currentPrefix fallback to '' only runs when the current version has no leading numeric match, which no existing test reached. Co-Authored-By: Claude Sonnet 5 <jamie.tanna+claude-code@mend.io> --------- Co-authored-by: Jamie Tanna <jamie.tanna@mend.io> Co-authored-by: Claude Sonnet 5 <jamie.tanna+claude-code@mend.io> |
||
|
|
44160755f9 |
feat(platform): validate gitea responses with schemas and share the implementation with forgejo (#45825)
* refactor(platform): share one implementation between gitea and forgejo The `forgejo` platform was copied from `gitea` in #37075 and the two have drifted since: fixes landed on one side only (zod validation #43900, empty email #44068, null issues #44048, team reviewers #39533, merge-style fallback #37768 vs #38040), and lint sweeps had to be applied to both copies by hand. Both platforms now build on `lib/modules/platform/gitea-common/`, which holds the zod schemas, API helpers, utils, PR cache and the platform object itself. `createPlatform()` closes over its own state, so `gitea` and `forgejo` keep separate config, caches and host types. `gitea/index.ts` and `forgejo/index.ts` are thin wrappers that supply the platform id, default endpoint, http client and the version gates that genuinely differ. Reconciliations, forgejo's behaviour adopted for gitea: - Responses are validated with zod instead of `getJsonUnchecked` casts. Gitea therefore tolerates empty emails, null PRs in list responses and lenient repo/issue payloads the same way Forgejo already did. - `getRepoContents` returns a discriminated union, so `getRawFile` returns null for dir/symlink/submodule entries instead of reading a missing field. The gitea preset resolver now throws PRESET_INVALID for non-file entries, like the forgejo one. - An unknown `default_merge_style` (e.g. `manually-merged`) is caught by the schema and skipped, instead of aborting the repository with REPOSITORY_BLOCKED from `isAllowed()`. - `initRepo()` resolves whether the repo owner is an org and only fetches org labels when it is, adding one `/orgs/{owner}` request and an `orgs` entry in the gitea repository cache. - Reviewers prefixed with `team:` are sent as `team_reviewers`. Gitea keeps its `v1.14.0` guard for requesting reviewers at all. - `getBranchStatusCheck()` maps unknown commit status values through the schema's `unknown` fallback rather than warning and returning yellow. Gitea's behaviour is kept where it is deliberately different: the Forgejo instance detection and its deprecation notice, the `1.24.0` (or `10.0.0` for a detected Forgejo) native automerge gate, and the reviewer version guard are passed in as options. Two shared log messages became static so they can be grouped in metrics: `{platform}-native automerge: fail` is now `Platform-native automerge: fail` and `{Platform} getRepos() error` is now `Error auto-discovering repositories`, both with the platform id in the metadata. The dead `defaults.hostType` field is gone. * refactor(platform): keep the shared gitea implementation under gitea/ Drop the `gitea-common/` folder. The zod schemas, API helpers, utils, PR cache and `createPlatform()` live in `lib/modules/platform/gitea/` under their original file names, and `forgejo/` imports them from there. The preset resolvers pass `giteaHttp` and `forgejoHttp` to `getRepoContents()` directly, which removes the two bound wrappers. The gitea changelog source uses the shared `RepoContents` type like the forgejo one already does. * refactor(http): derive ForgejoHttp from GiteaHttp The two clients were identical apart from the default `hostType`. `ForgejoHttp` now extends `GiteaHttp` and only keeps its own base url, so the pagination logic exists once. The platform helpers accept `GiteaHttp` directly instead of the `HttpBase` alias. Also covers the `updatePr()` branch where the updated pull request cannot be converted and therefore is not cached. * refactor(platform): create the gitea and forgejo http clients in index.ts `forgejo-helper.ts` only held the `ForgejoHttp` instance, so it is gone and `forgejo/index.ts` creates the client next to the platform. Gitea does the same for symmetry, which leaves `gitea-helper.ts` with only the shared API helpers. * refactor(platform): fetch gitea labels and issues with await `getLabelList()` and `getIssueList()` still memoize the in-flight promise, but build it from small async functions instead of `.then()` and `.catch()` chains. * refactor(platform): keep declaration order of gitea/index.ts Order the module the way it was before `createPlatform()` so the diff against main aligns: platform id and http client after the repo config interface, pure helpers before the factory, and `maxBodyLength()` after the platform object. * refactor(platform): move gitea label fetching to labels.ts `fetchLabelList()` takes the http client and the repository details instead of closing over the platform state, and gets its own spec. The factory only keeps the memoizing `getLabelList()`. * refactor(platform): move gitea label lookup to labels.ts * refactor(platform): create the gitea pr cache per platform `GiteaPrCache` is now created once per platform with its http client and platform id, and `initRepo()` records the repository. The per-repository state moves to a private `RepoPrCache` which is bound to the repository cache on first use and reused until the next `initRepo()`, instead of being rebuilt on every lookup. |
||
|
|
86890d2142 |
refactor(git): extract runtime config appending (#45165)
* refactor(git): centralize runtime config environment Preserve caller-supplied and inherited GIT_CONFIG_KEY_n and GIT_CONFIG_VALUE_n entries when appending runtime Git configuration. Co-authored-by: Jamie Tanna <jamie.tanna@mend.io> * refactor(git): extract runtime config appending * style(git): format runtime config tests * test(git): remove superseded environment cases * Apply suggestion from `@RahulGautamSingh` Co-authored-by: RahulGautamSingh <rahultesnik@gmail.com> * test(git): align invalid config count warning --------- Co-authored-by: MrVampy <4302946+MrVampy@users.noreply.github.com> Co-authored-by: Jamie Tanna <jamie.tanna@mend.io> Co-authored-by: Michael Kriese <michael.kriese@gmx.de> Co-authored-by: RahulGautamSingh <rahultesnik@gmail.com> |
||
|
|
7386ea49c4 |
refactor(host-rules): add an inherited trust tier
As part of looking to allow Inherit Config to manage Host Rules' grants, we need to perform a prefactor to create an additional trust tier between the global self-hosted administrator's config, and presets/repo config. The `find()` method already resolves sensitive fields - `headers`, `enabled`, and the computed internal-host grant - a "trust tier" at a time, so that the outcome cannot depend on how specific a rule from a less trusted tier makes itself. The new `inherit` tier slots in between the two we had: - `headers` are applied untrusted first, then inherited, then the administrator's, so theirs still win outright - `enabled` is taken from the administrator's rules, falling back to inherited config's, then a repository's - an administrator's `allowInternal: false` acts as a veto: it is resolved before inherited config's rules, and clears the scoped and implicit grants too, so inherited config cannot re-grant the host in any shape by out-scoping it. Meanwhile any inherited rule naming a host in its `matchHost` - credentialed or not - now implicitly permits it, like the admin's own rules do We introduce a separate field than `trusted` to avoid Inherit Config gaining the same precedence as the admin themselves. Co-authored-by: Claude Fable 5 <jamie.tanna+claude-code@mend.io> Co-authored-by: Claude Opus 5 <jamie.tanna+claude-code@mend.io> |
||
|
|
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> |
||
|
|
09d2da2f1b |
feat(host-rules): add allowInternal
As part of follow-up changes, we're looking to tighten the way that Renovate handles outgoing HTTP traffic. Before we do this, we can introduce a new config option, `hostRules[].allowInternal`, that allows a self-hosted administrator to mark an internal host as "trusted" for access. This wires in the new global-only config option, `allowInternal`, which implies a new internal field `internalHostGrant`. We'll add full documentation in a future commit, when the full enforcement is in place, as there are some wider changes to enforce it fully. Co-authored-by: Claude Fable 5 <jamie.tanna+claude-code@mend.io> Co-authored-by: Claude Opus 5 <jamie.tanna+claude-code@mend.io> |
||
|
|
7d3fe44060 |
fix(util/host-rules): resolve enabled per trust tier
As part of follow-up changes, we're looking to tighten the way that
Renovate handles outgoing HTTP traffic, and a self-hosted administrator
disabling a host needs to be something a repository cannot undo.
In a similar change to
|
||
|
|
0db7cdd91f |
refactor(util/http): add host guard address classifier
As part of follow-up changes, we're looking to tighten the way that Renovate handles outgoing HTTP traffic. Before we do this, we can add helper methods to manage classification of outgoing traffic to three "trust tiers": - the public Internet - which is generally OK - `internal` traffic - which should be carefully configured by a self-hosted administrator based on their risk tolerance, and includes loopback, RFC1918, link-local, CGNAT, multicast, unique-local and unspecified addresses - `metadata` traffic (i.e. to the AWS EC2 Metadata API) which should /NEVER/ be allowed. By splitting `metadata` out, we can always block access to them, while allowing `internal` hosts to be accessed. We use Node's built-in `net.BlockList` rather than adding a dependency, which also transparently classifies IPv4-mapped IPv6 addresses (in all their spellings) against the IPv4 rules. Co-authored-by: Claude Fable 5 <jamie.tanna+claude-code@mend.io> Co-authored-by: Claude Opus 5 <jamie.tanna+claude-code@mend.io> |
||
|
|
2361d04e6c |
refactor(util/http): share options preparation between request and stream
As part of follow-up changes, we need to have a single enforcement point for outbound HTTP requests. Before we do this, we can prefactor `stream()` to use the same options preparation as `request()`. This does lead to a slight behaviour change, wehre: - host rule lookups for streams now use the resolved URL, whereas previously a `baseUrl`-relative URL was passed as-is, so rules could fail to match - stream options are now deep-merged with the instance options, rather than a shallow spread, matching `request()` - a disabled host now logs a debug line on the stream path too Co-authored-by: Claude Fable 5 <jamie.tanna+claude-code@mend.io> |
||
|
|
8e19aef903 |
test(util): cover remaining implicit else branches (#45840)
Two `if` statements had their implicit else path untested: - deleteLocalFile: the no-localDir case never ran - getMergeConfidenceLevel: the API returning an unrecognised confidence value never ran, so the 'neutral' fallback was untested Co-authored-by: Claude Opus 5 <michael.kriese+claude-code@mend.io> |
||
|
|
b5211d4944 |
test(util): cover remaining lib/util coverage gaps (#45832)
test(util): cover minimatchFilter and cachePathIsFile gaps Brings lib/util to 100% coverage: - minimatchFilter: the closure returned on the uncached path was never invoked, and the options-based cache key branch was never taken - cachePathIsFile: only the "does not exist" path was tested, so stat().isFile() never ran Co-authored-by: Claude Opus 5 <michael.kriese+claude-code@mend.io> |
||
|
|
619ce40237 |
build(deps): update dependency @redis/client to v6 (main) (#45789)
* build(deps): update dependency @redis/client to v6 | datasource | package | from | to | | ---------- | ------------- | ------ | ----- | | npm | @redis/client | 5.12.1 | 6.2.1 | * fix(cache): keep RESP 2 protocol with @redis/client v6 @redis/client v6 defaults to RESP 3, which requires Redis server 6.0 or newer and changed the default type parameters, breaking the RedisClient type alias. Pin RESP 2 explicitly and type the client accordingly. The switch to RESP 3 is deferred to the next major release. --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: secustor <sebastian@poxhofer.at> |
||
|
|
b2d3b64c48 |
refactor(lint): enforce err key for errors in logger metadata (#45759)
* refactor(lint): enforce err key for errors in logger metadata * refactor(lint): flag error message and stack strings in logger-err-key |
||
|
|
f2896deaaf |
fix(merge-confidence): don't require auth in platform=local (#45769)
* fix(merge-confidence): don't require auth in `platform=local` When dry-running Renovate against a repository to validate it[0], if a repository is using `matchConfidence`, the whole Renovate run is aborted due to missing API credentials. This can be frustrating, and the workaround available is to temporarily remove all package rules use `matchConfidence`, which isn't the best experience. Instead, we should follow the existing pattern to return `null` when loading a Package Rule with `matchConfidence`, as well as DEBUG logging to inform the user why this is happening. [0]: https://www.jvt.me/posts/2026/03/08/renovate-test-config/ * fixup! fix(merge-confidence): don't validate auth in a With debugging from Claude to work out how to get the test to pass. Co-authored-by: Claude Sonnet 5 <jamie.tanna+claude-code@mend.io> --------- Co-authored-by: Claude Sonnet 5 <jamie.tanna+claude-code@mend.io> |
||
|
|
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> |
||
|
|
1d34441882 |
fix(http): improve warning message for ignored cross origin requests (#45802)
🐛 fix: improve warning message for ignored cross origin requests
|
||
|
|
2561284dc4 |
build(deps): update dependency got to v15 (main) (#43120)
* build(deps): update dependency got to v15 | datasource | package | from | to | | ---------- | ------- | ------ | ------ | | npm | got | 14.6.6 | 15.0.5 | * chore: adapt code to breaking changes * test: fix test --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: secustor <sebastian@poxhofer.at> Co-authored-by: Michael Kriese <michael.kriese@mend.io> |
||
|
|
c6578a476b |
feat(datasource/helm): support aws s3 as helm charts repository (#45584)
* support AWS S3 as a chart repository * support creds via hostRules * apply suggestions * fix(datasource/helm): classify s3 lookup errors * test: use await expect().resolves |
||
|
|
795d083264 |
feat(datasource): add APK datasource support (#40250)
* feat(datasource): add APK datasource support * remove missing manager link for now * lint docs * update tests * replace binary fixtures * fix coverage * Update lib/modules/datasource/apk/readme.md Co-authored-by: Jamie Tanna <github@jamietanna.co.uk> * Apply suggestion from @viceice Co-authored-by: Michael Kriese <michael.kriese@gmx.de> * use tar instead of tar-stream * use codeBlock * add ignore back * make method private * use an async extract instead of list * add usage example * stream apkindex download and parse line-by-line * refactor(datasource): address review comments on APK datasource Co-authored-by: Cursor <cursoragent@cursor.com> * chore: sync pnpm-lock.yaml after merge Co-authored-by: Cursor <cursoragent@cursor.com> * test: remove ignored lines As they seem to be covered, anyway. * fixup! Merge remote-tracking branch 'origin/HEAD' into apk-datasource * fixup! fixup! Merge remote-tracking branch 'origin/HEAD' into apk-datasource * lint: use `node:` prefix * Update lib/modules/datasource/apk/index.ts Co-authored-by: Michael Kriese <michael.kriese@gmx.de> * refactor(datasource/apk): use asTimestamp millis instead of new Date Co-authored-by: Cursor <cursoragent@cursor.com> * refactor(datasource/apk): rename uncached method to _getPackages Co-authored-by: Cursor <cursoragent@cursor.com> * refactor(datasource/apk): encode registry URL parts as query parameters Co-authored-by: Cursor <cursoragent@cursor.com> * fix(datasource/apk): reject invalid registry URLs, skip caching empty indexes Co-authored-by: Cursor <cursoragent@cursor.com> * fix(datasource/apk): throw on unparseable registry URL and drop unset sourceUrl support Co-authored-by: Cursor <cursoragent@cursor.com> * docs(datasource/apk): show how registry URL parameters build the index URLs Co-authored-by: Cursor <cursoragent@cursor.com> * Apply suggestions from code review Co-authored-by: RahulGautamSingh <rahultesnik@gmail.com> * fix(datasource/apk): keep credentials and port in component URLs Co-authored-by: Cursor <cursoragent@cursor.com> * fix(datasource/apk): extract APKINDEX by basename and key packages by name Co-authored-by: Cursor <cursoragent@cursor.com> * fix(datasource/apk): report a version served by two components only once Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Jamie Tanna <github@jamietanna.co.uk> Co-authored-by: Michael Kriese <michael.kriese@gmx.de> Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: Jamie Tanna <jamie.tanna@mend.io> Co-authored-by: RahulGautamSingh <rahultesnik@gmail.com> |
||
|
|
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. |
||
|
|
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 |
||
|
|
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> |
||
|
|
c97111e37e | fix(util/exec): disable execa rejection (#45651) | ||
|
|
6e03e8326f |
refactor(linters): add rule to enforce inline single use fixtures (#45230)
* feat(lint): add renovate/prefer-is-helpers and renovate/prefer-is-object rules Flag ad-hoc type checks that have @sindresorhus/is equivalents: - .filter(Boolean) -> .filter(isTruthy) - typeof x === 'string' -> isString(x) - x === null || x === undefined -> isNullOrUndefined(x) (and negation) The typeof x === 'object' shape lives in a separate suggestion-only rule (renovate/prefer-is-object, kept at warn) because isObject()/isPlainObject() are not drop-in equivalents: null, arrays and functions behave differently. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * refactor: fix renovate/prefer-is-helpers violations Replace ad-hoc type checks with @sindresorhus/is helpers across lib/: - 13x .filter(Boolean) -> .filter(isTruthy) - 36x typeof x ==/!== 'string' -> isString(x) / !isString(x) - 6x x === null || x === undefined -> isNullOrUndefined(x) (and negation) Promote renovate/prefer-is-helpers to error now that the codebase is clean. renovate/prefer-is-object stays at warn: its 15 remaining sites guard parsed YAML/JSON or unknown values where isObject()/isPlainObject() are not provably equivalent (null/array/function semantics). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * refactor: enforce renovate/prefer-is-object at error level Resolve the 15 remaining warnings and raise the rule from warn to error: - Migrate 10 sites to isObject() where the guarded value provably cannot be a function (parsed YAML/JSON, typed exec command arguments, or test-controlled fixtures): helm-values, woodpecker, bun, pre-commit managers, exec utils, file package cache, and the github http spec. In the bun manager this also fixes a latent crash where a null `workspaces` value would throw on the `in` operator. - Keep 5 sites as reasoned oxlint-disable comments where excluding functions is load-bearing: the byte-exact fingerprint canonicalizer (2), the circularity walker, the zod result guard, and the generic hasKey helper. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(lint): add renovate/inline-single-use-fixtures rule In spec files, a module-scope const fixture that is referenced from exactly one it()/test() block should be inlined into that test so the test is self-contained. The rule is a warn-level nudge (naming for clarity is a valid exception) with no autofix (moving code is risky). Uses oxlint jsPlugins scope analysis (getDeclaredVariables) for exact, shadowing-safe reference counting and bails out conservatively: only top-level non-exported single-declarator consts with multiline, non-function initializers whose every reference sits inside the callback function of one and the same test call are flagged. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * refactor: inline single-use spec fixtures into their tests Fix 26 renovate/inline-single-use-fixtures warnings in specs where the move is mechanical and value-preserving: codeBlock fixtures were re-indented uniformly (codeBlock strips the common indentation, so the produced string is unchanged) and Fixtures.get() calls resolve relative to the spec file, so relocating them inside the test body has no effect. The remaining 59 warnings are left for per-site judgment, as the rule is a warn-level nudge by design. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * refactor: inline remaining relocation-safe single-use spec fixtures Move the 24 remaining single-use module-scope fixtures whose initializer is a codeBlock tagged template or a Fixtures accessor call into the one it() block that uses them. Both shapes are value-preserving to relocate: codeBlock strips the common leading indentation, so the uniformly re-indented templates produce identical strings, and Fixtures.get() resolves relative to the spec file, not the call site's position in it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(lint): enforce inline-single-use-fixtures as error Narrow the rule to the initializer shapes where moving the declaration into the test body is provably value-preserving and unambiguously an improvement: codeBlock/stripIndent tagged templates (indentation- normalizing, so re-indenting is safe) and Fixtures accessor calls (resolve relative to the spec file). Plain object/array literals, untagged template literals (whose leading-newline formatting would need ugly re-indentation) and arbitrary call expressions remain per-site judgment calls and are no longer flagged. With the rule reduced to true defects and all remaining violations inlined, bump it from warn to error in the spec override block. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * refactor: inline contents of fixtures relocated into single-use tests The previous commits moved single-use module-scope Fixtures.get() consts into their one it() block, which only relocated the file indirection. Replace those relocated Fixtures.get() calls with the fixture file's actual content — codeBlock tagged templates for text fixtures — so the tests are self-contained, and delete the fixture files that no longer have any references. Relocated fixtures too large to inline comfortably (terraform-provider releases, sbt private-variable file, nuget v2 pkg list, bundled package-lock) keep their Fixtures.get() calls. Value preservation was verified by unchanged vitest results for every touched spec; all consumers parse the content, so codeBlock's trailing newline normalization is behavior-neutral. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * refactor: inline remaining large single-use fixtures Inline the four Fixtures.get() sites that were previously exempted for size, so every single-use fixture is now inlined regardless of size: - nuget v2_withoutProjectUrl.xml -> codeBlock (served via http mock) - terraform-provider releaseBackendIndexGoogleBeta.json -> plain object literal passed to the nock reply - npm locked-dependency bundled.package-lock.json -> codeBlock JSON string (consumed as raw lockFileContent) - sbt private-variable-dependency-file.scala -> codeBlock All consumers parse the content, so codeBlock's trailing-newline normalization is behavior-neutral; vitest results are unchanged for every touched spec, including the two pre-existing nuget failures. The nuget, terraform-provider and sbt fixture files have no remaining references and are deleted. bundled.package-lock.json is kept because package-lock/get-locked.spec.ts still reads it via Fixtures.getJson(). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * refactor: keep large bundled package-lock fixture in a file The bundled package-lock.json inlined into the "fails remediation if bundled" test is ~150 KB (~4,400 lines); inlining it as a codeBlock hurts readability more than it helps. Read it via Fixtures.get inside the test block instead — the inline-single-use-fixtures rule permits a Fixtures.get() call in test scope, and the fixture file is still present. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
0e90c69f91 |
perf(package-rules): avoid re-cloning invariant packageRules array on every rule match (#44781)
perf(package-rules): avoid re-cloning packageRules array on every rule merge applyPackageRules() merges each matching rule via mergeChildConfig(), which deep-clones the entire accumulated config — including the invariant packageRules array itself. On repos with many dependency instances and large rule arrays (e.g. vulnerabilityAlerts force-rules embedding advisory texts), this cloning dominates the whole run (observed at ~66% of total CPU in a production profile). Exclude packageRules from the working config while rules are applied and restore it afterwards, preserving the previous merge semantics for rules that themselves carry nested packageRules, and for null/empty/ absent inputs. Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
4732da7d2b |
fix(util/host-rules): apply the admin's headers over a repository's
As part of changes we've recently made to the merging of
`hostRules[].headers`, it highlighted that a repository - or a preset
it extends - could lead to a header an admin has configured for one host
to be sent to another.
This happened due to the way we additively merged i.e.
`Object.assign({}, ...matchedHeaders)`.
This doesn't require the repository to know a credential is being
set (for a specific host), or to act maliciously - any header of their
own for that host is enough to bring the administrator's masked one
back, with nothing logged or failing to show it happened.
To fix this, we can:
- apply admin headers last, as they are "trusted"
- stop repositories from being able to unset an admin-set header
- retain the existing logic for the "most specific rule" being used
- make sure that `trusted` isn't a field that could be injected by a
repository, and make sure we explicitly only set it where we know
we're using self-hosted administrator config
- make `filterAllowedHeaders` drop `headers` if every header name was
denied, so `{}` isn't returned and used to suppress headers when
merged
Co-authored-by: Claude Opus 5 <jamie.tanna+claude-code@mend.io>
|
||
|
|
c98f4f71c8 |
fix(util/host-rules): enforce allowedHeaders within add()
We previously enforced `allowedHeaders` by making sure that each `hostRules.add()` call had `filterAllowedHeaders`. This isn't super future proofed, and although we could add a linting rule, we should instead enforce this by making sure that `hostRules.add()` applies the filtering. This also catches cases like host rules from `.npmrc` which wasn't currently being filtered - albeit there was no means to bypass `allowedHeaders`. Moving `filterAllowedHeaders` into `lib/util` removes a dependency from the global bootstrap and `config-validator` importing from `lib/workers/repository/init/`. While doing so, we can also make sure that: - we log only one WARN for global `hostRules` being invalid, rather than once per `repositories[]` - `renovate-config-validator` validates a global config file's `hostRules` against that file's own `allowedHeaders`, like a real run would after parsing it Co-authored-by: Claude Fable 5 <jamie.tanna+claude-code@mend.io> |
||
|
|
3d56c38cf6 |
fix(util/host-rules): merge headers across matching rules
Previously, if there was a `hostRule[].headers` value for a given host set in both global self-hosted configuration and repo config, the repo config would overwrite /all/ headers set by the admin. To resolve this, we can now correctly merge the keys, with the repo config taking priority. This still only takes into account the `allowedHeaders`, so a repository cannot bypass admin settings. Co-authored-by: Claude Opus 5 <jamie.tanna+claude-code@mend.io> |
||
|
|
9a91b69f3c |
test(util/host-rules): cover existing header merging behaviour
As noticed while working on this branch, we currently do not merge an admin's `hostRules` settings, if a user has (in repo or preset config) specified `hostRules` themselves, when targeting the same `matchHost`. To cover the existing (broken) functionality, we can document this with a test. Co-authored-by: Claude Opus 5 <jamie.tanna+claude-code@mend.io> |
||
|
|
6d747ea8ff |
feat(manager): add apm manager for Agent Package Manager (#44390)
* feat(manager): add apm manager for Agent Package Manager Add a new `apm` manager that updates git-pinned dependencies in APM (Agent Package Manager) `apm.yml` manifests, as requested in renovatebot/renovate#42507. The manager parses the string entries under `dependencies.apm` and `devDependencies.apm` (`[host/]owner/repo[/subpath]#<ref>`) and selects the datasource dynamically from the git host, reusing the existing github-tags, gitlab-tags and git-tags datasources. MCP entries carry no pinnable version and are ignored. When an `apm.lock.yaml` lockfile is present, it is refreshed by running `apm install` via a new `apm` tool constraint (pypi `apm-cli`). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Vfjt3BpqDriiVsvwqVe9M9 * fix(manager/apm): resolve apm tool from github releases APM ships self-contained binaries as GitHub release assets (apm-linux-<arch>.tar.gz under vX.Y.Z tags), not as the PyPI apm-cli wheel. Point the apm tool constraint at the github-releases datasource so the version Renovate resolves matches the artifact that actually gets installed, stripping the leading `v` from the tag. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Vfjt3BpqDriiVsvwqVe9M9 * docs(manager/apm): point to canonical microsoft/apm repository The APM project's canonical home is github.com/microsoft/apm (originally danielmeppiel/apm). Update the manager URL and readme accordingly. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Vfjt3BpqDriiVsvwqVe9M9 * refactor(manager/apm): validate manifest with zod schema Address review feedback on the apm manager: - parse `apm.yml` with a dedicated zod schema via `parseSingleYaml(content, { customSchema })` instead of casting the parsed YAML to `ApmManifest` - `LooseArray(z.string())` keeps only the string dependency entries, so MCP objects and other non-string values are dropped by the schema and the manual `Array.isArray`/`is.string` guards are no longer needed - drop the `as SkipReason` casts now that the literals are inferred - remove the now-unused `types.ts` Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UbMAQBiMRNkCNxc8EGQw1C * Update schema.ts Co-authored-by: Sebastian Poxhofer <secustor@users.noreply.github.com> * Update readme.md Co-authored-by: Sebastian Poxhofer <secustor@users.noreply.github.com> * Update readme.md Co-authored-by: Sebastian Poxhofer <secustor@users.noreply.github.com> * refactor(manager/apm): simplify manifest schema and host detection Address review feedback: - default the `apm` array to `[]` in the schema (`LooseArray(...).catch([])`) since LooseArray already discards non-string entries, so downstream code no longer needs to handle `undefined` - use `coerceArray` when mapping the optional dependency sections - drop the redundant `host === 'github.com'` / `host === 'gitlab.com'` checks in datasource selection; `detectPlatform` already resolves those hosts (the explicit host comparison is only kept to decide registryUrls for self-hosted instances) - document that APM only supports remote git references and that dots in repo names / subpaths are handled, with a regression test Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UbMAQBiMRNkCNxc8EGQw1C * docs(manager/apm): fix readme trailing newline Remove the trailing blank line left after the readme edits so it passes `prettier` and `markdown-lint`. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UbMAQBiMRNkCNxc8EGQw1C * refactor(manager/apm): parse dependency path with named capture groups Replace the manual host/owner/repo splitting (and the standalone dot regex) with a single named-capture regex, as suggested in review. `owner` excludes dots so the optional hostname segment is unambiguous, while repo names and subpaths may still contain dots. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UbMAQBiMRNkCNxc8EGQw1C * test(manager/apm): align admin config with InternalGlobalConfigOptions `localDir` moved out of `RepoGlobalConfig` into `InternalGlobalConfigOptions` on main, so annotate the test admin config as `RepoGlobalConfig & InternalGlobalConfigOptions`, matching the other manager artifacts specs. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UbMAQBiMRNkCNxc8EGQw1C * fix(manager/apm): resolve GitLab nested groups and recognize SHA-pinned refs - resolve nested GitLab project slugs (e.g. `group/subgroup/project`) instead of capping at `owner/repo`, splitting the project from any virtual-package subpath using APM's primitive-dir / virtual-file markers - recognize the documented SHA-pinned form and skip it (`unversioned-reference`) rather than treating the SHA as a version or rewriting it * feat(manager/apm): support digest pinning for SHA-pinned deps Recover the release tag from the trailing YAML comment of a SHA-pinned entry (`owner/repo#<sha> # v2.0.0`) - which the structured parse strips - so it extracts as a digest (`currentDigest` + `currentValue`) and updates both via `pinDigests`, mirroring the github-actions manager. A bare SHA with no tag comment (or a quoted form we can't rewrite verbatim) is left skipped. * fix(manager/apm): map SHA-pin tag recovery per line to handle duplicates The raw-manifest scan matched the first line for the entry regardless of section, so the same `owner/repo#<sha>` pinned in both `dependencies` and `devDependencies` resolved to the first line's tag/replaceString. Consume each line at most once (in file order) so duplicate pins map to distinct lines. * fix(manager/apm): recognize `.chatmode.md` virtual files APM's VIRTUAL_FILE_EXTENSIONS includes `.chatmode.md`; add it to the subpath-boundary regex so a `*.chatmode.md` virtual dependency on a nested-group host (GitLab, Azure DevOps) is not folded into the repo path. * test(manager/apm): cover SHA-pin digest updates on git-tags hosts Lock in that a SHA-pinned dep on a non-github/gitlab host resolves via the git-tags datasource (which supports `getDigest` through `git ls-remote`), so the commit is preserved on a bump rather than dropped. * fix(manager/apm): commit regenerated harness files, not just the lockfile `apm install` re-deploys the harness directories APM consumers commit (`.github/`, `.claude/`, ...), but updateArtifacts only returned `apm.lock.yaml`, leaving instruction files stale after a bump. Capture every file the install changed via `getRepoStatus` (additions + deletions); the gitignored `apm_modules/` cache is excluded, and the manifest stays with `updatedPackageFiles`. * fix(manager/apm): declare lockFileMaintenanceIsDelegatedToPackageManager main now requires managers with `supportsLockFileMaintenance` to set this field. apm regenerates the lockfile via `apm install`, so it delegates to the package manager (`true`). * refactor(manager/apm): reuse shared regex and git-sha helpers Adopt the shared utilities per review: - use the exported `newlineRegex` from `util/regex` instead of a local copy - use `isLongCommitSha` from `util/schema-utils/git` instead of a bespoke SHA regex - filter path segments with `isTruthy` instead of `Boolean` * test(manager/apm): align docker exec snapshot with current base-image wrapper The shared `util/exec` docker wrapper now injects `-e CI` and single-quotes the `bash -l -c` payload. Update the docker-mode expectation to match, so the snapshot tracks the current wrapper output. --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> Co-authored-by: Sebastian Poxhofer <secustor@users.noreply.github.com> |
||
|
|
13ab659c5e |
fix(cache): re-add cacache.verify() to garbage collect orphaned content from put() overwrites (#44987)
* fix(cache): re-add cacache.verify() to garbage collect orphaned content from put() overwrites * Fix lint * Apply suggestion from @bbodenmiller * Apply suggestion from @bbodenmiller |
||
|
|
b62fe59594 |
fix: remove references to "bot" (#39305)
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> |
||
|
|
ea3b7acede |
test(util/git): add missing coverage of setGitAuthor (#45607)
As part of follow-up changes, we're removing the "bot" terminology. While doing so, GitHub Copilot Code Review discovered that there was no coverage of the defaults in this function. Before we change this, we can add coverage of the defaults. Co-authored-by: Claude Sonnet 5 <jamie.tanna+claude-code@mend.io> |
||
|
|
cfb2a76354 | fix(util/markdown): preserve at signs in URLs (#45555) |