renovate
Version:
Automated dependency updates. Flexible so you don't need to be.
694 lines • 28.8 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.id = void 0;
exports.initPlatform = initPlatform;
exports.getRepos = getRepos;
exports.getRawFile = getRawFile;
exports.getJsonFile = getJsonFile;
exports.initRepo = initRepo;
exports.getPrList = getPrList;
exports.getPr = getPr;
exports.findPr = findPr;
exports.getBranchPr = getBranchPr;
exports.getBranchStatusCheck = getBranchStatusCheck;
exports.getBranchStatus = getBranchStatus;
exports.createPr = createPr;
exports.updatePr = updatePr;
exports.ensureComment = ensureComment;
exports.ensureCommentRemoval = ensureCommentRemoval;
exports.setBranchStatus = setBranchStatus;
exports.mergePr = mergePr;
exports.massageMarkdown = massageMarkdown;
exports.maxBodyLength = maxBodyLength;
exports.findIssue = findIssue;
exports.ensureIssue = ensureIssue;
exports.ensureIssueClosing = ensureIssueClosing;
exports.getIssueList = getIssueList;
exports.addAssignees = addAssignees;
exports.addReviewers = addReviewers;
exports.deleteLabel = deleteLabel;
const tslib_1 = require("tslib");
const promises_1 = require("timers/promises");
const is_1 = tslib_1.__importDefault(require("@sindresorhus/is"));
const GitInterfaces_js_1 = require("azure-devops-node-api/interfaces/GitInterfaces.js");
const error_messages_1 = require("../../../constants/error-messages");
const logger_1 = require("../../../logger");
const external_host_error_1 = require("../../../types/errors/external-host-error");
const common_1 = require("../../../util/common");
const git = tslib_1.__importStar(require("../../../util/git"));
const hostRules = tslib_1.__importStar(require("../../../util/host-rules"));
const regex_1 = require("../../../util/regex");
const sanitize_1 = require("../../../util/sanitize");
const url_1 = require("../../../util/url");
const util_1 = require("../util");
const pr_body_1 = require("../utils/pr-body");
const azureApi = tslib_1.__importStar(require("./azure-got-wrapper"));
const azureHelper = tslib_1.__importStar(require("./azure-helper"));
const types_1 = require("./types");
const util_2 = require("./util");
let config = {};
const defaults = {
hostType: 'azure',
};
exports.id = 'azure';
function initPlatform({ endpoint, token, username, password, }) {
if (!endpoint) {
throw new Error('Init: You must configure an Azure DevOps endpoint');
}
if (!token && !(username && password)) {
throw new Error('Init: You must configure an Azure DevOps token, or a username and password');
}
// TODO: Add a connection check that endpoint/token combination are valid (#9593)
const res = {
endpoint: (0, url_1.ensureTrailingSlash)(endpoint),
};
defaults.endpoint = res.endpoint;
azureApi.setEndpoint(res.endpoint);
const platformConfig = {
endpoint: defaults.endpoint,
};
return Promise.resolve(platformConfig);
}
async function getRepos() {
logger_1.logger.debug('Autodiscovering Azure DevOps repositories');
const azureApiGit = await azureApi.gitApi();
const repos = await azureApiGit.getRepositories();
return repos
.filter((repo) => repo.isDisabled !== true)
.map((repo) => `${repo.project?.name}/${repo.name}`);
}
async function getRawFile(fileName, repoName, branchOrTag) {
try {
const azureApiGit = await azureApi.gitApi();
let repoId;
if (repoName) {
const repos = await azureApiGit.getRepositories();
const repo = (0, util_2.getRepoByName)(repoName, repos);
repoId = repo?.id;
}
else {
repoId = config.repoId;
}
if (!repoId) {
logger_1.logger.debug('No repoId so cannot getRawFile');
return null;
}
let item;
const versionDescriptor = {
version: branchOrTag,
};
// Try to get file from repo with tag first, if not found, then try with branch #36835
for (const versionType of [GitInterfaces_js_1.GitVersionType.Tag, GitInterfaces_js_1.GitVersionType.Branch]) {
versionDescriptor.versionType = versionType;
item = await azureApiGit.getItem(repoId, // repositoryId
fileName, // path
undefined, // project
undefined, // scopePath
undefined, // recursionLevel
undefined, // includeContentMetadata
undefined, // latestProcessedChange
undefined, // download
branchOrTag ? versionDescriptor : undefined, // versionDescriptor
true);
if (item) {
break; // exit loop if item is found
}
else {
logger_1.logger.debug(`File: ${fileName} not found in ${repoName} with ${versionType}: ${branchOrTag}`);
}
}
return item?.content ?? null;
}
catch (err) /* v8 ignore start */ {
if (err.message?.includes('<title>Azure DevOps Services Unavailable</title>')) {
logger_1.logger.debug('Azure DevOps is currently unavailable when attempting to fetch file - throwing ExternalHostError');
throw new external_host_error_1.ExternalHostError(err, exports.id);
}
if (err.code === 'ECONNRESET' || err.code === 'ETIMEDOUT') {
throw new external_host_error_1.ExternalHostError(err, exports.id);
}
if (err.statusCode && err.statusCode >= 500 && err.statusCode < 600) {
throw new external_host_error_1.ExternalHostError(err, exports.id);
}
throw err;
} /* v8 ignore stop */
}
async function getJsonFile(fileName, repoName, branchOrTag) {
const raw = await getRawFile(fileName, repoName, branchOrTag);
return (0, common_1.parseJson)(raw, fileName);
}
async function initRepo({ repository, cloneSubmodules, cloneSubmodulesFilter, }) {
logger_1.logger.debug(`initRepo("${repository}")`);
config = { repository };
const azureApiGit = await azureApi.gitApi();
const repos = await azureApiGit.getRepositories();
const repo = (0, util_2.getRepoByName)(repository, repos);
if (!repo) {
logger_1.logger.error({ repos, repo }, 'Could not find repo in repo list');
throw new Error(error_messages_1.REPOSITORY_NOT_FOUND);
}
logger_1.logger.debug({ repositoryDetails: repo }, 'Repository details');
if (repo.isDisabled) {
logger_1.logger.debug('Repository is disabled- throwing error to abort renovation');
throw new Error(error_messages_1.REPOSITORY_ARCHIVED);
}
/* v8 ignore start */
if (!repo.defaultBranch) {
logger_1.logger.debug('Repo is empty');
throw new Error(error_messages_1.REPOSITORY_EMPTY);
} /* v8 ignore stop */
// TODO #22198
config.repoId = repo.id;
config.project = repo.project.name;
config.owner = '?owner?';
logger_1.logger.debug(`${repository} owner = ${config.owner}`);
const defaultBranch = repo.defaultBranch.replace('refs/heads/', '');
config.defaultBranch = defaultBranch;
logger_1.logger.debug(`${repository} default branch = ${defaultBranch}`);
config.mergeMethods = {};
config.repoForceRebase = false;
const [projectName, repoName] = repository.split('/');
const opts = hostRules.find({
hostType: defaults.hostType,
url: defaults.endpoint,
});
// TODO: types (#22198)
const manualUrl = `${defaults.endpoint}${encodeURIComponent(projectName)}/_git/${encodeURIComponent(repoName)}`;
const url = repo.remoteUrl ?? manualUrl;
await git.initRepo({
...config,
url,
extraCloneOpts: (0, util_2.getStorageExtraCloneOpts)(opts),
cloneSubmodules,
cloneSubmodulesFilter,
});
const repoConfig = {
defaultBranch,
isFork: false,
repoFingerprint: (0, util_1.repoFingerprint)(repo.id, defaults.endpoint),
};
return repoConfig;
}
async function getPrList() {
logger_1.logger.debug('getPrList()');
if (!config.prList) {
const azureApiGit = await azureApi.gitApi();
let prs = [];
let fetchedPrs;
let skip = 0;
do {
fetchedPrs = await azureApiGit.getPullRequests(config.repoId, {
status: 4,
// fetch only prs directly created on the repo and not by forks
sourceRepositoryId: config.project,
}, config.project, 0, skip, 100);
prs = prs.concat(fetchedPrs);
skip += 100;
} while (fetchedPrs.length > 0);
config.prList = prs.map(util_2.getRenovatePRFormat);
logger_1.logger.debug(`Retrieved Pull Requests count: ${config.prList.length}`);
}
return config.prList;
}
async function getPr(pullRequestId) {
logger_1.logger.debug(`getPr(${pullRequestId})`);
if (!pullRequestId) {
return null;
}
const azurePr = (await getPrList()).find((item) => item.number === pullRequestId);
if (!azurePr) {
return null;
}
const azureApiGit = await azureApi.gitApi();
const labels = await azureApiGit.getPullRequestLabels(config.repoId, pullRequestId);
azurePr.labels = labels
.filter((label) => label.active)
.map((label) => label.name)
.filter(is_1.default.string);
return azurePr;
}
async function findPr({ branchName, prTitle, state = 'all', targetBranch, }) {
let prsFiltered = [];
try {
const prs = await getPrList();
prsFiltered = prs.filter((item) => item.sourceRefName === (0, util_1.getNewBranchName)(branchName));
if (prTitle) {
prsFiltered = prsFiltered.filter((item) => item.title.toUpperCase() === prTitle.toUpperCase());
}
switch (state) {
case 'all':
// no more filter needed, we can go further...
break;
case '!open':
prsFiltered = prsFiltered.filter((item) => item.state !== 'open');
break;
default:
prsFiltered = prsFiltered.filter((item) => item.state === state);
break;
}
}
catch (err) {
logger_1.logger.error({ err }, 'findPr error');
}
if (prsFiltered.length === 0) {
return null;
}
if (targetBranch && prsFiltered.length > 1) {
const pr = prsFiltered.find((item) => item.targetBranch === targetBranch);
if (pr) {
return pr;
}
}
return prsFiltered[0];
}
async function getBranchPr(branchName, targetBranch) {
logger_1.logger.debug(`getBranchPr(${branchName}, ${targetBranch})`);
const existingPr = await findPr({
branchName,
state: 'open',
targetBranch,
});
return existingPr ? getPr(existingPr.number) : null;
}
async function getStatusCheck(branchName) {
const azureApiGit = await azureApi.gitApi();
const branch = await azureApiGit.getBranch(config.repoId,
// TODO: fix undefined (#22198)
(0, util_2.getBranchNameWithoutRefsheadsPrefix)(branchName));
// only grab the latest statuses, it will group any by context
return azureApiGit.getStatuses(
// TODO #22198
branch.commit.commitId, config.repoId, undefined, undefined, undefined, true);
}
const azureToRenovateStatusMapping = {
[GitInterfaces_js_1.GitStatusState.Succeeded]: 'green',
[GitInterfaces_js_1.GitStatusState.NotApplicable]: 'green',
[GitInterfaces_js_1.GitStatusState.NotSet]: 'yellow',
[GitInterfaces_js_1.GitStatusState.Pending]: 'yellow',
[GitInterfaces_js_1.GitStatusState.PartiallySucceeded]: 'yellow',
[GitInterfaces_js_1.GitStatusState.Error]: 'red',
[GitInterfaces_js_1.GitStatusState.Failed]: 'red',
};
async function getBranchStatusCheck(branchName, context) {
const res = await getStatusCheck(branchName);
for (const check of res) {
if ((0, util_2.getGitStatusContextCombinedName)(check.context) === context) {
// TODO #22198
return azureToRenovateStatusMapping[check.state] ?? 'yellow';
}
}
return null;
}
async function getBranchStatus(branchName, internalChecksAsSuccess) {
logger_1.logger.debug(`getBranchStatus(${branchName})`);
const statuses = await getStatusCheck(branchName);
logger_1.logger.debug({ branch: branchName, statuses }, 'branch status check result');
if (!statuses.length) {
logger_1.logger.debug('empty branch status check result = returning "pending"');
return 'yellow';
}
const noOfFailures = statuses.filter((status) => status.state === GitInterfaces_js_1.GitStatusState.Error ||
status.state === GitInterfaces_js_1.GitStatusState.Failed).length;
if (noOfFailures) {
return 'red';
}
const noOfPending = statuses.filter((status) => status.state === GitInterfaces_js_1.GitStatusState.NotSet ||
status.state === GitInterfaces_js_1.GitStatusState.Pending).length;
if (noOfPending) {
return 'yellow';
}
if (!internalChecksAsSuccess &&
statuses.every((status) => status.state === GitInterfaces_js_1.GitStatusState.Succeeded &&
status.context?.genre === 'renovate')) {
logger_1.logger.debug('Successful checks are all internal renovate/ checks, so returning "pending" branch status');
return 'yellow';
}
return 'green';
}
async function getMergeStrategy(targetRefName) {
return (config.mergeMethods[targetRefName] ??
(config.mergeMethods[targetRefName] = await azureHelper.getMergeMethod(config.repoId, config.project, targetRefName, config.defaultBranch)));
}
async function createPr({ sourceBranch, targetBranch, prTitle: title, prBody: body, labels, draftPR = false, platformPrOptions, }) {
const sourceRefName = (0, util_1.getNewBranchName)(sourceBranch);
const targetRefName = (0, util_1.getNewBranchName)(targetBranch);
const description = (0, util_2.max4000Chars)((0, sanitize_1.sanitize)(body));
const azureApiGit = await azureApi.gitApi();
const workItemRefs = [
{
id: platformPrOptions?.azureWorkItemId?.toString(),
},
];
let pr = await azureApiGit.createPullRequest({
sourceRefName,
targetRefName,
title,
description,
workItemRefs,
isDraft: draftPR,
}, config.repoId);
if (platformPrOptions?.usePlatformAutomerge) {
const mergeStrategy = platformPrOptions.automergeStrategy === 'auto'
? await getMergeStrategy(pr.targetRefName)
: (0, util_2.mapMergeStrategy)(platformPrOptions.automergeStrategy);
pr = await azureApiGit.updatePullRequest({
autoCompleteSetBy: {
// TODO #22198
id: pr.createdBy.id,
},
completionOptions: {
mergeStrategy,
deleteSourceBranch: true,
mergeCommitMessage: title,
},
}, config.repoId,
// TODO #22198
pr.pullRequestId);
}
if (platformPrOptions?.autoApprove) {
await azureApiGit.createPullRequestReviewer({
reviewerUrl: pr.createdBy.url,
vote: types_1.AzurePrVote.Approved,
isFlagged: false,
isRequired: false,
}, config.repoId,
// TODO #22198
pr.pullRequestId, pr.createdBy.id);
}
await Promise.all(labels.map((label) => azureApiGit.createPullRequestLabel({
name: label,
}, config.repoId,
// TODO #22198
pr.pullRequestId)));
const result = (0, util_2.getRenovatePRFormat)(pr);
if (config.prList) {
config.prList.push(result);
}
return result;
}
async function updatePr({ number: prNo, prTitle: title, prBody: body, state, platformPrOptions, targetBranch, }) {
logger_1.logger.debug(`updatePr(${prNo}, ${title}, body)`);
const azureApiGit = await azureApi.gitApi();
const objToUpdate = {
title,
};
if (targetBranch) {
objToUpdate.targetRefName = (0, util_1.getNewBranchName)(targetBranch);
}
if (body) {
objToUpdate.description = (0, util_2.max4000Chars)((0, sanitize_1.sanitize)(body));
}
if (state === 'open') {
await azureApiGit.updatePullRequest({
status: GitInterfaces_js_1.PullRequestStatus.Active,
}, config.repoId, prNo);
}
else if (state === 'closed') {
objToUpdate.status = GitInterfaces_js_1.PullRequestStatus.Abandoned;
}
if (platformPrOptions?.autoApprove) {
const pr = await azureApiGit.getPullRequestById(prNo, config.project);
await azureApiGit.createPullRequestReviewer({
reviewerUrl: pr.createdBy.url,
vote: types_1.AzurePrVote.Approved,
isFlagged: false,
isRequired: false,
}, config.repoId,
// TODO #22198
pr.pullRequestId, pr.createdBy.id);
}
const updatedPr = await azureApiGit.updatePullRequest(objToUpdate, config.repoId, prNo);
if (config.prList) {
const prToCache = (0, util_2.getRenovatePRFormat)(updatedPr);
// We need to update the cached entry for this PR
const existingIndex = config.prList.findIndex((item) => item.number === prNo);
/* v8 ignore start: should not happen */
if (existingIndex === -1) {
logger_1.logger.warn({ prNo }, 'PR not found in cache');
// Add to cache
config.prList.push(prToCache);
} /* v8 ignore stop */
else {
// overwrite existing PR in cache
config.prList[existingIndex] = prToCache;
}
}
}
async function ensureComment({ number, topic, content, }) {
logger_1.logger.debug(`ensureComment(${number}, ${topic}, content)`);
const header = topic ? `### ${topic}\n\n` : '';
const body = `${header}${(0, sanitize_1.sanitize)(massageMarkdown(content))}`;
const azureApiGit = await azureApi.gitApi();
const threads = await azureApiGit.getThreads(config.repoId, number);
let threadIdFound;
let commentIdFound;
let commentNeedsUpdating = false;
threads.forEach((thread) => {
const firstCommentContent = thread.comments?.[0].content;
if ((topic && firstCommentContent?.startsWith(header)) === true ||
(!topic && firstCommentContent === body)) {
threadIdFound = thread.id;
commentIdFound = thread.comments?.[0].id;
commentNeedsUpdating = firstCommentContent !== body;
}
});
if (!threadIdFound) {
await azureApiGit.createThread({
comments: [{ content: body, commentType: 1, parentCommentId: 0 }],
status: 1,
}, config.repoId, number);
logger_1.logger.info({ repository: config.repository, issueNo: number, topic }, 'Comment added');
}
else if (commentNeedsUpdating) {
await azureApiGit.updateComment({
content: body,
}, config.repoId, number, threadIdFound,
// TODO #22198
commentIdFound);
logger_1.logger.debug({ repository: config.repository, issueNo: number, topic }, 'Comment updated');
}
else {
logger_1.logger.debug({ repository: config.repository, issueNo: number, topic }, 'Comment is already update-to-date');
}
return true;
}
async function ensureCommentRemoval(removeConfig) {
const { number: issueNo } = removeConfig;
const key = removeConfig.type === 'by-topic'
? removeConfig.topic
: removeConfig.content;
logger_1.logger.debug(`Ensuring comment "${key}" in #${issueNo} is removed`);
const azureApiGit = await azureApi.gitApi();
const threads = await azureApiGit.getThreads(config.repoId, issueNo);
let threadIdFound = null;
if (removeConfig.type === 'by-topic') {
const thread = threads.find((thread) => !!thread.comments?.[0].content?.startsWith(`### ${removeConfig.topic}\n\n`));
threadIdFound = thread?.id;
}
else {
const thread = threads.find((thread) => thread.comments?.[0].content?.trim() === removeConfig.content);
threadIdFound = thread?.id;
}
if (threadIdFound) {
await azureApiGit.updateThread({
status: 4, // close
}, config.repoId, issueNo, threadIdFound);
}
}
const renovateToAzureStatusMapping = {
['green']: GitInterfaces_js_1.GitStatusState.Succeeded,
['yellow']: GitInterfaces_js_1.GitStatusState.Pending,
['red']: GitInterfaces_js_1.GitStatusState.Failed,
};
async function setBranchStatus({ branchName, context, description, state, url: targetUrl, }) {
logger_1.logger.debug(`setBranchStatus(${branchName}, ${context}, ${description}, ${state}, ${targetUrl})`);
const azureApiGit = await azureApi.gitApi();
const branch = await azureApiGit.getBranch(config.repoId, (0, util_2.getBranchNameWithoutRefsheadsPrefix)(branchName));
const statusToCreate = {
description,
context: (0, util_2.getGitStatusContextFromCombinedName)(context),
state: renovateToAzureStatusMapping[state],
targetUrl,
};
await azureApiGit.createCommitStatus(statusToCreate,
// TODO #22198
branch.commit.commitId, config.repoId);
logger_1.logger.trace(`Created commit status of ${state} on branch ${branchName}`);
}
async function mergePr({ branchName, id: pullRequestId, strategy, }) {
logger_1.logger.debug(`mergePr(${pullRequestId}, ${branchName})`);
const azureApiGit = await azureApi.gitApi();
let pr = await azureApiGit.getPullRequestById(pullRequestId, config.project);
const mergeStrategy = strategy === 'auto'
? await getMergeStrategy(pr.targetRefName)
: (0, util_2.mapMergeStrategy)(strategy);
const objToUpdate = {
status: GitInterfaces_js_1.PullRequestStatus.Completed,
lastMergeSourceCommit: pr.lastMergeSourceCommit,
completionOptions: {
mergeStrategy,
deleteSourceBranch: true,
mergeCommitMessage: pr.title,
},
};
logger_1.logger.trace(`Updating PR ${pullRequestId} to status ${GitInterfaces_js_1.PullRequestStatus.Completed} (${GitInterfaces_js_1.PullRequestStatus[GitInterfaces_js_1.PullRequestStatus.Completed]}) with lastMergeSourceCommit ${
// TODO: types (#22198)
pr.lastMergeSourceCommit?.commitId} using mergeStrategy ${mergeStrategy} (${GitInterfaces_js_1.GitPullRequestMergeStrategy[mergeStrategy]})`);
try {
const response = await azureApiGit.updatePullRequest(objToUpdate, config.repoId, pullRequestId);
let retries = 0;
let isClosed = response.status === GitInterfaces_js_1.PullRequestStatus.Completed;
while (!isClosed && retries < 5) {
retries += 1;
const sleepMs = retries * 1000;
logger_1.logger.trace({ pullRequestId, status: pr.status, retries }, `Updated PR to closed status but change has not taken effect yet. Retrying...`);
await (0, promises_1.setTimeout)(sleepMs);
pr = await azureApiGit.getPullRequestById(pullRequestId, config.project);
isClosed = pr.status === GitInterfaces_js_1.PullRequestStatus.Completed;
}
if (!isClosed) {
logger_1.logger.warn({
pullRequestId,
status: pr.status,
expectedPRStatus: GitInterfaces_js_1.PullRequestStatus[GitInterfaces_js_1.PullRequestStatus.Completed],
actualPRStatus: GitInterfaces_js_1.PullRequestStatus[pr.status],
}, 'Expected PR to have completed status. However, the PR has a different status');
}
return true;
}
catch (err) {
logger_1.logger.debug({ err }, 'Failed to set the PR as completed.');
return false;
}
}
function massageMarkdown(input) {
// Remove any HTML we use
return (0, pr_body_1.smartTruncate)(input, maxBodyLength())
.replace('you tick the rebase/retry checkbox', 'PR is renamed to start with "rebase!"')
.replace('checking the rebase/retry box above', 'renaming the PR to start with "rebase!"')
.replace((0, regex_1.regEx)(`\n---\n\n.*?<!-- rebase-check -->.*?\n`), '')
.replace((0, regex_1.regEx)(/<!--renovate-(?:debug|config-hash):.*?-->/g), '');
}
function maxBodyLength() {
return 4000;
}
/* v8 ignore start */
function findIssue() {
// TODO: Needs implementation (#9592)
logger_1.logger.debug(`findIssue() is not implemented`);
return Promise.resolve(null);
} /* v8 ignore stop */
/* v8 ignore start */
function ensureIssue() {
// TODO: Needs implementation (#9592)
logger_1.logger.debug(`ensureIssue() is not implemented`);
return Promise.resolve(null);
} /* v8 ignore stop */
/* v8 ignore start */
function ensureIssueClosing() {
return Promise.resolve();
} /* v8 ignore stop */
/* v8 ignore start */
function getIssueList() {
logger_1.logger.debug(`getIssueList()`);
// TODO: Needs implementation (#9592)
return Promise.resolve([]);
} /* v8 ignore stop */
async function getUserIds(users) {
const azureApiGit = await azureApi.gitApi();
const azureApiCore = await azureApi.coreApi();
const repos = await azureApiGit.getRepositories();
const repo = repos.find((c) => c.id === config.repoId);
const requiredReviewerPrefix = 'required:';
const validReviewers = new Set();
// TODO #22198
const teams = await azureHelper.getAllProjectTeams(repo.project.id);
const members = await Promise.all(teams.map(async (t) => await azureApiCore.getTeamMembersWithExtendedProperties(
// TODO #22198
repo.project.id, t.id)));
const ids = [];
members.forEach((listMembers) => {
listMembers.forEach((m) => {
users.forEach((r) => {
let reviewer = r;
let isRequired = false;
if (reviewer.startsWith(requiredReviewerPrefix)) {
reviewer = reviewer.replace(requiredReviewerPrefix, '');
isRequired = true;
}
if (reviewer.toLowerCase() === m.identity?.displayName?.toLowerCase() ||
reviewer.toLowerCase() === m.identity?.uniqueName?.toLowerCase()) {
if (ids.filter((c) => c.id === m.identity?.id).length === 0) {
// TODO #22198
ids.push({
id: m.identity.id,
name: reviewer,
isRequired,
});
validReviewers.add(reviewer);
}
}
});
});
});
teams.forEach((t) => {
users.forEach((r) => {
let reviewer = r;
let isRequired = false;
if (reviewer.startsWith(requiredReviewerPrefix)) {
reviewer = reviewer.replace(requiredReviewerPrefix, '');
isRequired = true;
}
if (reviewer.toLowerCase() === t.name?.toLowerCase()) {
if (ids.filter((c) => c.id === t.id).length === 0) {
// TODO #22198
ids.push({ id: t.id, name: reviewer, isRequired });
validReviewers.add(reviewer);
}
}
});
});
for (const u of users) {
const reviewer = u.replace(requiredReviewerPrefix, '');
if (!validReviewers.has(reviewer)) {
logger_1.logger.once.info(`${reviewer} is neither an Azure DevOps Team nor a user associated with a Team`);
}
}
return ids;
}
/**
*
* @param {number} issueNo
* @param {string[]} assignees
*/
async function addAssignees(issueNo, assignees) {
logger_1.logger.trace(`addAssignees(${issueNo}, [${assignees.join(', ')}])`);
const ids = await getUserIds(assignees);
await ensureComment({
number: issueNo,
topic: 'Add Assignees',
content: ids.map((a) => `@<${a.id}>`).join(', '),
});
}
/**
*
* @param {number} prNo
* @param {string[]} reviewers
*/
async function addReviewers(prNo, reviewers) {
logger_1.logger.trace(`addReviewers(${prNo}, [${reviewers.join(', ')}])`);
const azureApiGit = await azureApi.gitApi();
const ids = await getUserIds(reviewers);
await Promise.all(ids.map(async (obj) => {
await azureApiGit.createPullRequestReviewer({
isRequired: obj.isRequired,
}, config.repoId, prNo, obj.id);
logger_1.logger.debug(`Reviewer added: ${obj.name}`);
}));
}
async function deleteLabel(prNumber, label) {
logger_1.logger.debug(`Deleting label ${label} from #${prNumber}`);
const azureApiGit = await azureApi.gitApi();
await azureApiGit.deletePullRequestLabels(config.repoId, prNumber, label);
}
//# sourceMappingURL=index.js.map