renovate/lib/modules/platform/azure/util.ts

204 lines
5.3 KiB
TypeScript

import type {
GitPullRequest,
GitRepository,
GitStatusContext,
} from 'azure-devops-node-api/interfaces/GitInterfaces.js';
import {
GitPullRequestMergeStrategy,
PullRequestStatus,
} from 'azure-devops-node-api/interfaces/GitInterfaces.js';
import type { MergeStrategy } from '../../../config/types';
import { logger } from '../../../logger';
import type { HostRule, PrState } from '../../../types';
import type { GitOptions } from '../../../types/git';
import { addSecretForSanitizing } from '../../../util/sanitize';
import { toBase64 } from '../../../util/string';
import { getPrBodyStruct } from '../pr-body';
import type { AzurePr } from './types';
export function getGitStatusContextCombinedName(
context: GitStatusContext | null | undefined,
): string | undefined {
if (!context) {
return undefined;
}
const combinedName = `${context.genre ? `${context.genre}/` : ''}${
// TODO: types (#22198)
context.name!
}`;
logger.trace(`Got combined context name of ${combinedName}`);
return combinedName;
}
export function getGitStatusContextFromCombinedName(
context: string | undefined | null,
): GitStatusContext | undefined {
if (!context) {
return undefined;
}
let name = context;
let genre: string | undefined;
const lastSlash = context.lastIndexOf('/');
if (lastSlash > 0) {
name = context.substring(lastSlash + 1);
genre = context.substring(0, lastSlash);
}
return {
genre,
name,
};
}
export function getBranchNameWithoutRefsheadsPrefix(
branchPath: string | undefined,
): string | undefined {
if (!branchPath) {
logger.error(`getBranchNameWithoutRefsheadsPrefix(undefined)`);
return undefined;
}
if (!branchPath.startsWith('refs/heads/')) {
logger.trace(
`The refs/heads/ name should have started with 'refs/heads/' but it didn't. (${branchPath})`,
);
return branchPath;
}
return branchPath.substring(11, branchPath.length);
}
export function getBranchNameWithoutRefsPrefix(
branchPath?: string,
): string | undefined {
if (!branchPath) {
logger.error(`getBranchNameWithoutRefsPrefix(undefined)`);
return undefined;
}
if (!branchPath.startsWith('refs/')) {
logger.trace(
`The ref name should have started with 'refs/' but it didn't. (${branchPath})`,
);
return branchPath;
}
return branchPath.substring(5, branchPath.length);
}
const stateMap = {
[PullRequestStatus.Abandoned]: 'closed',
[PullRequestStatus.Completed]: 'merged',
} as Record<PullRequestStatus, PrState | undefined>;
export function getRenovatePRFormat(azurePr: GitPullRequest): AzurePr {
const number = azurePr.pullRequestId;
const sourceBranch = getBranchNameWithoutRefsheadsPrefix(
azurePr.sourceRefName,
);
const targetBranch = getBranchNameWithoutRefsheadsPrefix(
azurePr.targetRefName,
);
const bodyStruct = getPrBodyStruct(azurePr.description);
const createdAt = azurePr.creationDate?.toISOString();
// TODO #22198
const state = stateMap[azurePr.status!] ?? 'open';
const sourceRefName = azurePr.sourceRefName;
return {
...azurePr,
sourceBranch,
state,
number,
bodyStruct,
sourceRefName,
targetBranch,
createdAt,
} as AzurePr;
}
export function getStorageExtraCloneOpts(config: HostRule): GitOptions {
let authType: string;
let authValue: string;
if (!config.token && config.username && config.password) {
authType = 'basic';
authValue = toBase64(`${config.username}:${config.password}`);
} else if (config.token?.length === 52) {
authType = 'basic';
authValue = toBase64(`:${config.token}`);
} else {
authType = 'bearer';
authValue = config.token!;
}
addSecretForSanitizing(authValue, 'global');
return {
'-c': `http.extraheader=AUTHORIZATION: ${authType} ${authValue}`,
};
}
export function max4000Chars(str: string): string {
if (str && str.length >= 4000) {
return str.substring(0, 3999);
}
return str;
}
export function getProjectAndRepo(str: string): {
project: string;
repo: string;
} {
logger.trace(`getProjectAndRepo(${str})`);
const strSplit = str.split(`/`);
if (strSplit.length === 1) {
return {
project: str,
repo: str,
};
}
if (strSplit.length === 2) {
return {
project: strSplit[0],
repo: strSplit[1],
};
}
const msg = `${str} can be only structured this way : 'repository' or 'projectName/repository'!`;
logger.error(msg);
throw new Error(msg);
}
export function getRepoByName(
name: string,
repos: (GitRepository | null | undefined)[] | undefined | null,
): GitRepository | null {
logger.trace(`getRepoByName(${name})`);
let { project, repo } = getProjectAndRepo(name);
project = project.toLowerCase();
repo = repo.toLowerCase();
const foundRepo = repos?.find(
(r) =>
project === r?.project?.name?.toLowerCase() &&
repo === r?.name?.toLowerCase(),
);
if (!foundRepo) {
logger.debug(`Repo not found: ${name}`);
}
return foundRepo ?? null;
}
export function mapMergeStrategy(
mergeStrategy?: MergeStrategy,
): GitPullRequestMergeStrategy {
switch (mergeStrategy) {
case 'rebase':
case 'fast-forward':
return GitPullRequestMergeStrategy.Rebase;
case 'merge-commit':
return GitPullRequestMergeStrategy.NoFastForward;
case 'squash':
return GitPullRequestMergeStrategy.Squash;
default:
return GitPullRequestMergeStrategy.NoFastForward;
}
}