mirror of
https://github.com/renovatebot/renovate.git
synced 2026-10-25 06:51:11 +00:00
86890d2142
* refactor(git): centralize runtime config environment Preserve caller-supplied and inherited GIT_CONFIG_KEY_n and GIT_CONFIG_VALUE_n entries when appending runtime Git configuration. Co-authored-by: Jamie Tanna <jamie.tanna@mend.io> * refactor(git): extract runtime config appending * style(git): format runtime config tests * test(git): remove superseded environment cases * Apply suggestion from `@RahulGautamSingh` Co-authored-by: RahulGautamSingh <rahultesnik@gmail.com> * test(git): align invalid config count warning --------- Co-authored-by: MrVampy <4302946+MrVampy@users.noreply.github.com> Co-authored-by: Jamie Tanna <jamie.tanna@mend.io> Co-authored-by: Michael Kriese <michael.kriese@gmx.de> Co-authored-by: RahulGautamSingh <rahultesnik@gmail.com>
209 lines
6.9 KiB
TypeScript
209 lines
6.9 KiB
TypeScript
import { isEmptyString } from '@sindresorhus/is';
|
|
import { PLATFORM_HOST_TYPES } from '../../constants/platforms.ts';
|
|
import { logger } from '../../logger/index.ts';
|
|
import type { HostRule } from '../../types/index.ts';
|
|
import { detectPlatform } from '../common.ts';
|
|
import type { ResolvedChildEnv } from '../exec/utils.ts';
|
|
import { find, getAll } from '../host-rules.ts';
|
|
import { regEx } from '../regex.ts';
|
|
import { createURLFromHostOrURL, isHttpUrl } from '../url.ts';
|
|
import { addGitConfigEnvironmentVariables } from './config.ts';
|
|
import type { AuthenticationRule } from './types.ts';
|
|
import { parseGitUrl } from './url.ts';
|
|
|
|
const githubApiUrls = new Set([
|
|
'github.com',
|
|
'api.github.com',
|
|
'https://api.github.com',
|
|
'https://api.github.com/',
|
|
]);
|
|
|
|
/**
|
|
* Append Git authentication configuration to a resolved child environment.
|
|
* @returns a new environment without modifying the input
|
|
*/
|
|
export function getGitAuthenticatedEnvironmentVariables(
|
|
environmentVariables: Readonly<ResolvedChildEnv>,
|
|
originalGitUrl: string,
|
|
{ token, username, password, hostType, matchHost }: HostRule,
|
|
): ResolvedChildEnv {
|
|
if (!token && !(username && password)) {
|
|
logger.warn(
|
|
{ host: matchHost },
|
|
`Could not create environment variable for host as neither token or username and password was set`,
|
|
);
|
|
return { ...environmentVariables };
|
|
}
|
|
|
|
let authenticationRules: AuthenticationRule[];
|
|
if (token) {
|
|
authenticationRules = getAuthenticationRulesWithToken(
|
|
originalGitUrl,
|
|
hostType,
|
|
token,
|
|
);
|
|
} else {
|
|
const encodedUsername = encodeURIComponent(username!);
|
|
const encodedPassword = encodeURIComponent(password!);
|
|
|
|
authenticationRules = getAuthenticationRules(
|
|
originalGitUrl,
|
|
hostType,
|
|
`${encodedUsername}:${encodedPassword}`,
|
|
);
|
|
}
|
|
|
|
return addGitConfigEnvironmentVariables(
|
|
environmentVariables,
|
|
authenticationRules.map((rule) => ({
|
|
key: `url.${rule.url}.insteadOf`,
|
|
value: rule.insteadOf,
|
|
})),
|
|
);
|
|
}
|
|
|
|
function getAuthenticationRulesWithToken(
|
|
url: string,
|
|
hostType: string | undefined | null,
|
|
authToken: string,
|
|
): AuthenticationRule[] {
|
|
let token = authToken;
|
|
let type = hostType;
|
|
type ??= detectPlatform(url);
|
|
if (type === 'gitlab') {
|
|
token = `gitlab-ci-token:${authToken}`;
|
|
}
|
|
return getAuthenticationRules(url, type, token);
|
|
}
|
|
|
|
/**
|
|
* Generates the authentication rules for later git usage for the given host
|
|
* @link https://coolaj86.com/articles/vanilla-devops-git-credentials-cheatsheet/
|
|
* @param gitUrl Git repository URL
|
|
* @param hostType Git host type
|
|
* @param token Authentication token or `username:password` string
|
|
*/
|
|
export function getAuthenticationRules(
|
|
gitUrl: string,
|
|
hostType: string | undefined | null,
|
|
token: string,
|
|
): AuthenticationRule[] {
|
|
const authenticationRules = [];
|
|
const hasUser = token.split(':').length > 1;
|
|
const insteadUrl = parseGitUrl(gitUrl);
|
|
let sshPort = insteadUrl.port;
|
|
|
|
if (hostType === 'bitbucket-server') {
|
|
// For Bitbucket Server/Data Center, `source` must be `bitbucket-server`
|
|
// to generate HTTP(s) URLs correctly.
|
|
// https://github.com/IonicaBizau/git-url-parse/blob/28828546c148d58bbcff61409915a4e1e8f7eb11/lib/index.js#L304
|
|
insteadUrl.source = 'bitbucket-server';
|
|
|
|
// sshPort is string, wrong type!
|
|
// https://github.com/DefinitelyTyped/DefinitelyTyped/pull/72361
|
|
// https://github.com/IonicaBizau/parse-path/blob/87f71f273da90f85f6845937a70b7c032eeae4e3/lib/index.js#L45
|
|
// v8 ignore next -- TODO: add test #40625
|
|
if (!sshPort || isEmptyString(sshPort)) {
|
|
// By default, bitbucket-server SSH port is 7999.
|
|
// For non-default port, the generated auth config will likely be incorrect.
|
|
sshPort = '7999';
|
|
}
|
|
}
|
|
|
|
const url = { ...insteadUrl };
|
|
const protocol = regEx(/^https?$/).test(url.protocol)
|
|
? url.protocol
|
|
: 'https';
|
|
|
|
// ssh protocol with user if empty
|
|
url.token = hasUser ? token : `ssh:${token}`;
|
|
authenticationRules.push({
|
|
url: url.toString(protocol),
|
|
// only edge case, need to stringify ourself because the exact syntax is not supported by the library
|
|
// https://github.com/IonicaBizau/git-url-parse/blob/246c9119fb42c2ea1c280028fe77c53eb34c190c/lib/index.js#L246
|
|
insteadOf: `ssh://git@${insteadUrl.resource}${
|
|
sshPort ? `:${sshPort}` : ''
|
|
}/${insteadUrl.full_name}${insteadUrl.git_suffix ? '.git' : ''}`,
|
|
});
|
|
|
|
// alternative ssh protocol with user if empty
|
|
url.token = hasUser ? token : `git:${token}`;
|
|
authenticationRules.push({
|
|
url: url.toString(protocol),
|
|
insteadOf: { ...insteadUrl, port: sshPort }.toString('ssh'),
|
|
});
|
|
|
|
// https protocol with no user as default fallback
|
|
url.token = token;
|
|
authenticationRules.push({
|
|
url: url.toString(protocol),
|
|
insteadOf: insteadUrl.toString(protocol),
|
|
});
|
|
|
|
return authenticationRules;
|
|
}
|
|
|
|
export function getGitEnvironmentVariables(
|
|
environmentVariables: Readonly<ResolvedChildEnv>,
|
|
hostTypes: readonly string[] = [],
|
|
): ResolvedChildEnv {
|
|
let gitEnvironmentVariables: ResolvedChildEnv = { ...environmentVariables };
|
|
|
|
// hard-coded logic to use authentication for github.com based on the githubToken for api.github.com
|
|
const gitHubHostRule = find({
|
|
hostType: 'github',
|
|
url: 'https://api.github.com/',
|
|
});
|
|
|
|
if (gitHubHostRule?.token) {
|
|
gitEnvironmentVariables = getGitAuthenticatedEnvironmentVariables(
|
|
gitEnvironmentVariables,
|
|
'https://github.com/',
|
|
gitHubHostRule,
|
|
);
|
|
}
|
|
|
|
// Manager-scoped rules are opt-in so unrelated credentials are not exposed to Git.
|
|
const gitAllowedHostTypes = new Set<string>([
|
|
...PLATFORM_HOST_TYPES,
|
|
...hostTypes,
|
|
]);
|
|
|
|
// filter rules without `matchHost` and `token` or username and password and github api github rules
|
|
const hostRules = getAll()
|
|
.filter((r) => r.matchHost && (r.token ?? (r.username && r.password)))
|
|
.filter((r) => !gitHubHostRule || !githubApiUrls.has(r.matchHost!));
|
|
|
|
// for each hostRule without hostType we add additional authentication variables to the environmentVariables
|
|
// for each hostRule with hostType we add additional authentication variables to the environmentVariables
|
|
for (const hostRule of hostRules) {
|
|
if (!hostRule.hostType || gitAllowedHostTypes.has(hostRule.hostType)) {
|
|
gitEnvironmentVariables = addAuthFromHostRule(
|
|
hostRule,
|
|
gitEnvironmentVariables,
|
|
);
|
|
}
|
|
}
|
|
return gitEnvironmentVariables;
|
|
}
|
|
|
|
function addAuthFromHostRule(
|
|
hostRule: HostRule,
|
|
environmentVariables: Readonly<ResolvedChildEnv>,
|
|
): ResolvedChildEnv {
|
|
const httpUrl = createURLFromHostOrURL(hostRule.matchHost!)?.toString();
|
|
if (isHttpUrl(httpUrl)) {
|
|
logger.trace(`Adding Git authentication for ${httpUrl} using token auth.`);
|
|
return getGitAuthenticatedEnvironmentVariables(
|
|
environmentVariables,
|
|
httpUrl!,
|
|
hostRule,
|
|
);
|
|
}
|
|
|
|
logger.debug(
|
|
`Could not parse registryUrl ${hostRule.matchHost!} or not using http(s). Ignoring`,
|
|
);
|
|
return { ...environmentVariables };
|
|
}
|