mirror of
https://github.com/renovatebot/renovate.git
synced 2026-10-27 07:50:47 +00:00
a473e73b3e
Adds a custom oxlint rule renovate/v8-ignore-no-count that flags the count form '/* v8 ignore next <N> */'. The count is not honored by vitest's ast-v8-to-istanbul hint parser, which captures only the keyword (if/else/next/file); 'next' always exempts exactly the next AST node. Trailing counts therefore suggest a line range that is never applied and mislead readers. The rule is autofixable (drops the count, preserving actual behavior) and has been applied to all existing occurrences.
228 lines
6.7 KiB
TypeScript
228 lines
6.7 KiB
TypeScript
import type { XmlDocument } from 'xmldoc';
|
|
import { logger } from '../../../logger/index.ts';
|
|
import * as packageCache from '../../../util/cache/package/index.ts';
|
|
import { asTimestamp } from '../../../util/timestamp.ts';
|
|
import { ensureTrailingSlash } from '../../../util/url.ts';
|
|
import { compare } from '../../versioning/maven/compare.ts';
|
|
import mavenVersion, * as mavenVersioning from '../../versioning/maven/index.ts';
|
|
import { Datasource } from '../datasource.ts';
|
|
import type {
|
|
GetReleasesConfig,
|
|
PostprocessReleaseConfig,
|
|
PostprocessReleaseResult,
|
|
RegistryStrategy,
|
|
Release,
|
|
ReleaseResult,
|
|
} from '../types.ts';
|
|
import { MAVEN_CENTRAL_URLS, MAVEN_REPO } from './common.ts';
|
|
import type { MavenDependency, MetadataResults } from './types.ts';
|
|
import {
|
|
createUrlForDependencyPom,
|
|
downloadMaven,
|
|
downloadMavenXml,
|
|
getDependencyInfo,
|
|
getDependencyParts,
|
|
getMavenUrl,
|
|
} from './util.ts';
|
|
|
|
function getLatestSuitableVersion(releases: Release[]): string | null {
|
|
/* v8 ignore next -- TODO: add test */
|
|
if (!releases?.length) {
|
|
return null;
|
|
}
|
|
const allVersions = releases.map(({ version }) => version);
|
|
const stableVersions = allVersions.filter((x) => mavenVersion.isStable(x));
|
|
const versions = stableVersions.length ? stableVersions : allVersions;
|
|
return versions.reduce((latestVersion, version) =>
|
|
compare(version, latestVersion) === 1
|
|
? version
|
|
: /* istanbul ignore next: hard to test */ latestVersion,
|
|
);
|
|
}
|
|
|
|
function extractVersions(metadata: XmlDocument): MetadataResults {
|
|
const res: MetadataResults = {};
|
|
const versions = metadata.descendantWithPath('versioning.versions');
|
|
const elements = versions?.childrenNamed('version');
|
|
if (!elements) {
|
|
return res;
|
|
}
|
|
res.versions = elements.map((el) => el.val);
|
|
const latest = metadata.descendantWithPath('versioning.latest');
|
|
if (latest?.val) {
|
|
res.tags ??= {};
|
|
res.tags.latest = latest.val;
|
|
}
|
|
const release = metadata.descendantWithPath('versioning.release');
|
|
if (release?.val) {
|
|
res.tags ??= {};
|
|
res.tags.release = release.val;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
export const defaultRegistryUrls = [MAVEN_REPO];
|
|
|
|
export class MavenDatasource extends Datasource {
|
|
static id = 'maven';
|
|
|
|
override readonly caching = true;
|
|
|
|
override readonly defaultRegistryUrls = defaultRegistryUrls;
|
|
|
|
override readonly defaultVersioning: string = mavenVersioning.id;
|
|
|
|
override readonly registryStrategy: RegistryStrategy = 'merge';
|
|
|
|
override readonly releaseTimestampSupport = true;
|
|
override readonly releaseTimestampNote =
|
|
'The release timestamp is determined from the `Last-Modified` header or the `lastModified` field in the results.';
|
|
override readonly sourceUrlSupport = 'package';
|
|
override readonly sourceUrlNote =
|
|
'The source URL is determined from the `scm` tags in the results.';
|
|
|
|
constructor(id = MavenDatasource.id) {
|
|
super(id);
|
|
}
|
|
|
|
async fetchVersionsFromMetadata(
|
|
dependency: MavenDependency,
|
|
repoUrl: string,
|
|
): Promise<MetadataResults> {
|
|
const metadataUrl = getMavenUrl(dependency, repoUrl, 'maven-metadata.xml');
|
|
const metadataXmlResult = await downloadMavenXml(this.http, metadataUrl);
|
|
return metadataXmlResult
|
|
.transform(({ data: metadata }) => extractVersions(metadata))
|
|
.onError((err) => {
|
|
logger.debug(
|
|
`Maven: error fetching versions for "${dependency.display}": ${err.type}`,
|
|
);
|
|
})
|
|
.unwrapOr({});
|
|
}
|
|
|
|
async getReleases({
|
|
packageName,
|
|
registryUrl,
|
|
}: GetReleasesConfig): Promise<ReleaseResult | null> {
|
|
/* v8 ignore next -- should never happen */
|
|
if (!registryUrl) {
|
|
return null;
|
|
}
|
|
|
|
const dependency = getDependencyParts(packageName);
|
|
const repoUrl = ensureTrailingSlash(registryUrl);
|
|
|
|
if (
|
|
// groupId
|
|
(packageName.includes('.gradle.plugin:') ||
|
|
// artifactId
|
|
packageName.endsWith('.gradle.plugin')) &&
|
|
MAVEN_CENTRAL_URLS.some((url) => repoUrl === ensureTrailingSlash(url))
|
|
) {
|
|
logger.debug(
|
|
`Maven: skipping Maven Central for suspected Gradle plugin "${packageName}"`,
|
|
);
|
|
return null;
|
|
}
|
|
|
|
logger.debug(`Looking up ${dependency.display} in repository ${repoUrl}`);
|
|
|
|
const metadata = await this.fetchVersionsFromMetadata(dependency, repoUrl);
|
|
if (!metadata.versions?.length) {
|
|
return null;
|
|
}
|
|
const releases = metadata.versions.map((version) => ({ version }));
|
|
|
|
logger.debug(
|
|
`Found ${releases.length} new releases for ${dependency.display} in repository ${repoUrl}`,
|
|
);
|
|
|
|
const latestSuitableVersion = getLatestSuitableVersion(releases);
|
|
const dependencyInfo =
|
|
latestSuitableVersion &&
|
|
(await getDependencyInfo(
|
|
this.http,
|
|
dependency,
|
|
repoUrl,
|
|
latestSuitableVersion,
|
|
));
|
|
|
|
const result: ReleaseResult = {
|
|
...dependency,
|
|
...dependencyInfo,
|
|
releases,
|
|
};
|
|
if (metadata.tags) {
|
|
result.tags = metadata.tags;
|
|
if (result.tags.latest) {
|
|
logger.debug(`Setting respectLatest=false for maven ${packageName}`);
|
|
result.respectLatest = false;
|
|
}
|
|
}
|
|
|
|
if (!this.defaultRegistryUrls.includes(registryUrl)) {
|
|
result.isPrivate = true;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
override async postprocessRelease(
|
|
{ packageName, registryUrl }: PostprocessReleaseConfig,
|
|
release: Release,
|
|
): Promise<PostprocessReleaseResult> {
|
|
const { version, versionOrig } = release;
|
|
const cacheKey = versionOrig
|
|
? `postprocessRelease:${registryUrl}:${packageName}:${versionOrig}:${version}`
|
|
: `postprocessRelease:${registryUrl}:${packageName}:${version}`;
|
|
const cachedResult = await packageCache.get<PostprocessReleaseResult>(
|
|
'datasource-maven:postprocess-reject',
|
|
cacheKey,
|
|
);
|
|
|
|
/* v8 ignore if: hard to test */
|
|
if (cachedResult) {
|
|
return cachedResult;
|
|
}
|
|
|
|
if (!packageName || !registryUrl) {
|
|
return release;
|
|
}
|
|
|
|
const dependency = getDependencyParts(packageName);
|
|
|
|
const pomUrl = await createUrlForDependencyPom(
|
|
this.http,
|
|
release.versionOrig ?? release.version,
|
|
dependency,
|
|
registryUrl,
|
|
);
|
|
|
|
const artifactUrl = getMavenUrl(dependency, registryUrl, pomUrl);
|
|
const fetchResult = await downloadMaven(this.http, artifactUrl);
|
|
const { val, err } = fetchResult.unwrap();
|
|
|
|
if (err) {
|
|
const result: PostprocessReleaseResult =
|
|
err.type === 'not-found' ? 'reject' : release;
|
|
if (result === 'reject') {
|
|
await packageCache.set(
|
|
'datasource-maven:postprocess-reject',
|
|
cacheKey,
|
|
result,
|
|
24 * 60,
|
|
);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
if (val.lastModified) {
|
|
release.releaseTimestamp = asTimestamp(val.lastModified);
|
|
}
|
|
|
|
return release;
|
|
}
|
|
}
|