Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(managers/custom): generic manager for json files #32784

Open
wants to merge 42 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 37 commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
cb0dd89
implement jsonata manager
RahulGautamSingh Nov 26, 2024
6caecbd
validation
RahulGautamSingh Nov 28, 2024
739b28f
Merge branch 'main' into feat/generic-manager
RahulGautamSingh Nov 28, 2024
0432ece
fix tests
RahulGautamSingh Nov 28, 2024
8f88d8d
Merge branch 'feat/generic-manager' of https://github.com/RahulGautam…
RahulGautamSingh Nov 28, 2024
754324d
update docs
RahulGautamSingh Nov 28, 2024
c497553
fix tests
RahulGautamSingh Nov 28, 2024
f8a32ea
fix lint issue
RahulGautamSingh Nov 28, 2024
a375c51
refactor
RahulGautamSingh Nov 28, 2024
0b54c6d
fix ci issues
RahulGautamSingh Nov 28, 2024
4c14ac6
remove duplicate code
RahulGautamSingh Nov 28, 2024
caf4513
add jsonata to customType allowed values
RahulGautamSingh Nov 28, 2024
55fc985
Apply Suggestions
RahulGautamSingh Nov 29, 2024
69f6745
refactor: remove unused types
RahulGautamSingh Nov 29, 2024
dabb627
docs: refactor
RahulGautamSingh Nov 29, 2024
7f60365
Update docs/usage/configuration-options.md
RahulGautamSingh Nov 29, 2024
bfd00e1
apply suggestions
RahulGautamSingh Nov 30, 2024
292c91f
docs: redo structure
RahulGautamSingh Nov 30, 2024
1324d83
apply suggestions
RahulGautamSingh Dec 5, 2024
ff13a21
Apply Suggestions
RahulGautamSingh Dec 7, 2024
c8db815
docs: remove redundant codeblock
RahulGautamSingh Dec 7, 2024
c92ad2b
refactor: tests
RahulGautamSingh Dec 7, 2024
28e64e5
fix: docs
RahulGautamSingh Dec 11, 2024
cb25557
fix: types
RahulGautamSingh Dec 11, 2024
d4cfb2d
feat: add new field fileFormat
RahulGautamSingh Dec 11, 2024
96c0024
refactor: simplify logic for handleMatching()
RahulGautamSingh Dec 11, 2024
1ef8764
refactor: apply DRY concept
RahulGautamSingh Dec 11, 2024
ffd6228
fix(types): indentation
RahulGautamSingh Dec 11, 2024
c746d87
Merge branch 'main' into feat/generic-manager
RahulGautamSingh Dec 17, 2024
81d9121
Merge branch 'main' into feat/generic-manager
RahulGautamSingh Dec 18, 2024
4e4739c
validation for JSONata manager
RahulGautamSingh Dec 18, 2024
e66b916
docs(customManagers): fileFormat
RahulGautamSingh Dec 18, 2024
e28ba81
Apply Suggestion
RahulGautamSingh Dec 18, 2024
76b0cc4
fix issues
RahulGautamSingh Dec 18, 2024
0e48b18
apply suggestions
RahulGautamSingh Dec 18, 2024
f4ec390
Apply Suggestions
RahulGautamSingh Dec 19, 2024
66f2577
fix test
RahulGautamSingh Dec 19, 2024
a632dc6
Merge branch 'main' into feat/generic-manager
RahulGautamSingh Dec 19, 2024
d1da658
rebase
RahulGautamSingh Dec 19, 2024
e9ab58a
matchStrings: update description
RahulGautamSingh Dec 19, 2024
a36550e
update docs
RahulGautamSingh Dec 19, 2024
3788661
fix test
RahulGautamSingh Dec 21, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 62 additions & 6 deletions docs/usage/configuration-options.md
Original file line number Diff line number Diff line change
Expand Up @@ -706,22 +706,22 @@ You can define custom managers to handle:
- Proprietary file formats or conventions
- Popular file formats not yet supported as a manager by Renovate

Currently we only have one custom manager.
Currently we have two custom managers.
The `regex` manager which is based on using Regular Expression named capture groups.
The `jsonata` manager which is based on using JSONata queries.

You must have a named capture group matching (e.g. `(?<depName>.*)`) _or_ configure its corresponding template (e.g. `depNameTemplate`) for these fields:
You must capture/extract the following three fields _or_ configure its corresponding template (e.g. `depNameTemplate`) for these fields:

- `datasource`
- `depName` and / or `packageName`
- `currentValue`

Use named capture group matching _or_ set a corresponding template.
We recommend you use only _one_ of these methods, or you'll get confused.

We recommend that you also tell Renovate what `versioning` to use.
If the `versioning` field is missing, then Renovate defaults to using `semver` versioning.

For more details and examples about it, see our [documentation for the `regex` manager](modules/manager/regex/index.md).
For more details and examples about it, see our documentation for the [`regex` manager](modules/manager/regex/index.md) and the [`JSONata` manager](modules/manager/jsonata/index.md).
For template fields, use the triple brace `{{{ }}}` notation to avoid Handlebars escaping any special characters.

<!-- prettier-ignore -->
Expand Down Expand Up @@ -763,6 +763,10 @@ This will lead to following update where `1.21-alpine` is the newest version of
image: my.new.registry/aRepository/andImage:1.21-alpine
```

<!-- prettier-ignore -->
!!! note
Can only be used with the custom regex maanger.

### currentValueTemplate

If the `currentValue` for a dependency is not captured with a named group then it can be defined in config using this field.
Expand All @@ -786,6 +790,21 @@ Example:
}
```

```json title="Parsing a JSON file with a custom manager"
{
"customManagers": [
{
"customType": "jsonata",
"fileFormat": "json",
"fileMatch": ["file.json"],
"matchStrings": [
"packages.{ \"depName\": package, \"currentValue\": version }"
]
}
]
}
```

### datasourceTemplate

If the `datasource` for a dependency is not captured with a named group then it can be defined in config using this field.
Expand All @@ -806,20 +825,53 @@ It will be compiled using Handlebars and the regex `groups` result.
If `extractVersion` cannot be captured with a named capture group in `matchString` then it can be defined manually using this field.
It will be compiled using Handlebars and the regex `groups` result.

### fileFormat

It specifies the syntax of the package file being managed by the custom JSONata manager.
This setting helps the system correctly parse and interpret the configuration file's contents.

Currently, only the `json` format is supported.

```json title="Parsing a JSON file with a custom manager"
{
"customManagers": [
{
"customType": "jsonata",
"fileFormat": "json",
"fileMatch": [".renovaterc"],
"matchStrings": [
"packages.{ \"depName\": package, \"currentValue\": version }"
]
}
]
}
```

### matchStrings

Each `matchStrings` must be a valid regular expression, optionally with named capture groups.
Each `matchStrings` must be one of the two:

1. a valid regular expression, optionally with named capture groups (if using `customType=regex`)
2. a valid, escaped [JSONata](https://docs.jsonata.org/overview.html) query (if using `customType=json`)

Example:

```json
```json title="matchStrings with a valid regular expression"
{
"matchStrings": [
"ENV .*?_VERSION=(?<currentValue>.*) # (?<datasource>.*?)/(?<depName>.*?)\\s"
]
}
```

```json title="matchStrings with a valid JSONata query"
{
"matchStrings": [
"packages.{ \"depName\": package, \"currentValue\": version }"
]
}
```

### matchStringsStrategy

`matchStringsStrategy` controls behavior when multiple `matchStrings` values are provided.
Expand All @@ -829,6 +881,10 @@ Three options are available:
- `recursive`
- `combination`

<!--prettier-ignore-->
!!! note
Only to be used with custom regex manager.

#### any

Each provided `matchString` will be matched individually to the content of the `packageFile`.
Expand Down
15 changes: 12 additions & 3 deletions lib/config/options/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2734,18 +2734,27 @@ const options: RenovateOptions[] = [
description:
'Custom manager to use. Valid only within a `customManagers` object.',
type: 'string',
allowedValues: ['regex'],
allowedValues: ['jsonata', 'regex'],
RahulGautamSingh marked this conversation as resolved.
Show resolved Hide resolved
parents: ['customManagers'],
cli: false,
env: false,
},
{
name: 'fileFormat',
description:
'It specifies the syntax of the package file being managed by the custom JSONata manager.',
type: 'string',
allowedValues: ['json'],
parents: ['customManagers'],
cli: false,
env: false,
},
{
name: 'matchStrings',
description:
'Regex capture rule to use. Valid only within a `customManagers` object.',
'Queries to use. Valid only within a `customManagers` object. See `customType` docs.',
RahulGautamSingh marked this conversation as resolved.
Show resolved Hide resolved
type: 'array',
subType: 'string',
format: 'regex',
parents: ['customManagers'],
cli: false,
env: false,
Expand Down
104 changes: 95 additions & 9 deletions lib/config/validation.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -722,7 +722,8 @@ describe('config/validation', () => {
currentValueTemplate: 'baz',
},
{
customType: 'regex',
customType: 'jsonata',
fileFormat: 'json',
fileMatch: ['foo'],
depNameTemplate: 'foo',
datasourceTemplate: 'bar',
Expand Down Expand Up @@ -792,6 +793,60 @@ describe('config/validation', () => {
expect(errors).toHaveLength(1);
});

it('error if no fileFormat in custom JSONata manager', async () => {
const config: RenovateConfig = {
customManagers: [
{
customType: 'jsonata',
fileMatch: ['package.json'],
matchStrings: [
'packages.{"depName": name, "currentValue": version, "datasource": "npm"}',
],
},
],
};
const { warnings, errors } = await configValidation.validateConfig(
'repo',
config,
true,
);
expect(warnings).toHaveLength(0);
expect(errors).toMatchObject([
{
topic: 'Configuration Error',
message: 'Each JSONata manager must contain a fileFormat field.',
},
]);
});

it('validates JSONata query for each matchStrings', async () => {
const config: RenovateConfig = {
customManagers: [
{
customType: 'jsonata',
fileFormat: 'json',
fileMatch: ['package.json'],
matchStrings: ['packages.{'],
depNameTemplate: 'foo',
datasourceTemplate: 'bar',
currentValueTemplate: 'baz',
},
],
};
const { warnings, errors } = await configValidation.validateConfig(
'repo',
config,
true,
);
expect(warnings).toHaveLength(0);
expect(errors).toMatchObject([
{
topic: 'Configuration Error',
message: `Invalid JSONata query for customManagers: \`packages.{\``,
},
]);
});

// testing if we get all errors at once or not (possible), this does not include customType or fileMatch
// since they are common to all custom managers
it('validates all possible regex manager options', async () => {
Expand Down Expand Up @@ -827,14 +882,12 @@ describe('config/validation', () => {
depTypeTemplate: 'apple',
},
{
customType: 'regex',
fileMatch: ['Dockerfile'],
matchStrings: ['ENV (?<currentValue>.*?)\\s'],
packageNameTemplate: 'foo',
datasourceTemplate: 'bar',
registryUrlTemplate: 'foobar',
extractVersionTemplate: '^(?<version>v\\d+\\.\\d+)',
depTypeTemplate: 'apple',
customType: 'jsonata',
fileFormat: 'json',
fileMatch: ['package.json'],
matchStrings: [
'packages.{"depName": depName, "currentValue": version, "datasource": "npm"}',
],
},
],
};
Expand Down Expand Up @@ -892,6 +945,39 @@ describe('config/validation', () => {
expect(errors).toHaveLength(1);
});

it('errors if customManager fields are missing: JSONataManager', async () => {
const config: RenovateConfig = {
customManagers: [
{
customType: 'jsonata',
fileFormat: 'json',
fileMatch: ['package.json'],
matchStrings: ['packages'],
},
],
};
const { warnings, errors } = await configValidation.validateConfig(
'repo',
config,
true,
);
expect(warnings).toHaveLength(0);
expect(errors).toMatchObject([
{
topic: 'Configuration Error',
message: `JSONata Managers must contain currentValueTemplate configuration or currentValue in the query `,
},
{
topic: 'Configuration Error',
message: `JSONata Managers must contain datasourceTemplate configuration or datasource in the query `,
},
{
topic: 'Configuration Error',
message: `JSONata Managers must contain depName or packageName in the query or their templates`,
},
]);
});

it('ignore keys', async () => {
const config = {
$schema: 'renovate.json',
Expand Down
Loading
Loading