mirror of
https://github.com/renovatebot/renovate.git
synced 2026-10-30 01:08:06 +00:00
fix/maven-wrapper-java-version
3720 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
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. |
||
|
|
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> |
||
|
|
a88aaed0fa |
refactor(changelog): derive api base urls from the platform family table (#46193)
`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. |
||
|
|
66f67475f2 |
refactor(changelog): move platform fetchers onto ChangeLogSource (#46192)
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()`. |
||
|
|
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 |
||
|
|
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. |
||
|
|
16474da0e5 |
refactor(changelog): share the Gitea changelog source with Forgejo (#46096)
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. |
||
|
|
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. |
||
|
|
907fa5932e |
fix(dashboard): handle OSV database fetch failures gracefully (#46306)
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. |
||
|
|
8dc06640a7 |
feat(workers/repository): allow users to bypass registryUrl when unknown-registry (#46323)
As part of follow-up changes in #45706, we want the `apk` / `apt` parsing in the `dockerfile` manager to attempt to determine which distribution - and therefore registry - to look up dependencies for. When there is an image that we don't know how to resolve, we want to treat them as a `skipReason: unknown-registry`. However, this doesn't provide any way for the user to say "I know which registry you should look up updates for". To handle this, we can treat `unknown-registry` differently, allowing it to un-skip itself when there are any `registryUrls`s defined. This allows for general usage, where if Renovate doesn't know how to interact with a given registry, but a user does, then the user can override the registry with a `packageRules` entry. We continue skipping a dependency when `unknown-registry` is set alongside a dependency's `registryUrl`. We add this un-skipping logic into `applyPackageRules()`, but need to make sure that we also gate it inside `lookup()`, so a dependency that's passed through `applyPackageRules()` is re-checked to ensure that the check is enforced. Co-authored-by: Claude Opus 5 <jamie.tanna+claude-code@mend.io> |
||
|
|
d89d8e513e |
refactor(workers/lookup): share the release classification between getBucket and getUpdateType (#46144)
`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.
|
||
|
|
20015e4894 |
refactor(workers/lookup): resolve the current version from one release list (#46143)
`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. |
||
|
|
bf848fa6c7 |
refactor(workers/repository/lookup): split lookupUpdates into testable stages (#46142)
`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. |
||
|
|
f5db0217f1 |
refactor(workers/repository/lookup): share the update-type config resolution (#46141)
`applyMinimumReleaseAgeToDigestUpdate()` and `filterInternalChecks()` both resolved the config their `minimumReleaseAge` check runs against by merging the `updateType`-scoped sub-config and then applying `packageRules` matching on `update-type`, and both carried a comment asking the other to be kept in sync by hand. Extract that sequence as `resolveUpdateTypeConfig()` in `filter-checks.ts` and use it from both, and share the `timestamp-optional` warning text as `missingReleaseTimestampWarning`. The warning stays a constant rather than a helper function so that each caller keeps its own `logger.once` call site. Both sync comments are gone. No behaviour change. |
||
|
|
c303a282a1 |
test(workers/repository/lookup): enforce http mocks in the lookup spec (#46140)
The spec's `afterEach` called `httpMock.clear(false)`, which skipped both the missing-request and the unused-mock checks for every test in the file. That kept nine tests green while they performed unmocked requests or left mocks unused, including two tests whose mocks were crossed: `handles github 404` mocked PyPI and `handles pypi 404` mocked the GitHub API, so neither ever exercised a 404. Drop the opt-out and fix the tests it was hiding: swap the two crossed 404 mocks, mock the GitHub GraphQL calls made by the digest lookups, add the missing packagist `~dev` metadata, gomod `?go-get=1` and PyPI requests, let the Maven `postprocessRelease` spy handle every call, and delete the dead `@types/helmet` mock in a test that returns before any release is fetched. `httpMock.clear(false)` remains in other specs, so the `check` parameter of `clear()` is kept. |
||
|
|
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> |
||
|
|
f1ace2dfdc |
test(workers/repository): close more branch coverage gaps (#46269)
* test(workers/repository): cover the dependency dashboard guards - a repository with no dashboard issue yet, so nothing is read back into the config - a dashboard rendered with an empty header and with abandonment reporting turned off - two vulnerabilities for the same package, which are grouped under one entry rather than recorded twice With these, `dependency-dashboard.ts` is fully covered. Co-authored-by: Claude Opus 5 <michael.kriese+claude-code@mend.io> * test(workers/repository): cover a locked update that reports no files `updateLockedDependency` can answer `updated` without handing back any file contents. Every test supplied files alongside that status, so the guard that skips merging them went untaken. Co-authored-by: Claude Opus 5 <michael.kriese+claude-code@mend.io> * test(workers/repository): cover a changelog release read from cache Every changelog test started from an empty package cache, so the branch that reuses a cached release rather than fetching it went untaken. Co-authored-by: Claude Opus 5 <michael.kriese+claude-code@mend.io> * ci: ratchet the branch coverage threshold A full run on this branch reports 100% statements (49441/49441), 100% functions (7414/7414), 100% lines (48930/48930) and 99.92% branches (25723/25742), up from 99.89% on the branch below. The threshold moves to 99.90, keeping the same rounding-down margin as before. Statements, functions and lines are already pinned at 100 and stay there. Co-authored-by: Claude Opus 5 <michael.kriese+claude-code@mend.io> --------- Co-authored-by: Claude Opus 5 <michael.kriese+claude-code@mend.io> |
||
|
|
f7e8104511 |
test(workers/repository): close more branch coverage gaps (#46259)
* test(workers/repository): cover five pr body and warning guards - an upgrade with no release notes of its own, which gets no summary title while the branch as a whole has notes - an upgrade with no homepage, source or dependency url, whose name is therefore left unlinked - an upgrade that defines no pr body columns - an assignee list that filtering empties out - a second warned dependency in a package file that is already listed, so the file is not recorded twice Co-authored-by: Claude Opus 5 <michael.kriese+claude-code@mend.io> * test(workers/repository): cover four changelog guards - `massageName` gets a describe of its own: it is exported but was only ever reached through a release that carried a tag, so the no-version path and the empty-result path went untaken - a lone in-range release with no earlier one to prepend - a bitbucket tree holding exactly one changelog candidate, so nothing is logged about choosing between several - a bitbucket-server source url that parses but matches neither the git nor the web repository pattern Co-authored-by: Claude Opus 5 <michael.kriese+claude-code@mend.io> * test(workers/repository): cover both extract failure paths - a manager whose `extractAllPackageFiles` returns null, so there is nothing to massage dep names in - a file whose `extractPackageFile` returns nothing, so it contributes no package file Co-authored-by: Claude Opus 5 <michael.kriese+claude-code@mend.io> * ci: ratchet the branch coverage threshold A full run on this branch reports 100% statements (49491/49491), 100% functions (7384/7384), 100% lines (48981/48981) and 99.86% branches (25726/25761), up from 99.82% on the branch below. The threshold moves to 99.84, keeping the same rounding-down margin as before. Statements, functions and lines are already pinned at 100 and stay there. Co-authored-by: Claude Opus 5 <michael.kriese+claude-code@mend.io> * test(workers/repository): cover four process guards - a libyear update released before the version currently in use, which yields a negative age and is therefore not counted - a pr that is closed, and one raised from the onboarding branch, neither of which counts towards the concurrent pr limit - a second base branch whose package files do not displace the first one's, on a platform that cannot sync a fork with its upstream The fork case redefines `syncForkWithUpstream` rather than deleting it, the same way the migration branch spec handles `refreshPr`. Co-authored-by: Claude Opus 5 <michael.kriese+claude-code@mend.io> * test(workers/repository): cover the extract cache guards - a cached extract whose manager fingerprints all still match, so the cache is reusable; the test reads the expected fingerprint out of `hashMap` rather than hard-coding a hash that regenerates - an extract that is told not to overwrite the cache - a cached scan for a branch that is still a configured base branch, so it survives the clean-up that drops the others With these, `lib/workers/repository/process` is fully covered. Co-authored-by: Claude Opus 5 <michael.kriese+claude-code@mend.io> * test(workers/repository): cover the branchPrefixOld miss `prAlreadyExisted` retries the lookup under the old branch prefix, but every test either hit on the first lookup or on the second, so the case where both miss went untaken. Co-authored-by: Claude Opus 5 <michael.kriese+claude-code@mend.io> * ci: ratchet the branch coverage threshold to 99.87 The tests added after the earlier bump in this branch close eight more branches. A full run now reports 100% statements (49491/49491), 100% functions (7384/7384), 100% lines (48981/48981) and 99.89% branches (25734/25761). The threshold moves from 99.84 to 99.87, keeping the same rounding-down margin as before. Statements, functions and lines are already pinned at 100 and stay there. Co-authored-by: Claude Opus 5 <michael.kriese+claude-code@mend.io> * test(workers/repository): cover three changelog embedding guards The pr worker copies release notes off each upgrade's changelog result. Three of its guards were never taken because every test supplied a complete result for a distinct repository: - a changelog with no project, so no repo name is recorded - a changelog with a project but no versions, so no releases are copied - a second upgrade for a repository whose notes were already committed, which is skipped rather than repeated Co-authored-by: Claude Opus 5 <michael.kriese+claude-code@mend.io> --------- Co-authored-by: Claude Opus 5 <michael.kriese+claude-code@mend.io> |
||
|
|
7960e5d077 |
test(workers/repository): close more branch coverage gaps (#46254)
* test(workers/repository): cover the pr note and closed-pr guards `handle-existing.ts` had no spec of its own and was only reached through the branch worker, so none of its early exits were exercised. Give it one, covering an open pr, a suppressed notification, a branch that is already gone, each of the three ignore messages, and both dry-run paths in each function. Also cover a pr body with no notes at all, extra notes where none of the three conditions hold, and a config warning issue raised from an error that carries no validation details and whose issue is neither created nor updated. Co-authored-by: Claude Opus 5 <michael.kriese+claude-code@mend.io> * test(workers/repository): cover five config migration guards - a `package.json` that carries no `renovate` key, so nothing is deleted from it before it is rewritten - a platform with no `refreshPr`, which the migration branch check has to tolerate - an old migration branch that is already gone, so there is nothing to delete before the new one is created - an editorconfig that sets no maximum line length - a migration pr with no footer configured The platform case redefines `refreshPr` rather than deleting it: the mocked module object refuses deletion but allows redefinition. Co-authored-by: Claude Opus 5 <michael.kriese+claude-code@mend.io> * test(workers/repository): cover the onboarding and reconfigure caches - deleting and updating the onboarding cache when there is no cache - an onboarding pr whose closing comment is suppressed - a reconfigure cache written together with an extract result Also drop the `if (extractResult)` in the reconfigure check: the catch above it returns, so the variable is always set by the time the comment is ensured, and the compiler narrows it on its own. Co-authored-by: Claude Opus 5 <michael.kriese+claude-code@mend.io> * test(workers/repository): cover the update flattening guards - a config whose `semanticCommits` is already decided, so no detection runs while flattening - a semantic commit with no scope configured Also drop the first `packagePath.length > 0` check while flattening: `String.split` always yields at least one element, so there is always a file name to drop. The check after the splice stays, since that one can leave the array empty. Co-authored-by: Claude Opus 5 <michael.kriese+claude-code@mend.io> * ci: ratchet the branch coverage threshold A full run on this branch reports 100% statements (49468/49468), 100% functions (7381/7381), 100% lines (48958/48958) and 99.82% branches (25709/25755), up from 99.73% on the branch below. The threshold moves to 99.80, keeping the same rounding-down margin as before. Statements, functions and lines are already pinned at 100 and stay there. Co-authored-by: Claude Opus 5 <michael.kriese+claude-code@mend.io> * Apply suggestion from `@secustor` Co-authored-by: Sebastian Poxhofer <secustor@users.noreply.github.com> --------- Co-authored-by: Claude Opus 5 <michael.kriese+claude-code@mend.io> Co-authored-by: Sebastian Poxhofer <secustor@users.noreply.github.com> |
||
|
|
4215f35059 |
feat(config): add minimumReleaseAgeBuffer option (#45577)
* feat(config): add minimumReleaseAgeBuffer option Adds a new config option which extends the time Renovate waits before suggesting an update by a percentage of minimumReleaseAge. This gives companion packages that are published after the main package (e.g. platform-specific binaries) time to pass the package manager's own release age cooldown, avoiding lock file artifact errors. * feat(config): make minimumReleaseAgeBuffer a duration with 10 minutes default * feat(config): raise minimumReleaseAgeBuffer default to 30 minutes |
||
|
|
59852479bf |
test: close branch coverage gaps outside the manager tree (#46252)
* test(versioning): cover four uncovered guards - `cargo`: a converted range whose operators are already attached to their versions, so `npm2cargo` leaves the elements alone - `hashicorp`: a widen on a complex range ending in `>=`, which the underlying semver implementation refuses and returns null for - `poetry`: a version list holding an unparseable entry, for both `getSatisfyingVersion` and `minSatisfyingVersion`, plus the same widen refusal, which falls back to the current value - `swift`: an open-ended range whose bound is not a valid semver Co-authored-by: Claude Opus 5 <michael.kriese+claude-code@mend.io> * refactor(versioning): drop two guards that cannot be false - `hermit`: `major` is the only mandatory group in the version regex, so the `if (major)` around the first release segment always holds; the sibling parser already pushes it unconditionally - `rpm`: after `c1 === c2` is ruled out, one of `c1 > c2` and `c1 < c2` must hold, so the second comparison becomes a ternary Co-authored-by: Claude Opus 5 <michael.kriese+claude-code@mend.io> * test(versioning/gradle): cover three uncovered guards - `getMajor` on a version whose leading token is not a number - `getSatisfyingVersion` with a later match that is not greater than the one already held - `minSatisfyingVersion` with a later match that is not smaller than the one already held Two branches in `compare.ts` are still open and are looked at next. Co-authored-by: Claude Opus 5 <michael.kriese+claude-code@mend.io> * test(versioning/ivy): cover a later match that is not the greatest `getSatisfyingVersion` kept every matching version it walked past, so the guard that holds on to the one already found was never taken. Co-authored-by: Claude Opus 5 <michael.kriese+claude-code@mend.io> * refactor(versioning): drop three guards that cannot be false - `gradle`: `tokenCmp` compares two tokens whose only types are `Number` and `String`, so once both-numbers and both-strings are handled exactly one side is a number; the chain becomes a ternary and the unreachable `return 0` goes - `ivy`: `tokenize` always yields at least one token for a non-empty version, so the length check around the last one always holds - `ivy`: `rangeToStr` only returns null for a null range, which the `length === 1` check above it already rules out Co-authored-by: Claude Opus 5 <michael.kriese+claude-code@mend.io> * test(versioning/nuget): cover a later match that is not the greatest Both loops in `getSatisfyingVersion` walked version lists that only ever climbed, so the guard that holds on to the candidate already found was never taken -- once for a bracket range, once for a bare version acting as a minimum. Also pin down that a floating range needs a major component: `-*` alone does not parse. Co-authored-by: Claude Opus 5 <michael.kriese+claude-code@mend.io> * refactor(versioning): drop three more guards that cannot be false - `maven`: the interval picked for auto-extension is only picked when the new value is at or past its upper bound, and that upper bound is the lower one incremented, so the new value never sorts below the lower bound - `maven`: an interval never has both bounds open, because the parser only nulls one side when the other is a version - `nuget`: a floating range always carries a major component, so neither the parser's NaN check nor the serializer's undefined check can fail Co-authored-by: Claude Opus 5 <michael.kriese+claude-code@mend.io> * refactor(versioning/pep440): drop three unreachable range paths `updateRangeValue` handles `!=`, a `.*` prefix, `~=`, `==` and `<=` itself, `handleUpperBound` handles `<`, and `parseCurrentRange` rejects `===`. Only `>` and `>=` can reach `handleLowerBound`, so its operator check always holds and it never returns null. Making it total removes the null check at the call site as well. This does drop the `logger.error` that followed, which was guarded by a comment saying it could not happen unless pep440 itself changed. `isLessThanRange` classifies the same fixed operator set, so the last check there is the only one left and its fallback was dead. `divideCompatibleReleaseRange` only ever sees a `~=` version, and pep440 requires at least two release components after that operator, so there is always a component to drop. A new test pins down that `~=7` is not a valid range. Co-authored-by: Claude Opus 5 <michael.kriese+claude-code@mend.io> * test(versioning): cover three more uncovered guards - `elm`: a widen where the new version sits below the lower bound, so the upper bound is kept rather than extended - `hex`: a widen on a complex range ending in `>=`, which the underlying semver implementation refuses and returns null for - `rust-release-channel`: a concrete current version paired with a channel name as the new version, which falls through to the new version unchanged Co-authored-by: Claude Opus 5 <michael.kriese+claude-code@mend.io> * refactor(versioning): drop two more guards that cannot be false - `composer`: `getNewValue` falls back to the new version whenever nothing else matched, so it never returns null and the recursive call in the or-range path always has a value to work with - `docker`: a version's prerelease is always a string, so once an empty one on either side is handled, the remaining pair is both non-empty and unequal Co-authored-by: Claude Opus 5 <michael.kriese+claude-code@mend.io> * test(config,datasource): cover two more uncovered guards - `config/validation`: `hostRules[].allowInternal` nested inside a `packageRules[]` entry, which reports a plain configuration error rather than a security one, because nested host rules are never applied - `datasource/docker`: a non-list manifest whose response carries no `docker-content-digest` header, so the digest falls back to the hash of the manifest body Co-authored-by: Claude Opus 5 <michael.kriese+claude-code@mend.io> * refactor(config,datasource): unwind three branches v8 could not count - `config/decrypt` and `datasource/docker/common` each picked a response with an `await` inside a ternary. v8 gives the branch that follows such a ternary a negative hit count, which the reporters read as uncovered even though both paths run. Spelling the choice as an if/else makes the count come out right; behaviour is unchanged. - `datasource/index` and `datasource/npm/npmrc` each ended an exhaustive chain with an `else if` whose implicit else v8 counts but nothing can reach: `registryStrategy` is one of three literals, and `npmrc` starts as an object and is only ever reassigned to one. Co-authored-by: Claude Opus 5 <michael.kriese+claude-code@mend.io> * test(workers/global): cover seven uncovered guards The config parsers rewrite legacy boolean spellings of `dryRun`, `requireConfig` and `platformCommit`, but nothing passed a value that was already current, so the fall-through of each chain went untaken. Cover it from both the CLI and the environment. Also cover a datasource env var that names no auth field, one whose value is undefined, a default config file that cannot be read, and the error `isLimitReached` throws when asked for a concurrent limit without a branch config. That last one had an ignore hint saying it should not happen; the error message documents the contract, so it is now tested and the hint is gone. Co-authored-by: Claude Opus 5 <michael.kriese+claude-code@mend.io> * refactor(workers/global): drop a guard that cannot be false `getConfig` in the additional-config-file parser returns early unless `RENOVATE_ADDITIONAL_CONFIG_FILE` is set, so by the time a parse error is being classified the variable is always present. Co-authored-by: Claude Opus 5 <michael.kriese+claude-code@mend.io> * refactor(workers/repository): drop a dead changelog file lookup The gitea, gitlab and forgejo changelog sources each declared `files` as an empty array and then guarded the only assignment to it with `if (!files.length)`, which is always true one line after the declaration. Collapse it to the filter itself. Co-authored-by: Claude Opus 5 <michael.kriese+claude-code@mend.io> * ci: ratchet the branch coverage threshold A full run on this branch reports 100% statements (49471/49471), 100% functions (7381/7381), 100% lines (48961/48961) and 99.73% branches (25690/25759), up from 99.53% before this branch's work. The threshold moves to 99.71, keeping the same rounding-down margin as before. Statements, functions and lines are already pinned at 100 and stay there. Co-authored-by: Claude Opus 5 <michael.kriese+claude-code@mend.io> * refactor(config,datasource): say why the ternary was unwound Both sites now carry a comment explaining that the if/else is deliberate: v8 gives the branch following an `await` inside a ternary a negative hit count, which the coverage reporters read as uncovered. `apiCheckResponse` also gets an explicit `HttpResponse<unknown>` type, which is what the two branches have in common and what `isECRMaxResultsResponse` already expects. Also fix a type error in the new config file spec: the `pathExists` spy needs the same `as never` cast the surrounding tests use. Co-authored-by: Claude Opus 5 <michael.kriese+claude-code@mend.io> * refactor(versioning/pep440): use a template literal in handleLowerBound The exclude-style return is the one line this branch rewrote in that function, so it now builds its string with a template literal instead of the `+` operator. Co-authored-by: Claude Opus 5 <michael.kriese+claude-code@mend.io> * test(workers/global): drop the cast from the limits throw test Calling the overload with a non-null assertion on the config argument says the same thing without retyping the whole function. Co-authored-by: Claude Opus 5 <michael.kriese+claude-code@mend.io> --------- Co-authored-by: Claude Opus 5 <michael.kriese+claude-code@mend.io> |
||
|
|
f6bbc9f579 |
fix(workers/onboarding): treat forgejo like gitea for onboarding-branch rebase and config-hash detection (#46200)
fix: treat forgejo like gitea for onboarding branch markdown/html-comment support Forgejo's platform implementation reuses Gitea's massageMarkdown and supports the same HTML-comment rendering, but three onboarding-branch checks only allowlisted 'github', 'gitea' and 'gitlab', causing Forgejo to skip onboarding-branch rebase, force unconditional PR update requests, and bypass config-hash change detection. Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com> |
||
|
|
5ddaf0a30e |
fix(changelog): prefer precise tags over floating ones for compare links (#46210)
* fix(changelog): prefer precise tags over floating ones for compare links As noticed in [0], if Renovate upgrades v7.0.0 → v7.0.1 on a repository with "floating" tags, the "floating" tag takes precedence over the non-floating tag. While the changelog is rendered correctly, the "Compare Source" link is broken. We should instead look for the most precise version (the version with the most `.` separators) that exists. Co-Authored-By: Claude Sonnet 5 <jamie.tanna+claude-code@mend.io> [0]: https://github.com/astral-sh/uv/pull/21654#issuecomment-5669818744 * fixup! fix(changelog): prefer precise tags over floating ones for compare links Add a test where a precise tag precedes a floating one that resolves to the same version, to cover the `reduce` branch that keeps the current most-precise candidate. Codecov flagged line 284 of source.ts as uncovered on PR #46210. Co-Authored-By: Claude Sonnet 5 <jamie.tanna+claude-code@mend.io> --------- Co-authored-by: Claude Sonnet 5 <jamie.tanna+claude-code@mend.io> |
||
|
|
54c7e5e77b |
perf(workers/repository): don't log all matched files at DEBUG (#46228)
On a repository with many hundreds (or thousands) of files matched by a Manager, this log line can be significantly large. For users who still want this information, we can relegate it to TRACE, but keep the high-level info as a DEBUG that's still useful to know. |
||
|
|
2e951d2254 |
feat(mise): support selector lockfile updates (#44615)
* mise: support selector lockfile updates * fix(mise): handle empty lockfile AST * test(mise): use valid empty TOML AST fixture * fix(mise): support current regex utility * fix: clarify lockfile-only lookup handling * fix(mise): handle selector lockfile updates correctly * test(mise): cover selector lockfile review fixes * fix(mise): scope selector artifact refresh * chore: retrigger CI * fix(mise): remove unreachable lockfile guard * refactor(lookup): clarify lockfile-only value handling * refactor(mise): use shared TOML utilities * fix(mise): handle selector lockfile edge cases * fix(mise): satisfy selector CI coverage * fix(mise): require dependency names * fix(mise): preserve configured dependency names * fix(mise): address selector lockfile review feedback * test(mise): cover major selector lockfile updates * chore(static-data): auto-update Java LTS versions So we don't have a hardcoded set of data in the mise manager. Co-authored-by: Claude Sonnet 5 <jamie.tanna+claude-code@mend.io> * fix(mise): address selector lockfile feedback * test(manager/mise): cover nonnumeric current version prefix formatLockedVersion()'s currentPrefix fallback to '' only runs when the current version has no leading numeric match, which no existing test reached. Co-Authored-By: Claude Sonnet 5 <jamie.tanna+claude-code@mend.io> --------- Co-authored-by: Jamie Tanna <jamie.tanna@mend.io> Co-authored-by: Claude Sonnet 5 <jamie.tanna+claude-code@mend.io> |
||
|
|
7aedcb24d2 |
test(workers/lookup): cover untested branches under lookup/ (#45930)
* test(workers/lookup): cover the remaining generateUpdate branches Adds a unit spec for `generateUpdate()` exercising the branches that the surrounding lookup tests never reached: a release carrying `checksumUrl`, `downloadUrl` and `newDigest`, a versioning api whose `getNewValue` throws, and an update with no current version. With those covered, the last istanbul ignores in the file can go, so `generate.ts` now reports full statement and branch coverage with nothing suppressed. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * test(workers/lookup): cover two untested lookup branches Adds tests for two paths flagged by the `TODO: add test #40625` markers that no existing test reached: - `filterInternalChecks()` with a release that has no `releaseTimestamp` while `minimumReleaseAgeBehaviour` is unset. The existing tests only cover `timestamp-required` and `timestamp-optional`, so the release is now checked to be accepted without being reported under either. - `lookupUpdates()` when no current version can be resolved but a `lockedVersion` is set, which must return without marking the dependency `invalid-value`. The ignore comments are left in place, so these branches stay excluded from the coverage report; each test instead asserts an outcome that only holds on the intended path. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * test(workers/lookup): cover error paths in bucket, current and rollback Adds unit specs for three exported helpers whose guard clauses no test reached: `getBucket()` when the versioning api cannot determine a major or a minor, `getCurrentVersion()` when the current value is not a string, and `getRollbackUpdate()` when the versioning api lacks `isLessThanRange()`, when `isLessThanRange()` throws, and when there is nothing older to roll back to. The ignore comments on those branches are left in place, so the coverage report is unchanged; each test asserts the return value that is only produced by the guard it targets. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * test(workers/lookup): cover filter and rollback guards Covers three more guards that no test reached: `filterVersions()` with no current version, and `filterByMaxMajorIncrement()` when the versioning api cannot determine the current major or a release's major. Adds a `lookupUpdates()` test for the case where rollbacks are enabled but nothing older exists to roll back to, which warns instead of updating. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * test(workers/lookup): cover the vulnerability alert range strategy fallback A vulnerability alert configured with `rangeStrategy=update-lockfile` but no locked version falls back to `bump`, which no test covered. The new test asserts the range is widened and the update is marked as a bump, which only happens once the fallback has been applied. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * docs(workers/lookup): explain the remaining coverage ignores Each ignore left under `lookup/` now records why the branch it hides is not reached, instead of pointing at a TODO with no detail. No ignore is added or removed, and the coverage report is unchanged. The explanation goes above the hint rather than after it: a hint only applies to the statement on the line immediately below, so trailing comment lines silently stop it from suppressing anything. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * test(workers/lookup): cover three more unreached lookup branches Covers the guards behind three of the remaining ignore hints: - `getRollbackUpdate()` when the release it falls back to carries an empty version, so there is nothing to roll back to after all - `filterVersions()` coercing a release version that the configured versioning accepts but semver cannot parse, which happens once `allowedVersions` falls back to semver range syntax - `lookupUpdates()` when the datasource rejects every candidate release during postprocessing, leaving a bucket with no release to update to The ignore comments stay in place, so the coverage report is unchanged; each test asserts an outcome that only holds on the intended path. Co-Authored-By: Claude Opus 5 <michael.kriese+claude-code@mend.io> * docs(workers/lookup): correct the ignore reasons this branch disproves Three of the reasons added alongside the previous batch of tests claimed their branch was unreachable, which the tests in this branch disprove: `getRollbackUpdate()` can pop a versionless release, `filterVersions()` does coerce a version semver cannot parse, and `filterInternalChecks()` yields no release when a datasource rejects every candidate. Each now describes what actually reaches it. Also sharpens two of the reasons that still stand. The `getBucket()` else contradicts the `isVersion()` filter every release has already passed, so it reads as unreachable rather than untested; and a lookup whose releases are all unversioned tags never reaches the empty-version block, because a configured digest routes it to the digest-only path first. No ignore is added or removed, and the coverage report is unchanged. Co-Authored-By: Claude Opus 5 <michael.kriese+claude-code@mend.io> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> Co-authored-by: Claude Opus 5 <michael.kriese+claude-code@mend.io> |
||
|
|
ec4ba7b381 |
refactor(workers/lookup): drop stale istanbul ignores in generateUpdate (#45929)
Vitest uses the v8 coverage provider, and v8-to-istanbul still honours istanbul hints, so these comments suppress real branch coverage rather than being inert. The `releaseTimestamp` and `registryUrl` branches are both exercised by the existing lookup tests, so their ignores only hid coverage that was already there. The remaining ignores in this file stay: no datasource used in these tests reports a checksum url, and the `getNewValue` catch block and the missing-currentVersion guard are still unreachable. Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
4b4d14ec00 |
feat(manager): pass extracted constraints to updateArtifacts (#45826)
* feat(manager): pass extracted constraints to updateArtifacts `extractedConstraints` were collected during extraction but only ever read by `fetch.ts` for the lookup. At artifact time every manager had to derive the same tool constraints a second time, and when the updated package file no longer carried the constraint there was nothing to fall back to. `generateBranchConfig()` now merges the `extractedConstraints` of all upgrades of a branch onto the branch config, which reaches `updateArtifacts()` through `patchConfigForArtifactsUpdate()`. `UpdateArtifactsConfig` declares the field and documents what it contains. The new `resolveToolConstraint()` helper implements the ladder once: user `constraints` > an optional artifact-time derivation > the extracted value. The derivation is ranked above the extracted value on purpose: it reads the package files as the branch changed them, so poetry (python is an updatable dependency there), composer, pipenv (`pipenv` can be a dev package) and pep621 (`requires-python` is an updatable dependency) would otherwise regress to a stale constraint. pipenv, pep621 uv/pdm, poetry and composer are migrated to the helper and keep their derivation. Behaviour change: these managers now use the extract-time constraint as a last resort instead of running unconstrained, for example on lock file maintenance where pipenv deletes `Pipfile.lock` before deriving. `execute-post-upgrade-commands.ts` is unaffected: it reads `upgrade.constraints` per upgrade, and the extracted values are kept in a separate field rather than folded into `constraints`, so post-upgrade task tool constraints still come from user config only. Not migrated, because their constraints are never extracted, so the fallback would be dead code: bundler (`ruby`/`bundler`), pixi (`pixi`), pep621 uv (`required-version`) and npm's node ladder, whose last step already reads `package.json` engines. * refactor(manager): resolve tool constraints through resolveToolConstraint() Every manager now obtains the constraint of a tool it runs through `resolveToolConstraint()` instead of reading `config.constraints` itself, so the precedence "user config, then a value derived from the updated files, then the constraints collected during extraction" applies everywhere. The helper treats an empty string as not set at every step and accepts the `PostUpdateConfig` shape, whose `constraints` may be `null`. Behaviour changes that go beyond the new fallback to extracted constraints: - `mix` applies its hardcoded erlang default only after the extracted constraint, so an erlang version collected at extraction can win over it. - `kustomize` decides `HELM_EXPERIMENTAL_OCI` from the fully resolved helm constraint; `generateHelmEnvs()` takes that value instead of the config. - `npm` (yarn): an upgrade of yarn without a new value now consults the user constraint before deriving one from package.json or the lock file. - The "Using <tool> constraint from config" debug lines in bundler, composer, copier and pip-compile are gone, as the helper is the only reader of the config now. Helpers that read the config synchronously became async: bundler `getBundlerConstraint()`, copier `getPythonVersionConstraint()` and `getCopierVersionConstraint()`, pip-compile `get*VersionConstraint()` and `getToolVersionConstraint()`, gomod `deriveGoToolchainConstraints()` and `getUpdateImportPathCmds()`, mise `getMiseLockToolConstraints()`. composer `getPhpConstraint()` is removed. Each converted manager has a test showing the extracted constraint reaching the executed tool, and, where a derive callback exists, that the derived value still wins over it. * refactor(linters): add prefer-resolve-tool-constraint rule Reports member reads and destructuring of `constraints` and `extractedConstraints` from a manager config in `lib/modules/manager/**`, so that tool constraints are resolved through `resolveToolConstraint()` in `lib/modules/manager/util.ts` only. Writes and the same properties on package files, which extraction has to populate, are not reported. The helper itself and spec files are exempt in `.oxlintrc.json`. * refactor(manager): inline thin resolveToolConstraint wrappers Drop copier/utils.ts and the pip-compile getPipToolsVersionConstraint / getUvVersionConstraint helpers, which only forwarded to resolveToolConstraint(). Callers now call it directly. * docs(development): note lock file maintenance in tool constraints section * refactor(manager): address review on tool constraint resolution composer only runs extractConstraints() when a user constraint is missing, via Lazy. UpdateArtifactsConfig no longer re-declares constraints and extractedConstraints, which ToolConstraintsConfig already provides. |
||
|
|
96e72f6a93 | fix(platform/gitlab): skip projects marked for deletion (#45928) | ||
|
|
874060f7ee |
refactor(lookup): remove redundant currentValue check (#45933)
* refactor(lookup): remove redundant currentValue check The inner `if (config.currentValue)` was already guaranteed by the enclosing condition, so the `v8 ignore else` comment was hiding an unreachable branch rather than an untested one. Drop both; the `invalid-value` path remains fully covered by existing tests. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * Update lib/workers/repository/process/lookup/index.ts Co-authored-by: Jamie Tanna <jamie.tanna@mend.io> * refactor(lookup): use safeStringify for invalid currentValue log Template interpolation of a non-string `currentValue` yields `[object Object]`, which hides the value that made it invalid. Use `safeStringify` so objects and arrays are logged readably. Co-Authored-By: Claude Opus 5 <michael.kriese+claude-code@mend.io> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> Co-authored-by: Jamie Tanna <jamie.tanna@mend.io> Co-authored-by: Claude Opus 5 <michael.kriese+claude-code@mend.io> |
||
|
|
44160755f9 |
feat(platform): validate gitea responses with schemas and share the implementation with forgejo (#45825)
* refactor(platform): share one implementation between gitea and forgejo The `forgejo` platform was copied from `gitea` in #37075 and the two have drifted since: fixes landed on one side only (zod validation #43900, empty email #44068, null issues #44048, team reviewers #39533, merge-style fallback #37768 vs #38040), and lint sweeps had to be applied to both copies by hand. Both platforms now build on `lib/modules/platform/gitea-common/`, which holds the zod schemas, API helpers, utils, PR cache and the platform object itself. `createPlatform()` closes over its own state, so `gitea` and `forgejo` keep separate config, caches and host types. `gitea/index.ts` and `forgejo/index.ts` are thin wrappers that supply the platform id, default endpoint, http client and the version gates that genuinely differ. Reconciliations, forgejo's behaviour adopted for gitea: - Responses are validated with zod instead of `getJsonUnchecked` casts. Gitea therefore tolerates empty emails, null PRs in list responses and lenient repo/issue payloads the same way Forgejo already did. - `getRepoContents` returns a discriminated union, so `getRawFile` returns null for dir/symlink/submodule entries instead of reading a missing field. The gitea preset resolver now throws PRESET_INVALID for non-file entries, like the forgejo one. - An unknown `default_merge_style` (e.g. `manually-merged`) is caught by the schema and skipped, instead of aborting the repository with REPOSITORY_BLOCKED from `isAllowed()`. - `initRepo()` resolves whether the repo owner is an org and only fetches org labels when it is, adding one `/orgs/{owner}` request and an `orgs` entry in the gitea repository cache. - Reviewers prefixed with `team:` are sent as `team_reviewers`. Gitea keeps its `v1.14.0` guard for requesting reviewers at all. - `getBranchStatusCheck()` maps unknown commit status values through the schema's `unknown` fallback rather than warning and returning yellow. Gitea's behaviour is kept where it is deliberately different: the Forgejo instance detection and its deprecation notice, the `1.24.0` (or `10.0.0` for a detected Forgejo) native automerge gate, and the reviewer version guard are passed in as options. Two shared log messages became static so they can be grouped in metrics: `{platform}-native automerge: fail` is now `Platform-native automerge: fail` and `{Platform} getRepos() error` is now `Error auto-discovering repositories`, both with the platform id in the metadata. The dead `defaults.hostType` field is gone. * refactor(platform): keep the shared gitea implementation under gitea/ Drop the `gitea-common/` folder. The zod schemas, API helpers, utils, PR cache and `createPlatform()` live in `lib/modules/platform/gitea/` under their original file names, and `forgejo/` imports them from there. The preset resolvers pass `giteaHttp` and `forgejoHttp` to `getRepoContents()` directly, which removes the two bound wrappers. The gitea changelog source uses the shared `RepoContents` type like the forgejo one already does. * refactor(http): derive ForgejoHttp from GiteaHttp The two clients were identical apart from the default `hostType`. `ForgejoHttp` now extends `GiteaHttp` and only keeps its own base url, so the pagination logic exists once. The platform helpers accept `GiteaHttp` directly instead of the `HttpBase` alias. Also covers the `updatePr()` branch where the updated pull request cannot be converted and therefore is not cached. * refactor(platform): create the gitea and forgejo http clients in index.ts `forgejo-helper.ts` only held the `ForgejoHttp` instance, so it is gone and `forgejo/index.ts` creates the client next to the platform. Gitea does the same for symmetry, which leaves `gitea-helper.ts` with only the shared API helpers. * refactor(platform): fetch gitea labels and issues with await `getLabelList()` and `getIssueList()` still memoize the in-flight promise, but build it from small async functions instead of `.then()` and `.catch()` chains. * refactor(platform): keep declaration order of gitea/index.ts Order the module the way it was before `createPlatform()` so the diff against main aligns: platform id and http client after the repo config interface, pure helpers before the factory, and `maxBodyLength()` after the platform object. * refactor(platform): move gitea label fetching to labels.ts `fetchLabelList()` takes the http client and the repository details instead of closing over the platform state, and gets its own spec. The factory only keeps the memoizing `getLabelList()`. * refactor(platform): move gitea label lookup to labels.ts * refactor(platform): create the gitea pr cache per platform `GiteaPrCache` is now created once per platform with its http client and platform id, and `initRepo()` records the repository. The per-repository state moves to a private `RepoPrCache` which is bound to the repository cache on first use and reused until the next `initRepo()`, instead of being rebuilt on every lookup. |
||
|
|
65f803630e |
fix(dashboard): keep abandoned note outside the <details> block (#45902)
fix(dashboard): keep abandoned note outside the <details> block GitHub only renders alert callouts at the top level of a body, not inside collapsible <details> sections. Move the abandoned-dependencies note above the details block so it renders as a styled note. |
||
|
|
ac22678812 |
feat(datasource/npm): integrity and tarball extraction (#44315)
* added support for extracting integrity and tarball from npm datasource * test(datasource/npm): keep integrity and tarball in packument shape assertions The packument-trimming tests on main pinned a cached shape that dropped `dist.tarball`, so they broke once the schema started retaining `integrity` and `tarball`. Update both expectations to the new shape and cover `integrity` alongside `tarball`. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * test(workers/lookup): expect integrity and tarball on npm updates `generateUpdate` copies `downloadUrl` and `newDigest` from the release onto the update, so every npm lookup in these tests now carries the tarball URL and, where the packument fixture has one, the integrity hash. Pin and rollback updates are built without those release fields, so their expectations are unchanged. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Michael Kriese <michael.kriese@mend.io> Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
e5ef25bbba |
feat(datasource/gitlab-tags): replace getJsonUnchecked with validated getJson (#44144)
* fix(datasource/gitlab-tags): replace getJsonUnchecked with validated getJson The go datasource embeds GitlabTagsDatasource, so its test mocks are updated to satisfy the stricter GitlabTag schema (commit.id/created_at). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * test: fixup tests --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
238e15a498 |
feat(config): add inheritConfigTrusted to trust inherited-config hostRules
Renovate deployments that are deployed across multiple organisations may take advantage of Inherited Config as a means to decentralise some decisions and/or preferences that their organisations would like to use for some self-hosted configuration. Similarly, the decision of which hosts to allowlist may be something that a self-hosted administrator /may/ want to allow their organisation admins to decide. We can make this an opt-in capability to indicate that the self-hosted administrator trusts the Inherited Config moreso, and allows someone who can write to it the ability to allowlist access to internal hosts that may not be intended. If a self-hosted administrator sets `allowInternal: false`, this takes precedence, allowing a "veto", and a self-hosted admin's `headers` are still preferred, too. It's not possible for Inherited Config to set grants for `extends`, as config resolution has already occurred, so this still needs to be performed at the self-hosted config level. This trust is transitive: `hostRules` defined in presets the inherited config `extends` are registered at the same inherited tier once resolved, so this option must only be enabled when those preset sources are trusted too. Co-authored-by: Claude Fable 5 <jamie.tanna+claude-code@mend.io> Co-authored-by: Claude Opus 5 <jamie.tanna+claude-code@mend.io> |
||
|
|
270a3b7719 |
fix(workers/repository/init): surface inherited-config validation errors
Indirectly related to our changes to Host Rules, it was noticed that we previously did not provide as helpful an error when a config validation error occurs with Inherited Config. Instead of throwing a `CONFIG_VALIDATION`, without a `validationSource`, `validationError` or `validationMessage`, we can instead throw it accordingly. Co-authored-by: Claude Fable 5 <jamie.tanna+claude-code@mend.io> Co-authored-by: Claude Opus 5 <jamie.tanna+claude-code@mend.io> |
||
|
|
acb46bc706 |
fix(config): surface blocked hosts more clearly when resolving config
With the new hardening on blocking internal hosts with outbound HTTP calls, we should build on top of this to ensure that the UX is better when a preset is blocked. We can do this by making sure that when `HOST_BLOCKED`, we report a more targeted config validation error. We can also make sure to pass around our `internalGrants`, similar to how we currently allow an admin's `headers` are handled. Co-authored-by: Claude Fable 5 <jamie.tanna+claude-code@mend.io> Co-authored-by: Claude Opus 5 <jamie.tanna+claude-code@mend.io> |
||
|
|
09d2da2f1b |
feat(host-rules): add allowInternal
As part of follow-up changes, we're looking to tighten the way that Renovate handles outgoing HTTP traffic. Before we do this, we can introduce a new config option, `hostRules[].allowInternal`, that allows a self-hosted administrator to mark an internal host as "trusted" for access. This wires in the new global-only config option, `allowInternal`, which implies a new internal field `internalHostGrant`. We'll add full documentation in a future commit, when the full enforcement is in place, as there are some wider changes to enforce it fully. Co-authored-by: Claude Fable 5 <jamie.tanna+claude-code@mend.io> Co-authored-by: Claude Opus 5 <jamie.tanna+claude-code@mend.io> |
||
|
|
5ec380f402 |
fix(changelog): log external host errors below the error level (#45798)
* fix(changelog): log external host errors below the error level * fix: restore index.spec.ts to upstream state * test: cover changelog external host error log level |
||
|
|
4afe14fb19 |
refactor: emojify pr body notes (#45646)
* refactor: emojify pr body notes Signed-off-by: Adam Setch <adam.setch@outlook.com> * refactor: emojify pr body notes Signed-off-by: Adam Setch <adam.setch@outlook.com> * refactor: emojify pr body notes Signed-off-by: Adam Setch <adam.setch@outlook.com> --------- Signed-off-by: Adam Setch <adam.setch@outlook.com> |
||
|
|
9cecb86eca |
feat(config): add experimental exitCodeForErrors option (#45752)
* feat(config): add experimental exitCodeForErrors option * feat(config): give unknown-error its own exit code * docs: restore exit codes 1 and 2 to the table |
||
|
|
4573aa6ac8 |
feat(automerge): attempt branch automerge before falling back to a PR on merge queue rejection (#45724)
* feat(automerge): attempt branch automerge before falling back to a PR on merge queue rejection * refactor(automerge): ask the platform instead of parsing merge queue rejection messages |
||
|
|
7b25e97ba3 |
feat(github): add support for using merge queues without auto merge (#45721)
* feat(github): add experimental support for using merge queues without auto merge * test(github): use vi.stubEnv for merge queue env flag * fix(github): do not re-enqueue PRs already waiting in the merge queue * refactor(github): reuse merge queue detection from #45252 for isBranchMergeQueueEnabled * refactor(github): read the base branch from the PR instead of extending MergePRConfig * feat(github): enable merge queue support without the RENOVATE_X_GITHUB_MERGE_QUEUE flag * Apply suggestion from @secustor |
||
|
|
b2d3b64c48 |
refactor(lint): enforce err key for errors in logger metadata (#45759)
* refactor(lint): enforce err key for errors in logger metadata * refactor(lint): flag error message and stack strings in logger-err-key |
||
|
|
9d74625d93 |
build(deps): update dependency prettier to v3.9.6 (main) (#45815)
* build(deps): update dependency prettier to v3.9.6 | datasource | package | from | to | | ---------- | -------- | ----- | ----- | | npm | prettier | 3.8.1 | 3.9.6 | * chore: prettier fixes --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Michael Kriese <michael.kriese@mend.io> |
||
|
|
0e5b2287e1 |
build(deps): update dependency markdown-it to v15 (main) (#45796)
* build(deps): update dependency markdown-it to v15 | datasource | package | from | to | | ---------- | ----------- | ------ | ------ | | npm | markdown-it | 14.3.1 | 15.0.1 | * fix: migration --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Michael Kriese <michael.kriese@mend.io> |
||
|
|
a806d2f735 |
feat(onboarding): note that vulnerabilityAlerts can be rate limited (#45686)
For instance, when a repository is onboarding with many dependency updates (some of which have many security releases), it would be useful to let the user know that they're not unlimited, but that the rate limit is taken into account. Now we have the capability to specify these, we should: - take them into account when determining how many PR(s) may be raised at a given time - point users to the documentation when they may want to limit them, if the number of security PRs is more than the `prConcurrentLimit`/`branchConcurrentLimit` Co-authored-by: Claude Sonnet 5 <jamie.tanna+claude-code@mend.io> |
||
|
|
05e1271376 |
test: use expect().resolves instead of awaiting inside expect (#45672)
Enables the vitest/prefer-expect-resolves oxlint rule and applies its autofix across the spec suite. |
||
|
|
df8c4051ff |
test(core): replace file snapshots with explicit assertions (#45660)
* test(core): replace file snapshots with explicit assertions * test(core): replace inline snapshots with explicit assertions * test(core): assert on relevant sections instead of full bodies * test(core): drop assertions implied by full equality * test(core): address review feedback * test(core): use resolves instead of awaiting inside expect |
||
|
|
e10ef79236 |
fix(bump-versions): collect bumpVersions from all upgrades, not just upgrades[0] (#44809)
* fix(bump-versions): collect bumpVersions from all upgrades, not just upgrades[0] * chore: add test for duplicate bumpversion configs * chore: use suggested safeStringify * chore: use coerceArray |