* fix(util/http): fall back to `go` host rules for `go-proxy` requests
Prior to 5665570f0b, all requests made by
the `go` Datasource - including those to a Go module proxy - used the
`go` host type.
When we refactored them into classes, this introduced the ability to
specify a `go-proxy` host type, which would only be used for direct
access to the Go module proxy.
As this isn't documented - and may change in the future - we can add a
fallback to the credentials for `hostType: go`, similar to how i.e.
`gitea-tags` and `gitea` do the same.
Co-Authored-By: Claude Opus 5.5 <jamie.tanna+claude-code@mend.io>
* fixup! fix(util/http): fall back to `go` host rules for `go-proxy` requests
Co-Authored-By: Claude Sonnet 5.5 <jamie.tanna+claude-code@mend.io>
* fixup! fix(util/http): fall back to `go` host rules for `go-proxy` requests
---------
Co-authored-by: Claude Opus 5.5 <jamie.tanna+claude-code@mend.io>
* fix(http): share concurrent identical cache provider requests
Identical requests that use a cache provider and run at the same time now
share one in-flight request instead of each fetching from the server.
Callers that join an in-flight request get a deep copy of the response.
This replaces the per-URL mutex, which only covered the cache check and
was released before the fetch finished.
Co-Authored-By: Claude Opus 5.5 <michael.kriese+claude-code@mend.io>
* fix(http): reuse cache providers for npm and typst lookups
The npm and typst datasources now use one module-level cache provider, so
concurrent lookups of the same package share one request. The request key
is only hashed for GET and HEAD requests and includes `throwHttpErrors`.
Co-Authored-By: Claude Opus 5.5 <michael.kriese+claude-code@mend.io>
Co-Authored-By: Claude Sonnet 5.5 <michael.kriese+claude-code@mend.io>
* fix(http): don't share requests that differ in response type
Add `responseType` and `responseBecomesConfig` to the key of shared
requests, and test that requests with different headers aren't shared
and that a shared response is copied for every caller.
Co-Authored-By: Claude Opus 5.5 <michael.kriese+claude-code@mend.io>
Co-Authored-By: Claude Sonnet 5.5 <michael.kriese+claude-code@mend.io>
* fix(http): keep in-flight requests per Http class and copy binary bodies
Co-Authored-By: Claude Opus 5.5 <michael.kriese+claude-code@mend.io>
---------
Co-authored-by: Claude Opus 5.5 <michael.kriese+claude-code@mend.io>
A deep copy of a `Uint8Array` body used `subarray()`, which returns a view
of the same memory, so changing the original also changed the copy. Use
`slice()`, which copies the bytes.
Co-authored-by: Claude Opus 5.5 <michael.kriese+claude-code@mend.io>
Responses served by the cache provider's `bypassServer` no longer wait
for a `maxRequestsPerSecond` throttle slot or a concurrency queue slot.
Only requests that reach the server are throttled and queued.
Co-authored-by: Claude Opus 5.5 <michael.kriese+claude-code@mend.io>
As we're now managing them via a `afterEach` in `test/setup.ts`, we can
remove any entries in tests themselves.
We don't update overrides of an outer hook or cases where there are more
explicit `clear()`s.
Co-authored-by: Claude Sonnet 5 <jamie.tanna+claude-code@mend.io>
In a similar way to how `allowedEnv` works, we should allow a
self-hosted admin to specify whichever headers they wish in their
`hostRules` - whether in their global config, a `repositories[]` entry,
or a preset a `repositories[]` entry extends.
Previously, this would require a self-hosted admin to add any header
they wished to set to `allowedHeaders`, which would also allow any of
their users' repositories to set that same header.
We can align this behaviour with `allowedEnv`, which makes sure that
the admin's own `headers` are always applied regardless of
`allowedHeaders`, while a repository's own `hostRules` - and those of
any preset it extends - remain constrained by it as before.
We make sure to use an explicit `[]` to make it clear when there are no
headers to allow, opposed to using something less clear like
`undefined`.
We do not currently allow an `extends` (without a `repositories[]`
entry) to bypass, and they'll be followed-up with in #45670.
Co-authored-by: Claude Opus 5 <jamie.tanna+claude-code@mend.io>
`lib/util/http/auth.ts` and `lib/util/http/host-rules.ts` were the last
readers of the `*_API_USING_HOST_TYPES` constants, which the previous
commits left as aliases of `PLATFORM_FAMILIES`. Both now read the table
directly and the aliases are deleted, so each family's host types are
declared in exactly one place.
Also covers the `forgejo` fallback in `findMatchingRule()`, which had no
test: the shared setup only registers a `gitea` host rule, so the
forgejo branch was never entered.
Behaviour changes: none. The aliases pointed at the same arrays.
* fix(util/http): make the legacy `headers` getter configurable
Every other property defined on `HttpError.prototype` in this file is
`configurable`, but `headers` was not. That makes the module fail with
`TypeError: Cannot redefine property: headers` whenever a test resets the
module registry and re-imports anything that pulls in `got.ts`.
Co-authored-by: Claude Opus 5 <michael.kriese+claude-code@mend.io>
* test(manager/pre-commit): cover the suppress-warning env var
`defaultConfig.prBodyNotes` drops the unsupported-manager warning when
`RENOVATE_X_SUPPRESS_PRE_COMMIT_WARNING` is set. Since the value is
computed at module load, the new spec resets the module registry and
re-imports the manager with the variable stubbed.
The `istanbul ignore next` on that arm is dropped because the branch is
now exercised.
Co-authored-by: Claude Opus 5 <michael.kriese+claude-code@mend.io>
* refactor(manager): remove unreachable fallbacks
Drop three defensive fallbacks that can never be taken:
- `docker-compose`: `config.services ?? config` always yields an object,
so the `|| {}` guard behind it is dead
- `git-submodules`: `git.raw()` resolves to a string, so the `?? ''`
never applies
- `maven`: the only site that builds a `MavenProp` always passes a
`packageFile` string, so narrow the type and drop the `?? undefined`
Co-authored-by: Claude Opus 5 <michael.kriese+claude-code@mend.io>
---------
Co-authored-by: Claude Opus 5 <michael.kriese+claude-code@mend.io>
* 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.
To harden Renovate from being able to access internal hosts, we can add
a level of enforcement on all outgoing requests.
We can split this into two types of traffic:
- where the response becomes configuration (i.e. `extends`)
- where the response does not become configuration (i.e.
`customDatasources`)
To harden this, we enforce on every outbound HTTP request:
- check the resolved URL before anything, including cache lookups, can
proceed
- catching IP-literal hosts in every spelling the WHATWG URL parser normalizes
- blocking known Cloud metadata IPs, too
- a `dnsLookup` wrapper validates every address a hostname resolves to,
on every new connection, which also defeats DNS rebinding
- a `beforeRedirect` hook re-runs the URL check on each redirect
target, with the new host getting its own grant lookup rather than
inheriting the original host's verdict
We default this enforcement to `warn`, to not break existing deployments
and instead give them visibility into required configuration before a
future major version (major+1) enables `block` by default.
We'll also remove allow in a future major version (major+2), to give
time for admins to migrate over.
When a request is blocked, this degrades like an unreachable registry,
so a Renovate run isn't aborted by a blocked request.
As part of this, we can also provide a worked example of the different
cases that do/do not need allowing via `hostRules`.
Requests through an admin-configured proxy resolve DNS at the proxy, so
`dnsLookup` never runs for them, so we can resolve the hostname locally
and validate every address before the request instead.
This doesn't catch everything, especially as the proxy may resolve the
name to a different result, and if we can't resolve the name - possible
in locked-down corporate environments - we fail open.
Child processes aren't currently able to route traffic through
Renovate's HTTP layer, so are unrestricted.
Co-authored-by: Claude Fable 5 <jamie.tanna+claude-code@mend.io>
Co-authored-by: Claude Opus 5 <jamie.tanna+claude-code@mend.io>
Co-authored-by: Claude Sonnet 5 <jamie.tanna+claude-code@mend.io>
As part of follow-up changes, we're looking to tighten the way that
Renovate handles outgoing HTTP traffic.
Before we do this, we can add helper methods to manage classification of
outgoing traffic to three "trust tiers":
- the public Internet - which is generally OK
- `internal` traffic - which should be carefully configured by a
self-hosted administrator based on their risk tolerance, and includes
loopback, RFC1918, link-local, CGNAT, multicast, unique-local and
unspecified addresses
- `metadata` traffic (i.e. to the AWS EC2 Metadata API) which should
/NEVER/ be allowed.
By splitting `metadata` out, we can always block access to them, while
allowing `internal` hosts to be accessed.
We use Node's built-in `net.BlockList` rather than adding a dependency,
which also transparently classifies IPv4-mapped IPv6 addresses (in all
their spellings) against the IPv4 rules.
Co-authored-by: Claude Fable 5 <jamie.tanna+claude-code@mend.io>
Co-authored-by: Claude Opus 5 <jamie.tanna+claude-code@mend.io>
As part of follow-up changes, we need to have a single enforcement point
for outbound HTTP requests.
Before we do this, we can prefactor `stream()` to use the same options
preparation as `request()`.
This does lead to a slight behaviour change, wehre:
- host rule lookups for streams now use the resolved URL, whereas
previously a `baseUrl`-relative URL was passed as-is, so rules could
fail to match
- stream options are now deep-merged with the instance options, rather
than a shallow spread, matching `request()`
- a disabled host now logs a debug line on the stream path too
Co-authored-by: Claude Fable 5 <jamie.tanna+claude-code@mend.io>
* refactor(lint): enable oxlint prefer-named-capture-group at error
Numbered capture groups (m[1], $1) break silently when a regex is
edited and group order shifts. Named groups (?<name>...) make intent
explicit and accesses (m.groups.name) resilient to reordering.
This enables the built-in eslint rule prefer-named-capture-group,
surfacing 321 pre-existing unnamed-group violations across ~121
files to be burned down incrementally in follow-up commits.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017RxHFNsSworLcv23xXk9Hk
* refactor(versioning): name capture groups for prefer-named-capture-group
Burns down all prefer-named-capture-group violations in
lib/modules/versioning/. Unused capture groups (only reachable via
.test() or whole-match) become non-capturing (?:...); groups whose
value is read become named (?<name>...), with accesses rewritten to
match.groups!.<name> (or destructured from .groups!) instead of
positional match[n]/$n so a future edit that reorders groups cannot
silently break the reader.
Two cases needed extra care to preserve behavior:
- semver/common.ts and poetry/patterns.ts split() on a regex whose
capture group is relied upon by String.prototype.split to keep the
separator in the output array; the group is kept capturing and
named, not converted to non-capturing.
- hashicorp/convertor.ts's semverRegex is only ever embedded via
.source into a named `(?<version>...)` group elsewhere and never
matched directly, so its internal groups are safe to make
non-capturing.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017RxHFNsSworLcv23xXk9Hk
* refactor(datasource): name capture groups for prefer-named-capture-group
Burns down all prefer-named-capture-group violations under
lib/modules/datasource/. Same treatment as the versioning module:
unused groups become non-capturing, used groups get names, and
positional match[n] reads move to match.groups!.<name>.
docker/ecr.ts and pypi/common.ts keep their positional destructuring
(`const [, region] = ...`, `!!m &&`) working unchanged since a named
capture group is still addressable by its numeric index in
JavaScript — only pypi/common.ts's access was rewritten to
m.groups!.owner for clarity since it read a single group directly.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017RxHFNsSworLcv23xXk9Hk
* refactor(platform): name capture groups for prefer-named-capture-group
Burns down all prefer-named-capture-group violations under
lib/modules/platform/. Uses the repo's established idiom of
`match.groups?.<name>` after an optional-chained exec()/match() (see
bitbucket/index.ts hostnameWithoutApiPrefix, gerrit/utils.ts
extractSourceBranch) and `$<name>` in String.replace replacement
strings. Unused groups (markdown/HTML massaging regexes matched only
to be stripped) become non-capturing.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017RxHFNsSworLcv23xXk9Hk
* refactor(manager): name capture groups in ansible-galaxy for prefer-named-capture-group
Burns down prefer-named-capture-group violations in
lib/modules/manager/ansible-galaxy/. newBlockRegEx and blockLineRegEx
dropped their unused outer wrapping group (only the inner key/value
groups were ever read) and named the key/value groups; nameMatchRegex's
remaining unused groups (protocol/user-info/port alternatives) become
non-capturing since only source/hostname/depName/version are read via
.groups. Accesses in collections.ts and roles.ts move from
lineMatch[2]/lineMatch[3] to lineMatch.groups!.key/value.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017RxHFNsSworLcv23xXk9Hk
* refactor(manager): name capture groups for prefer-named-capture-group (batch 1)
Burns down prefer-named-capture-group violations across the first
alphabetical slice of lib/modules/manager/ (ansible through mix).
Used groups get names with accesses rewritten to match.groups!.<name>
(or $<name> in replacement strings); groups whose value is never read
become non-capturing (?:...). Two String.split() call sites
(git-submodules/extract.ts, meteor/extract.ts) keep the established
"named group still readable positionally from the split() result
array" idiom instead of restructuring the destructuring.
bazel/rules/go.ts and buildpacks/extract.ts dockerRef verified
byte-for-byte equivalent before/after via a quick node script since
several nested groups collapsed to non-capturing at once.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017RxHFNsSworLcv23xXk9Hk
* refactor(manager): name capture groups for prefer-named-capture-group (batch 2)
Burns down prefer-named-capture-group violations across the remaining
alphabetical slice of lib/modules/manager/ (npm through terragrunt).
Same treatment as batch 1: used groups get names with accesses moved
to match.groups!.<name> or $<name> replacement strings; unused groups
(wrapping alternations, quantifier repetition groups only consumed via
.test(), or values never read from a match) become non-capturing.
terraform/extractors/others/modules.ts and terragrunt/modules.ts share
near-identical URL-parsing regexes (github/bitbucket/git/azure-devops
ref matchers) — the same set of always-unused `depth=`/`&depth=`
query-string groups is converted to non-capturing in both files.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017RxHFNsSworLcv23xXk9Hk
* refactor(config): name capture groups for prefer-named-capture-group
Burns down prefer-named-capture-group violations in lib/config/.
Used groups get names with replacement strings switched from $1/$2 to
$<name>; the unused separator group in schedule-migration.ts's
afterBeforeRegex becomes non-capturing. presets/parse.ts keeps
assigning through the already-declared `repo` variable rather than
array-destructuring the exec() result, matching this file's existing
style for the other named-group regexes just above it.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017RxHFNsSworLcv23xXk9Hk
* refactor(util): name capture groups for prefer-named-capture-group
Burns down prefer-named-capture-group violations across lib/util/ and
lib/logger/. Same treatment as previous batches: named groups for
values that are read (via $<name> replacement strings, .groups!.<name>,
or destructured from .groups), non-capturing groups where nothing reads
the captured value (e.g. semantic.ts's angular-commit .test()-only
regex). www-authenticate.ts's tokenizer entries each get their own
named group but keep accessing the match generically via match[1],
since a positionally-indexed named group is still readable by index.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017RxHFNsSworLcv23xXk9Hk
* refactor(workers): name capture groups for prefer-named-capture-group
Burns down prefer-named-capture-group violations in lib/workers/.
Used groups get names with $<name> replacement strings; unused
wrapper/alternation groups (e.g. semantic-commit-message.ts's optional
scope/issue wrappers, release-notes.ts's date-heading and
horizontal-rule matchers that are only probed with .search()/replaced
wholesale) become non-capturing.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017RxHFNsSworLcv23xXk9Hk
* refactor(test,tools): name capture groups for prefer-named-capture-group
Finishes the prefer-named-capture-group burndown: test/, tools/, and
vitest.config.mts. Groups whose value is read get names ($<name>
replacement strings or .groups.<name> access); groups only used via
.test()/.search() or that feed a .match(/…/g) call (whose return value
is the array of whole matches, not captured groups) become
non-capturing.
This is the last remaining slice of pre-existing violations, so the
rule (already enabled at error repo-wide) now passes with zero
findings across the whole codebase.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017RxHFNsSworLcv23xXk9Hk
* fix prettier failure
* refactor(lint): use a named capture group in `v8-ignore-no-count`
New rule from main trips the newly enabled `prefer-named-capture-group` oxlint rule.
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
* 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.
* 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(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>
* ci(linter): add renovate/require-regex-util rule
Adds a custom oxlint rule that flags raw RegExp usage in lib/
production code: any `new RegExp(...)` call and any regex literal
that is not the direct first argument of `regEx(...)`. The regEx()
helper from lib/util/regex.ts uses the RE2 engine for ReDoS-safe
matching and caches compiled patterns. lib/util/regex.ts itself is
exempt.
The rule is disabled for **/*.spec.ts, test/**, and tools/** via
config overrides, because vitest APIs such as expect.stringMatching
need bare RegExp instances (a past commit reverted regEx() there).
Severity is set to "warn" instead of "error": the repo currently has
197 pre-existing violations (above the agreed mechanical-fix
threshold of 80), and a number of them are deliberately
RE2-incompatible patterns (lookahead/lookbehind/named backreferences,
see TODO #12872/#12875 markers in e.g. lib/modules/versioning/rez/
pattern.ts and lib/modules/manager/dockerfile/extract.ts) that need
per-site judgment before they can be wrapped in regEx() or suppressed
with a justified disable comment. No violation fixes are included in
this commit.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* ci(linter): enforce renovate/require-regex-util at error
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* refactor: use regEx() for regex literals and RegExp construction in lib
Burn down all renovate/require-regex-util violations (197):
- wrap RE2-compatible regex literals and RegExp constructions in regEx()
- rewrite lookaround patterns where behavior is provably preserved:
goproxy separator split, sbt-package link extraction, leiningen
comment stripping and keyword search, npm GitHub owner validation
(unit-count bound replaced by an explicit length check)
- keep native RegExp with disable comments where RE2 cannot be used:
moo / good-enough-parser lexer patterns (moo rejects RE2 instances),
MigrationsService propertyName (matched via isRegExp, which RE2
instances fail), rez named backreferences and lookaheads, GitHub
markdown link negative lookbehind, emoji astral-plane matching
- drop stale TODO #12875 comments for patterns current re2 handles
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* refactor: reduce require-regex-util disable comments
Rewrite the github massage-markdown-links URL regex to capture an
optional `api.` prefix in a named group (skipped in code) instead of
relying on a negative lookbehind, so both the base and global variants
can use regEx()/RE2 and their two disable comments are removed. Add
spec cases for api-prefixed URLs that match the URL shape.
Consolidate the four related line disables in rez/pattern.ts (bound and
range patterns emulating Python conditional patterns via named
backreferences and lookaheads, which RE2 cannot express) into a single
block disable/enable pair, matching the treatment of the lexer files.
Disable comment count drops from 13 to 8.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* refactor(datasource/sbt-package): use named capture group in link regex
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* refactor(manager/cake): restore original multiLineComment regex spelling
The [^] -> [\\s\\S] rewrite was only needed for an RE2 wrap that was
later reverted; keep the diff minimal.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(datasource/go): skip empty GOPROXY segments
The switch from a lookahead split to a capture-group split (needed because
RE2 has no lookahead support) changed how empty GOPROXY segments parse:
`a|,b` yielded `{ url: ',', fallback: '|' }` before and
`{ url: '', fallback: ',' }` after. The comma fallback only continues on
404/410, so a failing proxy ahead of an empty segment silently stopped the
lookup instead of falling through to the remaining proxies.
Empty segments carry no url to query, so drop them.
* refactor(datasource/go): use isTruthy for empty segment filter
* fix(datasource/go): filter empty segments before mapping
Filtering after the map broke the contextual typing from the
GoproxyItem[] annotation, widening fallback to string.
* refactor(platform/github): use regEx() for merge queue query stripping
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
* feat(lint): add renovate/prefer-is-helpers and renovate/prefer-is-object rules
Flag ad-hoc type checks that have @sindresorhus/is equivalents:
- .filter(Boolean) -> .filter(isTruthy)
- typeof x === 'string' -> isString(x)
- x === null || x === undefined -> isNullOrUndefined(x) (and negation)
The typeof x === 'object' shape lives in a separate suggestion-only rule
(renovate/prefer-is-object, kept at warn) because isObject()/isPlainObject()
are not drop-in equivalents: null, arrays and functions behave differently.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* refactor: fix renovate/prefer-is-helpers violations
Replace ad-hoc type checks with @sindresorhus/is helpers across lib/:
- 13x .filter(Boolean) -> .filter(isTruthy)
- 36x typeof x ==/!== 'string' -> isString(x) / !isString(x)
- 6x x === null || x === undefined -> isNullOrUndefined(x) (and negation)
Promote renovate/prefer-is-helpers to error now that the codebase is clean.
renovate/prefer-is-object stays at warn: its 15 remaining sites guard
parsed YAML/JSON or unknown values where isObject()/isPlainObject() are
not provably equivalent (null/array/function semantics).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* refactor: enforce renovate/prefer-is-object at error level
Resolve the 15 remaining warnings and raise the rule from warn to error:
- Migrate 10 sites to isObject() where the guarded value provably cannot
be a function (parsed YAML/JSON, typed exec command arguments, or
test-controlled fixtures): helm-values, woodpecker, bun, pre-commit
managers, exec utils, file package cache, and the github http spec.
In the bun manager this also fixes a latent crash where a null
`workspaces` value would throw on the `in` operator.
- Keep 5 sites as reasoned oxlint-disable comments where excluding
functions is load-bearing: the byte-exact fingerprint canonicalizer
(2), the circularity walker, the zod result guard, and the generic
hasKey helper.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
To prevent potential SSRF attempts, we can ignore a
misconfigured/malicious `next` link, and log a warning.
Co-authored-by: Claude Opus 4.8 <jamie.tanna+claude-code@mend.io>
To prevent potential SSRF attempts, we can ignore a
misconfigured/malicious `next` link, and log a warning.
Co-authored-by: Claude Opus 4.8 <jamie.tanna+claude-code@mend.io>
As noted in #44756, this would allow templates across Renovate's
different usages to be able to use the `renovateVersion` string as a
template variable.
Co-authored-by: Claude Sonnet 5 <jamie.tanna+claude-code@mend.io>
fix(platform/gitlab): retry MR creation on transient source_branch race
GitLab occasionally rejects a merge request POST with
`400 {"message":{"source_branch":["does not exist"]}}` immediately after
a branch push succeeds, because the freshly pushed ref is not yet visible
to the API due to Gitaly eventual consistency. The request was not
retried, leaving an orphaned branch until the next run.
Extend the existing `calculateRetryDelay` hook (which already retries
transient `409 Resource lock` POST errors) to also retry this
`400 source_branch does not exist` case, reusing got's bounded
exponential backoff.
* feat(lint): add renovate/prefer-fs-util and renovate/prefer-luxon rules
Two shared-util substitution rules, both at warn level:
- renovate/prefer-fs-util flags value imports of fs, node:fs,
fs/promises, node:fs/promises, fs-extra and fs-extra/esm in lib/
source files outside lib/util/fs/, pointing to the scoped helpers
from lib/util/fs. Spec files and type-only imports are exempt.
A config-only change via no-restricted-imports was considered but
rejected: that rule is at error severity globally and per-path warn
severity is not expressible, so the 13 existing call sites would
break the baseline.
- renovate/prefer-luxon flags new Date(...) and Date.now() in lib/
source files, pointing to Luxon per best-practices. Date/time
utility modules (lib/util/date, lib/util/pretty-time,
lib/util/timestamp) and spec files (vi.setSystemTime etc.) are
exempt.
Baseline: 13 prefer-fs-util and 45 prefer-luxon warnings, zero errors.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* refactor: use Luxon instead of new Date() at remaining call sites
Fixes all renovate/prefer-luxon violations for `new Date(...)`:
- `new Date().toISOString()` -> `DateTime.utc().toISO()` (byte-identical
output) in the http cache provider, schedule logging and PR cache.
- `new Date()` -> `DateTime.now().toJSDate()` for the git commit-date
fallback and the rubygems `syncedAt` markers (same `Date` values).
- rust-release-channel now computes the Rust 1.0 release date and nightly
version dates via `DateTime.utc(...)`; previously `new Date(y, m, d)`
used the local timezone while the epoch used UTC, so the comparison was
timezone-dependent at the boundary date. Tests run with TZ=UTC, where
behavior is unchanged.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* refactor: use lib/util/fs helpers in repository worker
Fixes renovate/prefer-fs-util violations outside bootstrap code:
- `fs.ensureDir()` -> `ensureDir()` from lib/util/fs (same fs-extra call
underneath; it no-ops on an empty dir name, so the repository worker
spec now simulates the init failure via a rejected `initRepo` instead
of relying on `fs.ensureDir('')` throwing).
- `fs.remove(privateCacheDir())` -> `cachePathExists()` + `rmCache()`,
keeping the remove-if-present semantics while asserting cacheDir
containment.
Two call sites keep raw imports with reasoned disable comments:
- lib/util/modules.ts synchronously enumerates Renovate's own installed
module directories at import time.
- lib/util/cache/package/impl/sqlite.ts receives cacheDir as an explicit
argument during global initialization, before GlobalConfig (which the
scoped cache helpers resolve against) contains cacheDir.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* feat(lint): promote prefer-fs-util and prefer-luxon to error
Both rules are narrowed to what is actually accepted practice before
being promoted:
prefer-luxon no longer flags `Date.now()`: it returns UTC-based epoch
milliseconds used for elapsed-time measurement and TTL arithmetic, which
is timezone-independent by construction. The mined reviewer feedback
behind this rule ("use Luxon for date arithmetic outside util") targets
timezone-dependent date construction/formatting, which cannot occur with
a raw millis number; the rule keeps flagging `new Date(...)`.
prefer-fs-util exempts code that legitimately needs raw fs access
because the scoped lib/util/fs helpers resolve against GlobalConfig
localDir/cacheDir, which does not exist yet or does not apply there:
- lib/logger/: log-file streams at user-configured system paths during
bootstrap; lib/util/fs also depends on the logger (import cycle).
- lib/workers/global/: creates baseDir/cacheDir themselves and
reads/writes user-supplied paths before GlobalConfig is seeded.
- lib/config-validator.ts: standalone CLI without GlobalConfig.
- lib/util/git/: owns the localDir lifecycle (clone, emptyDir, symlinks)
and private-key files with sync cleanup on process exit.
Also fixes a pre-existing tsc error in prefer-fs-util.js: `importKind`
is a TypeScript-ESTree extension missing from eslint's base AST types.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* refactor(fs): make rmCache ignore missing paths
All rmCache callers use it for cleanup, where a missing path is a
no-op, so bake force: true in instead of guarding call sites with
an existence check.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* refactor(linters): exempt lib/instrumentation from prefer-fs-util
`lib/instrumentation/file-exporter.ts` landed on main while this branch
was open and imports `node:fs/promises` directly.
The exporter writes to a user-configured system path
(`RENOVATE_TRACING_FILE_EXPORTER_PATH`) outside `localDir`/`cacheDir`,
so the scoped helpers do not apply. `lib/instrumentation` is also
deliberately kept out of the module graph of instrumented libraries
(see the `⚠️` note in `lib/renovate.ts`), so it must not pull in
`lib/util/fs` and its config/logger dependencies. Exempt the directory,
as is already done for `lib/logger`.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* docs: shorten doc
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
* ci(linter): add renovate/v8-ignore-reason rule
Flag coverage-ignore comments ("v8 ignore") that carry no justification.
Accepted forms: "-- reason" / "--- reason" (recommended) and the legacy
colon form "v8 ignore <kind>[ <count>]: reason", which is already
widespread in the codebase (29 sites). Bare "v8 ignore stop" markers are
exempt because the justification lives on the paired "start" marker. The
"v8 ignore next <N>" count form remains allowed; this rule only requires
a justification.
Registered as "warn" instead of "error": the repo currently has 119
reason-less v8 ignore comments, too many to fix mechanically in one PR,
so existing sites are left untouched and can be fixed incrementally.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* ci(linter): promote renovate/v8-ignore-reason to error
Record the empirical verdict on the contested "v8 ignore next <N>" count
semantics in the rule doc: verified against vitest 4 with
ast-v8-to-istanbul that the count is not honored - the ignore hint always
exempts exactly the next AST node regardless of N. Counts are therefore
inert and stay allowed; the rule keeps requiring only a justification.
Promoting to error so all remaining reason-less coverage-ignore comments
must be fixed; the burndown follows in subsequent commits.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017RxHFNsSworLcv23xXk9Hk
* refactor: add justifications to coverage-ignore comments (batch 1)
Give every reason-less "v8 ignore" comment outside the large platform
index files a site-specific justification explaining why the exempted
branch is untestable or defensive, as now required by the
renovate/v8-ignore-reason rule.
Also guard against a nullish comment.loc in the rule itself; the
merged-in stricter tools typechecking flagged the unguarded access.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017RxHFNsSworLcv23xXk9Hk
* refactor: add justifications to coverage-ignore comments (batch 2)
Cover the five large platform index files (github, bitbucket,
bitbucket-server, codecommit, gitlab) with site-specific reasons on
every "v8 ignore" comment, completing the renovate/v8-ignore-reason
burndown. Two meaningless ignore hints inside commented-out CodeCommit
mergePr code are dropped instead of annotated.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017RxHFNsSworLcv23xXk9Hk
* Apply suggestions from code review
Co-authored-by: Michael Kriese <michael.kriese@gmx.de>
* Update lib/util/regex.ts
Co-authored-by: Michael Kriese <michael.kriese@visualon.de>
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Michael Kriese <michael.kriese@gmx.de>
Co-authored-by: Michael Kriese <michael.kriese@visualon.de>
* feat(platform/azure): support bearer token auth (auto-detect token type)
* refactor(platform/azure): use isPlainObject for JWT header check
Replace manual typeof/null check with isPlainObject() from
@sindresorhus/is in isProbablyJwt(). Remove unused _GlobalConfig
import from test file.
* refactor(platform/azure): use parseJsonc to parse JWT header
* test(platform/azure): use a real JWT in the set bearer token/endpoint test
* feat(platform/azure): log detected token type
* refactor(platform/azure): guard token before JWT check; remove outdated todo comment
* docs(platform/azure): restore TODO note about possibly-undefined token (#22198)
* refactor(platform/azure): move isProbablyJwt() and tests to new files lib/util/http/jwt.ts and lib/util/http/jwt.spec.ts
* refactor(platform/azure): use Json.safeParse() instead of parseJsonc() in isProbablyJwt()
* refactor(platform/azure): accept undefined token in isProbablyJwt and add guard to drop non-null assertions
* test(platform/azure): extract shared buildTestJwt helper and dedupe
* docs(platform/azure): reword the Entra ID token instructions
* chore(platform/azure): remove irrelevant lines from docstring
* refactor(platform/azure): export buildTestJwt() from new test/jwt-util.ts
* docs(platform/azure): use 1 for numbered lists
* refactor(test): extract arrow function from buildTestJwt
---------
Co-authored-by: Bastian Wenske <bastianwenske@outlook.de>
* refactor(oxlint): enable func-style
Enable the built-in func-style rule as ["warn", "declaration"] so that
named functions are written as function declarations instead of arrow or
function expressions assigned to variables, matching the convention in
docs/development/best-practices.md (Functions section). Inline arrow
callbacks are unaffected; prefer-arrow-callback continues to cover the
callback direction.
Severity is "warn" instead of "error" as a fallback: a repo-wide run
found 196 pre-existing violations (70 in spec files, 126 in production
code), exceeding the 80-violation fix threshold for this change, so no
violations were fixed here. Follow-up work can convert the offenders
(largest clusters: lib/modules/versioning/ruby, lib/util/cache/package,
lib/modules/versioning/maven, lib/modules/versioning/gradle) and then
raise the rule to "error". Note oxlint 1.72 also supports an
allowTypeAnnotation option if typed consts should remain arrows.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* ci(linter): enforce func-style at error
* refactor(func-style): convert const-arrow functions to declarations in with-cache spec and ruby versioning
* refactor(func-style): convert const-arrow functions to declarations in maven/gradle modules
* refactor(func-style): convert const-arrow functions to declarations in remaining versioning modules
* refactor(func-style): convert const-arrow functions to declarations in platform modules
Discriminated-union closures (ensureCommentRemoval byTopic/byContent
helpers in github, gitlab, bitbucket, bitbucket-server) needed the
narrowed field passed explicitly as a parameter, since function
declarations are hoisted and lose the outer if-narrowing that inline
arrow-function closures previously relied on.
* refactor(func-style): convert const-arrow functions to declarations across util/manager/datasource modules
One case (logger/renovate-logger.ts logOnceFn) is kept as an arrow
function with a documented oxlint-disable: it is stored on
this.logger.once[level] and relies on lexically-bound this, which a
hoisted function declaration would not preserve.
* refactor(func-style): convert remaining const-arrow test helpers to declarations
get-updated.spec.ts: hoisted expectPipCompilePackageAndLockFile to
module scope (it only closes over the module-level pipCompile mock)
and added a documented oxlint-disable for vitest/expect-expect on its
caller test, since oxlint's expect-expect check does not trace
assertions through an externally-defined helper function.
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
* ci(linter): add renovate/logger-static-message rule
Adds a custom oxlint rule that enforces a static msg component for
logger.warn/error/fatal calls (including logger.once variants) so the
messages can be grouped in metrics and error catching services, per
docs/development/best-practices.md. It also flags metadata properties
named 'error' that hold error-ish values, because the bunyan serializer
only handles the 'err' key.
The rule is enabled as an error for lib/ production code and disabled
for spec files. All 33 pre-existing violations are fixed by moving
interpolated values into the metadata object, making messages static,
and renaming 'error' metadata keys to 'err'. Affected log-message
assertions in co-located specs are updated accordingly.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* ci: ignore validator output
* fixup new merged in log messages
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Enable the type-aware typescript/no-unnecessary-type-arguments rule as
error and remove all 44 pre-existing redundant type arguments (type
arguments identical to the type parameter default), e.g.
HttpResponse<string>, ZodError<unknown>,
PackageDependency<Record<string, any>>.
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
refactor(oxlint): enable vitest/consistent-test-it
Enforce `it` over `test` in spec files via
vitest/consistent-test-it (fn: "it"), matching repo convention.
Renamed all 52 pre-existing `test(`/`test.each` usages across
13 spec files to `it(`/`it.each` using oxlint --fix.
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Enable no-else-return (with allowElseIf: false) and unicorn/no-lonely-if
as errors in .oxlintrc.json and fix all 131 pre-existing violations
(91 no-else-return, 40 unicorn/no-lonely-if) across 98 files.
The no-else-return violations were fixed with the oxlint autofix
(iterated to a fixpoint, plus two else-if chains that required
--fix-dangerously) followed by Prettier formatting. One comment dropped
by the autofix in lib/modules/manager/pub/artifacts.ts was restored
manually. The unicorn/no-lonely-if violations were merged by hand,
preserving comments and coverage-ignore hints.
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
ci(linter): add renovate/no-redundant-mock-reset rule
Vitest sets mockReset: true globally in vitest.config.mts, which resets
all mocks before each test. This makes vi.resetAllMocks() /
vi.clearAllMocks() and per-mock .mockReset() / .mockClear() calls inside
beforeEach/afterEach hooks redundant. Add a custom oxlint rule (enabled
for **/*.spec.ts and test/**) flagging such calls, and remove the 40
redundant calls it found. restoreAllMocks/mockRestore are not flagged
because restoreMocks is not enabled globally.
Two sites keep an inline disable with a reason: nested beforeEach hooks
in filter-checks.spec.ts and onboarding/pr/index.spec.ts intentionally
call mockReset() to discard once-values queued by an outer beforeEach.
Requested by maintainer review feedback (review comment 3052324877).
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
* fix(datasource/npm): require public Cache-Control to cache responses
The npm datasource opts out of the authorization-header privacy check
(checkAuthorizationHeader: false) and relies on Cache-Control alone to
classify responses. Previously, when a registry omitted Cache-Control
entirely, cacheAllowed fell through to "cache it" — so private packages
served by GitHub Packages (which sends no Cache-Control header) were
cached in the shared package cache.
Flip the semantic of checkCacheControlHeader: true to require an
affirmative Cache-Control: public signal. Missing or non-public headers
now disallow caching. registry.npmjs.org is unaffected because it
already sends Cache-Control: public, max-age=...
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(cache): avoid renewing disallowed package cache on 304
* Revert "fix(cache): avoid renewing disallowed package cache on 304"
This reverts commit 64801bb036269352465aac580b9861c5b7716cc1.
- Convert `param: T | undefined = undefined` to optional `param?: T`.
- Drop dead `= false` defaults on the required cache-provider options.
- Make `GraphqlPaginatedContent.nodes/edges` optional so the `= []`
fallbacks (load-bearing for partial GraphQL connections) are valid.
- Type the unchecked `default_branch` response field as optional so
the `'HEAD'` fallback is retained.
- Drop `tag = undefined` from the Forgejo/Gitea/GitLab preset configs.
Part of #43845
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>