Skip to content

Commit

Permalink
feat: support preset versioning with git tags (#11565)
Browse files Browse the repository at this point in the history
Co-authored-by: Rhys Arkins <rhys@arkins.net>
Co-authored-by: HonkingGoose <34918129+HonkingGoose@users.noreply.github.com>
Co-authored-by: Michael Kriese <michael.kriese@visualon.de>
Co-authored-by: Sergei Zharinov <zharinov@users.noreply.github.com>
  • Loading branch information
5 people committed Nov 5, 2021
1 parent 338fb6e commit 7579875
Show file tree
Hide file tree
Showing 18 changed files with 553 additions and 61 deletions.
50 changes: 41 additions & 9 deletions docs/development/shareable-configs.md
Expand Up @@ -50,12 +50,44 @@ If you use a non-scoped config, you must use a preset name!
In general, GitHub, GitLab or Gitea-based preset hosting is easier than npm because you avoid the "publish" step - simply commit preset code to the default branch and it will be picked up by Renovate the next time it runs.
An additional benefit of using source code hosting is that the same token/authentication can be reused by Renovate in case you want to make your config private.

| name | example use | preset | resolves as | filename |
| ----------------------- | -------------------- | --------- | ------------------------------------ | -------------- |
| GitHub default | `github>abc/foo` | `default` | `https://github.com/abc/foo` | `default.json` |
| GitHub with preset name | `github>abc/foo:xyz` | `xyz` | `https://github.com/abc/foo` | `xyz.json` |
| GitLab default | `gitlab>abc/foo` | `default` | `https://gitlab.com/abc/foo` | `default.json` |
| GitLab with preset name | `gitlab>abc/foo:xyz` | `xyz` | `https://gitlab.com/abc/foo` | `xyz.json` |
| Gitea default | `gitea>abc/foo` | `default` | `https://gitea.com/abc/foo` | `default.json` |
| Gitea with preset name | `gitea>abc/foo:xyz` | `xyz` | `https://gitea.com/abc/foo` | `xyz.json` |
| Local default | `local>abc/foo` | `default` | `https://github.company.com/abc/foo` | `default.json` |
You can set a Git tag (like a SemVer) to use a specific release of your shared config.

#### GitHub

| name | example use | preset | resolves as | filename | Git tag |
| ------------------------------------------- | ------------------------------- | --------- | ---------------------------- | --------------- | -------------- |
| GitHub default | `github>abc/foo` | `default` | `https://github.com/abc/foo` | `default.json` | Default branch |
| GitHub with preset name | `github>abc/foo:xyz` | `xyz` | `https://github.com/abc/foo` | `xyz.json` | Default branch |
| GitHub default with a tag | `github>abc/foo#1.5.4` | `default` | `https://github.com/abc/foo` | `default.json` | `1.5.4` |
| GitHub with preset name with a tag | `github>abc/foo:xyz#1.5.4` | `xyz` | `https://github.com/abc/foo` | `xyz.json` | `1.5.4` |
| GitHub with preset name and path with a tag | `github>abc/foo:path/xyz#1.5.4` | `xyz` | `https://github.com/abc/foo` | `path/xyz.json` | `1.5.4` |

#### GitLab

| name | example use | preset | resolves as | filename | Git tag |
| ------------------------------------------- | ------------------------------- | --------- | ---------------------------- | --------------- | -------------- |
| GitLab default | `gitlab>abc/foo` | `default` | `https://gitlab.com/abc/foo` | `default.json` | Default branch |
| GitLab with preset name | `gitlab>abc/foo:xyz` | `xyz` | `https://gitlab.com/abc/foo` | `xyz.json` | Default branch |
| GitLab default with a tag | `gitlab>abc/foo#1.5.4` | `default` | `https://gitlab.com/abc/foo` | `default.json` | `1.5.4` |
| GitLab with preset name with a tag | `gitlab>abc/foo:xyz#1.5.4` | `xyz` | `https://gitlab.com/abc/foo` | `xyz.json` | `1.5.4` |
| GitLab with preset name and path with a tag | `gitlab>abc/foo:path/xyz#1.5.4` | `xyz` | `https://gitlab.com/abc/foo` | `path/xyz.json` | `1.5.4` |

#### Gitea

| name | example use | preset | resolves as | filename | Git tag |
| ------------------------------------------ | ------------------------------ | --------- | --------------------------- | --------------- | -------------- |
| Gitea default | `gitea>abc/foo` | `default` | `https://gitea.com/abc/foo` | `default.json` | Default branch |
| Gitea with preset name | `gitea>abc/foo:xyz` | `xyz` | `https://gitea.com/abc/foo` | `xyz.json` | Default branch |
| Gitea default with a tag | `gitea>abc/foo#1.5.4` | `default` | `https://gitea.com/abc/foo` | `default.json` | `1.5.4` |
| Gitea with preset name with a tag | `gitea>abc/foo:xyz#1.5.4` | `xyz` | `https://gitea.com/abc/foo` | `xyz.json` | `1.5.4` |
| Gitea with preset name and path with a tag | `gitea>abc/foo:path/xyz#1.5.4` | `xyz` | `https://gitea.com/abc/foo` | `path/xyz.json` | `1.5.4` |

#### Self-hosted Git

| name | example use | preset | resolves as | filename | Git tag |
| ------------------------------------------ | ------------------------------ | --------- | ------------------------------------ | --------------- | -------------- |
| Local default | `local>abc/foo` | `default` | `https://github.company.com/abc/foo` | `default.json` | Default branch |
| Local with preset path | `local>abc/foo:path/xyz` | `default` | `https://github.company.com/abc/foo` | `path/xyz.json` | Default branch |
| Local default with a tag | `local>abc/foo#1.5.4` | `default` | `https://github.company.com/abc/foo` | `default.json` | `1.5.4` |
| Local with preset name with a tag | `local>abc/foo:xyz#1.5.4` | `default` | `https://github.company.com/abc/foo` | `xyz.json` | `1.5.4` |
| Local with preset name and path with a tag | `local>abc/foo:path/xyz#1.5.4` | `default` | `https://github.company.com/abc/foo` | `path/xyz.json` | `1.5.4` |
60 changes: 46 additions & 14 deletions docs/usage/config-presets.md
Expand Up @@ -38,22 +38,54 @@ In order to achieve these goals, preset configs allow for a very modular approac
## Preset Hosting

In general, GitHub, GitLab or Gitea-based preset hosting is easier than npm because you avoid the "publish" step - simply commit preset code to the default branch and it will be picked up by Renovate the next time it runs.

An additional benefit of using source code hosting is that the same token/authentication can be reused by Renovate in case you want to make your config private.

| name | example use | preset | resolves as | filename |
| ----------------------- | -------------------------- | --------- | ------------------------------------ | --------------- |
| GitHub default | `github>abc/foo` | `default` | `https://github.com/abc/foo` | `default.json` |
| GitHub with preset name | `github>abc/foo:xyz` | `xyz` | `https://github.com/abc/foo` | `xyz.json` |
| GitHub with preset path | `github>abc/foo//path/xyz` | `xyz` | `https://github.com/abc/foo` | `path/xyz.json` |
| GitLab default | `gitlab>abc/foo` | `default` | `https://gitlab.com/abc/foo` | `default.json` |
| GitLab with preset name | `gitlab>abc/foo:xyz` | `xyz` | `https://gitlab.com/abc/foo` | `xyz.json` |
| GitLab with preset path | `gitlab>abc/foo//path/xyz` | `xyz` | `https://gitlab.com/abc/foo` | `path/xyz.json` |
| Gitea default | `gitea>abc/foo` | `default` | `https://gitea.com/abc/foo` | `default.json` |
| Gitea with preset name | `gitea>abc/foo:xyz` | `xyz` | `https://gitea.com/abc/foo` | `xyz.json` |
| Local default | `local>abc/foo` | `default` | `https://github.company.com/abc/foo` | `default.json` |
| Local with preset path | `local>abc/foo//path/xyz` | `xyz` | `https://github.company.com/abc/foo` | `path/xyz.json` |

Note that you can't combine the path and sub-preset syntaxes (i.e. anything in the form `provider>owner/repo//path/to/file:subsubpreset`) is not supported. One workaround is to use distinct files instead of sub-presets.
You can set a Git tag (like a SemVer) to use a specific release of your shared config.

### GitHub

| name | example use | preset | resolves as | filename | Git tag |
| ------------------------------------------- | ------------------------------- | --------- | ---------------------------- | --------------- | -------------- |
| GitHub default | `github>abc/foo` | `default` | `https://github.com/abc/foo` | `default.json` | Default branch |
| GitHub with preset name | `github>abc/foo:xyz` | `xyz` | `https://github.com/abc/foo` | `xyz.json` | Default branch |
| GitHub default with a tag | `github>abc/foo#1.5.4` | `default` | `https://github.com/abc/foo` | `default.json` | `1.5.4` |
| GitHub with preset name with a tag | `github>abc/foo:xyz#1.5.4` | `xyz` | `https://github.com/abc/foo` | `xyz.json` | `1.5.4` |
| GitHub with preset name and path with a tag | `github>abc/foo:path/xyz#1.5.4` | `xyz` | `https://github.com/abc/foo` | `path/xyz.json` | `1.5.4` |

### GitLab

| name | example use | preset | resolves as | filename | Git tag |
| ------------------------------------------- | ------------------------------- | --------- | ---------------------------- | --------------- | -------------- |
| GitLab default | `gitlab>abc/foo` | `default` | `https://gitlab.com/abc/foo` | `default.json` | Default branch |
| GitLab with preset name | `gitlab>abc/foo:xyz` | `xyz` | `https://gitlab.com/abc/foo` | `xyz.json` | Default branch |
| GitLab default with a tag | `gitlab>abc/foo#1.5.4` | `default` | `https://gitlab.com/abc/foo` | `default.json` | `1.5.4` |
| GitLab with preset name with a tag | `gitlab>abc/foo:xyz#1.5.4` | `xyz` | `https://gitlab.com/abc/foo` | `xyz.json` | `1.5.4` |
| GitLab with preset name and path with a tag | `gitlab>abc/foo:path/xyz#1.5.4` | `xyz` | `https://gitlab.com/abc/foo` | `path/xyz.json` | `1.5.4` |

### Gitea

| name | example use | preset | resolves as | filename | Git tag |
| ------------------------------------------ | ------------------------------ | --------- | --------------------------- | --------------- | -------------- |
| Gitea default | `gitea>abc/foo` | `default` | `https://gitea.com/abc/foo` | `default.json` | Default branch |
| Gitea with preset name | `gitea>abc/foo:xyz` | `xyz` | `https://gitea.com/abc/foo` | `xyz.json` | Default branch |
| Gitea default with a tag | `gitea>abc/foo#1.5.4` | `default` | `https://gitea.com/abc/foo` | `default.json` | `1.5.4` |
| Gitea with preset name with a tag | `gitea>abc/foo:xyz#1.5.4` | `xyz` | `https://gitea.com/abc/foo` | `xyz.json` | `1.5.4` |
| Gitea with preset name and path with a tag | `gitea>abc/foo:path/xyz#1.5.4` | `xyz` | `https://gitea.com/abc/foo` | `path/xyz.json` | `1.5.4` |

### Self-hosted Git

| name | example use | preset | resolves as | filename | Git tag |
| ------------------------------------------ | ------------------------------ | --------- | ------------------------------------ | --------------- | -------------- |
| Local default | `local>abc/foo` | `default` | `https://github.company.com/abc/foo` | `default.json` | Default branch |
| Local with preset path | `local>abc/foo:path/xyz` | `xyz` | `https://github.company.com/abc/foo` | `path/xyz.json` | Default branch |
| Local default with a tag | `local>abc/foo#1.5.4` | `default` | `https://github.company.com/abc/foo` | `default.json` | `1.5.4` |
| Local with preset name with a tag | `local>abc/foo:xyz#1.5.4` | `xyz` | `https://github.company.com/abc/foo` | `xyz.json` | `1.5.4` |
| Local with preset name and path with a tag | `local>abc/foo:path/xyz#1.5.4` | `xyz` | `https://github.company.com/abc/foo` | `path/xyz.json` | `1.5.4` |

Note that you can't combine the path and sub-preset syntaxes.
This means that anything in the form `provider>owner/repo//path/to/file:subsubpreset` is not supported.
One workaround is to use distinct files instead of sub-presets.

## Example configs

Expand Down
32 changes: 32 additions & 0 deletions lib/config/presets/gitea/__snapshots__/index.spec.ts.snap
Expand Up @@ -187,6 +187,22 @@ Array [
]
`;

exports[`config/presets/gitea/index getPresetFromEndpoint() uses custom endpoint with a tag 1`] = `
Array [
Object {
"headers": Object {
"accept": "application/json",
"accept-encoding": "gzip, deflate, br",
"authorization": "token abc",
"host": "api.gitea.example.org",
"user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)",
},
"method": "GET",
"url": "https://api.gitea.example.org/repos/some/repo/contents/default.json?ref=someTag",
},
]
`;

exports[`config/presets/gitea/index getPresetFromEndpoint() uses default endpoint 1`] = `
Array [
Object {
Expand All @@ -202,3 +218,19 @@ Array [
},
]
`;

exports[`config/presets/gitea/index getPresetFromEndpoint() uses default endpoint with a tag 1`] = `
Array [
Object {
"headers": Object {
"accept": "application/json",
"accept-encoding": "gzip, deflate, br",
"authorization": "token abc",
"host": "gitea.com",
"user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)",
},
"method": "GET",
"url": "https://gitea.com/api/v1/repos/some/repo/contents/default.json?ref=someTag",
},
]
`;
42 changes: 41 additions & 1 deletion lib/config/presets/gitea/index.spec.ts
Expand Up @@ -30,7 +30,8 @@ describe('config/presets/gitea/index', () => {
const res = await gitea.fetchJSONFile(
'some/repo',
'some-filename.json',
giteaApiHost
giteaApiHost,
null
);
expect(res).toEqual({ from: 'api' });
expect(httpMock.getTrace()).toMatchSnapshot();
Expand Down Expand Up @@ -205,5 +206,44 @@ describe('config/presets/gitea/index', () => {
).toEqual({ from: 'api' });
expect(httpMock.getTrace()).toMatchSnapshot();
});

it('uses default endpoint with a tag', async () => {
httpMock
.scope(giteaApiHost)
.get(`${basePath}/default.json?ref=someTag`)
.reply(200, {
content: Buffer.from('{"from":"api"}').toString('base64'),
});
expect(
await gitea.getPresetFromEndpoint(
'some/repo',
'default',
undefined,
giteaApiHost,
'someTag'
)
).toEqual({ from: 'api' });
expect(httpMock.getTrace()).toMatchSnapshot();
});
it('uses custom endpoint with a tag', async () => {
httpMock
.scope('https://api.gitea.example.org')
.get(`${basePath}/default.json?ref=someTag`)
.reply(200, {
content: Buffer.from('{"from":"api"}').toString('base64'),
});
expect(
await gitea
.getPresetFromEndpoint(
'some/repo',
'default',
undefined,
'https://api.gitea.example.org',
'someTag'
)
.catch(() => ({ from: 'api' }))
).toEqual({ from: 'api' });
expect(httpMock.getTrace()).toMatchSnapshot();
});
});
});
20 changes: 16 additions & 4 deletions lib/config/presets/gitea/index.ts
Expand Up @@ -16,11 +16,14 @@ export const Endpoint = 'https://gitea.com/api/v1/';
export async function fetchJSONFile(
repo: string,
fileName: string,
endpoint: string
endpoint: string,
packageTag?: string
): Promise<Preset> {
let res: RepoContents;
try {
res = await getRepoContents(repo, fileName, null, { baseUrl: endpoint });
res = await getRepoContents(repo, fileName, packageTag, {
baseUrl: endpoint,
});
} catch (err) {
// istanbul ignore if: not testable with nock
if (err instanceof ExternalHostError) {
Expand All @@ -45,13 +48,15 @@ export function getPresetFromEndpoint(
pkgName: string,
filePreset: string,
presetPath: string,
endpoint = Endpoint
endpoint = Endpoint,
packageTag?: string
): Promise<Preset> {
return fetchPreset({
pkgName,
filePreset,
presetPath,
endpoint,
packageTag,
fetch: fetchJSONFile,
});
}
Expand All @@ -60,6 +65,13 @@ export function getPreset({
packageName: pkgName,
presetName = 'default',
presetPath,
packageTag = null,
}: PresetConfig): Promise<Preset> {
return getPresetFromEndpoint(pkgName, presetName, presetPath, Endpoint);
return getPresetFromEndpoint(
pkgName,
presetName,
presetPath,
Endpoint,
packageTag
);
}
32 changes: 32 additions & 0 deletions lib/config/presets/github/__snapshots__/index.spec.ts.snap
Expand Up @@ -187,6 +187,22 @@ Array [
]
`;

exports[`config/presets/github/index getPresetFromEndpoint() uses custom endpoint with a tag 1`] = `
Array [
Object {
"headers": Object {
"accept": "application/vnd.github.v3+json",
"accept-encoding": "gzip, deflate, br",
"authorization": "token abc",
"host": "api.github.example.org",
"user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)",
},
"method": "GET",
"url": "https://api.github.example.org/repos/some/repo/contents/default.json?ref=someTag",
},
]
`;

exports[`config/presets/github/index getPresetFromEndpoint() uses default endpoint 1`] = `
Array [
Object {
Expand All @@ -202,3 +218,19 @@ Array [
},
]
`;

exports[`config/presets/github/index getPresetFromEndpoint() uses default endpoint with a tag 1`] = `
Array [
Object {
"headers": Object {
"accept": "application/vnd.github.v3+json",
"accept-encoding": "gzip, deflate, br",
"authorization": "token abc",
"host": "api.github.com",
"user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)",
},
"method": "GET",
"url": "https://api.github.com/repos/some/repo/contents/default.json?ref=someTag",
},
]
`;

0 comments on commit 7579875

Please sign in to comment.