Commit Graph

4064 Commits

Author SHA1 Message Date
Jamie Tanna a5eb22bb2f fix(manager/maven-wrapper): correctly set Java version when handling a group
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>
2026-09-23 17:00:39 +01:00
Michael Kriese 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>
2026-09-23 14:12:16 +00:00
Sergei Zharinov ac2fc77641 refactor(http-mock): Remove request tracing (#44482)
* refactor(http-mock): remove request tracing

* test: replace remaining HTTP trace assertions
2026-09-23 09:01:01 +00:00
Gregor Zeitlinger 0d9888a4f2 feat(datasource): add GraalVM version datasource (#40497)
* 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
2026-09-23 08:40:24 +00:00
Michael Munch 2e23302665 feat(manager/helmv3): support token-only registry logins (#46373)
🐛 fix(helmv3): support token-only registry logins
2026-09-22 21:48:36 +00:00
Sebastian Poxhofer 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.
2026-09-22 19:30:25 +00:00
Sebastian Poxhofer a4b263a21e fix(datasource): declare metadata as instance fields, not statics (#46101)
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".
2026-09-22 19:30:24 +00:00
Kim Gustyr eee64df614 fix(workers/repository/extract): pass vulnerability alert state to managers (#46385)
* 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.
2026-09-22 17:43:43 +00:00
RahulGautamSingh 96ad4beb6a fix(versioning): use BigInt to avoid integer precision loss in gradle, maven and loose (#43901)
* 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>
2026-09-22 17:41:11 +00:00
Jamie Tanna fd5e9f403b feat(manager/mise): support pypi backend (#46359)
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>
2026-09-22 12:06:07 +00:00
Sebastian Poxhofer 2169a5786f fix(manager): share one local Helm chart path check (#46181)
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.
2026-09-22 11:59:41 +00:00
Sebastian Poxhofer c8ce514ff3 refactor(manager/helmv3): share OCI registry login helper with helmfile (#46180)
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.
2026-09-22 11:59:41 +00:00
Sebastian Poxhofer 35f44f3137 fix(manager/helmv3): share generateHelmEnvs with kustomize (#46179)
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.
2026-09-22 11:59:40 +00:00
Sebastian Poxhofer 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
2026-09-21 15:27:50 +00:00
Sebastian Poxhofer 58d9a01dd5 feat(platform): one "read repo file at ref" helper per host (#46190)
* 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.
2026-09-21 15:27:50 +00:00
Sebastian Poxhofer 5fdc338487 refactor(platform): drop the GiteaLikeHttp alias (#46098)
ForgejoHttp extends GiteaHttp, so the alias only added an indirection. The shared Gitea platform helpers type their Http parameters as GiteaHttp.
2026-09-21 12:05:08 +00:00
Aleksandr Mezin cbe339c5fd refactor(datasource/rpm): remove "gzip" from identifiers (#46264) 2026-09-21 09:21:36 +00:00
Darren Dooley 1762e24624 chore(docs): fix broken image rendering (#45893)
* 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
2026-09-21 09:01:54 +00:00
Charles LESECQ 75647e2bd0 fix(manager/quadlet): make Image optional for volume (#44959) 2026-09-21 08:56:36 +00:00
Alex Sweet 87111d8141 fix(manager/gomod): match gofmt-style unindented closing paren in block end regex (#45781)
* 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.
2026-09-21 08:54:34 +00:00
Sebastian Poxhofer bd86caa165 refactor(datasource): share the Gitea datasources with Forgejo (#46095)
* 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.
2026-09-21 08:25:46 +00:00
Sebastian Poxhofer 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>
2026-09-21 07:29:50 +00:00
Sebastian Poxhofer e069399843 feat(manager/mise): support arrays of inline tables as tool versions (#46330) 2026-09-21 07:12:40 +00:00
renovate[bot] 84b8df81b6 docs: update references to python to be8ccd0 (main) (#46349)
docs: update references to python to be8ccd0

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-09-21 00:24:13 +00:00
Aaron Paterson 6a1a69c3ef feat(versioning): support ranges for the regex scheme (#44834)
* 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.
2026-09-20 18:21:28 +00:00
Jamie Tanna 0253f4b23e fix(manager/dockerfile): don't attempt to update apk/apt for unknown image families (#46325)
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>
2026-09-18 15:59:58 +00:00
fxwx23 8a378fc5b4 fix(manager/swift): don't rewrite Package.resolved pins in unrelated packages (#44317)
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>
2026-09-18 11:02:17 +00:00
Sebastian Poxhofer 606b0b360a fix(manager): stop logging credential-bearing env and commands (#46129)
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.
2026-09-18 10:53:13 +00:00
Sebastian Poxhofer 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.
2026-09-18 10:53:12 +00:00
Sebastian Poxhofer f0dd862bd5 fix(python): share PyPI index credential resolution (#46127)
* 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.
2026-09-18 10:53:11 +00:00
Sebastian Poxhofer 4220c0fe36 feat(manager): share the .npmrc host-rule wrapper between bun, deno and npm (#46131)
* refactor(manager): share the .npmrc host-rule wrapper between bun, deno and npm

Adds `withNpmrcHostRules()` to the npm manager utils: it injects the host-rule lines into a directory's `.npmrc`, runs the callback, and restores the original `.npmrc` from a `finally` block. bun, deno, the corepack `updateArtifacts()` and the three lock file loops in `post-update/index.ts` now share it instead of repeating getNpmrcContent / updateNpmrcContent / resetNpmrcContent.

Adds `readUpdatedBinaryLockFile()` to the shared manager utils for the `Buffer.compare` plus file-addition block that bun and deno both carried.

Behaviour changes:

- bun and deno now restore `.npmrc` when `exec()` throws. The reset previously ran only after a successful install, so a failed lock file update left the injected registry credentials in the working tree. Covered by "restores .npmrc when the install fails" in `bun/artifacts.spec.ts` and `deno/artifacts.spec.ts`.
- The npm, yarn and pnpm loops in `post-update/index.ts` now restore `.npmrc` when the loop body throws an `ExternalHostError`, for the same reason.
- deno reads `config.isLockFileMaintenance` instead of deriving the flag from `config.updateType === 'lockFileMaintenance'`. Grouped branches overwrite `updateType` while `isLockFileMaintenance` is set explicitly, so the flag is the reliable source. The deno specs were updated to set it.

* refactor(manager): let updateLockFile() compare binary lock files

A Buffer as the existing content makes the skeleton read, compare and
return the new lock file as bytes, so bun and deno use it instead of a
separate readUpdatedBinaryLockFile() helper.

* refactor(manager/devbox): update the lock file through updateLockFile()

devbox kept the same Buffer compare as bun and deno; the trace log before
returning the lock file goes away with it.

* fix(manager/devbox): type the command list explicitly

The list is now only read inside the run() closure, where TypeScript
cannot resolve an evolving array type, so tsc reported an implicit any.
2026-09-18 09:30:27 +00:00
Jamie Tanna 6add79a9a5 feat(manager/dockerfile): extract apt install packages from RUN instructions (#46247)
* feat(manager/dockerfile): extract `apt install` packages from `RUN` instructions

Similar to the support we've recently added for extracting `apk`
packages in `Dockerfile`s, we can also extract `apt install` and
`apt-get install`s.

We can build on top of our shared extraction layer, and:

- preserving an `:arch` qualifier when replacing the version
- skipping versions/constraints we can't update

Co-Authored-By: Claude Opus 5 <jamie.tanna+claude-code@mend.io>

* docs(manager/dockerfile): clarify where to find skipped logs

---------

Co-authored-by: Claude Opus 5 <jamie.tanna+claude-code@mend.io>
2026-09-18 08:25:59 +00:00
Jamie Tanna 56983c3fdb refactor(manager/dockerfile): share the RUN command parsing (#46299)
As part of future changes, we want to extract system package managers'
packages in `Dockerfile`s more generally.

Instead of an `apk`-specific file, we can introduce a shared file for
extracting commands from `RUN`s.

From here, it can then be used by `apk` and follow-up tools by providing
the `names` to look for.

Co-authored-by: Claude Opus 5 <jamie.tanna+claude-code@mend.io>
2026-09-18 08:25:59 +00:00
Martin Costello bfd3c26c5c feat(datasource/github-runners): make ubuntu 26 stable (#46308)
Mark Ubuntu 26 GitHub Actions runners as stable.
2026-09-18 07:37:06 +00:00
Jamie Tanna d5faaa85ec docs(datasource/apk): add more in-depth information about using with a Wolfi Dockerfile (#45702)
Now we have support for extracting `apk add`s in the Dockerfile manager,
we should also add a more in-depth indication of how to use this with
Wolfi or Chainguard Images.

Co-authored-by: Claude Opus 5 <jamie.tanna+claude-code@mend.io>
2026-09-18 07:06:56 +00:00
Jamie Tanna 426bf15b26 feat(manager/dockerfile): update APK packages pinned with a fuzzy constraint (#45693)
In the Wolfi undistribution, which Chainguard Images are built upon,
it is common to use a fuzzy constraint rather than exact versions[0]:

    RUN apk update && apk add --no-cache libcurl-openssl4=~8.12.1
          curl=~8.12.1 make

which we previously skipped as `unsupported-version` when we started
extracting `apk add` packages.

As the `apk` version resolves these, we should extract the constraint as
the `currentValue`, and let the lookup evaluate it.

A constraint of `~8.12.1` matches every `8.12.1-rN` version, so we don't
need to update these constraints.

However, when the `newVersion` is i.e. `8.13.0-r0`, the constraint is
rewritten to `curl=~8.13.0`.

If using an exact pin, the version is upgraded with the same precision
i.e. `curl=8.12.1` -> `curl=8.13.0`.

We still skip constraints we can't handle with i.e. `contains-variable`
or `unsupported-version`.

[0]: https://github.com/elastic/crawler/blob/bc15328fb808affd00dff0cd92527bbe402f5ded/Dockerfile.wolfi#L7

Co-authored-by: Claude Opus 5 <jamie.tanna+claude-code@mend.io>
2026-09-18 07:06:55 +00:00
ELENASEMANOVA f3a314f51a fix(datasource/docker): fetch all pages for Red Hat registries (#46294)
[CWFHEALTH-5169] Extend list of registries

Extend a list of registries that require all pages to be read.

Assisted-by: Cursor
2026-09-17 13:52:16 +00:00
Tyler Gillson 2e1a079c7f feat(manager/helm-values): support version in sibling tag/version key (#46267)
Some charts define the image reference and its version as sibling keys
of the same object, with the parent key unrelated to the image name:

    cli:
      image: ghcr.io/fluxcd/flux-cli
      tag: v2.7.2

The walker previously extracted the `image` string via
matchesHelmValuesInlineImage but, without an embedded version, produced
a version-less dependency that could never be updated; the sibling
`tag`/`version` key was invisible because
matchesHelmValuesDockerHeuristic requires the keys to live inside an
object whose parent key ends in `image`.

When an inline reference carries no version and no digest, adopt a
sibling `tag` (preferred) or `version` string as the version, reusing
the same update mechanics as the repository/tag object form
(replaceString = version, docker versioning, `{{newValue}}` template).
Inline references with an embedded version or digest keep current
behavior, and repository/tag objects never reach the inline branch.

Discussion #46265 documents the layout with a real-world example.

Signed-off-by: Tyler Gillson <tyler.gillson@gmail.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-17 13:48:24 +00:00
Sergei Zharinov 30f0fe84d5 fix(datasource/jsr): set cacheable explicitly
Closes #46005
2026-09-17 13:49:20 +01:00
Sergei Zharinov bb378c3518 fix(datasource/rust-version): set cacheable explicitly
Closes #46016
2026-09-17 13:49:20 +01:00
Sergei Zharinov 3848bd95d1 fix(datasource/dotnet-version): set cacheable explicitly
Closes #45981
2026-09-17 13:49:20 +01:00
Sergei Zharinov 8fcbe1f7ca fix(datasource/galaxy): set cacheable explicitly
Closes #45986
2026-09-17 13:49:20 +01:00
Sergei Zharinov adc1b0c2fa fix(datasource/buildpacks-registry): set cacheable explicitly
Closes #45974
2026-09-17 13:49:20 +01:00
Jamie Tanna 9b87f8de2f feat(manager/dockerfile): extract apk add packages from RUN instructions (#45691)
* feat(manager/dockerfile): extract `apk add` packages from `RUN` instructions

As noted in #43711, users pin APK packages in their Dockerfiles and want
Renovate to update them, which so far has needed a custom manager[0].

Now that we have the `apk` datasource, we can extract these ourselves,
and parse the `RUN` instruction more effectively than a regex can.

We can do this by:

- extracting `apk add` dependencies when processing a `Dockerfile`
- ignoring common flags to `apk` before we extract the dependency names,
  which wasn't straightforward with a Custom Manager
- ignoring i.e. `--virtual .build-deps`
- handle example cases from [0] and other usages i.e. with a comment in
  between lines
- ensure that `final` doesn't run against the final `apk` install
- removing the docs for how to do this with a Custom Manager, as that
  complexity is no longer needed
- skipping `unspecified-version` and `contains-variable`

We inform the user that they will need to override the `registryUrls`,
as we already have a default on the `apk` datasource and can't override
it in a straightforward means.

[0]: https://www.jvt.me/posts/2025/03/24/renovate-regex-dockerfile-apk/

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

* fixup! feat(manager/dockerfile): extract `apk add` packages from `RUN` instructions

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

* fixup! feat(manager/dockerfile): extract `apk add` packages from `RUN` instructions

Co-Authored-By: Claude Opus 5 <jamie.tanna+claude-code@mend.io>

* fixup! feat(manager/dockerfile): extract `apk add` packages from `RUN` instructions

Find the command by name wherever it sits in the tokens, rather than
skipping an allowlist of shell keywords and variable assignments, so that
a wrapper program like `sudo`, `chroot /mnt` or `timeout 30` is handled
too.

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

* fixup! feat(manager/dockerfile): extract `apk add` packages from `RUN` instructions

---------

Co-authored-by: Claude Opus 5 <jamie.tanna+claude-code@mend.io>
2026-09-17 08:00:50 +00:00
Michael Kriese f711fd46e2 test: reach 100% branch coverage (#46284)
* test(workers/repository): mark seven unreachable guards for v8

These are defensive paths that no input reaches, and the guards stay as
they are. What changes is that v8 now knows to skip them: the existing
`istanbul ignore` comments are inert under the v8 provider, so the
branches behind them were still being counted.

- `versioning/index`: a string input can only fail the schema with the
  custom issue its own transform raises
- `versioning/conda`: pep440 always answers for a well-formed `==X.Y.*`
- `versioning/ruby`: callers only pass versions that have already parsed
- `repository/index`: only called when the repository logged a problem
- `config-migration/branch/rebase`: JSON5 output is always serializable
  back to JSON
- `onboarding/pr/pr-list`: base branches are unique Record keys, so the
  sort never compares one with itself
- `changelog/release-notes`: a matched release always carries its own url
- `changelog/source`: `getInRangeReleases` only returns null on paths it
  already ignores

Two notes on the hints. They have to be a single-line comment directly
above the statement -- a two-line reason, or anything in between such as
an oxlint directive, breaks the association silently. And `v8 ignore
else` covers an `if` with no else, while `v8 ignore next` covers an
expression-level branch such as `??` or a ternary.

`versioning/index` also swaps `issue && issue.code` for `issue?.code`,
which is what the oxlint directive sitting in the way was suppressing.

Co-authored-by: Claude Opus 5 <michael.kriese+claude-code@mend.io>

* test(workers/repository): cover the last five pr worker guards

None of these needed an ignore comment; each has a real input:

- `updatePrDebugData` gets a describe of its own. It is exported but was
  only reached through a pr whose debug data already carried labels, so
  the case where an existing pr has none went untaken.
- a `not-pending` branch whose status is no longer yellow
- a changelog error that is not a missing GitHub token
- an existing pr updated because a rebase was requested
- a platform that answers `createPr` with null, so assignees and
  reviewers are skipped

With these, `update/pr/index.ts` is fully covered.

Co-authored-by: Claude Opus 5 <michael.kriese+claude-code@mend.io>

* test(workers/repository): cover the last four branch update guards

- `bump-versions` reads the current version off disk when the pending
  change for that file carries no contents. All three guards involved are
  covered by one case where the file is pending in both the package file
  and the artifact list with no contents on either entry. An earlier
  attempt at this looked like it failed: the bump does happen, but the
  result is appended to `updatedPackageFiles` rather than
  `updatedArtifacts`, because the file is already pending there.
- `get-updated` keeps a package file another upgrade already wrote, for a
  manager that is asked for a lockfile-only update but has no
  `updateLockedDependency`.

With these, `lib/workers/repository` is fully covered.

Co-authored-by: Claude Opus 5 <michael.kriese+claude-code@mend.io>

* ci: pin the branch coverage threshold at 100

A full run on this branch reports 100% on all four metrics: statements
(49435/49435), branches (25727/25727), functions (7414/7414) and lines
(48924/48924).

Branches joins the other three at 100 rather than taking another
rounding-down step. There is no longer a margin to leave: any new
uncovered branch is a regression, and the threshold should say so.

Co-authored-by: Claude Opus 5 <michael.kriese+claude-code@mend.io>

---------

Co-authored-by: Claude Opus 5 <michael.kriese+claude-code@mend.io>
2026-09-17 07:08:48 +00:00
Maxime Brunet a22f8f0a09 fix(platform/github): attempt direct merge before falling back to the merge queue (#46271)
fix(github): attempt direct merge before falling back to the merge queue

PR automerge against a merge-queue-protected base branch could no longer
merge directly when the authenticated actor is allowed to bypass the
queue: mergePr() enqueues the PR unconditionally once the base branch has
a merge queue, and the enqueuePullRequest mutation evaluates queue-entry
requirements (e.g. required code-owner review) without honoring ruleset
bypass, leaving bypass-enabled installs unable to merge at all.

Attempt the direct REST merge first and enqueue only when that merge is
refused, mirroring the try-first design #45724 shipped for branch
automerge. Repositories without a bypass actor pay one refused REST merge
call per automerge attempt before the queue fallback, and up to four when
an unrecognized refusal message triggers method guessing.

Docs: describe the direct-merge-first behavior in the GitHub merge queue
section of the automerge documentation.
2026-09-17 07:07:56 +00:00
Jamie Tanna a14607f7e4 test(manager/renovate-config): guard supportedPresetSources against drift (#46256)
In #46201, we found that although `forgejo>` support was added to
Shareable Config Presets, the `renovate-config` manager didn't actually
implement the lookup, meaning we didn't previously propose updates we
should have.

We can validate this is done by making sure that the `{platform}-tags`
datasource is used to look up a preset.

Co-authored-by: Claude Sonnet 5 <jamie.tanna+claude-code@mend.io>
2026-09-16 15:31:57 +00:00
Skyler Mäntysaari 1f3b3a9d56 feat(manager/terragrunt): detect forgejo-tags datasource for git-tag sources (#46202)
feat(terragrunt): detect forgejo-tags datasource for git-tag sources

detectGitTagDatasource() in the terragrunt manager only handled
gitlab, bitbucket and gitea platforms, so Forgejo-hosted modules
(e.g. codeberg.org) fell through to the generic git-tags datasource
and lost release timestamps, sourceUrl and changelog data. Add a
'forgejo' case mapping to ForgejoTagsDatasource, mirroring the
existing pattern in the github-actions manager's detectDatasource().

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-16 13:15:37 +00:00
Sebastian Poxhofer 9fc451c44d refactor(manager): share the lock file updateArtifacts skeleton (#46093)
Migrate pub, gleam, helmfile, pixi, mise, poetry, pep621/pdm and
pep621/uv onto `updateLockFile()`, so each manager keeps only its
command and `ExecOptions` construction.

Behaviour changes:

- Artifact error text now comes from `artifactErrorMessageFromExecError`,
  i.e. a non-blank `err.stderr`, else a non-blank `err.stdout`, else
  `err.message`. Previously pixi used `${err}` ("Error: " prefix), mise
  joined stdout, stderr and message, and poetry rendered
  "<stdout>\n<stderr>" (literally "undefined\nundefined" when the error
  carried neither).
- helmfile, pixi, poetry, pdm and uv now return null instead of an
  addition when the lock file is missing or empty after the run, which
  is what gleam and mise already did.
- Unchanged/missing lock file debug messages are now uniformly
  "<lockFileName> is unchanged" / "No <lockFileName> found". pub gains
  such a message, mise loses "Returning updated mise lock file" and
  poetry loses "Returning updated <lockFileName>".
2026-09-16 13:13:42 +00:00
Sebastian Poxhofer bf5ff94d28 refactor(manager): build artifact results with the shared helpers (#46092)
Migrate apm, bun, cargo, cocoapods, composer, conan, copier, deno,
devbox, gomod, helmv3, jsonnet-bundler, kustomize, mix, nuget and vendir
onto `fileAddition()`, `artifactError()` and `artifactErrorResult()`.
copier's private `artifactError()` is replaced by the shared one.

Behaviour change: artifact error text now comes from
`artifactErrorMessageFromExecError` everywhere, i.e. a non-blank
`err.stderr`, else a non-blank `err.stdout`, else `err.message`.
Previously apm used `${err}` ("Error: " prefix), cocoapods used a
nullish coalesce that kept an empty stderr, nuget preferred stdout over
stderr, and jsonnet-bundler emitted an undefined stderr with no
fallback. The rest used `err.message` only and are unchanged for errors
without output.
2026-09-16 13:13:41 +00:00