mirror of
https://github.com/renovatebot/renovate.git
synced 2026-10-25 06:51:11 +00:00
476456b3c2
* fix(metadata): strip git+ scheme prefix from source urls for changelog fetching * fix(metadata): normalize git+ssh GitLab source urls Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: update URL handling to support additional github and gitlab formats #45941 --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Michael Kriese <michael.kriese@visualon.de>
209 lines
5.9 KiB
TypeScript
209 lines
5.9 KiB
TypeScript
import {
|
|
isEmptyString,
|
|
isNullOrUndefined,
|
|
isString,
|
|
isUndefined,
|
|
} from '@sindresorhus/is';
|
|
import parse from 'github-url-from-git';
|
|
import { detectPlatform } from '../../util/common.ts';
|
|
import { parseGitUrl } from '../../util/git/url.ts';
|
|
import * as hostRules from '../../util/host-rules.ts';
|
|
import { regEx } from '../../util/regex.ts';
|
|
import { asTimestamp } from '../../util/timestamp.ts';
|
|
import { isHttpUrl, parseUrl, trimTrailingSlash } from '../../util/url.ts';
|
|
import { manualChangelogUrls, manualSourceUrls } from './metadata-manual.ts';
|
|
import type { ReleaseResult } from './types.ts';
|
|
|
|
const githubPages = regEx('^https://([^.]+).github.com/([^/]+)$');
|
|
const gitPlusPrefix = regEx('^git\\+');
|
|
const gitPrefix = regEx('^git:/?/?');
|
|
|
|
export function massageUrl(sourceUrl: string): string {
|
|
// Replace git@ sourceUrl with https so hostname can be parsed
|
|
const massagedUrl = massageGitPrefixes(sourceUrl);
|
|
|
|
// Check if URL is valid
|
|
const parsedUrl = parseUrl(massagedUrl);
|
|
if (!parsedUrl) {
|
|
return '';
|
|
}
|
|
|
|
if (detectPlatform(massagedUrl) === 'gitlab') {
|
|
return massageGitlabUrl(sourceUrl);
|
|
}
|
|
return massageGithubUrl(sourceUrl);
|
|
}
|
|
|
|
export function massageGithubUrl(url: string): string {
|
|
const massagedUrl = massageGitPrefixes(url);
|
|
|
|
return massagedUrl
|
|
.replace('http:', 'https:')
|
|
.replace('http+git:', 'https:')
|
|
.replace('https+git:', 'https:')
|
|
.replace('ssh://git@', 'https://')
|
|
.replace(gitPrefix, 'https://')
|
|
.replace(githubPages, 'https://github.com/$1/$2')
|
|
.replace('www.github.com', 'github.com')
|
|
.split('/')
|
|
.slice(0, 5)
|
|
.join('/');
|
|
}
|
|
|
|
export function massageGitlabUrl(url: string): string {
|
|
const massagedUrl = massageGitPrefixes(url);
|
|
|
|
return massagedUrl
|
|
.replace('http:', 'https:')
|
|
.replace('ssh://git@', 'https://')
|
|
.replace(gitPrefix, 'https://')
|
|
.replace(regEx(/\/tree\/.*$/i), '')
|
|
.replace(regEx(/\/$/i), '')
|
|
.replace(regEx(/\.git$/i), '');
|
|
}
|
|
|
|
function massageGitPrefixes(url: string): string {
|
|
let massagedUrl = url.replace(gitPlusPrefix, '');
|
|
|
|
if (massagedUrl.startsWith('git@')) {
|
|
massagedUrl = massagedUrl.replace(':', '/').replace('git@', 'https://');
|
|
}
|
|
|
|
return massagedUrl;
|
|
}
|
|
|
|
function massageTimestamps(dep: ReleaseResult): void {
|
|
for (const release of dep.releases || []) {
|
|
let { releaseTimestamp } = release;
|
|
delete release.releaseTimestamp;
|
|
releaseTimestamp = asTimestamp(releaseTimestamp);
|
|
if (releaseTimestamp) {
|
|
release.releaseTimestamp = releaseTimestamp;
|
|
}
|
|
}
|
|
}
|
|
|
|
export function addMetaData(
|
|
dep: ReleaseResult,
|
|
datasource: string,
|
|
packageName: string,
|
|
): void {
|
|
massageTimestamps(dep);
|
|
|
|
const packageNameLowercase = packageName.toLowerCase();
|
|
const manualChangelogUrl =
|
|
manualChangelogUrls[datasource]?.[packageNameLowercase];
|
|
if (manualChangelogUrl) {
|
|
dep.changelogUrl = manualChangelogUrl;
|
|
}
|
|
|
|
const manualSourceUrl = manualSourceUrls[datasource]?.[packageNameLowercase];
|
|
if (manualSourceUrl) {
|
|
dep.sourceUrl = manualSourceUrl;
|
|
}
|
|
|
|
if (dep.sourceUrl && !dep.sourceDirectory) {
|
|
try {
|
|
const parsed = parseGitUrl(dep.sourceUrl);
|
|
if (parsed.filepathtype === 'tree' && parsed.filepath !== '') {
|
|
dep.sourceUrl = parsed.toString();
|
|
dep.sourceDirectory = parsed.filepath;
|
|
}
|
|
} catch {
|
|
// ignore invalid urls
|
|
}
|
|
}
|
|
|
|
if (
|
|
!dep.sourceUrl &&
|
|
dep.changelogUrl &&
|
|
detectPlatform(dep.changelogUrl) === 'github'
|
|
) {
|
|
dep.sourceUrl = dep.changelogUrl;
|
|
}
|
|
|
|
if (!dep.sourceUrl && dep.homepage) {
|
|
const platform = detectPlatform(dep.homepage);
|
|
if (platform === 'github' || platform === 'gitlab') {
|
|
dep.sourceUrl = dep.homepage;
|
|
}
|
|
}
|
|
const extraBaseUrls = [];
|
|
// istanbul ignore next
|
|
hostRules.hosts({ hostType: 'github' }).forEach((host) => {
|
|
extraBaseUrls.push(host, `gist.${host}`);
|
|
});
|
|
extraBaseUrls.push('gitlab.com');
|
|
if (dep.sourceUrl) {
|
|
// try massaging it
|
|
const massagedUrl = massageUrl(dep.sourceUrl);
|
|
if (isEmptyString(massagedUrl)) {
|
|
delete dep.sourceUrl;
|
|
} else {
|
|
// parse from github-url-from-git only supports Github URLs as its name implies
|
|
dep.sourceUrl =
|
|
parse(massagedUrl, {
|
|
extraBaseUrls,
|
|
}) || dep.sourceUrl;
|
|
// prefer massaged URL to source URL if the latter does not start with http:// or https://
|
|
// (e.g. git@somehost.com) and the detected platform is gitlab.
|
|
// this allows to retrieve changelogs from git hosts other than Github
|
|
if (
|
|
!isHttpUrl(dep.sourceUrl) &&
|
|
detectPlatform(massagedUrl) === 'gitlab'
|
|
) {
|
|
dep.sourceUrl = massagedUrl;
|
|
}
|
|
}
|
|
}
|
|
if (shouldDeleteHomepage(dep.sourceUrl, dep.homepage)) {
|
|
delete dep.homepage;
|
|
}
|
|
// Clean up any empty urls
|
|
const urlKeys: (keyof ReleaseResult)[] = [
|
|
'homepage',
|
|
'sourceUrl',
|
|
'changelogUrl',
|
|
'dependencyUrl',
|
|
];
|
|
for (const urlKey of urlKeys) {
|
|
const urlVal = dep[urlKey];
|
|
if (isString(urlVal) && isHttpUrl(urlVal.trim())) {
|
|
dep[urlKey] = urlVal.trim() as never;
|
|
} else {
|
|
delete dep[urlKey];
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns true if
|
|
* 1. it's a github or gitlab url and not a path within the repo.
|
|
* 2. it's equal to sourceURl
|
|
* @param sourceUrl
|
|
* @param homepage
|
|
*/
|
|
export function shouldDeleteHomepage(
|
|
sourceUrl: string | null | undefined,
|
|
homepage: string | undefined,
|
|
): boolean {
|
|
if (isNullOrUndefined(sourceUrl) || isUndefined(homepage)) {
|
|
return false;
|
|
}
|
|
const massagedSourceUrl = massageUrl(sourceUrl);
|
|
const platform = detectPlatform(homepage);
|
|
if (platform === 'github' || platform === 'gitlab') {
|
|
const sourceUrlParsed = parseUrl(massagedSourceUrl);
|
|
if (isNullOrUndefined(sourceUrlParsed)) {
|
|
return false;
|
|
}
|
|
const homepageParsed = parseUrl(homepage);
|
|
return (
|
|
homepageParsed !== null &&
|
|
trimTrailingSlash(homepageParsed.pathname) ===
|
|
trimTrailingSlash(sourceUrlParsed.pathname)
|
|
);
|
|
}
|
|
return massagedSourceUrl === homepage;
|
|
}
|