The `mutation-pr`/`mutation-full` CI jobs were failing outright, and a
full `stryker run` never got past the initial dry run, so none of this
was actually usable yet.
- `mutation.yml`: pin all actions to a commit SHA, add a `permissions`
block, set `persist-credentials: false`, and pass the base ref/changed
files through `env` instead of interpolating them into the shell
script directly (zizmor was flagging all of these). Also switch to the
project's own `./.github/actions/setup-node` composite action instead
of `pnpm/action-setup`/`actions/setup-node` (which additionally
referenced a non-existent `.node-version` file), and fix the `pnpm
mutation`/`pnpm stryker run --mutate` calls to use the actual
`test:mutation`/`test:mutation:changed` script names.
- Drop the now-unused `@stryker-mutator/typescript-checker` dependency,
left over from when `stryker.config.json` still had `checkers:
["typescript"]`.
- `lib/util/timestamp.ts`: the local-timezone-offset fallback used in
`asTimestamp()` was cached in a module-level constant computed once at
import time. Combined with vitest's `threads` pool (used by Stryker's
vitest-runner) not reliably honouring a `process.env.TZ` set from our
own `test/setup.ts`, this made `asTimestamp()` parse plain dates (e.g.
Jenkins' `buildDate: 'Jan 02, 2020'`) using the host's real timezone
instead of UTC. Compute the offset per call instead, and set `TZ=UTC`
in the actual environment via the `test:mutation*` scripts, since that's
the one thing that reliably works across pools.
- `test/setup.ts`: restore real timers in a top-level `afterAll`, so a
spec that leaves fake timers running (there's at least one) can't leak
a frozen clock into whichever spec runs next in the same worker -
something that's only reachable under low-concurrency/single-threaded
execution.
- Raise `dryRunTimeoutMinutes` and the global vitest `testTimeout`: our
full suite's per-test coverage instrumentation run took ~25 minutes
end-to-end, well past Stryker's 5 minute default, and a couple of
CPU-heavy specs (WASM PGP crypto) timed out under that contention too.
- Ignore `.stryker-tmp/` (Stryker's sandbox dir, which can survive an
interrupted run) in ls-lint/oxlint/biome/prettier/vitest, since a
leftover sandbox copy of the repo was otherwise picked up as extra
source/test files and broke those tools outright.
Co-Authored-By: Claude Sonnet 5 <jamie.tanna+claude-code@mend.io>
* 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
Sets each floor to the current measured coverage, rounded down to two
decimals:
statements 99.993944% -> 99.99
branches 98.703754% -> 98.70
functions 100% -> 100
lines 99.993880% -> 99.99
This leaves very little slack by design: one uncovered branch is
0.00386%, so the branches floor tolerates none.
Co-authored-by: Claude Opus 5 <michael.kriese+claude-code@mend.io>
lib/config/decrypt/legacy.ts was removed in #39111, so
getCoverageIgnorePatterns() only ever excluded a path that no longer
exists, and the TEST_LEGACY_DECRYPTION workflow env var that gated it
was read by nothing else.
Coverage is unaffected: a full run reports the same numbers before and
after, so the thresholds stay as they are.
Co-authored-by: Claude Opus 5 <michael.kriese+claude-code@mend.io>
Two `if` statements had their implicit else path untested:
- deleteLocalFile: the no-localDir case never ran
- getMergeConfidenceLevel: the API returning an unrecognised confidence
value never ran, so the 'neutral' fallback was untested
Co-authored-by: Claude Opus 5 <michael.kriese+claude-code@mend.io>
pnpm v11.23 reshaped the `registries` setting in `pnpm-workspace.yaml`: it
is now keyed by the registry URL, with the scopes routed to it listed in the
entry. Renovate only accepted the older `<scope>: <url>` shape, so parsing of
the whole workspace file failed and its catalogs and overrides were no longer
extracted at all.
Accept both shapes and flatten the new one into the old one, where the bare
`@` scope is the scope-less default registry. URL keys which interpolate an
env var, embed credentials, or are unparseable are skipped, matching what
pnpm itself ignores or refuses. A `registries` value which is neither shape
now falls back to `undefined` instead of taking the file's catalogs with it.
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
ci: tighten statements and functions thresholds
Both metrics have headroom against the current measured coverage
(statements 99.9899%, functions 99.9864%), so the floors can move up
without depending on any other pull request landing first.
Co-authored-by: Claude Opus 5 <michael.kriese+claude-code@mend.io>
test(util): cover minimatchFilter and cachePathIsFile gaps
Brings lib/util to 100% coverage:
- minimatchFilter: the closure returned on the uncached path was never
invoked, and the options-based cache key branch was never taken
- cachePathIsFile: only the "does not exist" path was tested, so
stat().isFile() never ran
Co-authored-by: Claude Opus 5 <michael.kriese+claude-code@mend.io>
* build(deps): update dependency @redis/client to v6
| datasource | package | from | to |
| ---------- | ------------- | ------ | ----- |
| npm | @redis/client | 5.12.1 | 6.2.1 |
* fix(cache): keep RESP 2 protocol with @redis/client v6
@redis/client v6 defaults to RESP 3, which requires Redis server 6.0 or
newer and changed the default type parameters, breaking the RedisClient
type alias. Pin RESP 2 explicitly and type the client accordingly. The
switch to RESP 3 is deferred to the next major release.
---------
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: secustor <sebastian@poxhofer.at>
* feat(presets): vale package versions custom manager
Co-Authored-By: Claude Code
* fix(presets): fix lint and type errors in valeVersions tests
Co-Authored-By: Claude Code
* fix(presets): fix vale.ini file pattern to use regex delimiter syntax
Co-Authored-By: Claude Code
* test(presets): update valeVersions examples and add non-pinned case
Co-Authored-By: Claude Code
* 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
* feat(gitlab): merge MRs through merge trains via the platform merge queue API
* feat(gitlab): report MRs waiting on a merge train through isPrInMergeQueue
* test(gitlab): drop targetBranch from mergePr calls
* Apply suggestion from @secustor
* refactor(gitlab): validate the merge train car response with a Zod schema
* 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
* fix(merge-confidence): don't require auth in `platform=local`
When dry-running Renovate against a repository to validate it[0], if a
repository is using `matchConfidence`, the whole Renovate run is aborted
due to missing API credentials.
This can be frustrating, and the workaround available is to temporarily
remove all package rules use `matchConfidence`, which isn't the best
experience.
Instead, we should follow the existing pattern to return `null` when
loading a Package Rule with `matchConfidence`, as well as DEBUG logging
to inform the user why this is happening.
[0]: https://www.jvt.me/posts/2026/03/08/renovate-test-config/
* fixup! fix(merge-confidence): don't validate auth in a
With debugging from Claude to work out how to get the test to pass.
Co-authored-by: Claude Sonnet 5 <jamie.tanna+claude-code@mend.io>
---------
Co-authored-by: Claude Sonnet 5 <jamie.tanna+claude-code@mend.io>
* feat(manager): share OCI Helm chart resolution across managers
Add getOciChartDep() to helmv3/oci.ts and use it in argocd, fleet, flux,
helmfile, helmsman, helmv3, kustomize, sveltos, terraform and vendir.
- set pinDigests: false for OCI charts in all of them
- apply registryAliases to OCI charts in argocd, helmfile and helmsman
- keep the registry port in terraform helm_release repositories
- skip fleet OCI charts without a version instead of using 'undefined'
* docs(manager/helmv3): correct the OCI chart pinDigests rationale
* refactor(manager/helmfile): drop redundant OCI repository url guard
The repository schema already requires url to be a string, so the falsy
branch could not be reached by parsed input and left the patch partially
covered.