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(datasource): add Gitlab Package support #11672

Merged
merged 48 commits into from Sep 16, 2021
Merged
Show file tree
Hide file tree
Changes from 47 commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
1565b2b
feat(datasource): add Gitlab Package support
samdolt Jul 20, 2021
f37f7b1
Fix sourceUrl, remove __snapshots__ file
samdolt Jul 20, 2021
671806b
Refactoring: update test, add documentation, use registry urls
samdolt Aug 2, 2021
b3f64bf
gitlab-packages documentation: fix style and typos
samdolt Aug 4, 2021
917bc48
gitlab-packages documentation: fix style and typos
samdolt Aug 4, 2021
67d93f2
gitlab-packages documentation: fix style and typos
samdolt Aug 4, 2021
fe8e5c9
Convert gitlab-packages datasource to a class
samdolt Aug 4, 2021
2cc1904
Use docker versionning
samdolt Aug 4, 2021
05d18c0
documentation: docker is the default versionning
samdolt Aug 4, 2021
00eee23
Update lib/datasource/gitlab-packages/index.ts
samdolt Aug 4, 2021
30b0e44
Add more tests
samdolt Aug 4, 2021
df03e07
fix typo in empty 200 tests
samdolt Aug 4, 2021
9930258
Fix linting problems
samdolt Aug 4, 2021
0018cf3
Update lib/datasource/gitlab-packages/index.ts
samdolt Aug 4, 2021
569c395
Update lib/datasource/gitlab-packages/readme.md
samdolt Aug 4, 2021
368eacb
Update lib/datasource/gitlab-packages/readme.md
samdolt Aug 4, 2021
f15f1fb
Update lib/datasource/gitlab-packages/index.spec.ts
samdolt Aug 6, 2021
7c04c5a
Update lib/datasource/gitlab-packages/index.ts
samdolt Aug 6, 2021
bf1face
Update lib/datasource/gitlab-packages/index.ts
samdolt Aug 6, 2021
0c3c8c4
Update lib/datasource/gitlab-packages/index.ts
samdolt Aug 6, 2021
9370f87
Update lib/datasource/gitlab-packages/index.ts
samdolt Aug 6, 2021
812c384
Update lib/datasource/gitlab-packages/index.spec.ts
samdolt Aug 6, 2021
b9f46cd
Update lib/datasource/gitlab-packages/index.spec.ts
samdolt Aug 6, 2021
79e8f0b
Add gitlab minimum version
samdolt Aug 6, 2021
199d7c7
Fix lint, test and typos
samdolt Aug 6, 2021
d725910
Update lib/datasource/gitlab-packages/readme.md
samdolt Aug 23, 2021
3a1808d
Update lib/datasource/gitlab-packages/readme.md
samdolt Aug 23, 2021
a12bdff
Update lib/datasource/gitlab-packages/index.spec.ts
samdolt Aug 23, 2021
37eee1f
Update lib/datasource/gitlab-packages/index.spec.ts
samdolt Aug 23, 2021
e6006a3
Update lib/datasource/gitlab-packages/index.spec.ts
samdolt Aug 23, 2021
c8e5ddc
Update lib/datasource/gitlab-packages/index.spec.ts
samdolt Aug 23, 2021
bfb0b5f
Update lib/datasource/gitlab-packages/readme.md
samdolt Aug 23, 2021
c355190
Update jest test snapshot
samdolt Sep 3, 2021
180d0df
Update lib/datasource/gitlab-packages/readme.md
samdolt Sep 6, 2021
cd25b4e
handle host rule
samdolt Sep 7, 2021
5db8e46
Merge branch 'main' into feat/gitlab-packages
viceice Sep 10, 2021
de32b29
move gitlab project name from registryUrl to a part of lookupName
samdolt Sep 10, 2021
ae9d12d
Merge branch 'main' into feat/gitlab-packages
rarkins Sep 11, 2021
cf85d28
Merge branch 'main' into feat/gitlab-packages
viceice Sep 14, 2021
b5f77a1
Update lib/datasource/gitlab-packages/readme.md
samdolt Sep 15, 2021
61dae10
Update lib/datasource/gitlab-packages/readme.md
samdolt Sep 15, 2021
8249729
Update lib/datasource/gitlab-packages/index.ts
samdolt Sep 15, 2021
c862f9c
Update lib/datasource/gitlab-packages/index.ts
samdolt Sep 15, 2021
e564e3b
Fix lint by running yarn lint-fix
samdolt Sep 15, 2021
e8f1914
Merge branch 'main' into feat/gitlab-packages
rarkins Sep 16, 2021
bd30c47
Merge branch 'main' into feat/gitlab-packages
viceice Sep 16, 2021
1784aec
Apply suggestions from code review
viceice Sep 16, 2021
1b892f8
Merge branch 'main' into feat/gitlab-packages
viceice Sep 16, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 4 additions & 0 deletions lib/constants/platform.spec.ts
@@ -1,5 +1,6 @@
import { id as GH_RELEASES_DS } from '../datasource/github-releases';
import { id as GH_TAGS_DS } from '../datasource/github-tags';
import { GitlabPackagesDatasource } from '../datasource/gitlab-packages';
import { GitlabReleasesDatasource } from '../datasource/gitlab-releases';
import { id as GL_TAGS_DS } from '../datasource/gitlab-tags';
import { id as POD_DS } from '../datasource/pod';
Expand All @@ -16,6 +17,9 @@ describe('constants/platform', () => {
expect(
GITLAB_API_USING_HOST_TYPES.includes(GitlabReleasesDatasource.id)
).toBeTrue();
expect(
GITLAB_API_USING_HOST_TYPES.includes(GitlabPackagesDatasource.id)
).toBeTrue();
expect(
GITLAB_API_USING_HOST_TYPES.includes(PLATFORM_TYPE_GITLAB)
).toBeTrue();
Expand Down
1 change: 1 addition & 0 deletions lib/constants/platforms.ts
Expand Up @@ -16,4 +16,5 @@ export const GITLAB_API_USING_HOST_TYPES = [
PLATFORM_TYPE_GITLAB,
'gitlab-releases',
'gitlab-tags',
'gitlab-packages',
];
2 changes: 2 additions & 0 deletions lib/datasource/api.ts
Expand Up @@ -11,6 +11,7 @@ import * as gitRefs from './git-refs';
import * as gitTags from './git-tags';
import * as githubReleases from './github-releases';
import * as githubTags from './github-tags';
import { GitlabPackagesDatasource } from './gitlab-packages';
import { GitlabReleasesDatasource } from './gitlab-releases';
import * as gitlabTags from './gitlab-tags';
import * as go from './go';
Expand Down Expand Up @@ -51,6 +52,7 @@ api.set('git-refs', gitRefs);
api.set('git-tags', gitTags);
api.set('github-releases', githubReleases);
api.set('github-tags', githubTags);
api.set('gitlab-packages', new GitlabPackagesDatasource());
api.set('gitlab-tags', gitlabTags);
api.set(GitlabReleasesDatasource.id, new GitlabReleasesDatasource());
api.set('go', go);
Expand Down
21 changes: 21 additions & 0 deletions lib/datasource/gitlab-packages/__snapshots__/index.spec.ts.snap
@@ -0,0 +1,21 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`datasource/gitlab-packages/index getReleases returns package from custom registry 1`] = `
Object {
"registryUrl": "https://gitlab.com",
"releases": Array [
Object {
"releaseTimestamp": "2020-03-04T18:01:37.000Z",
"version": "1.0.0",
},
Object {
"releaseTimestamp": "2020-04-04T18:01:37.000Z",
"version": "v1.1.0",
},
Object {
"releaseTimestamp": "2020-05-04T18:01:37.000Z",
"version": "v1.1.1",
},
],
}
`;
1 change: 1 addition & 0 deletions lib/datasource/gitlab-packages/common.ts
@@ -0,0 +1 @@
export const datasource = 'gitlab-packages';
102 changes: 102 additions & 0 deletions lib/datasource/gitlab-packages/index.spec.ts
@@ -0,0 +1,102 @@
import { getPkgReleases } from '..';
import * as httpMock from '../../../test/http-mock';
import { EXTERNAL_HOST_ERROR } from '../../constants/error-messages';
import { datasource } from './common';

describe('datasource/gitlab-packages/index', () => {
describe('getReleases', () => {
it('returns package from custom registry', async () => {
const body = [
{
version: '1.0.0',
created_at: '2020-03-04T12:01:37.000-06:00',
name: 'mypkg',
},
{
version: 'v1.1.0',
created_at: '2020-04-04T12:01:37.000-06:00',
name: 'mypkg',
},
{
version: 'v1.1.1',
created_at: '2020-05-04T12:01:37.000-06:00',
name: 'mypkg',
},
{
version: 'v2.0.0',
created_at: '2020-05-04T12:01:37.000-06:00',
name: 'otherpkg',
},
];
httpMock
.scope('https://gitlab.com')
.get('/api/v4/projects/user%2Fproject1/packages')
.query({
package_name: 'mypkg',
per_page: '100',
})
.reply(200, body);
const res = await getPkgReleases({
datasource,
registryUrls: ['https://gitlab.com'],
depName: 'user/project1:mypkg',
});
expect(res).toMatchSnapshot();
expect(res.releases).toHaveLength(3);
});

it('returns null for 404', async () => {
httpMock
.scope('https://gitlab.com')
.get('/api/v4/projects/user%2Fproject1/packages')
.query({
package_name: 'mypkg',
per_page: '100',
})
.reply(404);
expect(
await getPkgReleases({
datasource,
registryUrls: ['https://gitlab.com'],
depName: 'user/project1:mypkg',
})
).toBeNull();
});

it('returns null for empty 200 OK', async () => {
httpMock
.scope('https://gitlab.com')
.get('/api/v4/projects/user%2Fproject1/packages')
.query({
package_name: 'mypkg',
per_page: '100',
})
.reply(200, []);
expect(
await getPkgReleases({
datasource,
registryUrls: ['https://gitlab.com'],
depName: 'user/project1:mypkg',
})
).toBeNull();
});

it('throws for 5xx', async () => {
httpMock
.scope('https://gitlab.com')
.get('/api/v4/projects/user%2Fproject1/packages')
.query({
package_name: 'mypkg',
per_page: '100',
})
.reply(502);
await expect(
getPkgReleases({
datasource,
registryUrls: ['https://gitlab.com'],
depName: 'user/project1:mypkg',
})
).rejects.toThrow(EXTERNAL_HOST_ERROR);
});
});
});
84 changes: 84 additions & 0 deletions lib/datasource/gitlab-packages/index.ts
@@ -0,0 +1,84 @@
import { cache } from '../../util/cache/package/decorator';
import { GitlabHttp } from '../../util/http/gitlab';
import { joinUrlParts } from '../../util/url';
import { Datasource } from '../datasource';
import type { GetReleasesConfig, ReleaseResult } from '../types';
import { datasource } from './common';
import type { GitlabPackage } from './types';

// Gitlab Packages API: https://docs.gitlab.com/ee/api/packages.html

export class GitlabPackagesDatasource extends Datasource {
static readonly id = datasource;

protected override http: GitlabHttp;

override caching = true;

override customRegistrySupport = true;

override defaultRegistryUrls = ['https://gitlab.com'];

constructor() {
super(datasource);
this.http = new GitlabHttp();
}

static getGitlabPackageApiUrl(
registryUrl: string,
projectName: string,
packageName: string
): string {
const projectNameEncoded = encodeURIComponent(projectName);
const packageNameEncoded = encodeURIComponent(packageName);

return joinUrlParts(
registryUrl,
`api/v4/projects`,
projectNameEncoded,
`packages?package_name=${packageNameEncoded}&per_page=100`
);
}

@cache({
namespace: `datasource-${datasource}`,
key: ({ registryUrl, lookupName }: GetReleasesConfig) =>
`${registryUrl}-${lookupName}`,
})
async getReleases({
registryUrl,
lookupName,
}: GetReleasesConfig): Promise<ReleaseResult | null> {
const [projectName, packageName] = lookupName.split(':', 2);

const apiUrl = GitlabPackagesDatasource.getGitlabPackageApiUrl(
registryUrl,
projectName,
packageName
);

const result: ReleaseResult = {
releases: null,
};

let response: GitlabPackage[];
try {
response = (
await this.http.getJson<GitlabPackage[]>(apiUrl, { paginate: true })
).body;

result.releases = response
// Setting the package_name option when calling the GitLab API isn't enough to filter information about other packages
// because this option is only implemented on GitLab > 12.9 and it only does a fuzzy search.
.filter((r) => r.name === packageName)
.map(({ version, created_at }) => ({
version,
releaseTimestamp: created_at,
}));
} catch (err) {
this.handleGenericErrors(err);
}

return result.releases?.length ? result : null;
}
}
39 changes: 39 additions & 0 deletions lib/datasource/gitlab-packages/readme.md
@@ -0,0 +1,39 @@
[GitLab Packages API](https://docs.gitlab.com/ee/api/packages.html) supports looking up package versions from [all types of packages registry supported by GitLab](https://docs.gitlab.com/ee/user/packages/package_registry/index.html) and can be used in combination with [regex managers](https://docs.renovatebot.com/modules/manager/regex/) to keep dependencies up-to-date which are not specifically supported by Renovate.

To specify which specific repository should be queried when looking up a package, the `depName` should be formed with the project path first, then a `:` and finally the package name.

As an example, `gitlab-org/ci-cd/package-stage/feature-testing/new-packages-list:@gitlab-org/nk-js` would look for the`@gitlab-org/nk-js` packages in the generic packages repository of the `gitlab-org/ci-cd/package-stage/feature-testing/new-packages-list` project.

If you are using a self-hosted GitLab instance, please note the following requirements:

- If you are on the `Free` edition, this datasource requires at least GitLab 13.3
- If you are on the `Premium` or the `Ultimate` edition, this datasource requires at least GitLab 11.8, but GitLab 12.9 or more is recommended if you have a lot of packages with different names in the same project

**Usage Example**

A real-world example for this specific datasource would be maintaining package versions in a config file.
This can be achieved by configuring a generic regex manager in `renovate.json` for files named `versions.ini`:

```json
{
"regexManagers": [
{
"fileMatch": ["^versions.ini$"],
"matchStrings": [
"# renovate: datasource=(?<datasource>.*?) depName=(?<depName>.*?)( versioning=(?<versioning>.*?))?( registryUrl=(?<registryUrl>.*?))?\\s.*?_VERSION=(?<currentValue>.*)\\s"
],
"versioningTemplate": "{{#if versioning}}{{{versioning}}}{{else}}semver{{/if}}"
}
]
}
```
viceice marked this conversation as resolved.
Show resolved Hide resolved

Now you may use comments in your `versions.ini` files to automatically update dependencies, which could look like this:

```ini
# renovate: datasource=gitlab-packages depName=gitlab-org/ci-cd/package-stage/feature-testing/new-packages-list:@gitlab-org/nk-js versioning=semver registryUrl=https://gitlab.com
NKJS_VERSION=3.4.0

```

By default, `gitlab-packages` uses the `docker` versioning scheme.
5 changes: 5 additions & 0 deletions lib/datasource/gitlab-packages/types.ts
@@ -0,0 +1,5 @@
export interface GitlabPackage {
version: string;
created_at: string;
name: string;
}