mirror of
https://github.com/renovatebot/renovate.git
synced 2025-01-15 09:06:25 +00:00
225 lines
7.1 KiB
TypeScript
225 lines
7.1 KiB
TypeScript
// TODO: types (#7154)
|
|
/* eslint-disable @typescript-eslint/restrict-template-expressions */
|
|
import URL from 'url';
|
|
import { logger } from '../../../logger';
|
|
import { detectPlatform } from '../../../util/common';
|
|
import * as hostRules from '../../../util/host-rules';
|
|
import { Http } from '../../../util/http';
|
|
import { regEx } from '../../../util/regex';
|
|
import { trimLeadingSlash, trimTrailingSlash } from '../../../util/url';
|
|
import { BitBucketTagsDatasource } from '../bitbucket-tags';
|
|
import { GitTagsDatasource } from '../git-tags';
|
|
import { GithubTagsDatasource } from '../github-tags';
|
|
import { GitlabTagsDatasource } from '../gitlab-tags';
|
|
import type { DataSource } from './types';
|
|
|
|
// TODO: figure out class hierarchy (#10532)
|
|
export class BaseGoDatasource {
|
|
private static readonly gitlabHttpsRegExp = regEx(
|
|
/^(?<httpsRegExpUrl>https:\/\/[^/]*gitlab\.[^/]*)\/(?<httpsRegExpName>.+?)(?:\/v\d+)?[/]?$/
|
|
);
|
|
private static readonly gitlabRegExp = regEx(
|
|
/^(?<regExpUrl>gitlab\.[^/]*)\/(?<regExpPath>.+?)(?:\/v\d+)?[/]?$/
|
|
);
|
|
private static readonly gitVcsRegexp = regEx(
|
|
/^(?:[^/]+)\/(?<module>.*)\.git(?:$|\/)/
|
|
);
|
|
|
|
private static readonly id = 'go';
|
|
private static readonly http = new Http(BaseGoDatasource.id);
|
|
|
|
static async getDatasource(goModule: string): Promise<DataSource | null> {
|
|
if (goModule.startsWith('gopkg.in/')) {
|
|
const [pkg] = goModule.replace('gopkg.in/', '').split('.');
|
|
const packageName = pkg.includes('/') ? pkg : `go-${pkg}/${pkg}`;
|
|
return {
|
|
datasource: GithubTagsDatasource.id,
|
|
packageName,
|
|
registryUrl: 'https://github.com',
|
|
};
|
|
}
|
|
|
|
if (goModule.startsWith('github.com/')) {
|
|
const split = goModule.split('/');
|
|
const packageName = split[1] + '/' + split[2];
|
|
return {
|
|
datasource: GithubTagsDatasource.id,
|
|
packageName,
|
|
registryUrl: 'https://github.com',
|
|
};
|
|
}
|
|
|
|
if (goModule.startsWith('bitbucket.org/')) {
|
|
const split = goModule.split('/');
|
|
const packageName = split[1] + '/' + split[2];
|
|
return {
|
|
datasource: BitBucketTagsDatasource.id,
|
|
packageName,
|
|
registryUrl: 'https://bitbucket.org',
|
|
};
|
|
}
|
|
|
|
return await BaseGoDatasource.goGetDatasource(goModule);
|
|
}
|
|
|
|
private static async goGetDatasource(
|
|
goModule: string
|
|
): Promise<DataSource | null> {
|
|
const pkgUrl = `https://${goModule}?go-get=1`;
|
|
// GitHub Enterprise only returns a go-import meta
|
|
const res = (await BaseGoDatasource.http.get(pkgUrl)).body;
|
|
return (
|
|
BaseGoDatasource.goSourceHeader(res, goModule) ??
|
|
BaseGoDatasource.goImportHeader(res, goModule)
|
|
);
|
|
}
|
|
|
|
private static goSourceHeader(
|
|
res: string,
|
|
goModule: string
|
|
): DataSource | null {
|
|
const sourceMatch = regEx(
|
|
`<meta\\s+name="?go-source"?\\s+content="([^\\s]+)\\s+([^\\s]+)`
|
|
).exec(res);
|
|
if (!sourceMatch) {
|
|
return null;
|
|
}
|
|
const [, prefix, goSourceUrl] = sourceMatch;
|
|
if (!goModule.startsWith(prefix)) {
|
|
logger.trace({ goModule }, 'go-source header prefix not match');
|
|
return null;
|
|
}
|
|
logger.debug(`Go lookup source url ${goSourceUrl} for module ${goModule}`);
|
|
return this.detectDatasource(goSourceUrl, goModule);
|
|
}
|
|
|
|
private static detectDatasource(
|
|
goSourceUrl: string,
|
|
goModule: string
|
|
): DataSource | null {
|
|
if (goSourceUrl?.startsWith('https://github.com/')) {
|
|
return {
|
|
datasource: GithubTagsDatasource.id,
|
|
packageName: goSourceUrl
|
|
.replace('https://github.com/', '')
|
|
.replace(regEx(/\/$/), ''),
|
|
registryUrl: 'https://github.com',
|
|
};
|
|
}
|
|
const gitlabUrl =
|
|
BaseGoDatasource.gitlabHttpsRegExp.exec(goSourceUrl)?.groups
|
|
?.httpsRegExpUrl;
|
|
const gitlabUrlName =
|
|
BaseGoDatasource.gitlabHttpsRegExp.exec(goSourceUrl)?.groups
|
|
?.httpsRegExpName;
|
|
const gitlabModuleName =
|
|
BaseGoDatasource.gitlabRegExp.exec(goModule)?.groups?.regExpPath;
|
|
if (gitlabUrl && gitlabUrlName) {
|
|
if (gitlabModuleName?.startsWith(gitlabUrlName)) {
|
|
const vcsIndicatedModule = BaseGoDatasource.gitVcsRegexp.exec(goModule);
|
|
if (vcsIndicatedModule?.groups?.module) {
|
|
return {
|
|
datasource: GitlabTagsDatasource.id,
|
|
registryUrl: gitlabUrl,
|
|
packageName: vcsIndicatedModule.groups?.module,
|
|
};
|
|
}
|
|
return {
|
|
datasource: GitlabTagsDatasource.id,
|
|
registryUrl: gitlabUrl,
|
|
packageName: gitlabModuleName,
|
|
};
|
|
}
|
|
|
|
return {
|
|
datasource: GitlabTagsDatasource.id,
|
|
registryUrl: gitlabUrl,
|
|
packageName: gitlabUrlName,
|
|
};
|
|
}
|
|
|
|
if (hostRules.hostType({ url: goSourceUrl }) === 'gitlab') {
|
|
// get server base url from import url
|
|
const parsedUrl = URL.parse(goSourceUrl);
|
|
|
|
// TODO: `parsedUrl.pathname` can be undefined
|
|
const packageName = trimLeadingSlash(`${parsedUrl.pathname}`);
|
|
|
|
const registryUrl = `${parsedUrl.protocol}//${parsedUrl.host}`;
|
|
|
|
// a .git path indicates a concrete git repository, which can be different from metadata returned by gitlab
|
|
const vcsIndicatedModule = BaseGoDatasource.gitVcsRegexp.exec(goModule);
|
|
if (vcsIndicatedModule?.groups?.module) {
|
|
return {
|
|
datasource: GitlabTagsDatasource.id,
|
|
registryUrl,
|
|
packageName: vcsIndicatedModule.groups?.module,
|
|
};
|
|
}
|
|
|
|
return {
|
|
datasource: GitlabTagsDatasource.id,
|
|
registryUrl,
|
|
packageName,
|
|
};
|
|
}
|
|
/* istanbul ignore next */
|
|
return null;
|
|
}
|
|
|
|
private static goImportHeader(
|
|
res: string,
|
|
goModule: string
|
|
): DataSource | null {
|
|
const importMatch = regEx(
|
|
`<meta\\s+name="?go-import"?\\s+content="([^\\s]+)\\s+([^\\s]+)\\s+([^\\s]+)">`
|
|
).exec(res);
|
|
|
|
if (!importMatch) {
|
|
logger.trace({ goModule }, 'No go-source or go-import header found');
|
|
return null;
|
|
}
|
|
|
|
const [, prefix, , goImportURL] = importMatch;
|
|
if (!goModule.startsWith(prefix)) {
|
|
logger.trace({ goModule }, 'go-import header prefix not match');
|
|
return null;
|
|
}
|
|
|
|
logger.debug(`Go module: ${goModule} lookup import url ${goImportURL}`);
|
|
// get server base url from import url
|
|
const parsedUrl = URL.parse(goImportURL);
|
|
|
|
const datasource = this.detectDatasource(
|
|
goImportURL.replace(regEx(/\.git$/), ''),
|
|
goModule
|
|
);
|
|
if (datasource !== null) {
|
|
return datasource;
|
|
}
|
|
// fall back to old behaviour if detection did not work
|
|
|
|
if (detectPlatform(goImportURL) === 'github') {
|
|
// split the go module from the URL: host/go/module -> go/module
|
|
// TODO: `parsedUrl.pathname` can be undefined
|
|
const packageName = trimTrailingSlash(`${parsedUrl.pathname}`)
|
|
.replace(regEx(/\.git$/), '')
|
|
.split('/')
|
|
.slice(-2)
|
|
.join('/');
|
|
|
|
return {
|
|
datasource: GithubTagsDatasource.id,
|
|
registryUrl: `${parsedUrl.protocol}//${parsedUrl.host}`,
|
|
packageName,
|
|
};
|
|
}
|
|
|
|
// Fall back to git tags
|
|
|
|
return {
|
|
datasource: GitTagsDatasource.id,
|
|
packageName: goImportURL,
|
|
};
|
|
}
|
|
}
|