472 Commits

Author SHA1 Message Date
Michael Kriese 320f770c36 fix(cache): hash URL credentials in cache keys
Co-Authored-By: Claude Fable 5.1 <michael.kriese+claude-code@mend.io>
Co-Authored-By: Claude Opus 5 <michael.kriese+claude-code@mend.io>
Co-Authored-By: Claude Opus 5.5 <michael.kriese+claude-code@mend.io>
2026-10-20 09:05:00 +01:00
Sebastian Poxhofer 0070fc5e6a refactor: remove unused exports and types (#47130)
Found with knip.
2026-10-17 09:10:12 +00:00
Michael Kriese 969b3cb162 fix(util/git): use non-empty passwords for token credentials on more platforms (#47085)
* fix(util/git): use non-empty passwords for token credentials on more platforms

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>

* docs: update git token host rule notes

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>

---------

Co-authored-by: Claude Sonnet 5.5 <michael.kriese+claude-code@mend.io>
2026-10-15 13:42:10 +00:00
Michael Kriese a9677c36f4 fix(util/git): use a non-empty password for token-only git credentials (#47077)
* fix(util/git): use a non-empty password for token-only git credentials

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(util/git): pick token credentials from the host platform for manager host rules

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>

---------

Co-authored-by: Claude Sonnet 5.5 <michael.kriese+claude-code@mend.io>
2026-10-15 12:06:10 +00:00
renovate[bot] d1fd5df4bc build(deps): update dependency simple-git to v4 [security] (main) (#46781)
* build(deps): update dependency simple-git to v4 [security]

| datasource | package    | from   | to    |
| ---------- | ---------- | ------ | ----- |
| npm        | simple-git | 3.36.0 | 4.0.1 |

* fix(git): adapt to simple-git v4 environment guard

Allow the environment Renovate passes explicitly, use a dedicated instance for
authenticated submodule updates and allow url.insteadOf rewrites used by host
rules authentication.

* fix(deps): override @simple-git/argv-parser to v2.0.1

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: secustor <sebastian@poxhofer.at>
2026-10-08 07:51:45 +00:00
Jamie Tanna a2704ed3f1 test: always clear hostRules after each test (#46582)
Instead of needing to always make sure that we've cleared host rules in
tests that use them - or knowing when they're touched indirectly - we
can make sure that we have an `afterEach` that will clear our Host
Rules.

We can do this by making sure that we have a top-level `afterEach` that
handles this, which needs a dynamic import to avoid eagerly importing
and caching it, which would leave stale unmocked bindings for a given
file's test run.

We also need to make sure that `private-key.spec.ts`'s `sanitize.ts`
mock is updated to also stub `clearRepoSanitizedSecretsList`, since
`hostRules.clear()` now calls.

Co-authored-by: Claude Sonnet 5 <jamie.tanna+claude-code@mend.io>
2026-09-30 11:22:18 +00:00
Jamie Tanna 3b70d5ffe2 test(utill/git): add 30s timeout to beforeAll (#46386)
So we don't have tests exiting early when on i.e. a slower GitHub
Actions machine.
2026-09-22 15:03:15 +00:00
Michael Kriese 654431d69f test(util/git): share one clone across the read-only tests (#46287)
* test(util/git): skip the per-test clone for tests that need no repo

The spec's `beforeEach` rebuilds a bare clone and calls `initRepo()`
before every test, which costs about 0.7s each. Eight tests never touch
a repository at all -- `createSimpleGit`, `gitRetry`, `validateGitVersion`
and `getUrl` -- yet each paid for that clone. They now sit in a second
root describe with no clone in it, so the file drops from about 129s to
about 121s with the same 144 tests.

The two blocks that are pure functions are marked `describe.concurrent`.
The other two stay sequential: `createSimpleGit` spies on a prototype and
sets the custom env, and `gitRetry` stubs `NODE_ENV`.

This was the outcome of trying `it.concurrent` across the file. It cannot
help there, for two reasons stacked on each other. Vitest still runs
`beforeEach` for every concurrent test, so concurrency does not remove
the clone, it only interleaves several of them. And those interleaved
clones all write the same module singleton (`config`, `git`,
`gitInitialized`) and `GlobalConfig.localDir`, so one test's `initRepo()`
is overwritten by another's before its body runs. The only lever that
works is not running the clone for tests that do not need it.

Co-Authored-By: Claude Fable 5.1 <michael.kriese+claude-code@mend.io>

* test(util/git): share one clone across the read-only tests

The spec now has one root describe with three nested groups, split by how
much git setup a test needs:

- `without a repository`: pure functions, no setup at all.
- `shared clone`: 32 read-only tests. One bare clone of `base` and one
  `initRepo()` in `beforeAll`, so each test costs only the git commands
  it runs itself.
- `clone per test`: the tests that commit, push, check out or re-init.
  They keep the fresh clone and `initRepo()` per test.

The `base` repository moves up to the root describe so both clone groups
can use it. Groups run in file order, and some of the writing tests change
`base` without restoring it, so the shared-clone group has to stay ahead.

`isBranchModified()` stays with the writing tests although it never touches
the repository: it caches per-branch results in `config.branchIsModified`,
which only `initRepo()` clears, and its tests query the same branch with
different ignored authors.

Three describes are split by test: `getFileList`, `getBranchUpdateDate`
and `diffCommitTree` each have one test that writes, which stays behind
under the same describe name with a pointer to the read-only half.

Same 144 tests; the file drops from about 121s to about 100s.

Co-Authored-By: Claude Fable 5.1 <michael.kriese+claude-code@mend.io>

* test(util/git): keep the 30s test timeout for every test

The `{ timeout: 30000 }` option used to sit on the single root describe
and so covered every test in the file. The split left it on the
`clone per test` group only, which put the 32 shared-clone tests on the
5s default even though they still run real git commands. CI is a lot
slower than a local machine, so the option moves up to the root describe
again; nested describes inherit it.

Hooks are unaffected either way: they run under `hookTimeout`, not the
suite option, and the shared `beforeAll` does the same work the old
`beforeEach` did.

Co-Authored-By: Claude Fable 5.1 <michael.kriese+claude-code@mend.io>

* test(util/git): reuse one clone for the local-only writers

A fourth nested group, `reused clone`, holds the 36 tests that write to
the local checkout but never to `origin` or `base`: `checkoutBranch`,
`isBranchModified`, `mergeToLocal`, `isFileModeEnabled`,
`getCommitMessages`, `setGitAuthor`, `getRepoStatus`, `fetchRevSpec` and
single tests of `getBranchUpdateDate`, `mergeBranch`, `deleteBranch` and
`diffCommitTree`.

The group clones once, but `initRepo()` and `syncGit()` run again before
every test. On an existing checkout `syncGit()` takes its fetch path:
`fetch --prune`, `reset --hard`, `checkout` of the default branch,
`clean -fd` and deletion of every other local branch. With the fresh
`config` from `initRepo()` that undoes everything such a test can do to
the repository or the module singleton, including the per-branch
`branchIsModified` cache that kept `isBranchModified()` out of the
shared-clone group. Local git config is the one thing it leaves alone, so
the `beforeEach` re-sets the author that `setGitAuthor()` writes.

That reset costs about 0.35s per test against about 0.87s for a clone, so
the file drops from about 100s to about 87s. Same 144 tests.

Co-Authored-By: Claude Fable 5.1 <michael.kriese+claude-code@mend.io>

* test(util/git): trim the describe comments to the essentials

Review feedback: the block comments read more like a narration of how
the layout came to be than like documentation. Each now states only
what a group's setup is, what a test may or may not do to belong there,
and the ordering constraint about `base`. The explanation of the
`isBranchModified()` cache is gone too, since that test now runs with a
fresh `initRepo()` in the reused-clone group.

Comments only, no test changes.

Co-Authored-By: Claude Fable 5.1 <michael.kriese+claude-code@mend.io>

* test(util/git): move isBranchModified() into the shared-clone group

`isBranchModified()` only reads the repository, but it stores results in
`config.branchIsModified`, which only `initRepo()` cleared. That kept its
16 tests in the reused-clone group at about 0.35s each.

A new `clearBranchIsModifiedCache()` helper, marked `@internal`, clears
that store. The shared-clone `beforeEach` calls it along with the cheap
config setters (`setUserRepoConfig`, `setGitAuthor`,
`setPlatformIgnoredAuthors`), so those tests now share one `initRepo()`
and cost a few milliseconds each. The file drops from about 87s to about
83s.

A dynamic import with `vi.resetModules()` was considered instead: a fresh
module instance has no `config` and `gitInitialized = false`, so every
test would need its own `initRepo()` and `syncGit()` on it, which is the
reused-clone cost again plus re-importing the module graph.

Co-Authored-By: Claude Fable 5.1 <michael.kriese+claude-code@mend.io>

---------

Co-authored-by: Claude Fable 5.1 <michael.kriese+claude-code@mend.io>
2026-09-18 10:28:42 +00:00
Sebastian Poxhofer 9defb36404 refactor(manager): share repo status to file change conversion (#46090)
Add `collectFileChanges()` next to the other repository helpers, plus a
thin `fileChangesToArtifactResults()` in `lib/modules/manager/util.ts`,
and migrate the managers which re-implemented the same status loops:
apm, cocoapods, composer, copier, gomod, helmv3, jsonnet-bundler,
kustomize and vendir.

Each site keeps its own status bucket selection and path filter, so the
emitted artifacts are unchanged. Two dead pieces of logic are dropped:
vendir's `vendorDir || isFileInVendorDir` condition, which was always
true, and kustomize's optional chaining on a non-nullable status, which
only ever mattered for an unset test mock (now set explicitly).
2026-09-16 13:13:40 +00:00
Thomas Andersen 86890d2142 refactor(git): extract runtime config appending (#45165)
* refactor(git): centralize runtime config environment

Preserve caller-supplied and inherited GIT_CONFIG_KEY_n and GIT_CONFIG_VALUE_n entries when appending runtime Git configuration.

Co-authored-by: Jamie Tanna <jamie.tanna@mend.io>

* refactor(git): extract runtime config appending

* style(git): format runtime config tests

* test(git): remove superseded environment cases

* Apply suggestion from `@RahulGautamSingh`

Co-authored-by: RahulGautamSingh <rahultesnik@gmail.com>

* test(git): align invalid config count warning

---------

Co-authored-by: MrVampy <4302946+MrVampy@users.noreply.github.com>
Co-authored-by: Jamie Tanna <jamie.tanna@mend.io>
Co-authored-by: Michael Kriese <michael.kriese@gmx.de>
Co-authored-by: RahulGautamSingh <rahultesnik@gmail.com>
2026-09-14 06:42:38 +00:00
Sebastian Poxhofer 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.
2026-09-04 10:40:46 +00:00
Sebastian Poxhofer 3412de84ce refactor(linters): enable oxlint rule prefer-named-capture-group (#44714)
* 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>
2026-09-03 09:54:40 +00:00
Sebastian Poxhofer 6e03e8326f refactor(linters): add rule to enforce inline single use fixtures (#45230)
* 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>

* feat(lint): add renovate/inline-single-use-fixtures rule

In spec files, a module-scope const fixture that is referenced from
exactly one it()/test() block should be inlined into that test so the
test is self-contained. The rule is a warn-level nudge (naming for
clarity is a valid exception) with no autofix (moving code is risky).

Uses oxlint jsPlugins scope analysis (getDeclaredVariables) for exact,
shadowing-safe reference counting and bails out conservatively: only
top-level non-exported single-declarator consts with multiline,
non-function initializers whose every reference sits inside the callback
function of one and the same test call are flagged.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* refactor: inline single-use spec fixtures into their tests

Fix 26 renovate/inline-single-use-fixtures warnings in specs where the
move is mechanical and value-preserving: codeBlock fixtures were
re-indented uniformly (codeBlock strips the common indentation, so the
produced string is unchanged) and Fixtures.get() calls resolve relative
to the spec file, so relocating them inside the test body has no effect.

The remaining 59 warnings are left for per-site judgment, as the rule is
a warn-level nudge by design.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* refactor: inline remaining relocation-safe single-use spec fixtures

Move the 24 remaining single-use module-scope fixtures whose initializer
is a codeBlock tagged template or a Fixtures accessor call into the one
it() block that uses them. Both shapes are value-preserving to relocate:
codeBlock strips the common leading indentation, so the uniformly
re-indented templates produce identical strings, and Fixtures.get()
resolves relative to the spec file, not the call site's position in it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* feat(lint): enforce inline-single-use-fixtures as error

Narrow the rule to the initializer shapes where moving the declaration
into the test body is provably value-preserving and unambiguously an
improvement: codeBlock/stripIndent tagged templates (indentation-
normalizing, so re-indenting is safe) and Fixtures accessor calls
(resolve relative to the spec file). Plain object/array literals,
untagged template literals (whose leading-newline formatting would need
ugly re-indentation) and arbitrary call expressions remain per-site
judgment calls and are no longer flagged.

With the rule reduced to true defects and all remaining violations
inlined, bump it from warn to error in the spec override block.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* refactor: inline contents of fixtures relocated into single-use tests

The previous commits moved single-use module-scope Fixtures.get() consts
into their one it() block, which only relocated the file indirection.
Replace those relocated Fixtures.get() calls with the fixture file's
actual content — codeBlock tagged templates for text fixtures — so the
tests are self-contained, and delete the fixture files that no longer
have any references. Relocated fixtures too large to inline comfortably
(terraform-provider releases, sbt private-variable file, nuget v2 pkg
list, bundled package-lock) keep their Fixtures.get() calls.

Value preservation was verified by unchanged vitest results for every
touched spec; all consumers parse the content, so codeBlock's trailing
newline normalization is behavior-neutral.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* refactor: inline remaining large single-use fixtures

Inline the four Fixtures.get() sites that were previously exempted for
size, so every single-use fixture is now inlined regardless of size:

- nuget v2_withoutProjectUrl.xml -> codeBlock (served via http mock)
- terraform-provider releaseBackendIndexGoogleBeta.json -> plain object
  literal passed to the nock reply
- npm locked-dependency bundled.package-lock.json -> codeBlock JSON
  string (consumed as raw lockFileContent)
- sbt private-variable-dependency-file.scala -> codeBlock

All consumers parse the content, so codeBlock's trailing-newline
normalization is behavior-neutral; vitest results are unchanged for
every touched spec, including the two pre-existing nuget failures.

The nuget, terraform-provider and sbt fixture files have no remaining
references and are deleted. bundled.package-lock.json is kept because
package-lock/get-locked.spec.ts still reads it via Fixtures.getJson().

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* refactor: keep large bundled package-lock fixture in a file

The bundled package-lock.json inlined into the "fails remediation if
bundled" test is ~150 KB (~4,400 lines); inlining it as a codeBlock
hurts readability more than it helps. Read it via Fixtures.get inside
the test block instead — the inline-single-use-fixtures rule permits a
Fixtures.get() call in test scope, and the fixture file is still present.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-09-03 07:42:45 +00:00
Jamie Tanna b62fe59594 fix: remove references to "bot" (#39305)
We dropped the "bot" from our terminology long ago, but it looks like
we've got some outdated docs + code that reference it.

We should remove this to keep things consistent.

A `fix`, as we're changing the commit author's name, and log lines.

Co-authored-by: Claude Sonnet 5 <jamie.tanna+claude-code@mend.io>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-09-02 08:56:20 +00:00
Jamie Tanna ea3b7acede test(util/git): add missing coverage of setGitAuthor (#45607)
As part of follow-up changes, we're removing the "bot" terminology.

While doing so, GitHub Copilot Code Review discovered that there was no
coverage of the defaults in this function.

Before we change this, we can add coverage of the defaults.

Co-authored-by: Claude Sonnet 5 <jamie.tanna+claude-code@mend.io>
2026-09-02 08:56:19 +00:00
Sebastian Poxhofer c32e2391a0 refactor(linters): add prefer-coerce-object rule (#45493)
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.
2026-08-28 09:27:59 +00:00
Sebastian Poxhofer 97327c3d28 refactor(linters): add prefer-coerce-array rule (#45492)
* 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()`.
2026-08-28 09:27:58 +00:00
Sebastian Poxhofer fbedd41e82 test: replace env operation with vi.stubEnv() (#45490)
* 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.
2026-08-28 09:26:20 +00:00
Sebastian Poxhofer a473e73b3e fix(linters): forbid inert counts in v8 coverage-ignore comments (#45287)
Adds a custom oxlint rule renovate/v8-ignore-no-count that flags the
count form '/* v8 ignore next <N> */'. The count is not honored by
vitest's ast-v8-to-istanbul hint parser, which captures only the
keyword (if/else/next/file); 'next' always exempts exactly the next
AST node. Trailing counts therefore suggest a line range that is never
applied and mislead readers.

The rule is autofixable (drops the count, preserving actual behavior)
and has been applied to all existing occurrences.
2026-08-22 11:25:31 +00:00
Jamie Tanna 65be1c5cee test(git): disable Git's maintenance in test repositories (#45310)
We periodically see test flakiness in the `git` functionality, where
we're trying to clean up our temporary directories, and see i.e. [0]:

```
FAIL  lib/util/git/index.spec.ts > util/git/index > ...
Error: ENOTEMPTY: directory not empty, rmdir '/tmp/tmp-2701-SIDrNnhWZmc7/.git/objects'
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[1/4]⎯

 FAIL  lib/util/git/index.spec.ts > util/git/index > ...
Error: ENOTEMPTY: directory not empty, rmdir '/tmp/tmp-2701-8p6HvIMqwS4k/.git/info'
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[2/4]⎯

 FAIL  lib/util/git/index.spec.ts > util/git/index > ...
Error: ENOTEMPTY: directory not empty, rmdir '/tmp/tmp-2701-Q6gACVUhHYu1/.git/objects/pack'
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[3/4]⎯

 FAIL  lib/util/git/index.spec.ts > util/git/index > ...
Error: ENOTEMPTY: directory not empty, rmdir '/tmp/tmp-2701-5VelWdq8M1Iy/.git/objects/pack'
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[4/4]⎯
```

Investigating this with Claude Sonnet, it appears that this /could be/
down to Git's automated garbage collection processes.

The infrequency of the failures makes this less straightforward to
investigate the issue, but it seems like this may be the likely culprit.

We had enabled this on `base` and `origin` repos, but never the `local`
client which runs against `tmpDir`.

We can make sure that this is applied, and this should reduce the
flakiness.

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

[0]: https://github.com/renovatebot/renovate/actions/runs/32008122699/job/95322113612
2026-08-17 09:28:50 +00:00
Sebastian Poxhofer a1971afa78 fix(linters): add linting rule to enforce usage of our regEx utility (#44690)
* 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>
2026-08-14 14:30:46 +00:00
Sebastian Poxhofer 16ef1715e9 refactor(linters): add rules for @sindresorhus/is helpers (#45229)
* 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>
2026-08-13 14:22:03 +00:00
Sebastian Poxhofer e01b50c6ab refactor(linter): oxlint prefer partial typed casts for consts (#45239)
* refactor: flag all typed object literal casts in prefer-partial-in-specs

* refactor: type maven spec httpError helper against got response shape
2026-08-12 08:50:57 +00:00
user_9090 65ee1f18b4 feat: add regex support for gitIgnoredAuthors configuration (#43938)
* feat: add regex support for gitIgnoredAuthors configuration

* feat: add tests for gitIgnoredAuthors regex handling in isBranchModified

* refactor: simplify ignored authors matching in isBranchModified function

* refactor: use for-of loop for ignored author filtering

* fix(git): treat non-regex gitIgnoredAuthors as exact strings

* fix(git): remove duplicate import of matchRegexOrGlobList

* fix(git): reorder import statements for consistency

* feat(git): enhance gitIgnoredAuthors handling with regex validation and logging

* feat(git): enhance gitIgnoredAuthors support for glob patterns and improve regex handling

* fix(git): add support for escaped glob patterns in matchRegexOrGlobList

* feat(git): update gitIgnoredAuthors documentation and improve regex handling for glob patterns

* fix(git): improve escaping of brackets and backslashes in glob pattern handling

* feat(git): normalize GitHub bot email addresses in ignored authors handling

* feat(tests): add custom author with brackets to file list in index.spec.ts

* feat(git): refactor ignored author handling to support regex normalization

* feat(git): enhance ignored authors handling to support regex patterns

* chore: retrigger ci
2026-08-10 21:41:40 +00:00
Sergei Zharinov cca6227dfa fix(git): resolve child env before adding authentication (#45184)
* fix(git): preserve approved runtime config with authentication

* test(git): remove redundant authentication cases

* test(git): update post-upgrade environment assertions

* test(hermit): cover uninstall setup failure

* refactor(git): clarify authentication host types

* refactor(git): coerce exec options

* refactor(git): coerce docker env vars

* refactor(exec): coerce docker env vars
2026-08-10 11:54:18 +00:00
Felipe Santos d32e38ec2b fix(github): include commitTrailers in platform-native commits (#44918)
* fix(github): include commitTrailers in platform-native commits

The GitHub platform-native commit path (platformCommit) recreates the
commit via the REST API using only message, so trailers from
commitTrailers / :gitSignOff were dropped even though prepareCommit
wrote them into the local commit.

* test: use codeBlock for multi-line commit trailer expectations

* Remove unnecessary comment

* refactor(gerrit): format commit message with shared helper

Use formatCommitMessage for Gerrit reserved trailers so the final
message is built the same way as the git and GitHub platform-native
paths, then clear trailers so prepareCommit does not append them twice.

* Revert "refactor(gerrit): format commit message with shared helper"

This reverts commit d230fa3802.

* refactor(git): fold commit trailers into the message before scm

Remove CommitFilesConfig.trailers so platforms only ever see a fully
formatted message. Workers call formatCommitMessage once; prepareCommit
and GitHub platform-native commits pass message through as-is. Gerrit
parses the existing trailer block to merge Renovate-Branch/Change-Id.

* refactor(git): drop formatCommitMessage helper

Trailers are already folded into message at the worker boundary, so the
shared formatter only re-joined strings. Inline that at the few call sites
and keep splitCommitMessage for Gerrit's reserved-trailer merge.

* revert: keep the minimal commitTrailers platform-native fix

Drop the later refactors (fold trailers into message, Gerrit changes,
splitCommitMessage). Keep formatCommitMessage shared by prepareCommit
and GitHub platform-native commits only; Gerrit is unchanged.
2026-07-29 06:16:44 +00:00
Jamie Tanna a44bbfadcf test(util/git): reference default branch (#44877)
Otherwise this sometimes fails when a repository uses
`init.defaultBranch=main`.
2026-07-27 10:45:06 +00:00
Felipe Santos ddbbb687f9 feat: add commitTrailers option (#44651)
* feat(config): add commitTrailers option

Renovate's only way to add git trailers today is `commitBody`, but it's
broken on Gerrit: Renovate appends its own `Renovate-Branch`/`Change-Id`
block after the body, so user trailers end up in a non-final message
block and are not real trailers per git-interpret-trailers rules.

This adds a `commitTrailers: string[]` option (each entry a full
`Key: value` line), standardizes Gerrit's own trailers through the same
mechanism so user trailers and platform trailers merge into one final
block, and verifies the result at runtime with `git interpret-trailers`
(commitlint's trailer-exists rule uses the same approach). The
`:gitSignOff` preset is migrated from `commitBody` to `commitTrailers`
so sign-off works correctly on Gerrit too.

* Simplify a bit

* Simplify docs even more

* Simplify another bit

* Add comment about rawExec

* Drop runtime commit trailer verification

Bypass simple-git only to emit a proactive warn log is not worth the
extra surface area. Config validation already enforces Key: value form.

* test(git): cover string commit message with trailers

Codecov patch failed on the partial ternary branch that normalizes a
string message before appending trailers.

* test(gerrit): fix trailers test after scm rework

Align the commitTrailers merge test with the post-rework Gerrit SCM:
use fakeSha helpers and drop the removed force flag.

* Improve wording on gitSignOff docs

* Validate compiled commitTrailers as well

* Improve trailers example

* Use isString instead

* Update lib/util/git/index.ts

Co-authored-by: Sebastian Poxhofer <secustor@users.noreply.github.com>

* Update lib/workers/repository/onboarding/branch/create.ts

Co-authored-by: Sebastian Poxhofer <secustor@users.noreply.github.com>

* Format code

* Fix type-check

* Fix tests

---------

Co-authored-by: Sebastian Poxhofer <secustor@users.noreply.github.com>
2026-07-27 06:49:09 +00:00
Sebastian Poxhofer 716baf630b fix(linters): add rules for filesystem helpers and Date (#44685)
* 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>
2026-07-25 16:00:44 +00:00
Anand Kumria 3cba608bb1 fix(git): surface GitHub GH013 ruleset rejections as config errors (#44819)
When GitHub returns 'GH013: Repository rule violations found' during
a push, the failure is currently silently swallowed by handleCommitError
whenever the changeset touches a file under .github/workflows/. The
existing swallow was intended for the narrow case of an App token
missing the 'workflows' scope, but it also masks unrelated ruleset
violations such as:

  - GITHUB PUSH PROTECTION 'File path is restricted'
  - Secret scanning push protection
  - Signed commit / branch-naming / tag-deletion rulesets

Self-hosted operators cannot see these failures in aggregated log
reports because Renovate logs at info level, marks the branch as
aborted, and continues. Repositories that are effectively 100%
blocked by a ruleset appear healthy.

Detect the 'GH013' token before the workflow-files silent-swallow and
throw a CONFIG_VALIDATION error - mirroring the treatment already
applied to GH003 (force-push denied) and 'protected branch hook
declined'. This flags the repository as failing, populates
validationError / validationMessage so the failure reason is visible
in the repository summary, and lets any downstream reporting pick it
up as a real failure instead of an info-level abort.

Adds tests to lib/util/git/errors.spec.ts covering:

  1. A GH013 Push Protection error whose changeset is entirely under
     .github/workflows/ is thrown as CONFIG_VALIDATION with the
     expected validationSource / validationError / validationMessage.

  2. A generic non-GH013 workflow-file remote rejection still returns
     null (existing swallow behaviour preserved).
2026-07-24 09:08:04 +00:00
Jamie Tanna f932d52fbd feat(platform): get all branch update dates in a more performant manner (#44582)
* feat(platform): get all branch update dates in a more performant manner

When Renovate is working on a repository with many /potential/ branches,
as well as some actually created branches, it's possible for the
calculation for `commitHourlyLimit` to end up taking significant time.

This can lead to a less performant job generally, but can sometimes lead
to the Renovate job failing to complete, and then timing out when
executed by a job runner.

To handle this, we can instead use use `git for-each-ref` to look up
each Git ref (remotely) and request the commit date, which leads to a
much more performant lookup.

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

* fixup! feat(platform): get all branch update dates in a more performant manner

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

* fixup! Merge remote-tracking branch 'origin/HEAD' into wip/commit-hourly-slow

---------

Co-authored-by: Claude Sonnet 5 <jamie.tanna+claude-code@mend.io>
2026-07-21 13:17:58 +00:00
Sebastian Poxhofer 2dbfd85506 refactor(linters): enable node/no-sync and migrate to async calls (#44693)
* ci(linter): add renovate/no-sync-io rule

Flags calls to a fixed list of Node.js sync I/O APIs (readFileSync,
writeFileSync, mkdirSync, rmSync, readdirSync, statSync, existsSync,
copyFileSync, execSync, spawnSync, execFileSync) in tools/** and
**/*.spec.ts only; production lib code is not targeted.

Set to "warn" instead of "error": the repo has 40 pre-existing
violations, exceeding the agreed fix threshold of 30, so violations
are left in place for follow-up rather than mass-fixed here.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* ci(linter): enforce renovate/no-sync-io at error

Promote the rule from warn to error now that the codebase has been
migrated off synchronous I/O in tools/ and spec files.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* refactor(tools): migrate synchronous fs calls to async equivalents

Replace *Sync fs calls with their async/promise-based counterparts in
tools/ scripts (generate-imports, check runner, utils, cwdchanged hook)
now that renovate/no-sync-io is enforced at error there.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* test: migrate synchronous fs calls in specs to async equivalents

Replace *Sync fs calls (readdirSync, writeFileSync, existsSync, etc.)
with their async/promise-based counterparts across affected spec
files, making the enclosing it()/module-scope initializers async as
needed, to satisfy renovate/no-sync-io now enforced at error.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* ci(linter): replace custom no-sync-io rule with built-in node/no-sync

The oxc built-in covers a superset of the custom fixed-name list.
Migrate the additionally flagged sites: async glob in test-shards,
async execa in prepare-deps, promisified gzip/generateKeyPair in
rpm/hex specs, and rename the misleading execSync mock alias
(it mocks the async rawExec).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 14:28:26 +00:00
Sebastian Poxhofer 91e068f1fd test(linters): add linting rule to enforce v8 ignore reason declaration (#44726)
* 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>
2026-07-20 13:01:39 +00:00
Sebastian Poxhofer 1e03699cd6 test: add linter for as LongCommitSha in test files (#44681)
* test: add linter for `as LongCommitSha` in test files

Adds a custom oxlint rule `renovate/prefer-fake-sha-in-specs` that flags
`as LongCommitSha` casts in spec files and suggests the deterministic
`fakeSha()` helper from `test/util` instead. Enabled at error level for
`**/*.spec.ts` and `test/**`; all 131 pre-existing casts across 22 spec
files replaced.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* Update lib/modules/platform/bitbucket-server/index.spec.ts

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 10:08:09 +00:00
Sebastian Poxhofer 58771bb183 refactor(linters): enabled oxlints func-style (#44691)
* 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>
2026-07-19 11:14:22 +00:00
Sebastian Poxhofer 5ad36a2387 test(linters): add linting rule to enforce usage of host rules test util (#44692)
* ci(linter): add renovate/no-host-rules-mock rule

Flags vi.mock()/vi.doMock() calls whose module specifier contains
"host-rules" in spec files. Tests should configure real rules via
hostRules.add() instead of mocking the host-rules module.

Enabled at "warn" severity instead of "error": the repo currently has
35 pre-existing violations across 34 spec files, which exceeds the
agreed fix threshold of 12 files. No violation fixes are included;
migrating each spec to real hostRules.add()/clear() setup is a
semantic test rewrite that should be done incrementally, after which
the rule can be promoted to "error".

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* ci(linter): enforce renovate/no-host-rules-mock at error

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* test(datasource): use real host-rules instead of mocking

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* test(manager): use real host-rules instead of mocking

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* test(platform): use real host-rules instead of mocking

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* test(util,workers): use real host-rules instead of mocking

Also narrow renovate/no-host-rules-mock to the host-rules module itself so that mocking e.g. host-rules-from-env is not flagged.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* ci(linter): mention '~test/host-rules.ts' util in no-host-rules-mock message

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-19 10:36:12 +00:00
Felipe Santos 8e6a7fc2e6 fix(git): detect commits modified through rebase or amend (#44587)
* fix(config): make platformCommit global-only

* fix(git): reapply committer check for isBranchModified

Reapply #42975 (reverted in #43171): treat a different committer
(rebase/amend/Gerrit UI) as a branch modification.

* fix(git): ignore platformCommit committer from init-time list

Register noreply@${host} at GitHub initPlatform when platform commits
are active. isBranchModified only consumes the list — correct for
github.com, ghe.com, and self-hosted GHES.

Share isPlatformCommitEnabled() with GithubScm so commit path and
ignore-list registration use the same rule.

* refactor: stop threading platformCommit through commits

Decision is resolved once in GitHub initPlatform
(isPlatformCommitEnabled). Workers no longer pass the raw option on
CommitFilesConfig — it was unused after that change.

* refactor: simplify platformCommit / ignore-author wiring

Drop cached platformCommitEnabled flag — isPlatformCommitEnabled() is
a pure read of GlobalConfig + isGHApp. Trim redundant tests and
trace noise.

* Simplify env var setting

* revert: keep platformCommit packageRules-capable

Do not make platformCommit global-only. Restore CommitFilesConfig
threading and GithubScm reading commitConfig.platformCommit so rules
can still set platformCommit per package (see #44585 discussion).

* fix(github): always ignore platformCommit noreply committer

Do not gate noreply@host on isPlatformCommitEnabled (that needed
global platformCommit). Always register host noreply at init so
packageRules can still flip platformCommit per commit while
isBranchModified stays correct.

Remove isPlatformCommitEnabled; GithubScm again uses
commitConfig.platformCommit.

* Simplify some stuff

* Add one assertion

* Change some comments
2026-07-18 12:47:11 +00:00
Felipe Santos 18c9c9340e feat(gerrit): rework scm implementation (#38966)
* fix(gerrit): remove memCache to fix prNo coming as null when pr-created

* perf(gerrit): remove custom scm functions

* Fully remove login parameter from configureScm()

* Fetch refspecs in batches

* Remove unnecessary error handling

* Add tests

* Throw if branch init fails

* Simplify tests a little

* Reduce more the tests

* More test changes

* Reuse the existing test setup more

* Undo test import change

* Improve initializeBranchesFromRefspecs JSDoc

* The same for deleteBranchCreatedFromRefspec

* Reduce a log to trace level

* Abandon Code-Review -2 changes before initializing repository

* Simply initialize the branches after git.initRepo()

* Rework approach to be more generic

* Add TODO comment about commitAndPush()

* fix(gerrit): defer change creation to `createPr()`

Signed-off-by: Felipe Santos <felipe.santos@ericsson.com>

* Refactor hashtags handling

* Add prTitle to all missing commitAndPush()

* Fix type check

* Fix test issue

* Fix gerrit tests

* Add warning when prTitle is set in updatePr

* Remove now useless labels from commitAndPush

* Fix gerrit client coverage

* Fix gerrit scm coverage

* Fix rebase coverage

* Improve TODO about assignees

* Fix formatting

* Fix review comment

* Extract to a pushForReview function

* Fix after #41382

* Fix onboarding

* Cleanup code

* Fix ESLint

* Fix tests

* Restore gitRetry for git.merge

* Fix tests

* Simplify hashtags code

* Delete pendingChangeBranch in pushForReview()

* Fix test name

* Remove unnecessary test comments

* Improve test name again

* Fix another test name

* Delete local branch in Gerrit SCM

* Add test for deleting pending branch

* Add tests for pushForReview()

* Simplify/optimize commitAndPush

* Align jsdoc terminology on virtual branches

* Remove no longer relevant TODO

* Update virtal branch on push

* Simplify deleteVirtualBranch

* Fix test case name

* Improve another test name

* Restore test deleted by mistake

* Fix another small test inconsistency

* Fix coverage

* Fix Renovate not detecting modified commits

* Simplify Gerrit commitAndPush by leveraging git.commitFiles

* Fix some code style issues

* Revert "Simplify Gerrit commitAndPush by leveraging git.commitFiles"

This reverts commit d71edcc279.

* Fix another small code style issue

* Use isUndefined instead

* Fix one small test style issue

* Fix more code style issues

* Fix Gerrit mergeToLocal trying to fetch (and probably failing)

* Extract refs/remotes/origin to a function

* Rename virtualBranchRef to remoteBranchRef

* Fix remoteBranchRef tsc

* Consistently use currentRevision in Gerrit tests

* Remove unnecessary type assertion

* Instrument "fetch virtual branches"

* Remove pendingChangeBranches, avoid overriding deleteBranch and mergeToLocal

* Optimize virtual branches fetch (no longer requires update-ref), convert virtualBranches to Record, rename setVirtualBranch to registerVirtualBranch

* Combine registerVirtualBranch and updateVirtualBranch into a single setVirtualBranch, and update ref in virtual branch too

* Fix virtualBranches not being initialized

* Add some debug logs

* Simplify mergeToLocal for virtual branches

* Revert "Simplify mergeToLocal for virtual branches"

This reverts commit 375d57a474.

* Remove useless isVirtual const

* test(git): add toThrow message for virtualBranches fetch error

Satisfy vitest/require-to-throw-message so oxlint passes.

* Update lib/util/git/types.ts

Co-authored-by: RahulGautamSingh <rahultesnik@gmail.com>

* Address review feedback

---------

Signed-off-by: Felipe Santos <felipe.santos@ericsson.com>
Co-authored-by: RahulGautamSingh <rahultesnik@gmail.com>
2026-07-17 21:47:08 +00:00
Felipe Santos 636e888119 test(git): disable git maintenance to prevent test flakiness (#44670)
fix(git): disable git auto-maintenance in tests to prevent ENOTEMPTY flake

Background gc/maintenance can still be writing objects/pack when
unsafeCleanup removes the tmp dir, causing intermittent ENOTEMPTY errors.
2026-07-17 18:07:03 +00:00
Sergei Zharinov 19a38430eb fix(post-upgrade): preserve executable file modes (#44505)
* fix(post-upgrade): preserve executable file modes

* refactor(post-upgrade): clarify executable detection

* refactor(post-upgrade): use filesystem abstraction

* Clarify Git executable bit detection

* test(post-upgrade): use CI platform for mode coverage

* fix(post-upgrade): trust Git file mode setting

* perf(git): cache file mode setting

* test(post-upgrade): cover existing executable artifact

* test(post-upgrade): verify committed executable mode

* test(git): skip chmod regression without file mode support
2026-07-17 12:03:51 +00:00
Sebastian Poxhofer 9717259ffc refactor(oxlint): enable no-else-return and unicorn/no-lonely-if (#44553)
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>
2026-07-14 12:11:21 +00:00
Sebastian Poxhofer 7436b80cda test(linter): add renovate/no-redundant-mock-reset rule (#44557)
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>
2026-07-14 11:26:20 +00:00
Sebastian Poxhofer ad0b59f8b7 refactor(oxlint): enable vitest/require-to-throw-message (#44421) 2026-07-07 17:47:04 +00:00
Michael Kriese 289e2d2028 fix(git): allow overriding GIT_ASKPASS (#44322)
* fix(git): allow overriding `GIT_ASKPASS`

* fix: drop experimental options

* test: fix tests
2026-07-02 08:22:40 +00:00
Sebastian Poxhofer 171a591d84 feat(git): validate commit shas (#43988)
* feat(git): validate length commit shas

* test: adapt test mocks

* chore: update import paths

* feat(git): make the validation more strict and support repositories using sha256

* test: add fakeSha function for deterministic git commit shas in tests
2026-06-15 06:36:57 +00:00
Jiri Daněk def09c2661 fix(github): use base_tree in pushFiles to avoid org ruleset file-path restrictions (#42556)
* fix(github): use base_tree in pushFiles to avoid org ruleset file-path restrictions (#42554)

pushFiles sent the entire repo tree to POST /git/trees without base_tree.
GitHub validates all file paths against org rulesets, rejecting the tree
when it contains protected files (e.g. semgrep.yaml) — even if Renovate
only changed unrelated paths.

Use base_tree (parent commit's tree) with only the changed files from
git diff-tree, so unchanged protected files are inherited without
validation.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* test(git): add coverage for getCommitTreeSha and diffCommitTree

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(test): assert specific files instead of hardcoding diff count

The test fixture creates 2 known files between develop and
defaultBranch (master_file, file_to_delete), but CI produces 3
changed files — likely a .gitattributes from the CI environment.
Assert on the known files rather than the total count.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(git): handle renames, copies, and submodules in diffCommitTree

- Regex now matches multi-char status codes (R100, C100) and
  tab-separated source/target paths for renames and copies.
- Renames emit a deletion for the source path + addition for target.
- Derive tree item type from mode: 160000→commit, 040000→tree,
  otherwise blob.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* refactor(git): address review comments in diffCommitTree

Keep git utility types centralized and make diff status handling easier to follow.

Made-with: Cursor

* test(git): cover rename handling in diffCommitTree

Verify that renamed files are emitted as a deletion for the old path and an addition for the new path, matching the tree delta expected by GitHub.

Made-with: Cursor

* fix(git): validate parsed tree SHA

Fail fast when commit output cannot be parsed into a tree SHA so pushFiles does not propagate an invalid base_tree, and cover the failure path in tests.

Made-with: Cursor

* fix(git): enable rename detection and cover remaining diffCommitTree branches

Add -M to diff-tree so Git emits R status for renames, exercising the
existing case 'R' handler. Add focused tests for rename parsing,
submodule mode (160000), and tree mode (040000).

Made-with: Cursor

* fix(git): clarify copy vs rename comment in diffCommitTree

R uses both tab-separated paths (delete old + add new), while C falls
through to default where only the target path matters.

Made-with: Cursor

* refactor(git): remove dead listCommitTree and TreeItem

Nothing imports listCommitTree after the switch to diffCommitTree with
base_tree. Remove the function, its regex, its test, and the TreeItem
interface.

Made-with: Cursor

* refactor(git): tighten DiffTreeItem.type to GitObjectType union

Define GitObjectType = 'blob' | 'tree' | 'commit' and use it in
DiffTreeItem and treeTypeFromMode instead of plain string.

Made-with: Cursor

* fix(git): remove unreachable nullish coalescing in getCommitTreeSha

split()[0] on a non-empty string is always defined, so the ?? ''
fallback was unreachable and caused an uncovered branch in Codecov.

Made-with: Cursor

* fix(git): add v8 ignore for partial branch on throw template literal

v8 reports template literal interpolations inside throw as partial
branch coverage even when the line is tested. Use the established
v8 ignore pattern to suppress the false positive.

Made-with: Cursor

* fix(github): guard pushFiles against empty diff from diffCommitTree

GitHub's POST /git/trees rejects an empty tree array with 422
"Invalid tree info". While prepareCommit already prevents no-op
commits from reaching pushFiles, add a defensive early return so
the failure mode is a clear log message instead of a swallowed 422.

Made-with: Cursor

* docs(github): document hybrid git/REST commit strategy in pushFiles

Restore architectural knowledge from PRs #13824 and #14271 that was
lost when the old "for reasons unknown" comments were replaced. The
push-then-recreate pattern exists because custom refs avoid CI triggers,
REST-created commits get GitHub signing, and GraphQL lacks force-push
and file mode support.

Made-with: Cursor

* fix(git): wrap bare regex literals in regEx() in tests

Use the project's regEx() helper instead of bare /pattern/ in
toMatch and stringMatching calls, consistent with repo conventions.

Made-with: Cursor

* fix(git): revert regEx() in expect.stringMatching (only accepts bare RegExp)

expect.stringMatching() is a Vitest/Jest API that requires a native
RegExp, not the wrapped object from Renovate's regEx() helper.

Made-with: Cursor

* fix(git): drop line count from v8 ignore next

Vitest uses ast-v8-to-istanbul which is AST-aware: it ignores the
entire next AST node (here the whole throw statement), making the
line count unnecessary.

Made-with: Cursor

* refactor(git): replace magic mode strings with GitTreeMode enum

Introduce a documented GitTreeMode enum in types.ts and use it in
treeTypeFromMode, replacing raw '160000'/'040000' literals with named
members linked to the git data model documentation.

Made-with: Cursor

* fix(git): use const object instead of enum for GitTreeMode

Node's TypeScript strip-only mode does not support enum syntax.
Use `as const` object pattern consistent with the rest of the codebase.

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-09 09:08:20 +00:00
renovate[bot] 9245b57c58 chore(deps): update linters to v1.67.0 (main) (#42416)
* chore(deps): update linters to v1.67.0

| datasource | package | from   | to     |
| ---------- | ------- | ------ | ------ |
| npm        | oxlint  | 1.57.0 | 1.67.0 |

* test(oxlint): deactivate new tests for now

* fixup linting errors

* revert googleAuth mock implementation

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: secustor <sebastian@poxhofer.at>
2026-06-06 14:09:05 +00:00
Sebastian Poxhofer 6f9411c7d4 test(git): increase timeout to 30s (#43704)
test(git): increae timeout to 30s
2026-05-31 19:21:08 +00:00
Felipe Santos 392105b7c6 chore(oxlint): enable unicorn plugin and prefer-node-protocol (#43283)
chore(oxlint): enable unicorn plugin and prefer-node-protocol
2026-05-29 06:55:58 +00:00
renovate[bot] 24154eb71a build(deps): update dependency simple-git to v3.36.0 [security] (main) (#43122)
* build(deps): update dependency simple-git to v3.36.0 [security]

| datasource | package    | from   | to     |
| ---------- | ---------- | ------ | ------ |
| npm        | simple-git | 3.35.2 | 3.36.0 |

* chore(utils): set unsafe flags for SimpleGit (#43268)

* Enabled some unsafe flags for SimpleGit.

* Removed allowUnsafePager

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Dave <reduckted@outlook.com>
Co-authored-by: Sebastian Poxhofer <secustor@users.noreply.github.com>
2026-05-26 21:48:49 +00:00