From c5dfee3ec00cd226eeaa00d15aa51d656ae1304c Mon Sep 17 00:00:00 2001 From: Sergei Zharinov <zharinov@users.noreply.github.com> Date: Tue, 8 Oct 2024 03:57:27 -0300 Subject: [PATCH] refactor(sbt): Decouple `sbt-package` and `sbt-plugin` datasources (#31835) --- lib/modules/datasource/sbt-package/index.ts | 2 +- lib/modules/datasource/sbt-plugin/index.ts | 137 +++++++++++++++++++- 2 files changed, 131 insertions(+), 8 deletions(-) diff --git a/lib/modules/datasource/sbt-package/index.ts b/lib/modules/datasource/sbt-package/index.ts index c0d5403b63..ba081d56a6 100644 --- a/lib/modules/datasource/sbt-package/index.ts +++ b/lib/modules/datasource/sbt-package/index.ts @@ -16,7 +16,7 @@ import type { import { extractPageLinks, getLatestVersion } from './util'; export class SbtPackageDatasource extends MavenDatasource { - static override id = 'sbt-package'; + static override readonly id = 'sbt-package'; override readonly defaultRegistryUrls = [MAVEN_REPO]; diff --git a/lib/modules/datasource/sbt-plugin/index.ts b/lib/modules/datasource/sbt-plugin/index.ts index e053811936..fc84d65642 100644 --- a/lib/modules/datasource/sbt-plugin/index.ts +++ b/lib/modules/datasource/sbt-plugin/index.ts @@ -1,12 +1,13 @@ +import { XmlDocument } from 'xmldoc'; import { logger } from '../../../logger'; import { Http } from '../../../util/http'; import { regEx } from '../../../util/regex'; import { ensureTrailingSlash } from '../../../util/url'; import * as ivyVersioning from '../../versioning/ivy'; import { compare } from '../../versioning/maven/compare'; +import { Datasource } from '../datasource'; import { MAVEN_REPO } from '../maven/common'; import { downloadHttpProtocol } from '../maven/util'; -import { SbtPackageDatasource } from '../sbt-package'; import { extractPageLinks, getLatestVersion } from '../sbt-package/util'; import type { GetReleasesConfig, @@ -17,17 +18,15 @@ import type { export const SBT_PLUGINS_REPO = 'https://repo.scala-sbt.org/scalasbt/sbt-plugin-releases'; -export const defaultRegistryUrls = [SBT_PLUGINS_REPO, MAVEN_REPO]; +export class SbtPluginDatasource extends Datasource { + static readonly id = 'sbt-plugin'; -export class SbtPluginDatasource extends SbtPackageDatasource { - static override readonly id = 'sbt-plugin'; + override readonly defaultRegistryUrls = [SBT_PLUGINS_REPO, MAVEN_REPO]; - override readonly defaultRegistryUrls = defaultRegistryUrls; + override readonly defaultVersioning = ivyVersioning.id; override readonly registryStrategy: RegistryStrategy = 'merge'; - override readonly defaultVersioning = ivyVersioning.id; - override readonly sourceUrlSupport = 'package'; override readonly sourceUrlNote = 'The source URL is determined from the `scm` tags in the results.'; @@ -37,6 +36,130 @@ export class SbtPluginDatasource extends SbtPackageDatasource { this.http = new Http('sbt'); } + // istanbul ignore next: to be rewritten + async getArtifactSubdirs( + searchRoot: string, + artifact: string, + scalaVersion: string, + ): Promise<string[] | null> { + const pkgUrl = ensureTrailingSlash(searchRoot); + const res = await downloadHttpProtocol(this.http, pkgUrl); + const indexContent = res?.body; + if (indexContent) { + const rootPath = new URL(pkgUrl).pathname; + let artifactSubdirs = extractPageLinks(indexContent, (href) => { + const path = href.replace(rootPath, ''); + if ( + path.startsWith(`${artifact}_native`) || + path.startsWith(`${artifact}_sjs`) + ) { + return null; + } + + if (path === artifact || path.startsWith(`${artifact}_`)) { + return path; + } + + return null; + }); + + if ( + scalaVersion && + artifactSubdirs.includes(`${artifact}_${scalaVersion}`) + ) { + artifactSubdirs = [`${artifact}_${scalaVersion}`]; + } + return artifactSubdirs; + } + + return null; + } + + // istanbul ignore next: to be rewritten + async getPackageReleases( + searchRoot: string, + artifactSubdirs: string[] | null, + ): Promise<string[] | null> { + if (artifactSubdirs) { + const releases: string[] = []; + for (const searchSubdir of artifactSubdirs) { + const pkgUrl = ensureTrailingSlash(`${searchRoot}/${searchSubdir}`); + const res = await downloadHttpProtocol(this.http, pkgUrl); + const content = res?.body; + if (content) { + const rootPath = new URL(pkgUrl).pathname; + const subdirReleases = extractPageLinks(content, (href) => { + const path = href.replace(rootPath, ''); + if (path.startsWith('.')) { + return null; + } + + return path; + }); + + subdirReleases.forEach((x) => releases.push(x)); + } + } + if (releases.length) { + return [...new Set(releases)].sort(compare); + } + } + + return null; + } + + // istanbul ignore next: to be rewritten + async getUrls( + searchRoot: string, + artifactDirs: string[] | null, + version: string | null, + ): Promise<Partial<ReleaseResult>> { + const result: Partial<ReleaseResult> = {}; + + if (!artifactDirs?.length) { + return result; + } + + if (!version) { + return result; + } + + for (const artifactDir of artifactDirs) { + const [artifact] = artifactDir.split('_'); + const pomFileNames = [ + `${artifactDir}-${version}.pom`, + `${artifact}-${version}.pom`, + ]; + + for (const pomFileName of pomFileNames) { + const pomUrl = `${searchRoot}/${artifactDir}/${version}/${pomFileName}`; + const res = await downloadHttpProtocol(this.http, pomUrl); + const content = res?.body; + if (content) { + const pomXml = new XmlDocument(content); + + const homepage = pomXml.valueWithPath('url'); + if (homepage) { + result.homepage = homepage; + } + + const sourceUrl = pomXml.valueWithPath('scm.url'); + if (sourceUrl) { + result.sourceUrl = sourceUrl + .replace(regEx(/^scm:/), '') + .replace(regEx(/^git:/), '') + .replace(regEx(/^git@github.com:/), 'https://github.com/') + .replace(regEx(/\.git$/), ''); + } + + return result; + } + } + } + + return result; + } + async resolvePluginReleases( rootUrl: string, artifact: string, -- GitLab