feat(datasource/galaxy-collection): support ansible automation hub (#25675)

Co-authored-by: Rhys Arkins <rhys@arkins.net>
Co-authored-by: Sebastian Poxhofer <secustor@users.noreply.github.com>
Co-authored-by: HonkingGoose <34918129+HonkingGoose@users.noreply.github.com>
This commit is contained in:
jon4hz 2023-11-27 15:02:54 +01:00 committed by GitHub
parent 3d47d2a615
commit f7faaa4918
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 125 additions and 6 deletions

View file

@ -2,7 +2,40 @@
exports[`modules/datasource/galaxy-collection/index getReleases processes real data 1`] = ` exports[`modules/datasource/galaxy-collection/index getReleases processes real data 1`] = `
{ {
"registryUrl": "https://galaxy.ansible.com", "registryUrl": "https://galaxy.ansible.com/api",
"releases": [
{
"dependencies": {},
"downloadUrl": "https://galaxy.ansible.com/api/v3/plugin/ansible/content/published/collections/artifacts/community-kubernetes-0.11.1.tar.gz",
"isDeprecated": false,
"newDigest": "cd197084b32f8976394f269eb005bf475eff2122fddbb48380c76154ab4d4530",
"sourceUrl": "https://github.com/ansible-collections/community.kubernetes",
"version": "0.11.1",
},
{
"dependencies": {},
"downloadUrl": "https://galaxy.ansible.com/api/v3/plugin/ansible/content/published/collections/artifacts/community-kubernetes-1.2.0.tar.gz",
"isDeprecated": false,
"newDigest": "a53eaf6a51987d30cc48ebcd20f0102dae0f17a7a02071928381e5a62951a0ed",
"sourceUrl": "https://github.com/ansible-collections/community.kubernetes",
"version": "1.2.0",
},
{
"dependencies": {},
"downloadUrl": "https://galaxy.ansible.com/api/v3/plugin/ansible/content/published/collections/artifacts/community-kubernetes-1.2.1.tar.gz",
"isDeprecated": false,
"newDigest": "38e064bb32ee86781f0c6e56bd29fcfbaf48180f993e129185eb8420caabf223",
"sourceUrl": "https://github.com/ansible-collections/community.kubernetes",
"version": "1.2.1",
},
],
"sourceUrl": "https://github.com/ansible-collections/community.kubernetes",
}
`;
exports[`modules/datasource/galaxy-collection/index getReleases processes real data with automation hub URL 1`] = `
{
"registryUrl": "https://my.automationhub.local/api/galaxy/content/published",
"releases": [ "releases": [
{ {
"dependencies": {}, "dependencies": {},

View file

@ -18,9 +18,9 @@ const communityKubernetesDetails0111 = Fixtures.get(
'community_kubernetes_version_details_0.11.1.json', 'community_kubernetes_version_details_0.11.1.json',
); );
const baseUrl = 'https://galaxy.ansible.com'; const baseUrl = 'https://galaxy.ansible.com/api/';
const collectionAPIPath = const collectionAPIPath =
'api/v3/plugin/ansible/content/published/collections/index'; 'v3/plugin/ansible/content/published/collections/index';
const datasource = GalaxyCollectionDatasource.id; const datasource = GalaxyCollectionDatasource.id;
@ -163,5 +163,47 @@ describe('modules/datasource/galaxy-collection/index', () => {
expect(res).toBeDefined(); expect(res).toBeDefined();
expect(res?.releases).toHaveLength(3); expect(res?.releases).toHaveLength(3);
}); });
it('returns null but matches automation hub URL', async () => {
httpMock
.scope('https://my.automationhub.local/api/galaxy/content/community/')
.get(`/v3/plugin/ansible/content/community/collections/index/foo/bar/`)
.reply(500);
await expect(
getPkgReleases({
datasource,
packageName: 'foo.bar',
registryUrls: [
'https://my.automationhub.local/api/galaxy/content/community/',
],
}),
).rejects.toThrow(EXTERNAL_HOST_ERROR);
});
it('processes real data with automation hub URL', async () => {
httpMock
.scope('https://my.automationhub.local/api/galaxy/content/published/')
.get(`/${collectionAPIPath}/community/kubernetes/`)
.reply(200, communityKubernetesBase)
.get(`/${collectionAPIPath}/community/kubernetes/versions/`)
.reply(200, communityKubernetesVersions)
.get(`/${collectionAPIPath}/community/kubernetes/versions/1.2.1/`)
.reply(200, communityKubernetesDetails121)
.get(`/${collectionAPIPath}/community/kubernetes/versions/1.2.0/`)
.reply(200, communityKubernetesDetails120)
.get(`/${collectionAPIPath}/community/kubernetes/versions/0.11.1/`)
.reply(200, communityKubernetesDetails0111);
const res = await getPkgReleases({
datasource,
packageName: 'community.kubernetes',
registryUrls: [
'https://my.automationhub.local/api/galaxy/content/published/',
],
});
expect(res).toMatchSnapshot();
expect(res).not.toBeNull();
expect(res).toBeDefined();
expect(res?.releases).toHaveLength(3);
});
}); });
}); });

View file

@ -2,12 +2,17 @@ import is from '@sindresorhus/is';
import { logger } from '../../../logger'; import { logger } from '../../../logger';
import { cache } from '../../../util/cache/package/decorator'; import { cache } from '../../../util/cache/package/decorator';
import * as p from '../../../util/promises'; import * as p from '../../../util/promises';
import { regEx } from '../../../util/regex';
import { ensureTrailingSlash, joinUrlParts } from '../../../util/url'; import { ensureTrailingSlash, joinUrlParts } from '../../../util/url';
import * as pep440Versioning from '../../versioning/pep440'; import * as pep440Versioning from '../../versioning/pep440';
import { Datasource } from '../datasource'; import { Datasource } from '../datasource';
import type { GetReleasesConfig, Release, ReleaseResult } from '../types'; import type { GetReleasesConfig, Release, ReleaseResult } from '../types';
import { GalaxyV3, GalaxyV3DetailedVersion, GalaxyV3Versions } from './schema'; import { GalaxyV3, GalaxyV3DetailedVersion, GalaxyV3Versions } from './schema';
const repositoryRegex = regEx(
/^\S+\/api\/galaxy\/content\/(?<repository>[^/]+)/,
);
export class GalaxyCollectionDatasource extends Datasource { export class GalaxyCollectionDatasource extends Datasource {
static readonly id = 'galaxy-collection'; static readonly id = 'galaxy-collection';
@ -15,9 +20,11 @@ export class GalaxyCollectionDatasource extends Datasource {
super(GalaxyCollectionDatasource.id); super(GalaxyCollectionDatasource.id);
} }
override readonly customRegistrySupport = false; override readonly customRegistrySupport = true;
override readonly defaultRegistryUrls = ['https://galaxy.ansible.com']; override readonly registryStrategy = 'hunt';
override readonly defaultRegistryUrls = ['https://galaxy.ansible.com/api/'];
override readonly defaultVersioning = pep440Versioning.id; override readonly defaultVersioning = pep440Versioning.id;
@ -31,10 +38,15 @@ export class GalaxyCollectionDatasource extends Datasource {
}: GetReleasesConfig): Promise<ReleaseResult | null> { }: GetReleasesConfig): Promise<ReleaseResult | null> {
const [namespace, projectName] = packageName.split('.'); const [namespace, projectName] = packageName.split('.');
const repository =
repositoryRegex.exec(registryUrl!)?.groups?.repository ?? 'published';
const baseUrl = ensureTrailingSlash( const baseUrl = ensureTrailingSlash(
joinUrlParts( joinUrlParts(
registryUrl!, registryUrl!,
'api/v3/plugin/ansible/content/published/collections/index', 'v3/plugin/ansible/content',
repository,
'collections/index',
namespace, namespace,
projectName, projectName,
), ),

View file

@ -0,0 +1,32 @@
By default, the `galaxy-collection` datasource checks for dependencies on `https://galaxy.ansible.com`.
But you can override the default if you want.
Set your own registries by:
- setting a `source` in your `requirements.yaml` file, _or_
- writing a `packageRule` to set a new `registryURLs`
Then you can use Renovate with a private automation hub.
```yaml title="Example config for requirements.yaml"
---
collections:
- name: community.general
version: 3.0.0
source: https://hub.mydomain.com/api/galaxy/content/community/
```
```json title="Example config for renovate.json"
{
"packageRules": [
{
"matchDatasources": ["galaxy-collection"],
"registryUrls": [
"https://hub.mydomain.com/api/galaxy/content/community/",
"https://hub.mydomain.com/api/galaxy/content/certified/",
"https://hub.mydomain.com/api/galaxy/content/myprivaterepo/"
]
}
]
}
```