From 06264fd206c8fe16dcf16c14bc23464da8f044a4 Mon Sep 17 00:00:00 2001 From: Sergei Zharinov <zharinov@users.noreply.github.com> Date: Fri, 11 Oct 2024 01:54:28 -0300 Subject: [PATCH] feat(sbt-package): Support `releaseTimestamp` (#31882) --- .../datasource/sbt-package/index.spec.ts | 36 ++++++++++ lib/modules/datasource/sbt-package/index.ts | 70 +++++++++++++++---- 2 files changed, 93 insertions(+), 13 deletions(-) diff --git a/lib/modules/datasource/sbt-package/index.spec.ts b/lib/modules/datasource/sbt-package/index.spec.ts index 85e6e33d29..45510d2c94 100644 --- a/lib/modules/datasource/sbt-package/index.spec.ts +++ b/lib/modules/datasource/sbt-package/index.spec.ts @@ -2,12 +2,17 @@ import { codeBlock } from 'common-tags'; import { getPkgReleases } from '..'; import { Fixtures } from '../../../../test/fixtures'; import * as httpMock from '../../../../test/http-mock'; +import { mocked } from '../../../../test/util'; +import * as _packageCache from '../../../util/cache/package'; import { regEx } from '../../../util/regex'; import * as mavenVersioning from '../../versioning/maven'; import { MAVEN_REPO } from '../maven/common'; import { extractPageLinks } from './util'; import { SbtPackageDatasource } from '.'; +jest.mock('../../../util/cache/package'); +const packageCache = mocked(_packageCache); + describe('modules/datasource/sbt-package/index', () => { it('parses Maven index directory', () => { expect( @@ -278,4 +283,35 @@ describe('modules/datasource/sbt-package/index', () => { expect(res).toMatchObject({}); }); }); + + describe('postprocessRelease', () => { + const datasource = new SbtPackageDatasource(); + + it('extracts URL from Maven POM file', async () => { + const registryUrl = 'https://repo.maven.apache.org/maven2/'; + const packageName = 'org.example:example'; + packageCache.get.mockImplementation(((ns: string, k: string) => + ns === 'datasource-sbt-package' && + k === `package-urls:${registryUrl}:${packageName}` + ? Promise.resolve([`${registryUrl}org/example/`]) + : Promise.resolve(undefined)) as never); + + httpMock + .scope(registryUrl) + .get('/org/example/1.2.3/example-1.2.3.pom') + .reply(200, codeBlock`<project></project>`, { + 'last-modified': 'Wed, 21 Oct 2015 07:28:00 GMT', + }); + + const res = await datasource.postprocessRelease( + { packageName, registryUrl }, + { version: '1.2.3' }, + ); + + expect(res).toEqual({ + version: '1.2.3', + releaseTimestamp: '2015-10-21T07:28:00.000Z', + }); + }); + }); }); diff --git a/lib/modules/datasource/sbt-package/index.ts b/lib/modules/datasource/sbt-package/index.ts index 519ed4097f..463661b51c 100644 --- a/lib/modules/datasource/sbt-package/index.ts +++ b/lib/modules/datasource/sbt-package/index.ts @@ -2,6 +2,7 @@ import * as upath from 'upath'; import { XmlDocument } from 'xmldoc'; import { logger } from '../../../logger'; import * as packageCache from '../../../util/cache/package'; +import { cache } from '../../../util/cache/package/decorator'; import { Http } from '../../../util/http'; import { regEx } from '../../../util/regex'; import { ensureTrailingSlash, trimTrailingSlash } from '../../../util/url'; @@ -10,6 +11,7 @@ import { compare } from '../../versioning/maven/compare'; import { MavenDatasource } from '../maven'; import { MAVEN_REPO } from '../maven/common'; import { downloadHttpProtocol } from '../maven/util'; +import { normalizeDate } from '../metadata'; import type { GetReleasesConfig, PostprocessReleaseConfig, @@ -26,6 +28,12 @@ interface ScalaDepCoordinate { scalaVersion?: string; } +interface PomInfo { + homepage?: string; + sourceUrl?: string; + releaseTimestamp?: string; +} + export class SbtPackageDatasource extends MavenDatasource { static override readonly id = 'sbt-package'; @@ -187,6 +195,11 @@ export class SbtPackageDatasource extends MavenDatasource { return null; } + const releases: Release[] = [...allVersions] + .sort(compare) + .map((version) => ({ version })); + const res: ReleaseResult = { releases, dependencyUrl }; + const latestVersion = getLatestVersion(versions); const pomInfo = await this.getPomInfo( registryUrl, @@ -195,10 +208,15 @@ export class SbtPackageDatasource extends MavenDatasource { packageUrls, ); - const releases: Release[] = [...allVersions] - .sort(compare) - .map((version) => ({ version })); - return { releases, dependencyUrl, ...pomInfo }; + if (pomInfo?.homepage) { + res.homepage = pomInfo.homepage; + } + + if (pomInfo?.sourceUrl) { + res.sourceUrl = pomInfo.sourceUrl; + } + + return res; } async getPomInfo( @@ -206,9 +224,7 @@ export class SbtPackageDatasource extends MavenDatasource { packageName: string, version: string | null, pkgUrls?: string[], - ): Promise<Pick<ReleaseResult, 'homepage' | 'sourceUrl'>> { - const result: Pick<ReleaseResult, 'homepage' | 'sourceUrl'> = {}; - + ): Promise<PomInfo | null> { const packageUrlsKey = `package-urls:${registryUrl}:${packageName}`; // istanbul ignore next: will be covered later const packageUrls = @@ -220,12 +236,12 @@ export class SbtPackageDatasource extends MavenDatasource { // istanbul ignore if if (!packageUrls?.length) { - return result; + return null; } // istanbul ignore if if (!version) { - return result; + return null; } const invalidPomFilesKey = `invalid-pom-files:${registryUrl}:${packageName}:${version}`; @@ -265,6 +281,13 @@ export class SbtPackageDatasource extends MavenDatasource { continue; } + const result: PomInfo = {}; + + const releaseTimestamp = normalizeDate(res.headers['last-modified']); + if (releaseTimestamp) { + result.releaseTimestamp = releaseTimestamp; + } + const pomXml = new XmlDocument(content); const homepage = pomXml.valueWithPath('url'); @@ -287,7 +310,7 @@ export class SbtPackageDatasource extends MavenDatasource { } await saveCache(); - return result; + return null; } override async getReleases( @@ -316,12 +339,33 @@ export class SbtPackageDatasource extends MavenDatasource { return null; } - // istanbul ignore next: to be rewritten + @cache({ + namespace: 'datasource-sbt-package', + key: ( + { registryUrl, packageName }: PostprocessReleaseConfig, + { version }: Release, + ) => `postprocessRelease:${registryUrl}:${packageName}:${version}`, + ttlMinutes: 30 * 24 * 60, + }) override async postprocessRelease( config: PostprocessReleaseConfig, release: Release, ): Promise<PostprocessReleaseResult> { - const mavenResult = await super.postprocessRelease(config, release); - return mavenResult === 'reject' ? release : mavenResult; + // istanbul ignore if + if (!config.registryUrl) { + return release; + } + + const res = await this.getPomInfo( + config.registryUrl, + config.packageName, + release.version, + ); + + if (res?.releaseTimestamp) { + release.releaseTimestamp = res.releaseTimestamp; + } + + return release; } } -- GitLab