* refactor(onboarding): return repository result from ensureOnboarding
Rename ensureOnboardingPr to ensureOnboarding and have it return a new
'onboarding' RepositoryResult. renovateRepository now branches on
repoIsOnboarded, so exactly one of ensureOnboarding/updateRepo produces
the repository result and the non-null assertion on the update result
(TODO #22198) is no longer needed. updateRepo/update no longer return
undefined for non-onboarded repositories.
* refactor(onboarding): keep ensureOnboardingPr name
When Go updates dependencies using i.e. `go get` or `go mod download`,
it uses Minimal Version Selection (MVS)[0], which takes the lowest
version available from the given packages.
In the case of an updated dependency, in practice this means that Go
will chose the version of an indirect (transitive) package dependency as
the version that the updated package points to in their `go.mod`, which
must be older than the updated package, as the updated package has only
just been updated.
This protects against cases in other ecosystems where an unpinned
transitive dependency can be pulled by an updated package, potentially
bypassing `minimumReleaseAge`.
However, I can imagine there may be some questions about this, so we can
preemptively add a note into the `Artifact update notice` when
`minimumReleaseAge` is set.
[0]: https://go.dev/ref/mod#minimal-version-selection
As noted in #36073, having support for the different protocols for
exporting OpenTelemetry data would be useful, especially if this is
leading to users seeing issues when they have auto-injected
configuration being set.
Co-authored-by: Claude Sonnet 5 <jamie.tanna+claude-code@mend.io>
* fix(github): treat graphql_rate_limit as rate limiting
GitHub answers a spent app-installation budget with a GraphQL error carrying
code graphql_rate_limit, which neither call site recognised: the repository
query reported platform-unknown-error, and app authentication reported
Init: Authentication failure. Both readings send the user looking for a
problem that is not there, and neither is retried the way rate limiting is.
The two shapes are now recognised in one place and both call sites use it.
* fix(github): propagate GraphQL rate limits
* fix(github): preserve GraphQL response contract
---------
Co-authored-by: Sergei Zharinov <zharinov@users.noreply.github.com>
Refs #45216
The object counterpart to `prefer-coerce-array`: `x ?? {}` becomes
`coerceObject(x)`. The rule flags a `??` whose default is a bare `{}`
literal, so a populated default or an asserted `({} as Foo)` still
passes; a `x ?? y ?? {}` chain maps onto `coerceObject(x, y)`.
Every hit across `lib/` converted cleanly, so no disable comments were
needed.
* refactor(linters): add prefer-coerce-array rule
Refs #45217
`x ?? []` and `coerceArray(x)` say the same thing, but only the second
one is searchable and consistent. The rule flags a `??` whose default is
a bare `[]` literal; a non-empty default or an asserted `([] as Foo[])`
still passes.
Applying it across `lib/` surfaced three spots the helper cannot cover,
which keep `?? []` with a disable comment or a narrower fix:
- `package-files.ts` destructures a tuple, which `coerceArray()` widens
- `generate.spec.ts` defaults `matchAll()`, an iterator rather than an array
- `mise/upgradeable-tooling.ts` declares a `Record` return, so its
`?? []` fallback was wrong to begin with and becomes `coerceObject()`
* refactor(platform/github): hoist the GHE version header lookup
`Object.entries(...).find()` returns a tuple, and defaulting it with
`?? []` only to destructure the second element reads as an array
default when it is not one. Name the found entry instead, which drops
the `?? []` rather than routing a tuple through `coerceArray()`.
fix(manager/copier): set isExecutable on added and modified files
Detect the owner execute bit on files written by copier update and
propagate it as isExecutable on the returned file additions, so
committed files keep their executable flag.
* test: replace manual process.env mutation with vi.stubEnv
Enable Vitest's `unstubEnvs` so env stubs are reverted automatically
before every test, and convert the manual `process.env.X = ...` /
`delete process.env.X` pairs in specs to `vi.stubEnv()`.
This removes the hand-written `afterEach` cleanup blocks and the
`const OLD_ENV = process.env` / `process.env = { ...OLD_ENV }`
save-restore dance, which silently leaked state between tests when a
test set a variable without a matching delete.
Left as-is (incompatible with `unstubEnvs`, which unstubs before each
test, not after):
- module top-level env writes (cocoapods, gomod, post-update, pip-compile
and bundler artifacts specs)
- `beforeAll` env writes -- the two in `util/http` moved to `beforeEach`
* test: stop asserting on ambient environment variables
Three specs asserted on values that the surrounding environment can
override, so they only passed on a machine that happened not to set them:
- `util/exec/env`: `getChildProcessEnv()` forwards every entry of
`basicEnvVars`, but the spec only seeded a subset. Any other forwarded
variable that was set ambiently (e.g. a lowercase `http_proxy`) leaked
into the `toEqual` assertion. Clear all of them first.
- `util/git/index`: `process.env.GIT_SSH_COMMAND` deliberately overrides
the default that the two env-inheritance tests assert on. Clear it.
- `logger/pretty-stdout`: the colorized level strings are built at import
time, so the `beforeEach` setting `FORCE_COLOR` could never affect them
-- it was dead code, and the test silently assumed a terminal without
colour support. Derive the expectation the same way the module does.
* test(datasource/nuget): use a throwaway cache dir for nupkg downloads
The `determine source URL from nupkg` tests do not mock the filesystem,
so they really wrote downloaded packages into a fixed `/tmp/cache` path.
That fails wherever the system tmpdir is not writable, and two checkouts
running the suite at once would share the same directory.
Use `tmp.dir({ unsafeCleanup: true })` and clean it up afterwards, which
is the pattern the other specs that touch disk already use.
* feat(lint): add renovate/prefer-stub-env rule
Enforces in specs what the migration to `vi.stubEnv` established:
- assigning to or deleting `process.env.X` inside `beforeEach`, `afterEach`,
`it` or `test` is reported, with a fixer that rewrites it to `vi.stubEnv()`
- module scope, `describe` bodies and `beforeAll` / `afterAll` are left alone:
`unstubEnvs` reverts stubs *before* each test, so a stub created there is
gone by the time the first test runs, and those must keep assigning directly
- replacing the `process.env` object is reported everywhere and never fixed.
`vi.stubEnv()` captures the original object when the worker starts, so after
a reassignment its deletes silently target the old object
The rule found 28 further violations in specs the earlier sweep missed, since
that one was anchored on `delete process.env`. Twelve were auto-fixed; the
rest replaced `process.env` wholesale and are now `clearEnv()` from test/util.
`util/exec/index.spec.ts` opts out with a comment: it asserts on the whole
child env, and `vi.stubEnv()` cannot express that because it maps `PROD`,
`DEV` and `SSR` onto '' instead of deleting them.
feat(changelog): stop fetching PR changelog entries when we're at platform limits
A regular issue that we see across Renovate users - often with
packages like traefik (#45482) - are that a large number of
changelogs needing to be fetched can lead to a slow run (at best) or
a full out-of-memory error (at worst).
As noted in #45501, as a follow-up to #45500, we can stop fetching
more entries for the changelog generation by checking Platform
limits for a given Pull Request.
We can ignore `fetchChangeLogs=branch`, because users opting into
that behaviour are hopefully aware that adding their changelogs will
be sufficiently large.
Co-authored-by: Claude Sonnet 5 <jamie.tanna+claude-code@mend.io>
As noted in #45500, we want to refactor how changelogs work to fetch
them in newest-first order.
This will allow in future changes to stop fetching changelogs at the
point that we'll likely hit platform limits, avoiding unnecessary HTTP
calls and as noted in #45482, hitting Node out-of-memory errors.
We need to make sure that we `push` instead of `unshift` to return the
results array in newest-first.
Co-authored-by: Claude Sonnet 5 <jamie.tanna+claude-code@mend.io>
As part of follow-up changes, we'll be making changes to the changelog
functionality.
To determine more meaningful insight into whether this has succeeded, we
can add instrumentation to determine how many HTTP calls are being made.
feat(lint): add `types-location` rule and enable it for `lib/modules`
Reviewers consistently ask for exported types to be moved into a
colocated `types.ts`. Add a `renovate/types-location` oxlint rule that
reports `export interface` / `export type` declarations outside such a
file, and turn it on across `lib/modules`.
Re-exports (`export type { X }`, `export type *`), file-local types, and
values (enums, classes, functions) are left alone. `schema.ts` files and
`schema/` directories are excluded via config, matching how
`zod-schema-location` is configured — inferred types belong next to the
schema they are derived from.
Relocating the 87 existing violations means:
- new `types.ts` files for the modules that had none
- `terragrunt/common.ts` folded into its `types.ts`, as it held nothing
else
- `RangeBound` in `versioning/gradle/compare.ts` un-exported, since only
a file-local interface referenced it
- `bazel-module/parser/fragments.ts` split from its inferred types, which
now live in `parser/types.ts` and infer through a namespace import so
each schema keeps its merged name
The containous/traefik repository was renamed to traefik/traefik in 2020. GitHub still redirects, but PR release notes render the old organization name.
Some registries publish several tags for one version, e.g. Docker Hub has both 3.7.12 and v3.7.12 for the traefik image. Both survive datasource deduplication, which compares raw version strings, so the changelog renders the release notes once per tag.
* fix(lint): use `parseInt` over `Number`
As a code review comment that comes up every so often, we can add a
linting rule to enforce this check.
This also fixes any existing usages.
As this could be breaking, we can release it as a `fix`.
Co-authored-by: Claude Sonnet 5 <jamie.tanna+claude-code@mend.io>
* fixup! fix(lint): use `parseInt` over `Number`
* fixup! fix(lint): use `parseInt` over `Number`
---------
Co-authored-by: Claude Sonnet 5 <jamie.tanna+claude-code@mend.io>
When certain meta fields are logged, we pull them out of the meta fields
and into the log line, such as:
DEBUG: Branch is not pending, removing pending upgrades (branch="renovate/express-4.x")
However, this also would affect non-string values such as:
DEBUG: Git operations statistics (branch="[object Object]")
This only affects the "pretty" logs, but would mean no `branch` metadata
appears.
Instead, we can only include them when they're a string.
Co-authored-by: Claude Sonnet 5 <jamie.tanna+claude-code@mend.io>
As part of future changes, we need to correct the stringification logic
when `branch` is an object.
While looking at this, we noticed that `dependencies` is - likely - an
array of items being handled by `.toString()`ing them.
However, there's no existing cases in the codebase of this behaviour, so
we can remove it from `metaFields`.
Co-authored-by: Claude Sonnet 5 <jamie.tanna+claude-code@mend.io>
As part of future changes, we need to correct the stringification logic
when `branch` is an object.
Before we do this, we can make sure we have full coverage of the
existing behaviour.
Co-authored-by: Claude Sonnet 5 <jamie.tanna+claude-code@mend.io>
fix(gomod): handle bare ../ replace paths in gomodTidyAll
When a go.mod file uses a replace directive pointing to the parent
directory with no sub-path (e.g. `replace example.com/root => ../`),
gomodTidyAll would silently skip running `go mod tidy` on that module
and any of its dependents.
The root cause was in the `localReplace` regex where the localPath
capture group used `\S+` (one or more non-whitespace), so `../` —
with nothing after the slash — was never matched. Only paths like
`../subdir` were picked up.
Changing `\S+` to `\S*` allows zero-or-more trailing characters,
so bare `../` and `./` now parse correctly and the dependency graph
is built properly.
* feat(presets): add @oxlint/* packages to packages:linters
Groups oxlint plugin packages together with oxlint itself.
* feat(presets): add @oxlint/plugins to monorepo:oxlint
* test(presets): update linters snapshots for @oxlint/**
`fixParsedRange()`'s `ordValues` array is only populated for a bare
(operator-less) segment when it's adjacent to `||`.
Any other bare segment throws earlier at `ordValues[i].includes('||')`
before either of these branches can be reached, so a falsy, non-`||`
`operator` is unreachable in both `fixParsedRange()` and `bumpRange()`.
Co-authored-by: Claude Sonnet 5 <jamie.tanna+claude-code@mend.io>
The `else if (gitRefsMatch.repoName)` branch was unreachable, so we can
simplify this check.
Co-authored-by: Claude Sonnet 5 <jamie.tanna+claude-code@mend.io>
* feat(http/bitbucket): warn when an endpoint announces a deprecation
To highlight to a self-hosted administrator when they are using
deprecated/removed functionality, we can look for i.e.
sunset: Fri, 21 Aug 2026 00:00:00 UTC
deprecation: @1771545600
link: <https://developer.atlassian.com/cloud/bitbucket/changelog#CHANGE-3071>; rel="deprecation"; type="text/html"
This provides visibility into a soon-to-be-removed piece of
functionality, and should allow us to catch these deprecations more
centrally.
We make sure to log them - as a WARN - once, and with ISO8601 dates for
readability.
If we can't parse either `sunset` or `deprecation`, we log them as-is,
so we don't accidentally remove any necessary context.
Co-authored-by: Claude Opus 5 <jamie.tanna+claude-code@mend.io>
* fixup! feat(http/bitbucket): warn when an endpoint announces a deprecation
* fixup! feat(http/bitbucket): warn when an endpoint announces a deprecation
Co-Authored-By: Claude Sonnet 5 <jamie.tanna+claude-code@mend.io>
---------
Co-authored-by: Claude Opus 5 <jamie.tanna+claude-code@mend.io>
As part of follow-up changes, we'll want to check the response's
headers, which requires we have an additional hook that we can use.
By default, this is a no-op.
Co-authored-by: Claude Opus 5 <jamie.tanna+claude-code@mend.io>
To surface the deprecation or removal of features by Bitbucket Cloud,
there may be an error body such as:
{
"type": "error",
"error": {
"message": "CHANGE-3071 - Functionality has been deprecated",
"detail": "Please read the changelog entry for more details.",
"data": {
"announcement_url": "https://developer.atlassian.com/cloud/bitbucket/changelog#CHANGE-3071"
}
}
}
To make it more visible to a self-hosted administrator that they're
using deprecated/removed functionality, we can elevate this to a WARN
log line, if there is an `announcement_url`.
We make sure to handle both parsed JSON response bodies and a raw string
body used for other requests.
To reduce noise to a self-hosted administrator, we can log this once per
run.
Co-authored-by: Claude Opus 5 <jamie.tanna+claude-code@mend.io>
On 2026-02-20, Atlassian announced that they would be removing the
native issue tracker in Bitbucket Cloud[0].
As of 2026-08-24[1], this change is now complete.
When Renovate attempts to update an existing Dependency Dashboard Issue,
we see:
DEBUG: GET https://api.bitbucket.org/2.0/repositories/some/repo/issues?q=... = (code=ERR_NON_2XX_3XX_RESPONSE, statusCode=410 retryCount=0, duration=87)
WARN: Error finding issues
And when trying to create one, we see:
DEBUG: findIssue(Dependency Dashboard)
DEBUG: Issues are disabled - cannot findIssue
Because it's not possible - until #20568 - to use another source for
Issues on Bitbucket Cloud, we need to remove support for the Dependency
Dashboard.
When we do try and interact with them, we can DEBUG log as to why it's a
no-op (similar to `platform=bitbucket-server`), and reference the deprecation.
As we now don't touch Issues, we can remove the additional permissions
that were previously required.
As there are no longer Issues, we can remove `has_issues` from our
types.
As well as the Dependency Dashboard, this also means that i.e. Config
Warning Issues are no longer available.
[0]: https://developer.atlassian.com/cloud/bitbucket/changelog/#CHANGE-3071
[0]: https://developer.atlassian.com/cloud/bitbucket/changelog/#CHANGE-3401
Co-authored-by: Claude Opus 5 <jamie.tanna+claude-code@mend.io>