UNPKG

renovate

Version:

Automated dependency updates. Flexible so you don't need to be.

694 lines • 28.8 kB
"use strict"; 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