Similar to a fix already made for gradle-wrapper, this fixes an issue
with grouped dependencies leading to a wrong Java version, as
`config.currentValue` may be the version of an unrelated dependency in
the group rather than the version of maven itself.
We can instead look up the `maven` dependency's own `currentValue` in
`updatedDeps`.
This does change behaviour when only `maven-wrapper` (and not `maven`
itself) is being updated: previously `config.currentValue` happened to
hold the wrapper's own version in that single-upgrade case, which
mostly tracks the same major version as the actual Maven release.
Now, with no `maven` entry in `updatedDeps` to look up, this falls
back to the default Java constraint. This is deemed acceptable, as it
matches the same trade-off the gradle-wrapper fix already made.
Co-Authored-By: Claude Sonnet 5 <jamie.tanna+claude-code@mend.io>
* feat(datasource): add GraalVM version datasource with support for JDK and JRE releases
* feat(datasource): add GraalVM version datasource with support for JDK and JRE releases
* feat(datasource): add GraalVM version datasource with support for JDK and JRE releases
* feat(datasource): add GraalVM version datasource with support for JDK and JRE releases
* feat(datasource): add GraalVM version datasource with support for JDK and JRE releases
* add coverage
* fix
* fix
* fix
* code cov
* code cov
* code cov
* refactor(datasource): integrate GraalVM into java-version datasource
Merges graalvm-version datasource into java-version as a unified multi-vendor
Java distribution datasource. Implements vendor-based modular architecture with
routing layer for Adoptium (Eclipse Temurin) and Oracle GraalVM.
Architecture:
- Vendor-specific handlers (adoptium.ts, graalvm.ts) with routing in index.ts
- Vendor-aware system detection with different arch/os mappings per vendor
- Custom registry support for GraalVM's mise-java API
- Adoptium remains the default registry for backwards compatibility
Package names:
- New explicit: adoptium-jdk, adoptium-jre, oracle-graalvm-jdk, oracle-graalvm-jre
- Legacy (map to Adoptium): java, java-jdk, java-jre
Changes:
- Created adoptium.ts and graalvm.ts vendor handlers
- Enhanced common.ts with vendor detection and vendor-specific system mappings
- Updated index.ts with routing layer and custom registry support
- Merged test suites with comprehensive coverage for both vendors
- Updated mise manager integration to use JavaVersionDatasource
- Removed graalvm-version datasource directory
All 65 datasource tests and 93 mise manager tests passing.
* fix
* test(datasource): add coverage for non-HttpError paths in java-version
* fix(datasource): remove dead code and add coverage for java-version
Remove unreachable HttpError handling from _getReleases catch block
since both getAdoptiumReleases and getGraalvmReleases handle HttpError
internally. Add test for the graalvmRegistryUrl fallback when
registryUrl is undefined.
* fix(datasource): address review comments for GraalVM datasource
- Remove unused 'datasource-graalvm-version' cache namespace
- Convert MiseJavaRelease interface to Zod schema and derive type from it
- Use http.getJson() with the Zod schema in getGraalvmReleases instead
of getJsonUnchecked, letting the schema handle invalid/empty responses
- Update getGraalvmReleases test mock to use getJson
* docs: remove unused datasource-graalvm-version from cacheTtlOverride docs
* fix(datasource): preserve Adoptium config typing
* fix(datasource): harden GraalVM release parsing
* fix(datasource): address GraalVM review feedback
* test: cover GraalVM mise package mapping
* test(datasource): cover default GraalVM registry fallback
* test(datasource): cover legacy registry resolution
* test(datasource): type legacy datasource mocks
* fix(datasource): harden GraalVM defaults and responses
* refactor(datasource): use function-only registry accessors
* test(datasource): cover registry accessor functions
* test: restore coverage exclusions
* chore: retrigger CI checks
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.
Seven datasources (bitbucket-tags, bitbucket-server-tags,
gitlab-releases, gitea-tags, gitea-releases, forgejo-tags,
forgejo-releases) declared DatasourceApi metadata fields
(defaultRegistryUrls, releaseTimestampSupport/Note,
sourceUrlSupport/Note) as class `static readonly` instead of
instance fields. The contract (DatasourceApi, datasource.ts) reads
these off the instance, so the base class defaults silently applied
instead. Convert them to instance fields (`override readonly`, the
terraform-module pattern), keeping a static array only where a
static helper (or lib/modules/datasource/go/) still needs it, backed
by `override readonly defaultRegistryUrls = <Class>.defaultRegistryUrls`.
Delete the redundant `static readonly registryStrategy = 'first'`
(gitlab-releases, bitbucket-tags) and `static readonly
customRegistrySupport = true` (bitbucket-tags), which only matched
the base default by coincidence.
Replace the dead 'validates datasource' test in
lib/modules/datasource/index.spec.ts, which filtered out every
class-based datasource before validating (all 82 are class-based), with
a metadata contract test over getDatasources(): no listed metadata
name may be declared as a static-only property, releaseTimestampNote
requires releaseTimestampSupport, and sourceUrlNote requires
sourceUrlSupport !== 'none'.
Behaviour changes:
- These 7 datasources now report a real defaultRegistryUrls (or
releaseTimestampSupport/sourceUrlSupport) to the registry
(lib/modules/datasource/index.ts), so requests without an explicit
registryUrl now go through the registry-URL resolution and
getRegistryReleases package-cache path instead of calling
getReleases directly. getDigestConfig now receives a resolved
registryUrl instead of undefined for these datasources.
- Generated docs (tools/docs/datasources.ts,
tools/docs/minimum-release-age.ts) will render the real
"Release timestamp support" / "Source URL support" values and list
these datasources under minimumReleaseAge support, instead of "No".
* fix(extract): pass vulnerability alert state to managers
Managers cannot tell whether vulnerability remediation is active, so they
cannot decide to extract dependencies which are only useful to a security
update, e.g. transitive dependencies in a lock file.
Add `hasVulnerabilityAlertsRules` to `ExtractConfig`, derived from any
vulnerability source: `osvVulnerabilityAlerts`, or platform alerts, which
are turned into `isVulnerabilityAlert` package rules during init.
The flag is part of the extraction fingerprint, so cached extract results
are invalidated when it changes.
* refactor: drop redundant JSDoc on hasVulnerabilityAlertsRules
* docs(manager): document hasVulnerabilityAlertsRules on ExtractConfig
* fix(extract): omit hasVulnerabilityAlertsRules from fingerprint when false
Keeps the extract config hash unchanged for repositories which have no
vulnerability alerts, so they do not re-extract once on upgrade.
* fix(versioning): use BigInt to avoid integer precision loss in gradle, maven and loose
* fix(versioning): preserve exact update types
* Update lib/modules/versioning/maven/index.ts
* fix(versioning): satisfy func-style and prefer-at lint rules
* treat missing loose version section as zero in isSame
* fix coverage
* revert unrelated custom-manager rangeStrategy change
---------
Co-authored-by: Sergei Zharinov <zharinov@users.noreply.github.com>
As noted in #46343, upstream mise renamed a large number of `pipx`
registry entries to use the `pypi` backend instead, e.g. `ansible`
went from `pipx:ansible` to `pypi:ansible`.
As we didn't previously recognise `pypi` as a backend, we'd silently
stop updating those dependencies when we updated the mise registry
in #46343.
As the `pypi` uses the same tool syntax as `pipx`, we can share the
existing implementation.
Co-authored-by: Claude Sonnet 5 <jamie.tanna+claude-code@mend.io>
helmfile, flux, fleet and terraform each decided differently whether a Helm chart reference points at a local path. fleet and terraform used terraform's checkIfStringIsPath, whose regex was unanchored with unescaped dots, so it matched any string containing a slash followed by a non-slash character.
Add isLocalChartPath to helmv3/utils.ts with helmfile's prefix check and use it in all four managers. This drops the fleet to terraform dependency and removes checkIfStringIsPath, which had no other users.
Extract a shared generateRegistryLoginCmd(name, registry) helper in
helmv3/common.ts that strips the oci:// prefix, derives the host and
looks up the matching host rule before delegating to generateLoginCmd.
helmv3/artifacts.ts and helmfile/artifacts.ts now both call this
helper instead of each building a RepositoryRule inline, removing the
stale oci:// TODO. helmfile/utils.ts loses its duplicate
generateRegistryLoginCmd, and its unrelated isOCIRegistry(repository)
(checking the oci flag) is renamed to isOciRepositoryFlagSet so it no
longer collides in name with helmv3/oci.ts's prefix-based
isOCIRegistry(string), which helmfile/extract.ts also imports.
Addresses audit finding M8. Generated `helm registry login` commands
are unchanged, proved by the existing artifacts specs passing with
their original expectations untouched.
refactor(manager/helmv3): share generateHelmEnvs with kustomize
Move kustomize's generateHelmEnvs into helmv3/common.ts and delete the
kustomize copy. The merged function always sets the three HELM_*
path variables and sets HELM_EXPERIMENTAL_OCI unless a helm
constraint is present and proves helm >= 3.8 (which ignores the
variable).
Behaviour change: kustomize now sets HELM_EXPERIMENTAL_OCI when the
helm constraint is unknown (previously it only set it for a known
constraint below 3.8). helmv3 and helmfile now omit the variable
once a resolved helm constraint proves helm >= 3.8.
`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.
`ChangeLogSource` looked its tags datasource up from a constructor
argument every subclass repeated, and every subclass implemented
`getAPIBaseUrl` itself, so the same derivation existed twice: once in the
changelog source and once in `PLATFORM_FAMILIES`.
Take only the platform in the constructor, read the tags datasource and
the API base URL derivation from the table, and drop the five
`getAPIBaseUrl` overrides (Forgejo already inherited Gitea's). The
GitHub source's token lookup for github.com now asks the base class for
the API base URL instead of repeating `https://api.github.com/`; the
lookup for a GitHub Enterprise Server host keeps using the source URL.
`GiteaChangeLogSource` keeps its optional platform parameter so that
`ForgejoChangeLogSource` can still extend it, and now needs no datasource
argument.
Behaviour changes: none, every derived URL is the same string as before.
The changelog release-notes module dispatched on `project.type` in two
six-way switches, one per fetcher, even though a `ChangeLogSource` has
been threaded into `getReleaseNotesMd`/`addReleaseNotes` since #45260.
Move each `<platform>/index.ts` body into the matching
`<platform>/source.ts` class: `getReleaseNotesMd` is now an abstract
method on `ChangeLogSource`, and `getReleaseList` a virtual one whose
default returns an empty list (bitbucket and bitbucket-server keep the
default). The module-level `Http` instance becomes a class field. The
Gitea/Forgejo body that `gitea/common.ts` built through
`createChangelogApi()` becomes two methods on `GiteaChangeLogSource`,
which `ForgejoChangeLogSource` inherits, overriding only the `http`
field, so `gitea/common.ts` is gone as well.
`getReleaseList`, `getCachedReleaseList`, `getReleaseNotes`,
`getReleaseNotesMdFile` and `getReleaseNotesMdFileInner` now take the
source, and both switches are gone. The platform id list is no longer
repeated in `source.ts`: the constructor's datasource is typed as
`${ChangeLogPlatform}-tags`, and `platform` becomes protected so the
shared Gitea methods can name themselves when logging.
The `<platform>/index.spec.ts` files are renamed to `source.spec.ts` to
follow the file they now cover.
Behaviour changes, all limited to logging:
- the `logger.info('Unsupported Bitbucket Cloud feature. Skipping
release fetching.')` and `logger.trace('Unsupported Bitbucket Server
feature. Skipping release fetching.')` messages are replaced by one
trace message in the base class default.
- the `logger.warn(..., 'Invalid project type')` fallbacks are gone;
an unknown platform can no longer reach these functions, because
`getChangeLogSourceFor` already returns null for it.
- the gitea and forgejo fetchers traced `{ id: '<platform>-changelog' }`
plus a bare function name; they now trace `<platform>.<function>()`
like the other four platforms, and the forgejo `getReleaseList` trace
no longer says `getReleaseNotesMd()`.
* 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
* refactor(util): one "read repo file at ref" helper per host
The "fetch a single file from a repository at a ref" logic was implemented
separately in the platform, preset and changelog layers for every host, and
`lib/config/presets` reached into `lib/modules/platform` to reuse the Gitea
client and its contents helper.
Add per-host util modules that own the URL shape, the response schema and the
decoding, taking the caller's own `Http` instance so the host type stays with
the calling layer:
- `lib/util/gitea/contents.ts` (shared by Gitea and Forgejo): the `RepoContents`
and `ContentsListResponse` schemas, `getRepoFile()` and `listRepoDir()`
- `lib/util/github/contents.ts`, `lib/util/gitlab/files.ts`,
`lib/util/bitbucket/files.ts`: `getRepoFile()`
Callers rewired: `getRawFile` for github/gitlab/bitbucket and the shared
Gitea/Forgejo platform, `fetchJSONFile` for the github/gitlab presets and the
shared Gitea/Forgejo preset source, and `getReleaseNotesMd` for the bitbucket
changelog source and the shared Gitea/Forgejo changelog source.
`getRepoContents` is gone from `gitea-helper.ts`, the contents schemas move out
of `platform/gitea/schema.ts`, and the Gitea and Forgejo preset sources build
their own `Http` instance instead of importing the platform's.
Behaviour changes:
- GitLab presets no longer look up the project's default branch before
fetching; they request `?ref=HEAD` like the platform already does, and they
use the JSON contents endpoint instead of `/raw`. This removes one HTTP
request per preset lookup.
- A GitHub contents response without a `content` field now surfaces as
`PRESET_DEP_NOT_FOUND` instead of an uncaught `TypeError`, because the
base64 decode moved inside the try block.
- GitLab `getRawFile` escapes the file name with `encodeURIComponent` instead
of only escaping `/`.
- Gitea/Forgejo contents URLs escape each path segment on its own, so a nested
path keeps its slashes: the platform `getRawFile` and the preset source now
request `contents/path/custom.json` where they used to request
`contents/path%2Fcustom.json`. Both spellings work; the unescaped one is the
documented form and the one the changelog source already used.
- The Gitea/Forgejo changelog source now escapes the segments of the source
directory and of the changelog file path, as the platform and preset callers
already did.
- The `Preset URL` trace log in the GitHub and GitLab presets is gone; the URL
is built inside the util helper.
Not covered here: Bitbucket Server (platform reads `browse`, changelog reads
`raw` - genuinely different endpoints) and the GitHub/GitLab changelog sources,
which use the git tree/blob APIs rather than a path lookup.
* refactor(platform): keep the repo-file helpers in the platform folders
The helpers landed under `lib/util/<host>/`, which added a second home for
host API code next to the platform modules that already own it. Move them
into the platform folder of their host instead, so there is one place per
host rather than two:
- `lib/util/github/contents.ts` -> `lib/modules/platform/github/contents.ts`
- `lib/util/gitlab/files.ts` -> `lib/modules/platform/gitlab/files.ts`
- `lib/util/bitbucket/files.ts` -> `lib/modules/platform/bitbucket/files.ts`
- `lib/util/gitea/contents.ts` -> `lib/modules/platform/gitea/contents.ts`
The preset and changelog layers import them from there, which is the
direction they already had: the preset sources read `gitea-helper.ts` and the
changelog sources read `platform/*/schema.ts`.
`RepoContents` and `ContentsListResponse` go back to `platform/gitea/schema.ts`
with their tests, because `renovate/zod-schema-location` keeps zod schemas
under `lib/modules/**` in a `schema.ts`. That is also where the changelog
sources read them from today.
The functions keep taking the caller's `Http` instance, so the host type and
its host rules still belong to the calling layer; only the file location
changes.
* refactor(presets): keep using the platform's Gitea and Forgejo clients
The preset sources were switched to `new GiteaHttp()` / `new ForgejoHttp()` to
keep `lib/config/presets` from importing `lib/modules/platform`. That boundary
is gone now that the contents helpers live in the platform folder, and the
second instance never bought anything: `giteaHttp` and `forgejoHttp` are
themselves plain `new GiteaHttp()` / `new ForgejoHttp()`, an `HttpBase` holds
only its host type and its merged default options, and `baseUrl` is module
state shared by every instance of the class rather than per-instance.
Both files are back to what they were, so the preset sources no longer appear
in this change at all.
* refactor(platform): resolve repo-file URLs against the client base url
`getRepoFile()` and `listRepoDir()` took an `apiBaseUrl` positional argument
that meant three different things depending on the caller: an empty string or
a slash for "use the client's own base", a path prefix for Gitea, and a full
API base URL for the preset and changelog layers.
`Http` already owns this: `resolveUrl()` reads `options.baseUrl ?? this.baseUrl`.
The helpers now build a URL relative to the API root and take no base of their
own, so callers reading the host their client is configured for pass nothing,
and callers reading a foreign host pass `baseUrl` in the options they already
pass.
The GitHub, GitLab and Bitbucket platform clients are based at the API root,
so those three call sites lose their placeholder argument outright. The Gitea
client is based at the web root and the rest of that folder prefixes every URL
with `API_PATH`, so the Gitea callers pass that same constant instead of the
duplicate `API_BASE_PATH` this stack had added next to it.
* test(platform): cover the client base url fallback for repo files
The GitHub and GitLab `getRepoFile()` specs always passed a `baseUrl` option,
so the fallback to the client's own base url - the path the platform callers
take now that they pass no base at all - was never exercised. Both specs get a
case that sets a self-hosted base url on the client and reads a file without
passing one, which also pins that a configured endpoint reaches the request.
The Bitbucket and Gitea specs already covered it.
* refactor(platform): name the repo-file helper files.ts on every platform
The helper was named after each vendor's own word for the endpoint:
GitHub and Gitea call it "contents", GitLab calls it "files" and
Bitbucket calls it "src", so the same helper sat under two different
names and neither matched Bitbucket.
Use files.ts everywhere, so a reader looking for "read a file from a
repo" finds the same filename in every platform folder. The vendor's
noun still appears where it belongs: in the request paths, the doc links
and the Gitea schema names.
* docs: fix images rendered as code inside figure blocks
The docs build forces tab_length 2, so content indented by two spaces
inside a <figure markdown> block is parsed as an indented code block.
Six images were printed as literal markdown instead of being rendered.
De-indent the six figure blocks and add a test that fails if indented
content reappears inside one.
* chore: add markdownlint rule for indented figure content
Replace the vitest check with a markdownlint custom rule. It runs with
pnpm markdown-lint, reports file and line like any other markdown
violation, and uses the micromark tokens so a <figure markdown> block
inside a fenced code sample is not flagged.
* Revert "chore: add markdownlint rule for indented figure content"
This reverts commit b853804501.
* chore: remove test
* fix(manager/gomod): match gofmt-style unindented closing paren in block end regex
gofmt places the closing ) of require/replace/exclude blocks at column 0,
but endBlockRegex required at least one leading whitespace character. This
caused inExcludeBlock to never reset after an exclude (...) block formatted
by gofmt, silently discarding every subsequent line in the file, including
all later require/replace blocks.
* fix(manager/gomod): correct test expectation for valid version
v0.17.3 is a valid version so no skipReason is set; the test incorrectly
asserted skipReason: 'invalid-version', copied from other fixtures using
the deliberately invalid v1.2 placeholder.
The Forgejo preset source was a copy of the Gitea one. createPresetSource() builds fetchJSONFile(), getPresetFromEndpoint() and getPreset() for a given Http client and default endpoint; both modules bind it to their own.
The Forgejo changelog module and ChangeLogSource were rename-copies of the Gitea ones. createChangelogApi() builds getReleaseNotesMd() and getReleaseList() for a given Http client, and ForgejoChangeLogSource extends GiteaChangeLogSource.
* refactor(datasource): share the Gitea datasources with Forgejo
forgejo-tags and forgejo-releases were copies of gitea-tags and gitea-releases, and both -releases datasources re-implemented the digest lookup of the -tags ones. A GiteaDatasource base class now holds the shared implementation; the Forgejo datasources only declare their id, default registry URL, cache namespace and Http client. Results are only written to the shared package cache when they come from the public default instance.
* test(datasource): cover the default registry fallback
`GiteaDatasource.getRegistryUrl()` falls back to the instance's default
registry when the caller passes none, which the datasource dispatcher never
does because it resolves a registry URL first. Call `getTagCommit()` on the
instance instead, where the parameter is declared optional, once per default
registry so the Forgejo case also proves the base class reads the subclass's
`defaultRegistryUrls` rather than the Gitea static field.
getDashboardMarkdownVulnerabilities() calls Vulnerabilities.create()
and fetchVulnerabilities() with no error handling. Its sibling call in
extract-update.ts's fetchVulnerabilities() wraps the identical call in
a try/catch and logs a warning on failure, but this path does not, so
any OSV database fetch failure (network issue, or an upstream
osv-offline release with no attached asset, which is what triggered
this) throws all the way up to the repository-level catch-all and
aborts the whole repository run with UNKNOWN_ERROR, even though
dependencyDashboardOSVVulnerabilitySummary is a purely informational,
best-effort feature.
Wrap the fetch in a try/catch mirroring the extract-update.ts pattern,
so a failed OSV lookup degrades to a fallback message in the
Vulnerabilities section instead of crashing repository processing.
This PR was written primarily by Claude Code.
* 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>
* feat(versioning): comparator ranges for the regex scheme
The regex versioning orders versions by the release array built from its
captured groups (major, minor, patch, build, revision) but matches every range
by exact equality, so a comparator range in `allowedVersions` (`<=1.2.3` and
friends) never resolves against a regex-versioned dependency.
Add a scheme-independent comparator parser (`generic-range.ts`) and wire it
into the regex scheme's `isValid`/`matches`/`getSatisfyingVersion`/
`minSatisfyingVersion`, evaluating each `<`, `<=`, `>`, `>=`, `=`, `==` term
through the scheme's own `_compare`. A pattern that captures a `-ee.N` suffix
as an ordered `build` component then resolves `<=X-ee.0` correctly, where npm
`semver` cannot: as a prerelease the suffix breaks the range, as build metadata
it is ignored, and a four-part release is not valid semver.
The parser defers operand validation and comparison to the caller, so it stays
independent of npm `semver` (used by the semver family) and consistent with the
scheme's own ordering. Dependency values stay exact tags, so `supportsRanges`
remains `false`.
* test(versioning): cover regex ordering by capture-group name, not position
Add a calendar-versioning case (`YYYY.DD.MM`) showing comparison follows the `major`/`minor`/`patch` capture-group names in precedence order, not their left-to-right position: naming the month `minor` and the day `patch` sorts by year, then month, then day even though the day is the middle field.
Also reword the readme, which described this as "the same capture-group ordering" and read as positional, to state the major/minor/patch precedence explicitly.
* refactor(versioning): declare RangeComparator in types.ts
The `renovate/types-location` rule requires an exported type to live in a
colocated `types.ts`, so `RangeComparator` is declared there and
`generic-range.ts` imports it.
If we don't discover the image family for a given image, instead of
suggesting a package update that may not exist - as we may look up a
`defaultRegistryUrls` entry that doesn't match the distro we're using -
we should not suggest any update.
In these cases, we can specify the `skipReason: unknown-registry`.
In #45706, we'll try and auto-detect this where possible.
Co-authored-by: Claude Opus 5 <jamie.tanna+claude-code@mend.io>
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>
Update replacements.json
<!-- If this is your first pull request: sign the CLA with this GitHub app: https://cla-assistant.io/renovatebot/renovate -->
<!-- Make sure the `Allow edits and access to secrets by maintainers` checkbox is checked on this pull request. -->
<!-- Please read https://github.com/renovatebot/renovate/blob/main/.github/contributing.md before you create your pull request.-->
<!-- If you're an AI/LLM agent, follow this template, putting any additional information under "Changes" -->
## Changes
Adds a replacement for https://www.npmjs.com/package/@temporalio/interceptors-opentelemetry to the newer version https://www.npmjs.com/package/@temporalio/interceptors-opentelemetry-v2, which targets v2 of OpenTelemetry. The non-v2 version is stuck on v1 of OpenTelemetry
<!-- Describe what behavior is changed by this PR. -->
## Context
Please select one of the following:
- [ ] This closes an existing Issue, Closes: # <!-- NOTE that this should NOT be a Discussion -->
- [x] This doesn't close an Issue, but I accept the risk that this PR may be closed if maintainers disagree with its opening or implementation
## AI assistance disclosure
<!-- We request this information to assist reviewers in identifying AI-generated errors and other issues specific to AI usage. While we typically permit the use of AI tools, we appreciate being notified when they are employed. -->
<!-- If you're an AI/LLM agent, you MUST disclose usage. Please note which model you are, too -->
Did you use AI tools to create any part of this pull request?
Please select one option and, if yes, briefly describe how AI was used (e.g., code, tests, docs) and which tool(s) you used.
- [ ] No — I did not use AI for this contribution.
- [x] Yes — minimal assistance (e.g., IDE autocomplete, small code completions, grammar fixes).
- [ ] Yes — substantive assistance (AI-generated non‑trivial portions of code, tests, or documentation).
- [ ] Yes — other (please describe):
## Documentation (please check one with an [x])
- [ ] I have updated the documentation, or
- [x] No documentation update is required
## How I've tested my work (please select one)
I have verified these changes via:
- [x] Code inspection only, or
- [ ] Newly added/modified unit tests, or
- [ ] No unit tests, but ran on a real repository, or
- [ ] Both unit tests + ran on a real repository
The public repository: <URL>
<!-- If you have any suggestions about this PR template, edit it here: https://github.com/renovatebot/renovate/edit/main/.github/pull_request_template.md -->
<!-- Please do not force push to your PR's branch after you have created your PR, as doing so forces us to review the whole PR again. This makes it harder for us to review your work because we don't know what has changed. -->
<!-- PRs will always be squashed by us when we merge your work. You can commit as many times as you need in this branch. -->
<!-- All the commit messages will be part of the final commit - if you have strong thoughts about amending your squashed commit message before merge, please let a maintainer know -->
For exact (single-version) specs, only update a pin whose current version
matches the version being upgraded from. This prevents the repo-wide
Package.resolved sweep from rewriting a same-named transitive pin in an
unrelated package to a version that package's constraints don't allow.
Co-authored-by: Jamie Tanna <jamie.tanna@mend.io>
Two trace statements emitted registry credentials and relied on the secret
sanitizer alone to mask them:
- `pip-compile` logged the whole `extraEnv` object, which holds the
`KEYRING_SERVICE_PASSWORD_n` values built from host rules.
- `helmv3` logged the generated `helm registry login` command, which
carries `--password <password>`.
The sanitizer only registers secrets longer than three characters and
only those that went through `hostRules.add()`, so both lines could leak
a short or an externally sourced password.
`pip-compile` now logs the credential variable names alongside the command
and `helmv3` logs the target host only. Specs assert that neither logger
call receives the credential values.
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.
* refactor(python): share PyPI index credential resolution
pipenv, poetry, uv, pip-compile and the pypi datasource each resolved
index credentials differently, so a host rule that worked for one Python
manager did not necessarily work for the next. Add
`findPypiIndexCredentials()` in `lib/modules/datasource/pypi/host-rules.ts`,
which strips the URL's own `user:password@` part, runs a `pypi`-scoped
`hostRules.find()`, and only falls back to Google Artifact Registry when
the matched rule has no username and no password. The repeated
`.pkg.dev` test moves into a single `isGoogleArtifactRegistry()`
predicate next to `getGoogleAuthHostRule()`.
Behaviour changes:
- pipenv now supports Google Artifact Registry; previously it only ever
consulted host rules.
Spec: manager/pipenv/artifacts > "passes Google Artifact Registry
credential environment vars"
- pip-compile now supports Google Artifact Registry.
Spec: manager/pip-compile/common > getRegistryCredVarsFromPackageFiles()
> "supports Google Artifact Registry"
- pip-compile now matches `hostType: pypi` rules; its find call was
unscoped, so such rules were skipped.
Spec: manager/pip-compile/common > getRegistryCredVarsFromPackageFiles()
> "ignores a rule scoped to another hostType"
- pip-compile now matches a `matchHost` that contains a path; it reduced
every registry URL to its origin first, and `matchesHost()` compares
with `href.startsWith()`, so a path-scoped rule could never match.
Spec: manager/pip-compile/common > getRegistryCredVarsFromPackageFiles()
> "matches a rule scoped to a path"
- poetry and uv now strip `${USER}:${PASS}@` style placeholders from the
index URL before matching, so a `matchHost` can match those URLs too.
Spec: datasource/pypi/host-rules > "strips credentials from the URL
before matching"
- poetry now falls back to Google Artifact Registry when the matched rule
carries no username or password; any non-empty rule used to suppress
the fallback. Its dead unscoped `find({ url })` fallback is gone: a
`pypi`-scoped find already matches every rule an unscoped one would.
Spec: manager/poetry/artifacts > "falls back to Google auth when the
matching rule has no credentials"
- the pypi datasource now prefers host rule credentials over the Google
token for `*.pkg.dev`; it took the Google token unconditionally, and
the resulting `authorization` header made `applyHostRule()` skip the
configured credentials.
Spec: datasource/pypi/index > getReleases > "prefers host rule
credentials over Google Auth"
`getRegistryCredEnvVars()` also no longer logs the whole matched host
rule as the log merge object.
* test(pipenv): inline the Google Artifact Registry Pipfile
The fixture was used by a single spec case; inline it as requested in the
review of #46127.
* test(datasource/pypi): cover partial Google Artifact Registry credentials
A host rule that carries only a username or only a password still produces a
Basic header, but nothing exercised those two `?? ''` fallbacks, so the merge
queue failed the global branch threshold.
* 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>
`getUpdateType()` never read its `config` argument and `UpdateTypeConfig` was a
field-for-field copy of `BucketConfig`, while `getBucket()` and
`getUpdateType()` each ran their own major/minor comparison. Both now call a
single `classifyRelease(versioningApi, currentVersion, newVersion)`, which asks
`isSame()` for the minor as well as the major and falls back to comparing
`getMajor()`/`getMinor()` when the versioning does not implement it.
`getUpdateType()` and `UpdateTypeConfig` are gone; the two callers use
`classifyRelease()` directly.
Behaviour changes:
- `getBucket()` now honours `isSame('major')`, so `pvp` no longer buckets
1.1.x -> 1.10.x as non-major just because `getMajor()` squashes both into the
float 1.1.
- The update type now reports a difference rather than an increase, matching
what `getBucket()` has always done. Epoch-aware versionings (deb, debian,
pep440, rpm) can produce a greater version with a lower major or minor
number; those updates were bucketed as major/minor but labelled `patch`, and
are now labelled to match their bucket.
`resolveCurrentVersion()` took an `allVersions: string[]` and a separate
`nonDeprecatedVersions: string[]`. The caller built the first from the
releases filtered by `isVersion()` and `followTag`, but the second from the
unfiltered `dependency.releases`, so the "prefer non-deprecated, fall back to
all" passes searched different sets of releases.
It now takes the filtered `Release[]` and derives both lists from it, the same
way `getNewestMatchingVersion()` already did. The shared pair of passes moved
into a private `getCurrentVersionFromReleases()` helper used by both.
No behaviour change in practice: the non-deprecated pass no longer sees
releases that are not versions or that `followTag` excluded, but a non-version
release could only have been picked if `versioningApi.matches()` accepted it,
and the whole lookup spec (including every `followTag` case) is unchanged.
`lookupUpdates()` was a single ~840-line function whose stages could only be
reached end-to-end through the datasource layer, which is why its spec had
grown to 6700 lines. Move seven of those stages into separately testable
functions, leaving `lookupUpdates()` as a linear orchestrator:
- `matchVersionCompatibility()` / `restoreVersionCompatibility()` in the new
`version-compatibility.ts`
- `resolveRangeStrategy()` in the new `range-strategy.ts`
- `resolveCurrentVersion()` and `getNewestMatchingVersion()` in `current.ts`
- `applyVulnerabilityFixFilter()` in the new `vulnerability.ts`
- `groupReleasesIntoBuckets()` in `bucket.ts`
- `resolveUpdateDigests()` in the new `digest.ts`, which also takes over
`couldApplyMinimumReleaseAgeToDigest()`, `applyMinimumReleaseAgeToDigestUpdate()`
and `DigestLikeUpdate` from `index.ts`
- `stripNoopUpdates()` in `utils.ts`
`lookupUpdates()` keeps its signature and `index.ts` drops from 1020 to 665
lines. Each new stage gets a focused spec; the end-to-end specs are unchanged.
No behaviour change.