Skip to content

Commit

Permalink
feat(datasource): add Hackage datasource (#32944)
Browse files Browse the repository at this point in the history
Co-authored-by: Sebastian Poxhofer <[email protected]>
  • Loading branch information
ysangkok and secustor authored Dec 13, 2024
1 parent 2a51500 commit d63ff71
Show file tree
Hide file tree
Showing 5 changed files with 123 additions and 0 deletions.
2 changes: 2 additions & 0 deletions lib/modules/datasource/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import { GlasskubePackagesDatasource } from './glasskube-packages';
import { GoDatasource } from './go';
import { GolangVersionDatasource } from './golang-version';
import { GradleVersionDatasource } from './gradle-version';
import { HackageDatasource } from './hackage';
import { HelmDatasource } from './helm';
import { HermitDatasource } from './hermit';
import { HexDatasource } from './hex';
Expand Down Expand Up @@ -111,6 +112,7 @@ api.set(GlasskubePackagesDatasource.id, new GlasskubePackagesDatasource());
api.set(GoDatasource.id, new GoDatasource());
api.set(GolangVersionDatasource.id, new GolangVersionDatasource());
api.set(GradleVersionDatasource.id, new GradleVersionDatasource());
api.set(HackageDatasource.id, new HackageDatasource());
api.set(HelmDatasource.id, new HelmDatasource());
api.set(HermitDatasource.id, new HermitDatasource());
api.set(HexDatasource.id, new HexDatasource());
Expand Down
57 changes: 57 additions & 0 deletions lib/modules/datasource/hackage/index.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { getPkgReleases } from '..';
import * as httpMock from '../../../../test/http-mock';
import { HackageDatasource, versionToRelease } from './index';

const baseUrl = 'https://hackage.haskell.org/';

describe('modules/datasource/hackage/index', () => {
describe('versionToRelease', () => {
it('should make release with given version', () => {
expect(
versionToRelease('3.1.0', 'base', 'http://localhost').version,
).toBe('3.1.0');
});
});

describe('getReleases', () => {
it('return null with empty registryUrl', async () => {
expect(
await new HackageDatasource().getReleases({
packageName: 'base',
registryUrl: undefined,
}),
).toBeNull();
});

it('returns null for 404', async () => {
httpMock.scope(baseUrl).get('/package/base.json').reply(404);
expect(
await getPkgReleases({
datasource: HackageDatasource.id,
packageName: 'base',
}),
).toBeNull();
});

it('returns release for 200', async () => {
httpMock
.scope(baseUrl)
.get('/package/base.json')
.reply(200, { '4.20.0.1': 'normal' });
expect(
await getPkgReleases({
datasource: HackageDatasource.id,
packageName: 'base',
}),
).toEqual({
registryUrl: baseUrl,
releases: [
{
changelogUrl: baseUrl + 'package/base-4.20.0.1/changelog',
version: '4.20.0.1',
},
],
});
});
});
});
54 changes: 54 additions & 0 deletions lib/modules/datasource/hackage/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import is from '@sindresorhus/is';
import { joinUrlParts } from '../../../util/url';
import * as pvpVersioning from '../../versioning/pvp';
import { Datasource } from '../datasource';
import type { GetReleasesConfig, Release, ReleaseResult } from '../types';
import { HackagePackageMetadata } from './schema';

export class HackageDatasource extends Datasource {
static readonly id = 'hackage';

constructor() {
super(HackageDatasource.id);
}

override readonly defaultVersioning = pvpVersioning.id;
override readonly customRegistrySupport = false;
override readonly defaultRegistryUrls = ['https://hackage.haskell.org/'];

async getReleases(config: GetReleasesConfig): Promise<ReleaseResult | null> {
const { registryUrl, packageName } = config;
if (!is.nonEmptyString(registryUrl)) {
return null;
}
const massagedPackageName = encodeURIComponent(packageName);
const url = joinUrlParts(
registryUrl,
'package',
`${massagedPackageName}.json`,
);
const res = await this.http.getJson(url, HackagePackageMetadata);
const keys = Object.keys(res.body);
return {
releases: keys.map((version) =>
versionToRelease(version, packageName, registryUrl),
),
};
}
}

export function versionToRelease(
version: string,
packageName: string,
registryUrl: string,
): Release {
return {
version,
changelogUrl: joinUrlParts(
registryUrl,
'package',
`${packageName}-${version}`,
'changelog',
),
};
}
7 changes: 7 additions & 0 deletions lib/modules/datasource/hackage/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
This datasource uses
[the Hackage JSON API](https://hackage.haskell.org/api#package-info-json)
to fetch versions for published Haskell packages.

While not all versions use [PVP](https://pvp.haskell.org), the majority does.
This manager assumes a default versioning set to PVP.
Versioning can be overwritten using `packageRules`, e.g. with `matchDatasources`.
3 changes: 3 additions & 0 deletions lib/modules/datasource/hackage/schema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { z } from 'zod';

export const HackagePackageMetadata = z.record(z.string());

0 comments on commit d63ff71

Please sign in to comment.