renovate
Version:
Automated dependency updates. Flexible so you don't need to be.
188 lines (187 loc) • 8.33 kB
JavaScript
import { regEx } from "../../../util/regex.js";
import { logger } from "../../../logger/index.js";
import { ensureTrailingSlash, parseUrl, trimTrailingSlash } from "../../../util/url.js";
import { compare } from "../../versioning/maven/compare.js";
import "../../versioning/ivy/index.js";
import { get, set } from "../../../util/cache/package/index.js";
import { withCache } from "../../../util/cache/package/with-cache.js";
import { Http } from "../../../util/http/index.js";
import { asTimestamp } from "../../../util/timestamp.js";
import { MAVEN_REPO } from "../maven/common.js";
import { downloadHttpContent, downloadHttpProtocol } from "../maven/util.js";
import { MavenDatasource } from "../maven/index.js";
import { extractPageLinks, getLatestVersion } from "./util.js";
import upath from "upath";
import { XmlDocument } from "xmldoc";
//#region lib/modules/datasource/sbt-package/index.ts
var SbtPackageDatasource = class SbtPackageDatasource extends MavenDatasource {
static id = "sbt-package";
defaultRegistryUrls = [MAVEN_REPO];
defaultVersioning = "ivy";
registryStrategy = "hunt";
sourceUrlSupport = "package";
sourceUrlNote = "The source URL is determined from the `scm` tags in the results.";
constructor(id = SbtPackageDatasource.id) {
super(id);
this.http = new Http("sbt");
}
static parseDepCoordinate(packageName) {
const [groupId, javaArtifactId] = packageName.split(":");
const [artifactId, scalaVersion] = javaArtifactId.split("_");
return {
groupId,
artifactId,
scalaVersion
};
}
async getSbtReleases(registryUrl, packageName) {
const { groupId, artifactId, scalaVersion } = SbtPackageDatasource.parseDepCoordinate(packageName);
const groupIdSplit = groupId.split(".");
const repoRootUrl = ensureTrailingSlash(registryUrl);
const validRootUrlKey = `valid-root-url:${registryUrl}:${packageName}`;
const validRootUrl = await get("datasource-sbt-package", validRootUrlKey);
const packageRootUrls = [];
// istanbul ignore if: not easily testable
if (validRootUrl) packageRootUrls.push(validRootUrl);
else {
const packageRootUrlWith = (sep) => `${repoRootUrl}${groupIdSplit.join(sep)}`;
packageRootUrls.push(ensureTrailingSlash(packageRootUrlWith("/")));
packageRootUrls.push(ensureTrailingSlash(packageRootUrlWith(".")));
}
let dependencyUrl;
let packageUrls;
for (const packageRootUrl of packageRootUrls) {
const packageRootContent = await downloadHttpContent(this.http, packageRootUrl);
if (!packageRootContent) continue;
await set("datasource-sbt-package", validRootUrlKey, packageRootUrl, 720 * 60);
dependencyUrl = trimTrailingSlash(packageRootUrl);
const parsedPackageRootUrl = parseUrl(packageRootUrl);
if (!parsedPackageRootUrl) {
logger.warn({ packageRootUrl }, "Failed to parse packageURL");
continue;
}
const rootPath = parsedPackageRootUrl.pathname;
const artifactSubdirs = extractPageLinks(packageRootContent, (href) => {
const path = href.replace(rootPath, "");
if (path.startsWith(`${artifactId}_native`) || path.startsWith(`${artifactId}_sjs`)) return null;
if (path === artifactId || path.startsWith(`${artifactId}_`)) return ensureTrailingSlash(`${packageRootUrl}${path}`);
return null;
});
if (scalaVersion) {
const scalaSubdir = artifactSubdirs.find((x) => x.endsWith(`/${artifactId}_${scalaVersion}/`));
if (scalaSubdir) {
packageUrls = [scalaSubdir];
break;
}
}
packageUrls = artifactSubdirs;
break;
}
if (!packageUrls) return null;
const invalidPackageUrlsKey = `invalid-package-urls:${registryUrl}:${packageName}`;
const invalidPackageUrls = new Set(await get("datasource-sbt-package", invalidPackageUrlsKey));
packageUrls = packageUrls.filter((url) => !invalidPackageUrls.has(url));
const allVersions = /* @__PURE__ */ new Set();
for (const pkgUrl of packageUrls) {
const parsedPkgUrl = parseUrl(pkgUrl);
if (!parsedPkgUrl) {
invalidPackageUrls.add(pkgUrl);
continue;
}
const packageContent = await downloadHttpContent(this.http, pkgUrl);
// istanbul ignore if
if (!packageContent) {
invalidPackageUrls.add(pkgUrl);
continue;
}
const rootPath = parsedPkgUrl.pathname;
const versions = extractPageLinks(packageContent, (href) => {
const path = href.replace(rootPath, "");
if (path.startsWith(".")) return null;
return path;
});
for (const version of versions) allVersions.add(version);
}
if (invalidPackageUrls.size > 0) await set("datasource-sbt-package", invalidPackageUrlsKey, [...invalidPackageUrls], 720 * 60);
if (packageUrls.length > 0) await set("datasource-sbt-package", `package-urls:${registryUrl}:${packageName}`, packageUrls, 720 * 60);
const versions = [...allVersions];
if (!versions.length) return null;
const res = {
releases: [...allVersions].sort(compare).map((version) => ({ version })),
dependencyUrl
};
const latestVersion = getLatestVersion(versions);
const pomInfo = await this.getPomInfo(registryUrl, packageName, latestVersion, packageUrls);
if (pomInfo?.homepage) res.homepage = pomInfo.homepage;
if (pomInfo?.sourceUrl) res.sourceUrl = pomInfo.sourceUrl;
return res;
}
async getPomInfo(registryUrl, packageName, version, pkgUrls) {
const packageUrlsKey = `package-urls:${registryUrl}:${packageName}`;
// istanbul ignore next: will be covered later
const packageUrls = pkgUrls ?? await get("datasource-sbt-package", packageUrlsKey);
// istanbul ignore if
if (!packageUrls?.length) return null;
// istanbul ignore if
if (!version) return null;
const invalidPomFilesKey = `invalid-pom-files:${registryUrl}:${packageName}:${version}`;
const invalidPomFiles = new Set(await get("datasource-sbt-package", invalidPomFilesKey));
const saveCache = async () => {
if (invalidPomFiles.size > 0) await set("datasource-sbt-package", invalidPomFilesKey, [...invalidPomFiles], 720 * 60);
};
for (const packageUrl of packageUrls) {
const artifactDir = upath.basename(packageUrl);
const [artifact] = artifactDir.split("_");
for (const pomFilePrefix of [artifactDir, artifact]) {
const pomUrl = `${packageUrl}${version}/${`${pomFilePrefix}-${version}.pom`}`;
if (invalidPomFiles.has(pomUrl)) continue;
const { val } = (await downloadHttpProtocol(this.http, pomUrl)).unwrap();
if (!val) {
invalidPomFiles.add(pomUrl);
continue;
}
const result = {};
const releaseTimestamp = asTimestamp(val.lastModified);
if (releaseTimestamp) result.releaseTimestamp = releaseTimestamp;
const pomXml = new XmlDocument(val.data);
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$/), "");
await saveCache();
return result;
}
}
await saveCache();
return null;
}
async getReleases(config) {
const { packageName, registryUrl } = config;
// istanbul ignore if
if (!registryUrl) return null;
const sbtReleases = await this.getSbtReleases(registryUrl, packageName);
if (sbtReleases) return sbtReleases;
logger.debug(`Sbt: no versions discovered for ${packageName} listing organization root package folder, fallback to maven datasource for version discovery`);
const mavenReleaseResult = await super.getReleases(config);
if (mavenReleaseResult) return mavenReleaseResult;
logger.debug(`Sbt: no versions found for "${packageName}"`);
return null;
}
async _postprocessRelease(config, release) {
/* v8 ignore next 3 -- should never happen */
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;
}
postprocessRelease(config, release) {
return withCache({
namespace: "datasource-sbt-package",
key: `postprocessRelease:${config.registryUrl}:${config.packageName}:${release.version}`,
ttlMinutes: 720 * 60
}, () => this._postprocessRelease(config, release));
}
};
//#endregion
export { SbtPackageDatasource };
//# sourceMappingURL=index.js.map