* refactor(manager/helmv3): share generateHelmEnvs with kustomize
Move kustomize's generateHelmEnvs into helmv3/common.ts and delete the
kustomize copy. The merged function always sets the three HELM_*
path variables and sets HELM_EXPERIMENTAL_OCI unless a helm
constraint is present and proves helm >= 3.8 (which ignores the
variable).
Behaviour change: kustomize now sets HELM_EXPERIMENTAL_OCI when the
helm constraint is unknown (previously it only set it for a known
constraint below 3.8). helmv3 and helmfile now omit the variable
once a resolved helm constraint proves helm >= 3.8.
* refactor(manager/helmv3): share OCI registry login helper with helmfile
Extract a shared generateRegistryLoginCmd(name, registry) helper in
helmv3/common.ts that strips the oci:// prefix, derives the host and
looks up the matching host rule before delegating to generateLoginCmd.
helmv3/artifacts.ts and helmfile/artifacts.ts now both call this
helper instead of each building a RepositoryRule inline, removing the
stale oci:// TODO. helmfile/utils.ts loses its duplicate
generateRegistryLoginCmd, and its unrelated isOCIRegistry(repository)
(checking the oci flag) is renamed to isOciRepositoryFlagSet so it no
longer collides in name with helmv3/oci.ts's prefix-based
isOCIRegistry(string), which helmfile/extract.ts also imports.
Addresses audit finding M8. Generated `helm registry login` commands
are unchanged, proved by the existing artifacts specs passing with
their original expectations untouched.
* fix(manager): share one local Helm chart path check
helmfile, flux, fleet and terraform each decided differently whether a Helm chart reference points at a local path. fleet and terraform used terraform's checkIfStringIsPath, whose regex was unanchored with unescaped dots, so it matched any string containing a slash followed by a non-slash character.
Add isLocalChartPath to helmv3/utils.ts with helmfile's prefix check and use it in all four managers. This drops the fleet to terraform dependency and removes checkIfStringIsPath, which had no other users.
* refactor(manager/helm-requirements): reuse helmv3 alias and repository helpers
Replace the duplicated alias-resolution and repository-classification logic in helm-requirements/extract.ts with the shared isAlias, resolveAlias and parseRepository helpers from helmv3/utils.ts (audit finding M9). Behaviour is unchanged: same skipReasons, same registryUrls, same handling of a missing repository or missing registryAliases config.
* refactor(manager/dockerfile): add an options form of getDep for CI managers
`getDep()` gains an overload taking a `GetDepOptions` object with
`specifyReplaceString`, `registryAliases` and `depType`, replacing the
opaque boolean second positional argument at the CI manager call sites.
The positional form keeps working for all other callers.
Migrated: bitbucket-pipelines, droneci, circleci, github-actions,
azure-pipelines and woodpecker.
Behaviour changes:
- azure-pipelines container resources now honour `registryAliases`, which
`extractContainer()` previously ignored
- woodpecker dependencies now carry `depType: 'docker'`, matching the
other CI managers
* refactor(manager/woodpecker): do not set depType on docker image deps
* refactor(manager/dockerfile): drop the positional getDep signature
* refactor(manager/helm-requirements): reuse helmv3 alias and repository helpers
Replace the duplicated alias-resolution and repository-classification logic in helm-requirements/extract.ts with the shared isAlias, resolveAlias and parseRepository helpers from helmv3/utils.ts (audit finding M9). Behaviour is unchanged: same skipReasons, same registryUrls, same handling of a missing repository or missing registryAliases config.
* refactor(constants): add a platform family capability table
Every consumer of `detectPlatform` carried its own mapping from the
detected platform to a tags datasource or a web UI path, and the seven
`*_API_USING_HOST_TYPES` arrays were checked one by one to produce that
platform in the first place.
Introduce `PLATFORM_FAMILIES`, one record keyed by family holding the
host types, the hostnames which identify the family outright, the tags
datasource, the API base URL derivation and the web UI path to a
directory at the default branch. A family is a group of platforms, so
the ids are checked against `PlatformId`: a family which is not a
platform is a compile error. Not every platform is a family, because
codecommit, gerrit, local and scm-manager are never recognized from a
URL.
`detectPlatform` loops the table for both its hostname and its hostType
matching, and the consumers look their answers up:
- `github-actions` and `pre-commit` read `tagsDatasource` instead of
importing the tags datasource classes.
- The PR body reads `webDirPath` instead of special-casing Bitbucket and
Bitbucket Server.
`apiBaseUrl` has no consumer yet; the changelog sources pick it up in
the next commit.
`lib/util/http/auth.ts` and `lib/util/http/host-rules.ts` still read six
of the old arrays by name, so those stay as aliases of the table and are
migrated in a follow-up. `AZURE_API_USING_HOST_TYPES` had no other
consumer and is dropped.
Behaviour changes: none. `detectPlatform` now tests every known hostname
before its name heuristics instead of interleaving the two, which is
equivalent because the hostname lists are pairwise disjoint and no known
hostname contains the name of another family.
* Apply suggestion from @secustor
* test(manager): close azure-pipelines, bundler and cocoapods gaps
Adds a pipeline whose step task is not versioned, so nothing is
extracted, and a gem lock line carrying no version, which is skipped.
Marks four guards that cannot be false: `extractContainer()` always
returns a dep, `getLockFilePath()` always returns a path, and the
cocoapods dep name, spec name and git account all derive from values
their enclosing check already proved present.
Co-Authored-By: Claude Opus 5 <michael.kriese+claude-code@mend.io>
* test(manager): close cargo, composer and cpanfile gaps
Adds a composer repository map that disables something other than
packagist, which is dropped rather than recorded.
Marks five guards: the cargo warning only fires for a crate dep and its
retry only runs when a dep was already current, a gitlab rule without a
token never passes the auth check, and both cpanfile handlers run only
once their match produced a value.
Two implicit elses stay counted, in the cargo version resolver and the
composer repository mapper, since a coverage-ignore hint does not attach
to an `else if`.
Co-Authored-By: Claude Opus 5 <michael.kriese+claude-code@mend.io>
* test(manager): close deps-edn, docker-compose and dockerfile gaps
Adds three extractions: a deps.edn alias whose value is not a map, a
compose extension carrying no image, and a dockerfile escape directive
naming a character other than a backtick, which leaves the default in
place.
Marks the deps-edn repository name check, since object keys are always
strings, and notes the repository spec chain, where a coverage-ignore
hint does not attach to an `else if`.
Co-Authored-By: Claude Opus 5 <michael.kriese+claude-code@mend.io>
* test(manager/flux): close the flux coverage gaps
Adds two GitRepository resources whose url is ssh rather than http, one
pinned to a tag and one to a commit. Neither yields a source url, since
only http urls can be turned into one.
Marks the Kustomization image guard: the schema rejects an image without
a name, so `extractImage()` cannot return null there.
Co-Authored-By: Claude Opus 5 <michael.kriese+claude-code@mend.io>
* test(manager): mark github-actions, gitlabci and glasskube guards
None of these six needs a test. A parsed action ref is either a docker
or a repository ref, `getDep()` always returns a dep for a job container
and a service, and the remaining three need a listed file that cannot be
read, a listed file that yields no deps, or a manifest whose
repositories match no name.
The glasskube resource kind chain stays counted, since a coverage-ignore
hint does not attach to an `else if`.
Co-Authored-By: Claude Opus 5 <michael.kriese+claude-code@mend.io>
* test(manager): mark gomod and gradle-wrapper guards
Four guards that need setups no test provides: a removed dep that is not
added back, a go.mod the replace massaging leaves unchanged, and a
daemon-jvm file carrying no toolchain version. A parsed go directive
always yields a version.
The additional-dependency count chain stays counted, since a
coverage-ignore hint does not attach to an `else if`.
Co-Authored-By: Claude Opus 5 <michael.kriese+claude-code@mend.io>
---------
Co-authored-by: Claude Opus 5 <michael.kriese+claude-code@mend.io>
* refactor(manager/github-actions): don't duplicate parsing logic for first-party Actions
Instead of having a separate set of Action parsing and their
configuration between first-party GitHub-owned Actions and those of
third-party Actions, we can merge them into a single collection.
This also leads to `https://<host>/` handling noted in #35777 now being
handled out-of-the-box, as it was already supported on third-party
actions.
Co-Authored-By: Claude Sonnet 5 <jamie.tanna+claude-code@mend.io>
* fixup! refactor(manager/github-actions): don't duplicate parsing logic for first-party Actions
---------
Co-authored-by: Claude Sonnet 5 <jamie.tanna+claude-code@mend.io>
feat(lint): add `types-location` rule and enable it for `lib/modules`
Reviewers consistently ask for exported types to be moved into a
colocated `types.ts`. Add a `renovate/types-location` oxlint rule that
reports `export interface` / `export type` declarations outside such a
file, and turn it on across `lib/modules`.
Re-exports (`export type { X }`, `export type *`), file-local types, and
values (enums, classes, functions) are left alone. `schema.ts` files and
`schema/` directories are excluded via config, matching how
`zod-schema-location` is configured — inferred types belong next to the
schema they are derived from.
Relocating the 87 existing violations means:
- new `types.ts` files for the modules that had none
- `terragrunt/common.ts` folded into its `types.ts`, as it held nothing
else
- `RangeBound` in `versioning/gradle/compare.ts` un-exported, since only
a file-local interface referenced it
- `bazel-module/parser/fragments.ts` split from its inferred types, which
now live in `parser/types.ts` and infer through a namespace import so
each schema keeps its merged name
* feat(manager/github-actions): add `workflow` depType for reusable workflow calls
A job-level `uses:` pointing at `owner/repo/.github/workflows/<file>.yml@<ref>`
calls a reusable workflow rather than running an action, but both got the
`action` depType, so `matchDepTypes` could not tell them apart.
Give a reference whose sub-path is exactly `.github/workflows/<file>.yml|.yaml`
the new `workflow` depType. Every other sub-path, such as
`github/codeql-action/init`, stays an action.
The existing depType-keyed sites now accept both values, so nothing else
changes behaviour: the `actions.lock` file records reusable workflow calls too,
and `helpers:pinGitHubActionDigests` pinned them before this split.
* Apply suggestions from code review
Co-authored-by: Sebastian Poxhofer <secustor@users.noreply.github.com>
* refactor(manager/github-actions): make `extractPackageFile` async
As part of follow-up changes we'll need to read `.github/workflows/actions.lock`
while extracting, which requires we are an async function.
To reduce the diff, we can prefactor this change first.
Co-authored-by: Claude Opus 5 <jamie.tanna+claude-code@mend.io>
* feat(manager/github-actions): add support for Actions Lockfiles
In June, GitHub announced a public preview for the GitHub Actions
lockfile functionality[0].
To add support for this, we can extend our support for GitHub Actions to
handle the presence of an `actions.lock` file, and if so, update it
after updating other manager files.
Although our ideal case would be to have a pure Typescript-based parsing
and updating of the `actions.lock`, due to the early nature of the
Actions Lockfile syntax, and its lack of stability, we can invoke the
`gh actions-lock` extension when updating.
After the schema has reached a v1 release, we can then migrate to
Typescript.
To make sure that the version of the `actions-lock` extension is always
version pinned, we can expose a `constraints` to control the version,
but allow Renovate to keep the pinned default updated as needed.
This doesn't imply that we have support for `lockFileMaintenance`, as we
are only updating the lockfile when there is a dependency update, not
periodically recreating the lockfile from the current ranges.
`gh actions-lock` re-reads every workflow from disk and rewrites the
whole lockfile, so it does not fit `updateArtifacts`, which is called
once per updated package file.
We can instead follow the shape the `npm` manager uses for its
lock files, and have `processBranch` invoke this once per branch, after
every updated package file has been written to disk.
We skip workflows that aren't onboarded to the lockfile, but still run
for any other package file, as a local composite action can be a
transitive dependency of a workflow which is.
`gh actions-lock` rewrites the workflows it manages back to plain refs
when it regenerates them, which strips out any inline digest pin:
```diff
- uses: actions/setup-java@1bcf9fb12c # v5.4.0
+ uses: actions/setup-java@v5.7.0
```
This would lead to a repository with `pinDigests` set "flip flopping",
and would cause a lot of erroneous noise for repository owners.
Instead, we can make sure that:
- we don't suggest a `pinDigest` for workflows that are managed by the
lockfile
- digest pinning is still enabled for `docker://` references
- already-committed digests are updated when they're present - we only
avoid suggesting /new/ digests to be pinned
Co-authored-by: Sebastian Poxhofer <secustor@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <jamie.tanna+claude-code@mend.io>
Co-authored-by: Claude Fable 5 <jamie.tanna+claude-code@mend.io>
[0]: https://github.com/github/actions-lockfile
* coverage
Co-authored-by: Claude Sonnet 5 <jamie.tanna+claude-code@mend.io>
* fixup! feat(manager/github-actions): add support for Actions Lockfiles
Co-authored-by: Claude Opus 5 <jamie.tanna+claude-code@mend.io>
---------
Co-authored-by: Claude Opus 5 <jamie.tanna+claude-code@mend.io>
* refactor(manager/github-actions): return dependency arrays from community `with:` schemas
A `withSchema` now yields `PackageDependency[]` directly, instead of a
`{ val, ...meta }` object whose extra fields were merged in through an untyped
`Record<string, unknown>`. Schemas describe the dependency they find, and a
single step may yield more than one.
`isInvalid` moves out of `CommunityActionConfig` into `valSchema`, its only
caller. The config fields are now applied to every dependency a schema yields.
No behavior change.
* feat(manager/github-actions): support `pnpm/setup`
`pnpm/setup` replaces `pnpm/action-setup` for pnpm 11+. Its `version:` input
resolves as the `pnpm` package, and its `runtime:` input as the JavaScript
runtime to install - `node`, `bun` or `deno`, named as `<name>@<version>` to
match pnpm's `packageManager` field syntax.
An unknown runtime name is skipped as `invalid-name`, so a typo does not pass
silently.
* feat(lint): add renovate/zod-schema-location and renovate/prefer-json-pipe rules
Backlog item #23: reviewers repeatedly ask for consistent Zod schema
structure. Two new oxlint JS-plugin rules, both at "warn":
- renovate/zod-schema-location flags `export const X = <zod schema>` at
module scope in lib/modules/**/ files that aren't schema.ts (or a
colocated schema/ directory), reusing the leftmost-identifier schema
detection from zod-schema-naming.js / prefer-nullish-util.js. Only
exported top-level schemas are flagged — non-exported ones are treated
as intentionally local/trivial (e.g. tightly coupled to file-local
helpers), which keeps the rule's signal high.
- renovate/prefer-json-pipe flags `Schema.parse(JSON.parse(x))` /
`Schema.safeParse(JSON.parse(x))` and suggests `Json.pipe(Schema)` from
lib/util/schema-utils instead.
A third mined slice (require a matching `z.infer` type export for every
exported schema) was evaluated and skipped: ~275+ exported schema-like
consts in schema.ts files vs. 231 existing z.infer exports repo-wide is
too high a mismatch to flag mechanically without heavy per-site
judgment, so it would be mostly false positives.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* refactor: fix a clean subset of renovate/zod-schema-location and renovate/prefer-json-pipe violations
renovate/prefer-json-pipe: fixed all 5 safe `.parse(JSON.parse(...))`
call sites (bazel datasource, generate-mise-registry script, a hook
spec) by switching to `Json.pipe(Schema).parse(...)`. Left the 2
`.safeParse(JSON.parse(...))` sites (pretooluse-check.ts,
github-query-items.ts) unfixed: JSON.parse throwing before safeParse is
reached today crashes the script, whereas Json.pipe folds the bad-JSON
case into a graceful `{ success: false }`, which is an observable
behavior change, not just a mechanical rewrite.
renovate/zod-schema-location: fixed the one clean, self-contained
violation (platform/github's GithubIssue schema, moved into the
already-existing platform/github/schema.ts) as a representative
example. Left the other 20: they require moving exported schemas out of
files where they're built from local helper functions/config tables
(manager/github-actions/community.ts) or restructuring an entire
directory into a new schema.ts with import-graph updates across many
files (manager/bazel/rules/*.ts, manager/bazel-module/parser/*.ts) —
real refactors needing per-site judgment, not mechanical fixes.
Both rules stay at "warn": renovate/prefer-json-pipe has 2 remaining
violations that shouldn't be auto-fixed, and renovate/zod-schema-location
has 20.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* refactor: resolve remaining prefer-json-pipe and zod-schema-location findings
- github-actions: move the derived `CommunityActions` union into the
existing colocated `schema.ts`; the action config table and the
`actionSchema` builder stay in `community.ts` and are now exported.
- tools/docs: fold `JSON.parse` + `safeParse`/rethrow of the `gh` CLI
output into `Json.pipe(...)`. Invalid JSON now surfaces as a ZodError
instead of a SyntaxError; the only consumer catches generically, so
behavior is preserved.
- tools/agents/hooks: keep `JSON.parse` + `safeParse` with a reasoned
disable comment — hook scripts must stay dependency-light, and the
`Json` helper lives in lib/util/schema-utils, which drags in the full
lib import graph (logger, yaml, toml).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* feat(lint): enforce zod-schema-location and prefer-json-pipe as errors
Narrow `renovate/zod-schema-location` to files whose directory already
contains a colocated `schema.ts`. The mined reviewer feedback ("move
this to schema.ts") presumes the module follows that convention and the
schema leaked into a logic file. Modules that deliberately export
schemas from differently-named schema-only files — bazel `rules/*.ts`
Target schemas, bazel-module `parser/fragments.ts` — are a different,
accepted layout, not "schema mixed into logic", and are no longer
flagged.
With that narrowing and the preceding migrations, both rules are clean
repo-wide, so promote them from "warn" to "error".
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* refactor: drop GithubIssue re-export from issue.ts
Consumers now import the schema directly from schema.ts.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(lint): register zod-schema-location rule
The rule registration was lost while merging main, so oxlint failed to
parse .oxlintrc.json with "Rule 'zod-schema-location' not found in plugin
'renovate'". Re-add the import and rules entry, and restore alphabetical
ordering for prefer-json-pipe.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(lint): allow sync fs in zod-schema-location rule
ESLint rules execute synchronously, so the sibling `schema.ts` lookup
cannot use async fs. Silence `no-sync` with a justification.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
* test(noNewURL): add new rule enforcing parseUrl() from 'lib/util/url.ts'
* replace newURL with parseUrl
* remove non-null assertions marks if appropiate
* test: add additional tests
* test: add additional tests and ignore where it makes sense
* implement suggestions
* test: further improve coverage
* apply suggestion
The changes in da04b8edac mostly worked,
but given `astral-sh/setup-uv` is now using Immutable Tags it _must_ be
resolved as `versioning=semver`, to make sure that version updates are
correctly raised.
However, we can't treat it as `versioning=semver`, as that will then
fail to parse the current version correctly.
Instead, we must use a `regex` versioning scheme, which allows us to
optionally parse the minor and patch versions.
This allows `newVersion` to be pinned accordingly to SemVer, but does
not require previous versions to be so.
We can do this - for now - with an explicit allowlist.
As per #42331, we should migrate to using `semver-partial` for GitHub
Actions' tags versions, instead of `docker`.
This mirrors actual usage of the way that the ecosystem tags Action
versions, and also has the benefit of automagically migrating users of
Actions that have enabled Immutable Tags, which now must be a full
SemVer tagged version.
As we're now specifying the `versioning: semver-partial`, we don't need
to specify it in `pinGitHubActionDigestsToSemver`.
* fix(manager/github-actions): disable updates for naked SHA pins without version comment
* docs(manager/github-actions): document disabled updates for bare SHA pins
* test(manager/github-actions): use single toEqual for version comment assertions
* docs(manager/github-actions): recommend version comment as primary fix for bare SHA pins
* fix(manager/github-actions): add skipReason for naked SHA pins
* docs(manager/github-actions): remove package rule example for re-enabling bare SHA updates
Co-authored-by: Jamie Tanna <github@jamietanna.co.uk>
---------
Co-authored-by: Jamie Tanna <github@jamietanna.co.uk>
* feat(github-actions): Support non-semver refs (tags and branches)
* refactor(github-actions): drop ref= prefix, use bare token comments
Address review feedback: use bare `# cargo-llvm-cov` instead of
`# ref=cargo-llvm-cov` for non-semver pin comments, consistent with
how version comments work (`# v4.0.0`).
- Rename pinnedVersionRe -> pinTokenRe, add bareTokenRe fallback
- Add `ref` field to CommentData, separate from pinnedVersion
- Guard replaceString extension to only use ref for SHA-pinned actions
- Remove redundant autoReplaceStringTemplate override
- Deduplicate versionLikeRe (export from parse.ts)
- Add datasource assertions to non-semver tests
* refactor(github-actions): remove unreachable parseComment branch
* test(github-actions): add coverage for non-version ref fallback
* fix: handling of quoted Docker image references in Github Actions
The problem with the use of '\s' is that it swallows the newline at the
end of the line, meaning the $ anchor doesn't have anything to match
against, which results in it failing to match quoted entries. By simply
switching to a dot (which doesn't match newlines), the issue is
resolved for all references.
* fix: further modify regex for docker image refs in github actions