* 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>
* fix(config/global): return default values for `GlobalConfig.get`
As noted in #39949, it can be awkward working with in `GlobalConfig.get`, where
we would previously need to specify the default value when calling `.get`,
which could lead to drift with the defaults for a given config option.
To reduce this risk, we can rely on a given `default` for a configuration
option, providing that as a default.
Because this now definitely returns a value that matches the config option, it
now allows us to remove `!` assertions.
* fixup! fix(config/global): return default values for `GlobalConfig.get`
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
* fixup! fix(config/global): return default values for `GlobalConfig.get`
* test(presets): remove impossible test
As it's now not possible to set a value as `undefined` (as
`GlobalConfig` will then use the default), this test no longer validates
anything.
* test(workers/repository): add mock
Now `platform` defaults to `github`, we need to mock out the call to
fetch config.
Co-authored-by: Claude Sonnet 4.6 <jamie.tanna+claude-code@mend.io>
* test: coverage
* test: coverage
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.6 <jamie.tanna+claude-code@mend.io>
feat(utilgit): log warning if file(s) contain hidden Unicode characters
With the rise of prompt injection, there is an increased number of
attacks occurring using hidden Unicode characters.
These hidden characters can also confuse other tools, so it's best for
Renovate to surface these to repository owners, as we read files that
may be problematic.
To avoid multiple reads of files, we can do this every time our
`readLocalFile` function is called, as we already have the file content.
An alternate option would be that in `workers/repository` we could check
the read package files, but given `extractAllPackageFiles` may be used
by a Manager, we can't inject the logic in, without Managers needing to
implement the logic themselves.
This should be performant, and not introduce too many false-positives,
but this will also depend on how (package) files are set up in
repositories.
The list of suspicious characters has been largely compiled by Claude
Sonnet 4.5.
Co-authored-by: Claude Sonnet 4.5 <jamie.tanna+github-copilot@mend.io>
Ignore environment variables and instead use static cache directory for third party tools.
Closes#10094
BREAKING CHANGE: Renovate will now override any package manager cache locations configured in env variables.
# Conflicts:
# lib/manager/gomod/__snapshots__/artifacts.spec.ts.snap
# lib/manager/gomod/artifacts.ts