renovate-mcp 1.6.8 → 1.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +80 -34
- package/dist/data/datasources.generated.js +92 -0
- package/dist/index.js +6 -3
- package/dist/lib/configLinter.js +152 -23
- package/dist/lib/configLocations.js +14 -0
- package/dist/lib/dependencyExplainer.js +3 -1
- package/dist/lib/endpointValidator.js +1 -1
- package/dist/lib/externalPresetFetcher.js +81 -9
- package/dist/lib/gitRemote.js +24 -11
- package/dist/lib/packageRulesAnalysis.js +3 -1
- package/dist/lib/packageRulesWorker.js +4 -3
- package/dist/lib/packageRulesWorkerImpl.js +249 -2
- package/dist/lib/presetResolver.js +36 -17
- package/dist/lib/renovateCli.js +1 -1
- package/dist/lib/renovateWorker.js +2 -1
- package/dist/lib/scheduleWorker.js +34 -0
- package/dist/lib/scheduleWorkerImpl.js +102 -0
- package/dist/lib/setupCheck.js +46 -20
- package/dist/lib/toolInputs.js +19 -1
- package/dist/lib/version.js +1 -1
- package/dist/prompts/workflows.js +31 -5
- package/dist/resources/options.js +28 -0
- package/dist/tools/annotateDryRun.js +6 -4
- package/dist/tools/checkSetup.js +2 -0
- package/dist/tools/dryRun.js +3 -2
- package/dist/tools/dryRunDiff.js +2 -0
- package/dist/tools/explainConfig.js +6 -4
- package/dist/tools/explainDependency.js +2 -1
- package/dist/tools/explainSchedule.js +88 -0
- package/dist/tools/getVersion.js +2 -0
- package/dist/tools/lintConfig.js +2 -0
- package/dist/tools/migrateConfig.js +2 -0
- package/dist/tools/previewCustomManager.js +2 -1
- package/dist/tools/readConfig.js +4 -3
- package/dist/tools/resolveConfig.js +8 -7
- package/dist/tools/resolveConfigDiff.js +6 -5
- package/dist/tools/suggestPresets.js +2 -0
- package/dist/tools/testPackageRules.js +35 -11
- package/dist/tools/validateConfig.js +2 -0
- package/dist/tools/writeConfig.js +1 -0
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -9,24 +9,15 @@ An MCP server for designing [Renovate](https://github.com/renovatebot/renovate)
|
|
|
9
9
|
|
|
10
10
|
## Quick start
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
curl -fsSL https://raw.githubusercontent.com/tibuntu/renovate-mcp/main/install.sh | bash
|
|
14
|
-
```
|
|
15
|
-
|
|
16
|
-
Or add this entry manually to your client's `mcpServers` config:
|
|
12
|
+
Needs Node.js ≥ 24.11 on Linux or macOS. With [Claude Code](https://docs.claude.com/en/docs/claude-code):
|
|
17
13
|
|
|
18
|
-
```
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
"command": "npx",
|
|
23
|
-
"args": ["-y", "renovate-mcp"]
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
}
|
|
14
|
+
```bash
|
|
15
|
+
npm install -g renovate-mcp
|
|
16
|
+
claude mcp add renovate -s user -- renovate-mcp
|
|
17
|
+
claude mcp list # renovate should show as connected
|
|
27
18
|
```
|
|
28
19
|
|
|
29
|
-
|
|
20
|
+
Then try the prompt: *"List the namespaces available under `renovate://presets`."* A response listing `config`, `docker`, `npm`, … confirms the server is reachable. No tokens are needed for the offline tools. Other clients, tokens and upgrades are under [Install](#install) below.
|
|
30
21
|
|
|
31
22
|
## What you can do
|
|
32
23
|
|
|
@@ -39,7 +30,7 @@ Restart your client and try the prompt: *"List the namespaces available under `r
|
|
|
39
30
|
|
|
40
31
|
## Tools & resources
|
|
41
32
|
|
|
42
|
-
|
|
33
|
+
Eighteen tools, seven resources, and four workflow prompts. Each tool name below links to its full reference in [`docs/tools.md`](docs/tools.md).
|
|
43
34
|
|
|
44
35
|
| Tool | Purpose |
|
|
45
36
|
| --- | --- |
|
|
@@ -47,10 +38,10 @@ Seventeen tools, six resources, and three workflow prompts. Each tool name below
|
|
|
47
38
|
| [`get_version`](docs/tools.md#get_version) | Report the renovate-mcp server version and whether it's a released or local/dev build. |
|
|
48
39
|
| [`read_config`](docs/tools.md#read_config) | Locate and parse a repo's Renovate config in Renovate's own discovery order. |
|
|
49
40
|
| [`suggest_presets`](docs/tools.md#suggest_presets) | Search built-in (and optional local-repo) presets by natural-language intent; ranks matches and sketches an unvalidated draft config skeleton for multi-facet or weakly-covered intents. Offline. |
|
|
50
|
-
| [`resolve_config`](docs/tools.md#resolve_config) | Expand every `extends` preset offline. Opt in to fetching `github>` / `gitlab>` presets over HTTPS
|
|
41
|
+
| [`resolve_config`](docs/tools.md#resolve_config) | Expand every `extends` preset offline. Opt in to fetching `github>` / `gitlab>` / `gitea>` / `forgejo>` presets over HTTPS, or Bitbucket Cloud presets via `local>` + `platform`. |
|
|
51
42
|
| [`explain_config`](docs/tools.md#explain_config) | Inverse of `resolve_config`: annotate every leaf field with the chain of presets that set it. |
|
|
52
43
|
| [`resolve_config_diff`](docs/tools.md#resolve_config_diff) | Offline structural diff of two fully-resolved configs — changed fields plus an order-insensitive set diff of array keys (`packageRules`, `customManagers`, …). The refactor-friendly counterpart to `dry_run_diff`. |
|
|
53
|
-
| [`test_package_rules`](docs/tools.md#test_package_rules) | Offline what-if: which `packageRules` match a hypothetical dependency, which matcher decided each, and what each contributes. Faithful (Renovate's real matchers in a worker); fields you don't supply are reported as unevaluatable, not non-matches. |
|
|
44
|
+
| [`test_package_rules`](docs/tools.md#test_package_rules) | Offline what-if: which `packageRules` match a hypothetical dependency, which matcher decided each, and what each contributes — plus the `branchName` / `commitMessage` / `prTitle` the matched config would render for it. Faithful matching (Renovate's real matchers in a worker); fields you don't supply are reported as unevaluatable, not non-matches, and the rendered names list the post-lookup template variables they could not fill. |
|
|
54
45
|
| [`preview_custom_manager`](docs/tools.md#preview_custom_manager) | Preview a `customManagers` entry (regex or JSONata) against a local repo. Offline. |
|
|
55
46
|
| [`validate_config`](docs/tools.md#validate_config) | Run `renovate-config-validator` against a file or inline object. |
|
|
56
47
|
| [`lint_config`](docs/tools.md#lint_config) | Semantic lint pass for Renovate-specific footguns the schema validator declares valid. Offline. |
|
|
@@ -58,6 +49,7 @@ Seventeen tools, six resources, and three workflow prompts. Each tool name below
|
|
|
58
49
|
| [`dry_run_diff`](docs/tools.md#dry_run_diff) | Stateless semantic diff between two `dry_run` reports — added / removed / changed updates. |
|
|
59
50
|
| [`annotate_dry_run`](docs/tools.md#annotate_dry_run) | Attribute each proposed update in a `dry_run` report to the `packageRules` that caused it; flags rules that never matched and report fields the matchers needed but the report lacked. Stateless + offline. |
|
|
60
51
|
| [`explain_dependency`](docs/tools.md#explain_dependency) | Answer "why was/wasn't dependency X updated?" from a `dry_run` report: per-occurrence `skipReason`, proposed `updates`, `warnings`, a one-line verdict and remedy hints; optionally the `packageRules` that matched it. Stateless + offline. |
|
|
52
|
+
| [`explain_schedule`](docs/tools.md#explain_schedule) | Answer "when will this `schedule` actually run?": active-now plus the next run windows in UTC and the config's `timezone`, computed with Renovate's own schedule matcher; invalid schedules return Renovate's message and the fact that it then runs at any time. Offline. |
|
|
61
53
|
| [`migrate_config`](docs/tools.md#migrate_config) | Apply Renovate's built-in migrations and return the migrated config plus a unified diff. Does not write. |
|
|
62
54
|
| [`write_config`](docs/tools.md#write_config) | Validate, then atomically write a config to disk. Preserves comments/key order on existing JSON-with-comments files; for `package.json` edits only the nested `renovate` key. |
|
|
63
55
|
| [`renovate://presets`](docs/tools.md#renovatepresets) (resource) | Markdown index of all built-in presets grouped by namespace. |
|
|
@@ -66,21 +58,23 @@ Seventeen tools, six resources, and three workflow prompts. Each tool name below
|
|
|
66
58
|
| [`renovate://options`](docs/tools.md#renovateoptions) (resource) | Markdown index of all Renovate config options, split into repository and global/self-hosted-only sections. |
|
|
67
59
|
| [`renovate://option/{name}`](docs/tools.md#renovateoptionname) (resource) | Full definition of one config option as JSON. |
|
|
68
60
|
| [`renovate://managers`](docs/tools.md#renovatemanagers) (resource) | Markdown list of all manager names, including custom managers. |
|
|
61
|
+
| [`renovate://datasources`](docs/tools.md#renovatedatasources) (resource) | Markdown list of all built-in datasource ids. |
|
|
69
62
|
|
|
70
|
-
**Prompts.**
|
|
63
|
+
**Prompts.** Four workflow prompts package the tool sequences above into ready-to-run starting points. Claude Code surfaces MCP prompts as slash commands (e.g. `/mcp__renovate__design-renovate-config`); other MCP clients expose them through their own prompt picker.
|
|
71
64
|
|
|
72
65
|
| Prompt | Drives |
|
|
73
66
|
| --- | --- |
|
|
74
67
|
| [`design-renovate-config`](docs/tools.md#design-renovate-config) | `check_setup` → `read_config` → `suggest_presets` → `resolve_config`/`explain_config` → `validate_config`/`lint_config` → `dry_run` → `write_config` on confirmation. |
|
|
75
|
-
| [`debug-package-rule`](docs/tools.md#debug-package-rule) | `check_setup` → `read_config` → `test_package_rules` → `dry_run` (with `reportOutputPath`) → `annotate_dry_run` → `explain_dependency` → a verdict naming rule indices and matchers. |
|
|
68
|
+
| [`debug-package-rule`](docs/tools.md#debug-package-rule) | `check_setup` → `read_config` → `test_package_rules` (matches + rendered names) → `dry_run` (with `reportOutputPath`) → `annotate_dry_run` → `explain_dependency` → a verdict naming rule indices and matchers. |
|
|
76
69
|
| [`author-custom-manager`](docs/tools.md#author-custom-manager) | Draft a `customManagers` entry → `preview_custom_manager` iteratively → `validate_config`/`lint_config` → `dry_run` → `write_config` on confirmation. |
|
|
70
|
+
| [`onboard-repo`](docs/tools.md#onboard-repo) | For repos without a config: `check_setup` → `read_config` (expect none) → `dry_run` (extract mode) → `suggest_presets` → `lint_config`/`validate_config` → `write_config` on confirmation → full `dry_run`. |
|
|
77
71
|
|
|
78
72
|
## Requirements
|
|
79
73
|
|
|
80
74
|
- **Linux or macOS.** Windows is not supported — `package.json` declares `"os": ["darwin", "linux"]`, so `npm i` surfaces an `EBADPLATFORM` warning on Windows and the server exits with a clear stderr message at startup. Use WSL2 or a Linux/macOS host instead.
|
|
81
75
|
- **Node.js ≥ 24.11** (aligns with Renovate's own engine requirement).
|
|
82
76
|
|
|
83
|
-
Renovate ships bundled — the `renovate` package is a runtime dependency, so `validate_config`, `dry_run`, and `write_config` work out of the box with no separate install. The offline tools (`read_config`, `suggest_presets`, `resolve_config`, `explain_config`, `resolve_config_diff`, `test_package_rules`, `preview_custom_manager`, `lint_config`, `dry_run_diff`, `annotate_dry_run`, `explain_dependency`, `migrate_config`) never spawn Renovate at all — nor do `check_setup` (by default it reads version metadata from the bundled install instead of spawning) or `get_version` (it only reports the renovate-mcp server's own version).
|
|
77
|
+
Renovate ships bundled — the `renovate` package is a runtime dependency, so `validate_config`, `dry_run`, and `write_config` work out of the box with no separate install. The offline tools (`read_config`, `suggest_presets`, `resolve_config`, `explain_config`, `resolve_config_diff`, `test_package_rules`, `preview_custom_manager`, `lint_config`, `dry_run_diff`, `annotate_dry_run`, `explain_dependency`, `explain_schedule`, `migrate_config`) never spawn Renovate at all — nor do `check_setup` (by default it reads version metadata from the bundled install instead of spawning) or `get_version` (it only reports the renovate-mcp server's own version).
|
|
84
78
|
|
|
85
79
|
**Optional env vars:**
|
|
86
80
|
|
|
@@ -93,30 +87,76 @@ Renovate ships bundled — the `renovate` package is a runtime dependency, so `v
|
|
|
93
87
|
|
|
94
88
|
## Install
|
|
95
89
|
|
|
96
|
-
###
|
|
90
|
+
### 1. Install the package
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
npm install -g renovate-mcp
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Install before you register the server. The package bundles Renovate (about 670 packages, 350 MB), so a client that starts `npx -y renovate-mcp` against a cold npm cache downloads all of that during server startup and can time out.
|
|
97
|
+
|
|
98
|
+
Upgrade with `npm install -g renovate-mcp@latest` and restart your client. With nvm or fnm, global packages belong to one Node version — reinstall after switching.
|
|
99
|
+
|
|
100
|
+
### 2. Register it with your client
|
|
101
|
+
|
|
102
|
+
**Claude Code:**
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
claude mcp add renovate -s user -- renovate-mcp
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
`-s user` makes the server available in every project. Leave it out to register it for the current project only, or use `-s project` to write a shared `.mcp.json`.
|
|
109
|
+
|
|
110
|
+
**Claude Desktop and other GUI clients** don't load your shell's PATH, so with nvm, fnm or Homebrew they may find no Node or an older one. Use absolute paths:
|
|
97
111
|
|
|
98
112
|
```bash
|
|
99
|
-
|
|
113
|
+
which node # → "command"
|
|
114
|
+
echo "$(npm root -g)/renovate-mcp/dist/index.js" # → "args"
|
|
100
115
|
```
|
|
101
116
|
|
|
102
|
-
|
|
117
|
+
Claude Desktop reads `~/Library/Application Support/Claude/claude_desktop_config.json`:
|
|
103
118
|
|
|
104
|
-
|
|
119
|
+
```json
|
|
120
|
+
{
|
|
121
|
+
"mcpServers": {
|
|
122
|
+
"renovate": {
|
|
123
|
+
"command": "/Users/you/.nvm/versions/node/v24.14.1/bin/node",
|
|
124
|
+
"args": ["/Users/you/.nvm/versions/node/v24.14.1/lib/node_modules/renovate-mcp/dist/index.js"]
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Any other client that can launch a stdio MCP server works with the same command.
|
|
105
131
|
|
|
106
|
-
|
|
132
|
+
### 3. Add tokens (optional)
|
|
133
|
+
|
|
134
|
+
The offline tools need nothing. Add env vars only for what you use:
|
|
135
|
+
|
|
136
|
+
| Use case | Env vars |
|
|
137
|
+
| --- | --- |
|
|
138
|
+
| Local `dry_run` on a repo with github.com dependencies | `GITHUB_COM_TOKEN` (read-only PAT, avoids anonymous rate limits) |
|
|
139
|
+
| `dry_run` against GitHub, or private `github>` presets | `RENOVATE_PLATFORM=github` + `GITHUB_TOKEN` |
|
|
140
|
+
| `dry_run` against GitLab, or private `gitlab>` presets | `RENOVATE_PLATFORM=gitlab` + `GITLAB_TOKEN` |
|
|
141
|
+
|
|
142
|
+
Self-hosted GitLab and GitHub Enterprise also need `RENOVATE_ENDPOINT` — see [Platform setup](#platform-setup).
|
|
143
|
+
|
|
144
|
+
In Claude Code, re-register with `-e`:
|
|
107
145
|
|
|
108
146
|
```bash
|
|
109
|
-
|
|
110
|
-
|
|
147
|
+
claude mcp remove renovate -s user
|
|
148
|
+
claude mcp add renovate -s user -e GITHUB_COM_TOKEN=ghp_xxx -- renovate-mcp
|
|
111
149
|
```
|
|
112
150
|
|
|
113
|
-
|
|
151
|
+
- `-e` stores the value in plain text in `~/.claude.json`. Keep tokens in `user` or `local` scope, never in a `project`-scope `.mcp.json` that gets committed.
|
|
152
|
+
- `-e` accepts several values, so keep the `--` before `renovate-mcp`.
|
|
153
|
+
- Other clients take the same variables in an `"env": { … }` block next to `"args"`.
|
|
114
154
|
|
|
115
|
-
|
|
116
|
-
- **Claude Desktop** — `~/Library/Application Support/Claude/claude_desktop_config.json`.
|
|
117
|
-
- **Other MCP clients** — any client that can launch a stdio MCP server works; point it at the same `npx -y renovate-mcp` command.
|
|
155
|
+
### Alternatives
|
|
118
156
|
|
|
119
|
-
|
|
157
|
+
- **npx without a global install** — register `npx -y renovate-mcp` instead, after warming the npm cache once with `npm exec --yes --package=renovate-mcp -- node -e 0`.
|
|
158
|
+
- **Installer script** — `curl -fsSL https://raw.githubusercontent.com/tibuntu/renovate-mcp/main/install.sh | bash` runs steps 1 and 2 interactively: it checks Node, asks for `npx` or global mode, verifies the server with an MCP `initialize` handshake, and registers it via `claude mcp add` when the Claude Code CLI is on PATH. Flags (after `bash -s --`): `--global`, `--npx`, `--no-mcp-add`, `--mcp-scope=user|project|local`, `--version=X.Y.Z`; also accepted as env vars (`RENOVATE_MCP_GLOBAL=1`, `RENOVATE_MCP_NO_MCP_ADD=1`, `RENOVATE_MCP_VERSION=…`).
|
|
159
|
+
- **From source** — clone, run `npm install && npm run build`, and point your client at `node /absolute/path/to/renovate-mcp/dist/index.js`.
|
|
120
160
|
|
|
121
161
|
## Platform setup
|
|
122
162
|
|
|
@@ -128,6 +168,11 @@ For `dry_run` against a remote platform or `resolve_config` with `externalPreset
|
|
|
128
168
|
| GitHub Enterprise | `github` | `https://github.example.com/api/v3/` | `RENOVATE_TOKEN` *or* `GITHUB_TOKEN` |
|
|
129
169
|
| gitlab.com | `gitlab` | (omit) | `RENOVATE_TOKEN` *or* `GITLAB_TOKEN` |
|
|
130
170
|
| Self-hosted GitLab | `gitlab` | `https://gitlab.example.com/api/v4/` | `RENOVATE_TOKEN` *or* `GITLAB_TOKEN` |
|
|
171
|
+
| gitea.com / Forgejo (code.forgejo.org) | — | (omit) | `RENOVATE_TOKEN` *or* `GITEA_TOKEN` / `FORGEJO_TOKEN` |
|
|
172
|
+
| Self-hosted Gitea / Forgejo | — | `https://forgejo.example.com/api/v1/` | `RENOVATE_TOKEN` *or* `GITEA_TOKEN` / `FORGEJO_TOKEN` |
|
|
173
|
+
| Bitbucket Cloud | — | (omit) | `RENOVATE_TOKEN` *or* `BITBUCKET_TOKEN` |
|
|
174
|
+
|
|
175
|
+
The Gitea / Forgejo / Bitbucket Cloud rows apply to the `resolve_config` family only: `dry_run` runs Renovate as `local` / `github` / `gitlab` and never reads those tokens. Pass `platform` (`gitea` / `forgejo` / `bitbucket`) and `endpoint` as tool inputs instead of `RENOVATE_PLATFORM` / `RENOVATE_ENDPOINT`.
|
|
131
176
|
|
|
132
177
|
See [`docs/platform-setup.md`](docs/platform-setup.md) for a worked self-hosted GitLab example, `local>` preset handling, and private-registry credentials.
|
|
133
178
|
|
|
@@ -208,6 +253,7 @@ npm run generate:presets # regenerate src/data/presets.generated.ts
|
|
|
208
253
|
npm run generate:managers # regenerate src/data/managers.generated.ts
|
|
209
254
|
npm run generate:migrations # regenerate src/data/migrations.generated.ts
|
|
210
255
|
npm run generate:options # regenerate src/data/options.generated.ts
|
|
256
|
+
npm run generate:datasources # regenerate src/data/datasources.generated.ts
|
|
211
257
|
npm run sync:jsonata-pin # sync package.json's jsonata pin to Renovate's own
|
|
212
258
|
npm run check:snapshot-versions # fail if any src/data/*.generated.ts is stale vs installed renovate
|
|
213
259
|
```
|
|
@@ -228,4 +274,4 @@ See [`docs/development.md`](docs/development.md) for snapshot-file mechanics, CI
|
|
|
228
274
|
- **Not a Renovate replacement.** This server doesn't open PRs, run scheduled updates, or execute in CI — it's a design-time companion for a local `renovate.json`. Use the real Renovate for the actual dependency-update pipeline.
|
|
229
275
|
- **`resolve_config` merges faithfully.** Preset expansion runs offline against a committed snapshot, then folds with Renovate's real `mergeChildConfig` in a worker thread — `mergeQuality: "faithful"` (it falls back to an approximate in-process merge as `"preview"` only if the worker is unavailable). Template substitution still implements only positional `{{argN}}` placeholders — non-positional tokens and Handlebars helpers are flagged in `warnings` and pass through verbatim. For full config resolution (datasource lookups, etc.), run `dry_run`.
|
|
230
276
|
- **`preview_custom_manager` is a subset of Renovate's custom managers.** It covers `customType: "regex"` (with `matchStringsStrategy` of `any` / `combination` / `recursive`) and `customType: "jsonata"` (with `fileFormat: "json" | "yaml" | "toml"`). File selection follows Renovate's `managerFilePatterns` semantics (globs or `/regex/`, entries unioned like Renovate's extract phase); the deprecated `fileMatch` is still accepted with a warning. Template substitution is `{{groupName}}` only — full Handlebars helpers/conditionals are not implemented. Other custom types (e.g. `html`) are out of scope. Use it for fast iteration; confirm with `dry_run`. Full coverage matrix in [`docs/tools.md#preview_custom_manager`](docs/tools.md#preview_custom_manager).
|
|
231
|
-
- **`dry_run` still uses fake binaries in CI.** The bundled Renovate is available (it's a runtime dependency), and `validate_config` / `write_config`
|
|
277
|
+
- **`dry_run` still uses fake binaries in CI.** The bundled Renovate is available (it's a runtime dependency), and `validate_config` / `write_config` have real-validator integration tests (`test/integration/realValidator.test.ts`) exercising the actual bundled `renovate-config-validator` — but `dry_run` itself still runs per-PR against fake binaries via `RENOVATE_BIN` / `RENOVATE_CONFIG_VALIDATOR_BIN` env overrides, for determinism and speed. A separate nightly workflow re-runs the full suite against `renovate@latest` end-to-end. Run `dry_run` locally against a real config to validate behaviour.
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
// AUTO-GENERATED by scripts/generate-datasources.mjs from renovate@44.181.5.
|
|
2
|
+
// Do not edit by hand. Run `npm run generate:datasources` to refresh.
|
|
3
|
+
export const RENOVATE_VERSION = "44.181.5";
|
|
4
|
+
/**
|
|
5
|
+
* All built-in datasource ids accepted by Renovate's `matchDatasources` and
|
|
6
|
+
* `customManagers[].datasourceTemplate`. Custom datasources (`custom.<name>`)
|
|
7
|
+
* are not listed.
|
|
8
|
+
*/
|
|
9
|
+
export const DATASOURCES = Object.freeze([
|
|
10
|
+
"apk",
|
|
11
|
+
"artifactory",
|
|
12
|
+
"aws-eks-addon",
|
|
13
|
+
"aws-machine-image",
|
|
14
|
+
"aws-rds",
|
|
15
|
+
"azure-bicep-resource",
|
|
16
|
+
"azure-pipelines-tasks",
|
|
17
|
+
"azure-tags",
|
|
18
|
+
"bazel",
|
|
19
|
+
"bitbucket-server-tags",
|
|
20
|
+
"bitbucket-tags",
|
|
21
|
+
"bitrise",
|
|
22
|
+
"buildpacks-registry",
|
|
23
|
+
"cdnjs",
|
|
24
|
+
"clojure",
|
|
25
|
+
"conan",
|
|
26
|
+
"conda",
|
|
27
|
+
"cpan",
|
|
28
|
+
"crate",
|
|
29
|
+
"custom",
|
|
30
|
+
"dart",
|
|
31
|
+
"dart-version",
|
|
32
|
+
"deb",
|
|
33
|
+
"deno",
|
|
34
|
+
"devbox",
|
|
35
|
+
"docker",
|
|
36
|
+
"dotnet-version",
|
|
37
|
+
"elm-package",
|
|
38
|
+
"endoflife-date",
|
|
39
|
+
"flutter-version",
|
|
40
|
+
"forgejo-releases",
|
|
41
|
+
"forgejo-tags",
|
|
42
|
+
"galaxy",
|
|
43
|
+
"galaxy-collection",
|
|
44
|
+
"git-refs",
|
|
45
|
+
"git-tags",
|
|
46
|
+
"gitea-releases",
|
|
47
|
+
"gitea-tags",
|
|
48
|
+
"github-digest",
|
|
49
|
+
"github-release-attachments",
|
|
50
|
+
"github-releases",
|
|
51
|
+
"github-runners",
|
|
52
|
+
"github-tags",
|
|
53
|
+
"gitlab-packages",
|
|
54
|
+
"gitlab-releases",
|
|
55
|
+
"gitlab-tags",
|
|
56
|
+
"glasskube-packages",
|
|
57
|
+
"go",
|
|
58
|
+
"golang-version",
|
|
59
|
+
"gradle-version",
|
|
60
|
+
"hackage",
|
|
61
|
+
"helm",
|
|
62
|
+
"hermit",
|
|
63
|
+
"hex",
|
|
64
|
+
"hexpm-bob",
|
|
65
|
+
"java-version",
|
|
66
|
+
"jenkins-plugins",
|
|
67
|
+
"jsr",
|
|
68
|
+
"kubernetes-api",
|
|
69
|
+
"maven",
|
|
70
|
+
"nextcloud",
|
|
71
|
+
"node-version",
|
|
72
|
+
"npm",
|
|
73
|
+
"nuget",
|
|
74
|
+
"orb",
|
|
75
|
+
"packagist",
|
|
76
|
+
"pod",
|
|
77
|
+
"puppet-forge",
|
|
78
|
+
"pypi",
|
|
79
|
+
"python-version",
|
|
80
|
+
"repology",
|
|
81
|
+
"rpm",
|
|
82
|
+
"ruby-version",
|
|
83
|
+
"rubygems",
|
|
84
|
+
"rust-version",
|
|
85
|
+
"sbt-package",
|
|
86
|
+
"sbt-plugin",
|
|
87
|
+
"terraform-module",
|
|
88
|
+
"terraform-provider",
|
|
89
|
+
"typst",
|
|
90
|
+
"unity3d",
|
|
91
|
+
"unity3d-packages"
|
|
92
|
+
]);
|
package/dist/index.js
CHANGED
|
@@ -15,6 +15,7 @@ import { registerDryRunDiff } from "./tools/dryRunDiff.js";
|
|
|
15
15
|
import { registerTestPackageRules } from "./tools/testPackageRules.js";
|
|
16
16
|
import { registerAnnotateDryRun } from "./tools/annotateDryRun.js";
|
|
17
17
|
import { registerExplainDependency } from "./tools/explainDependency.js";
|
|
18
|
+
import { registerExplainSchedule } from "./tools/explainSchedule.js";
|
|
18
19
|
import { registerWriteConfig } from "./tools/writeConfig.js";
|
|
19
20
|
import { registerMigrateConfig } from "./tools/migrateConfig.js";
|
|
20
21
|
import { registerGetVersion } from "./tools/getVersion.js";
|
|
@@ -50,13 +51,14 @@ const BASE_INSTRUCTIONS = [
|
|
|
50
51
|
" 11. dry_run_diff — semantic diff between two dry_run reports (added/removed/changed updates)",
|
|
51
52
|
" 12. annotate_dry_run — attribute each proposed update in a dry_run report to the packageRules that caused it (and flag rules that never matched)",
|
|
52
53
|
" 13. explain_dependency — why was/wasn't a given dependency updated: skipReason, updates, warnings and matched rules, read from a dry_run report (offline)",
|
|
53
|
-
" 14.
|
|
54
|
-
" 15.
|
|
54
|
+
" 14. explain_schedule — when will a `schedule` actually run: active-now plus the next windows in UTC and the config's timezone, or Renovate's own invalid-schedule message (offline)",
|
|
55
|
+
" 15. migrate_config — apply Renovate's built-in migrations (deprecated keys → current schema) and return the migrated config",
|
|
56
|
+
" 16. write_config — save the agreed-upon config (validates first)",
|
|
55
57
|
"",
|
|
56
58
|
"Before the first repo-touching tool call in a session (read_config, resolve_config, dry_run, write_config, …), call check_setup with the same repoPath. It surfaces token / endpoint / connectivity problems up front (e.g. \"set GITHUB_TOKEN or github-actions deps will be skipped\") instead of waiting for dry_run to fail. Skip if the user has already confirmed setup or is doing offline-only work that doesn't depend on git origin or registries.",
|
|
57
59
|
"If any tool fails unexpectedly, call check_setup to diagnose CLI availability.",
|
|
58
60
|
"Built-in preset reference: renovate://presets (namespace index), renovate://presets/{namespace} (one namespace), renovate://preset/{name} (one preset's expanded JSON).",
|
|
59
|
-
"Config option reference: renovate://options (index of all config options), renovate://option/{name} (one option's full definition). Manager names: renovate://managers.",
|
|
61
|
+
"Config option reference: renovate://options (index of all config options), renovate://option/{name} (one option's full definition). Manager names: renovate://managers. Datasource ids: renovate://datasources.",
|
|
60
62
|
].join("\n");
|
|
61
63
|
if (process.platform === "win32") {
|
|
62
64
|
process.stderr.write("renovate-mcp does not support Windows. Supported platforms are Linux and macOS — see the `os` field in package.json. Run on WSL2 or a Linux/macOS host instead.\n");
|
|
@@ -84,6 +86,7 @@ registerDryRunDiff(server);
|
|
|
84
86
|
registerTestPackageRules(server);
|
|
85
87
|
registerAnnotateDryRun(server);
|
|
86
88
|
registerExplainDependency(server);
|
|
89
|
+
registerExplainSchedule(server);
|
|
87
90
|
registerMigrateConfig(server);
|
|
88
91
|
registerWriteConfig(server);
|
|
89
92
|
registerGetVersion(server);
|
package/dist/lib/configLinter.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ALL_MANAGERS, CUSTOM_MANAGERS } from "../data/managers.generated.js";
|
|
2
|
+
import { DATASOURCES } from "../data/datasources.generated.js";
|
|
2
3
|
import { DEPRECATED_KEYS, } from "../data/migrations.generated.js";
|
|
3
4
|
import { isRecord } from "./util.js";
|
|
4
5
|
const REGEX_AWARE_FIELDS = new Set([
|
|
@@ -12,23 +13,28 @@ const VALID_MANAGER_NAMES = new Set([
|
|
|
12
13
|
...ALL_MANAGERS,
|
|
13
14
|
...CUSTOM_MANAGERS.map((m) => `custom.${m}`),
|
|
14
15
|
]);
|
|
16
|
+
const VALID_DATASOURCES = new Set(DATASOURCES);
|
|
15
17
|
const DEPRECATED_KEY_LOOKUP = new Map(DEPRECATED_KEYS.map((e) => [e.oldKey, e]));
|
|
16
18
|
// Hand-maintained snapshot of keys that count as "actions" on a packageRules entry
|
|
17
19
|
// (i.e. keys whose presence means the rule actually *does* something to the matched
|
|
18
20
|
// deps). Source of truth: `node_modules/renovate/dist/config/options/index.js` —
|
|
19
21
|
// entries whose `parents` array includes `"packageRules"`, plus the broader set of
|
|
20
22
|
// inheritable config options Renovate allows inside a packageRules entry
|
|
21
|
-
// (groupName, automerge, labels, schedule, …).
|
|
22
|
-
//
|
|
23
|
-
//
|
|
24
|
-
//
|
|
25
|
-
//
|
|
23
|
+
// (groupName, automerge, labels, schedule, …). We deliberately do NOT generate this
|
|
24
|
+
// list at build time: per CLAUDE.md, the linter must stay independent of the runtime
|
|
25
|
+
// `renovate` import, so this stays a curated snapshot — analogous to the other
|
|
26
|
+
// allow-lists under `src/data/`. Drift guard: test/unit/allowListDrift.test.ts checks
|
|
27
|
+
// every entry against the installed Renovate's option registry (real option, not
|
|
28
|
+
// global-only, `scopes` allow packageRules) and that every packageRules-only option
|
|
29
|
+
// is listed, so a `renovate` bump fails there instead of leaving this stale.
|
|
26
30
|
//
|
|
27
31
|
// Explicitly NOT in this set (kept here as documentation):
|
|
28
32
|
// - `description` (metadata)
|
|
29
33
|
// - every `match*` / `exclude*` key (selectors)
|
|
30
34
|
// - `paths` / `excludePaths` (path-scope selectors)
|
|
31
35
|
// - `matchJsonata`, `matchNewValue`, `matchCurrentAge` etc. (selectors)
|
|
36
|
+
// - `vulnerabilityAlerts`, `dependencyDashboardLabels` (Renovate's option `scopes`
|
|
37
|
+
// restrict them to the top level; its validator warns inside a packageRule)
|
|
32
38
|
// - `changelogUrl`, `sourceDirectory`, `sourceUrl` (metadata/locator overrides —
|
|
33
39
|
// treated as actions below since they actively override resolved fields)
|
|
34
40
|
export const PACKAGE_RULE_ACTION_KEYS = new Set([
|
|
@@ -51,7 +57,6 @@ export const PACKAGE_RULE_ACTION_KEYS = new Set([
|
|
|
51
57
|
"commitMessageSuffix",
|
|
52
58
|
"commitMessageTopic",
|
|
53
59
|
"dependencyDashboardApproval",
|
|
54
|
-
"dependencyDashboardLabels",
|
|
55
60
|
"draftPR",
|
|
56
61
|
"enabled",
|
|
57
62
|
"extends",
|
|
@@ -102,7 +107,6 @@ export const PACKAGE_RULE_ACTION_KEYS = new Set([
|
|
|
102
107
|
"sourceUrl",
|
|
103
108
|
"stopUpdatingLabel",
|
|
104
109
|
"versioning",
|
|
105
|
-
"vulnerabilityAlerts",
|
|
106
110
|
]);
|
|
107
111
|
const DEPRECATED_KEY_CONTAINERS = ["packageRules", "hostRules", "customManagers"];
|
|
108
112
|
// Schedule-validation heuristic. Behavioral source of truth:
|
|
@@ -121,6 +125,9 @@ const DEPRECATED_KEY_CONTAINERS = ["packageRules", "hostRules", "customManagers"
|
|
|
121
125
|
// a deliberate bias toward false negatives over false positives: a non-later
|
|
122
126
|
// string that happens to contain one of these anchor tokens gets a free pass,
|
|
123
127
|
// but every plausible later-text input will contain at least one of them.
|
|
128
|
+
// Drift guard: test/unit/allowListDrift.test.ts parses each token (in a minimal
|
|
129
|
+
// phrase) with the `@breejs/later` Renovate ships, so a token the grammar does
|
|
130
|
+
// not know can't sneak in or linger after a bump.
|
|
124
131
|
// Special-cased sentinels meaning "no schedule". Renovate treats these as
|
|
125
132
|
// always-on.
|
|
126
133
|
const SCHEDULE_ANY_TIME = new Set(["", "at any time"]);
|
|
@@ -132,12 +139,10 @@ const SCHEDULE_MAPPING_LITERALS = new Set([
|
|
|
132
139
|
]);
|
|
133
140
|
// Lowercase tokens. Presence of at least one (whole-word, case-insensitive)
|
|
134
141
|
// makes a non-cron string plausibly-later-text. Curated from later's grammar.
|
|
135
|
-
const LATER_TEXT_ANCHOR_TOKENS = new Set([
|
|
142
|
+
export const LATER_TEXT_ANCHOR_TOKENS = new Set([
|
|
136
143
|
// Time anchors
|
|
137
144
|
"am",
|
|
138
145
|
"pm",
|
|
139
|
-
"noon",
|
|
140
|
-
"midnight",
|
|
141
146
|
"before",
|
|
142
147
|
"after",
|
|
143
148
|
"between",
|
|
@@ -181,10 +186,7 @@ const LATER_TEXT_ANCHOR_TOKENS = new Set([
|
|
|
181
186
|
"feb",
|
|
182
187
|
"mar",
|
|
183
188
|
"apr",
|
|
184
|
-
"jun",
|
|
185
|
-
"jul",
|
|
186
189
|
"aug",
|
|
187
|
-
"sep",
|
|
188
190
|
"sept",
|
|
189
191
|
"oct",
|
|
190
192
|
"nov",
|
|
@@ -195,9 +197,7 @@ const LATER_TEXT_ANCHOR_TOKENS = new Set([
|
|
|
195
197
|
"week",
|
|
196
198
|
"weeks",
|
|
197
199
|
"weekday",
|
|
198
|
-
"weekdays",
|
|
199
200
|
"weekend",
|
|
200
|
-
"weekends",
|
|
201
201
|
"month",
|
|
202
202
|
"months",
|
|
203
203
|
"year",
|
|
@@ -206,9 +206,6 @@ const LATER_TEXT_ANCHOR_TOKENS = new Set([
|
|
|
206
206
|
"hours",
|
|
207
207
|
"minute",
|
|
208
208
|
"minutes",
|
|
209
|
-
"morning",
|
|
210
|
-
"evening",
|
|
211
|
-
"night",
|
|
212
209
|
]);
|
|
213
210
|
// Cron-shape: 5 or 6 whitespace-separated fields, every char in `[0-9*/,\-]`,
|
|
214
211
|
// FIRST field is exactly `*` (Renovate explicitly rejects non-`*` minutes).
|
|
@@ -241,6 +238,9 @@ export function lintConfig(config) {
|
|
|
241
238
|
checkAutomergeIncludesMajor(config, findings);
|
|
242
239
|
checkDuplicatePackageRuleMatchers(config, findings);
|
|
243
240
|
checkHostRuleInlineSecret(config, findings);
|
|
241
|
+
checkDatasourceTemplates(config, findings);
|
|
242
|
+
checkScheduleWithoutTimezone(config, findings);
|
|
243
|
+
checkPackageRuleLabelsOverrides(config, findings);
|
|
244
244
|
return findings;
|
|
245
245
|
}
|
|
246
246
|
function isSelectorKey(key) {
|
|
@@ -422,6 +422,76 @@ function checkDuplicatePackageRuleMatchers(config, findings) {
|
|
|
422
422
|
}
|
|
423
423
|
}
|
|
424
424
|
}
|
|
425
|
+
// First `schedule` entry (anywhere in the tree, same reach as `invalid-schedule`)
|
|
426
|
+
// that actually restricts timing, as [path, value].
|
|
427
|
+
function findRestrictingSchedule(node, pathStr) {
|
|
428
|
+
if (Array.isArray(node)) {
|
|
429
|
+
for (let i = 0; i < node.length; i++) {
|
|
430
|
+
const hit = findRestrictingSchedule(node[i], `${pathStr}[${i}]`);
|
|
431
|
+
if (hit)
|
|
432
|
+
return hit;
|
|
433
|
+
}
|
|
434
|
+
return null;
|
|
435
|
+
}
|
|
436
|
+
if (!isRecord(node))
|
|
437
|
+
return null;
|
|
438
|
+
for (const [key, value] of Object.entries(node)) {
|
|
439
|
+
const childPath = pathStr ? `${pathStr}.${key}` : key;
|
|
440
|
+
if (key === "schedule") {
|
|
441
|
+
if (typeof value === "string" && !SCHEDULE_ANY_TIME.has(value)) {
|
|
442
|
+
return [childPath, value];
|
|
443
|
+
}
|
|
444
|
+
if (Array.isArray(value)) {
|
|
445
|
+
const i = value.findIndex((v) => typeof v === "string" && !SCHEDULE_ANY_TIME.has(v));
|
|
446
|
+
if (i !== -1)
|
|
447
|
+
return [`${childPath}[${i}]`, value[i]];
|
|
448
|
+
}
|
|
449
|
+
continue;
|
|
450
|
+
}
|
|
451
|
+
const hit = findRestrictingSchedule(value, childPath);
|
|
452
|
+
if (hit)
|
|
453
|
+
return hit;
|
|
454
|
+
}
|
|
455
|
+
return null;
|
|
456
|
+
}
|
|
457
|
+
function checkScheduleWithoutTimezone(config, findings) {
|
|
458
|
+
if (!isRecord(config) || config.timezone)
|
|
459
|
+
return;
|
|
460
|
+
const hit = findRestrictingSchedule(config, "");
|
|
461
|
+
if (!hit)
|
|
462
|
+
return;
|
|
463
|
+
const [path, value] = hit;
|
|
464
|
+
// One finding per config: the fix (a root `timezone`) is the same for all.
|
|
465
|
+
findings.push({
|
|
466
|
+
ruleId: "schedule-without-timezone",
|
|
467
|
+
severity: "warn",
|
|
468
|
+
path,
|
|
469
|
+
value,
|
|
470
|
+
message: `${path} sets a schedule but the root config has no \`timezone\`, so Renovate evaluates every schedule in UTC. ` +
|
|
471
|
+
"Ignore this if an extended preset sets the timezone.",
|
|
472
|
+
suggestion: 'Set a top-level "timezone" to an IANA name, e.g. "Europe/Berlin".',
|
|
473
|
+
});
|
|
474
|
+
}
|
|
475
|
+
function checkPackageRuleLabelsOverrides(config, findings) {
|
|
476
|
+
if (!isRecord(config))
|
|
477
|
+
return;
|
|
478
|
+
if (!Array.isArray(config.labels) || config.labels.length === 0)
|
|
479
|
+
return;
|
|
480
|
+
if (!Array.isArray(config.packageRules))
|
|
481
|
+
return;
|
|
482
|
+
config.packageRules.forEach((entry, i) => {
|
|
483
|
+
if (!isRecord(entry) || !("labels" in entry))
|
|
484
|
+
return;
|
|
485
|
+
findings.push({
|
|
486
|
+
ruleId: "package-rule-labels-overrides",
|
|
487
|
+
severity: "warn",
|
|
488
|
+
path: `packageRules[${i}].labels`,
|
|
489
|
+
value: JSON.stringify(entry.labels),
|
|
490
|
+
message: `packageRules[${i}].labels replaces the root labels (${JSON.stringify(config.labels)}) for matched deps instead of adding to them.`,
|
|
491
|
+
suggestion: `Use addLabels in packageRules[${i}] to append to the root labels.`,
|
|
492
|
+
});
|
|
493
|
+
});
|
|
494
|
+
}
|
|
425
495
|
const HOST_RULE_SECRET_FIELDS = ["token", "password"];
|
|
426
496
|
function checkHostRuleInlineSecret(config, findings) {
|
|
427
497
|
if (!isRecord(config))
|
|
@@ -537,6 +607,18 @@ function walk(node, pathStr, findings) {
|
|
|
537
607
|
});
|
|
538
608
|
}
|
|
539
609
|
}
|
|
610
|
+
else if (key === "matchDatasources") {
|
|
611
|
+
if (Array.isArray(value)) {
|
|
612
|
+
value.forEach((entry, i) => {
|
|
613
|
+
if (typeof entry === "string") {
|
|
614
|
+
checkDatasource(entry, `${childPath}[${i}]`, findings);
|
|
615
|
+
}
|
|
616
|
+
});
|
|
617
|
+
}
|
|
618
|
+
else if (typeof value === "string") {
|
|
619
|
+
checkDatasource(value, childPath, findings);
|
|
620
|
+
}
|
|
621
|
+
}
|
|
540
622
|
else if (MANAGER_FIELDS.has(key)) {
|
|
541
623
|
if (Array.isArray(value)) {
|
|
542
624
|
value.forEach((entry, i) => {
|
|
@@ -604,26 +686,73 @@ function checkSchedule(value, path, findings) {
|
|
|
604
686
|
"preset such as `schedule:earlyMondays`.",
|
|
605
687
|
});
|
|
606
688
|
}
|
|
689
|
+
// Renovate evaluates matchManagers and matchDatasources with
|
|
690
|
+
// matchRegexOrGlobList: `/…/` (optionally `/…/i`) is a regex and anything with
|
|
691
|
+
// glob metachars is a glob — neither is a literal id to check against a list.
|
|
692
|
+
// Deliberately permissive: a typo that happens to contain a glob char (e.g.
|
|
693
|
+
// `docker[`) is skipped rather than risk an error-severity finding on a valid glob.
|
|
694
|
+
function isRegexOrGlobEntry(name) {
|
|
695
|
+
return /^\/.+\/i?$/.test(name) || /[*?[\]{}]/.test(name);
|
|
696
|
+
}
|
|
607
697
|
function checkManager(raw, path, findings) {
|
|
608
|
-
|
|
698
|
+
// `!` is Renovate's negation prefix for every matchRegexOrGlobList field.
|
|
699
|
+
const name = raw.startsWith("!") ? raw.slice(1) : raw;
|
|
700
|
+
if (VALID_MANAGER_NAMES.has(name) || isRegexOrGlobEntry(name))
|
|
609
701
|
return;
|
|
610
|
-
const suggestion =
|
|
702
|
+
const suggestion = nearestName(name, VALID_MANAGER_NAMES);
|
|
611
703
|
const hint = suggestion ? ` Did you mean '${suggestion}'?` : "";
|
|
612
704
|
const finding = {
|
|
613
705
|
ruleId: "matchManagers-unknown-name",
|
|
614
706
|
severity: "error",
|
|
615
707
|
path,
|
|
616
708
|
value: raw,
|
|
617
|
-
message: `'${
|
|
709
|
+
message: `'${name}' is not a known Renovate manager. Renovate will silently apply this rule to zero packages.${hint}`,
|
|
710
|
+
};
|
|
711
|
+
if (suggestion)
|
|
712
|
+
finding.suggestion = suggestion;
|
|
713
|
+
findings.push(finding);
|
|
714
|
+
}
|
|
715
|
+
function checkDatasourceTemplates(config, findings) {
|
|
716
|
+
if (!isRecord(config) || !Array.isArray(config.customManagers))
|
|
717
|
+
return;
|
|
718
|
+
config.customManagers.forEach((entry, i) => {
|
|
719
|
+
if (!isRecord(entry))
|
|
720
|
+
return;
|
|
721
|
+
const tpl = entry.datasourceTemplate;
|
|
722
|
+
// A templated value is resolved per match; only a literal id can be checked.
|
|
723
|
+
// A literal template is an id, never a pattern, so no `!` / regex / glob leniency.
|
|
724
|
+
if (typeof tpl !== "string" || tpl.includes("{{"))
|
|
725
|
+
return;
|
|
726
|
+
checkDatasource(tpl, `customManagers[${i}].datasourceTemplate`, findings, false);
|
|
727
|
+
});
|
|
728
|
+
}
|
|
729
|
+
function checkDatasource(raw, path, findings, isPattern = true) {
|
|
730
|
+
// Renovate matches `matchDatasources` via matchRegexOrGlobList: `!` negates,
|
|
731
|
+
// `/…/` is a regex and anything with glob metachars is a glob — none of those
|
|
732
|
+
// are literal ids. `custom.<name>` routes to the user's customDatasources.
|
|
733
|
+
const name = isPattern && raw.startsWith("!") ? raw.slice(1) : raw;
|
|
734
|
+
if (VALID_DATASOURCES.has(name) ||
|
|
735
|
+
name.startsWith("custom.") ||
|
|
736
|
+
(isPattern && isRegexOrGlobEntry(name))) {
|
|
737
|
+
return;
|
|
738
|
+
}
|
|
739
|
+
const suggestion = nearestName(name, VALID_DATASOURCES);
|
|
740
|
+
const hint = suggestion ? ` Did you mean '${suggestion}'?` : "";
|
|
741
|
+
const finding = {
|
|
742
|
+
ruleId: "matchDatasources-unknown-name",
|
|
743
|
+
severity: "error",
|
|
744
|
+
path,
|
|
745
|
+
value: raw,
|
|
746
|
+
message: `'${name}' is not a known Renovate datasource. Renovate does not validate datasource ids, so this silently matches (or looks up) nothing.${hint}`,
|
|
618
747
|
};
|
|
619
748
|
if (suggestion)
|
|
620
749
|
finding.suggestion = suggestion;
|
|
621
750
|
findings.push(finding);
|
|
622
751
|
}
|
|
623
|
-
function
|
|
752
|
+
function nearestName(name, candidates) {
|
|
624
753
|
let best = null;
|
|
625
754
|
let bestDistance = Infinity;
|
|
626
|
-
for (const candidate of
|
|
755
|
+
for (const candidate of candidates) {
|
|
627
756
|
const d = damerauLevenshtein(name, candidate);
|
|
628
757
|
if (d < bestDistance) {
|
|
629
758
|
bestDistance = d;
|