UNPKG

renovate

Version:

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

535 lines • 20.4 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.id = void 0; exports.initPlatform = initPlatform; exports.initRepo = initRepo; exports.getPrList = getPrList; exports.findPr = findPr; exports.getBranchPr = getBranchPr; exports.getPr = getPr; exports.getRepos = getRepos; exports.massageMarkdown = massageMarkdown; exports.maxBodyLength = maxBodyLength; exports.getJsonFile = getJsonFile; exports.getRawFile = getRawFile; exports.createPr = createPr; exports.updatePr = updatePr; exports.mergePr = mergePr; exports.addReviewers = addReviewers; exports.addAssignees = addAssignees; exports.findIssue = findIssue; exports.ensureIssue = ensureIssue; exports.getIssueList = getIssueList; exports.ensureIssueClosing = ensureIssueClosing; exports.deleteLabel = deleteLabel; exports.getBranchStatus = getBranchStatus; exports.getBranchStatusCheck = getBranchStatusCheck; exports.setBranchStatus = setBranchStatus; exports.ensureComment = ensureComment; exports.ensureCommentRemoval = ensureCommentRemoval; const tslib_1 = require("tslib"); const node_buffer_1 = require("node:buffer"); const client_codecommit_1 = require("@aws-sdk/client-codecommit"); const error_messages_1 = require("../../../constants/error-messages"); const logger_1 = require("../../../logger"); const array_1 = require("../../../util/array"); const common_1 = require("../../../util/common"); const env_1 = require("../../../util/env"); const git = tslib_1.__importStar(require("../../../util/git")); const regex_1 = require("../../../util/regex"); const sanitize_1 = require("../../../util/sanitize"); const util_1 = require("../util"); const pr_body_1 = require("../utils/pr-body"); const client = tslib_1.__importStar(require("./codecommit-client")); exports.id = 'codecommit'; let config = {}; async function initPlatform({ endpoint, username, password, token: awsToken, }) { const accessKeyId = username; const secretAccessKey = password; const env = (0, env_1.getEnv)(); let region; if (accessKeyId) { env.AWS_ACCESS_KEY_ID = accessKeyId; } if (secretAccessKey) { env.AWS_SECRET_ACCESS_KEY = secretAccessKey; } if (awsToken) { env.AWS_SESSION_TOKEN = awsToken; } if (endpoint) { const regionReg = (0, regex_1.regEx)(/.*codecommit\.(?<region>.+)\.amazonaws\.com/); const codeCommitMatch = regionReg.exec(endpoint); region = codeCommitMatch?.groups?.region; if (region) { env.AWS_REGION = region; } else { logger_1.logger.warn("Can't parse region, make sure your endpoint is correct"); } } // If any of the below fails, it will throw an exception stopping the program. client.buildCodeCommitClient(); // To check if we have permission to codecommit, throws exception if failed. await client.listRepositories(); const platformConfig = { endpoint: endpoint ?? `https://git-codecommit.${env.AWS_REGION ?? 'us-east-1'}.amazonaws.com/`, }; return Promise.resolve(platformConfig); } async function initRepo({ repository, endpoint, }) { logger_1.logger.debug(`initRepo("${repository}")`); config = { repository }; let repo; try { repo = await client.getRepositoryInfo(repository); } catch (err) { logger_1.logger.error({ err }, 'Could not find repository'); throw new Error(error_messages_1.REPOSITORY_NOT_FOUND); } if (!repo?.repositoryMetadata) { logger_1.logger.error({ repository }, 'Could not find repository'); throw new Error(error_messages_1.REPOSITORY_NOT_FOUND); } logger_1.logger.debug({ repositoryDetails: repo }, 'Repository details'); const metadata = repo.repositoryMetadata; const url = client.getCodeCommitUrl(metadata, repository); try { await git.initRepo({ url, }); } catch (err) { logger_1.logger.debug({ err }, 'Failed to git init'); throw new Error(error_messages_1.PLATFORM_BAD_CREDENTIALS); } if (!metadata.defaultBranch || !metadata.repositoryId) { logger_1.logger.debug('Repo is empty'); throw new Error(error_messages_1.REPOSITORY_EMPTY); } const defaultBranch = metadata.defaultBranch; config.defaultBranch = defaultBranch; logger_1.logger.debug(`${repository} default branch = ${defaultBranch}`); return { repoFingerprint: (0, util_1.repoFingerprint)(metadata.repositoryId, endpoint), defaultBranch, isFork: false, }; } async function getPrList() { logger_1.logger.debug('getPrList()'); if (config.prList) { return config.prList; } const listPrsResponse = await client.listPullRequests(config.repository); const fetchedPrs = []; if (listPrsResponse && !listPrsResponse.pullRequestIds) { return fetchedPrs; } const prIds = (0, array_1.coerceArray)(listPrsResponse.pullRequestIds); for (const prId of prIds) { const prRes = await client.getPr(prId); if (!prRes?.pullRequest) { continue; } const prInfo = prRes.pullRequest; const pr = { targetBranch: prInfo.pullRequestTargets[0].destinationReference, sourceBranch: prInfo.pullRequestTargets[0].sourceReference, destinationCommit: prInfo.pullRequestTargets[0].destinationCommit, sourceCommit: prInfo.pullRequestTargets[0].sourceCommit, state: prInfo.pullRequestStatus === client_codecommit_1.PullRequestStatusEnum.OPEN ? 'open' : 'closed', number: Number.parseInt(prId), title: prInfo.title, body: prInfo.description, }; fetchedPrs.push(pr); } config.prList = fetchedPrs; logger_1.logger.debug(`Retrieved Pull Requests, count: ${fetchedPrs.length}`); return fetchedPrs; } async function findPr({ branchName, prTitle, state = 'all', }) { let prsFiltered = []; try { const prs = await getPrList(); const refsHeadBranchName = (0, util_1.getNewBranchName)(branchName); prsFiltered = prs.filter((item) => item.sourceBranch === refsHeadBranchName); if (prTitle) { prsFiltered = prsFiltered.filter((item) => item.title.toUpperCase() === prTitle.toUpperCase()); } switch (state) { case 'all': break; case '!open': prsFiltered = prsFiltered.filter((item) => item.state !== 'open'); break; default: prsFiltered = prsFiltered.filter((item) => item.state === 'open'); break; } } catch (err) { logger_1.logger.error({ err }, 'findPr error'); } if (prsFiltered.length === 0) { return null; } return prsFiltered[0]; } async function getBranchPr(branchName) { logger_1.logger.debug(`getBranchPr(${branchName})`); const existingPr = await findPr({ branchName, state: 'open', }); return existingPr ? getPr(existingPr.number) : null; } async function getPr(pullRequestId) { logger_1.logger.debug(`getPr(${pullRequestId})`); const prRes = await client.getPr(`${pullRequestId}`); if (!prRes?.pullRequest) { return null; } const prInfo = prRes.pullRequest; let prState; if (prInfo.pullRequestTargets[0].mergeMetadata?.isMerged) { prState = 'merged'; } else { prState = prInfo.pullRequestStatus === client_codecommit_1.PullRequestStatusEnum.OPEN ? 'open' : 'closed'; } return { sourceBranch: prInfo.pullRequestTargets[0].sourceReference, sourceCommit: prInfo.pullRequestTargets[0].sourceCommit, state: prState, number: pullRequestId, title: prInfo.title, targetBranch: prInfo.pullRequestTargets[0].destinationReference, destinationCommit: prInfo.pullRequestTargets[0].destinationCommit, body: prInfo.description, }; } async function getRepos() { logger_1.logger.debug('Autodiscovering AWS CodeCommit repositories'); let reposRes; try { reposRes = await client.listRepositories(); //todo do we need pagination? maximum number of repos is 1000 without pagination, also the same for free account } catch (error) { logger_1.logger.error({ error }, 'Could not retrieve repositories'); return []; } const res = []; const repoNames = (0, array_1.coerceArray)(reposRes?.repositories); for (const repo of repoNames) { if (repo.repositoryName) { res.push(repo.repositoryName); } } return res; } function massageMarkdown(input) { // Remove any HTML we use return input .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)(/<\/?summary>/g), '**') .replace((0, regex_1.regEx)(/<\/?details>/g), '') .replace((0, regex_1.regEx)(`\n---\n\n.*?<!-- rebase-check -->.*?\n`), '') .replace((0, regex_1.regEx)(/\]\(\.\.\/pull\//g), '](../../pull-requests/') .replace((0, regex_1.regEx)(/(?<hiddenComment><!--renovate-(?:debug|config-hash):.*?-->)/g), '[//]: # ($<hiddenComment>)'); } /** * Unsed, no Dashboard */ function maxBodyLength() { return Infinity; } async function getJsonFile(fileName, repoName, branchOrTag) { const raw = await getRawFile(fileName, repoName, branchOrTag); return (0, common_1.parseJson)(raw, fileName); } async function getRawFile(fileName, repoName, branchOrTag) { const fileRes = await client.getFile(repoName ?? config.repository, fileName, branchOrTag); if (!fileRes?.fileContent) { return null; } const buf = node_buffer_1.Buffer.from(fileRes.fileContent); return buf.toString(); } const AMAZON_MAX_BODY_LENGTH = 10239; async function createPr({ sourceBranch, targetBranch, prTitle: title, prBody: body, }) { const description = (0, pr_body_1.smartTruncate)((0, sanitize_1.sanitize)(body), AMAZON_MAX_BODY_LENGTH); const prCreateRes = await client.createPr(title, (0, sanitize_1.sanitize)(description), sourceBranch, targetBranch, config.repository); if (!prCreateRes.pullRequest?.title || !prCreateRes.pullRequest?.pullRequestId || !prCreateRes.pullRequest?.description || !prCreateRes.pullRequest?.pullRequestTargets?.length) { throw new Error('Could not create pr, missing PR info'); } return { number: Number.parseInt(prCreateRes.pullRequest.pullRequestId), state: 'open', title: prCreateRes.pullRequest.title, sourceBranch, targetBranch, sourceCommit: prCreateRes.pullRequest.pullRequestTargets[0].sourceCommit, destinationCommit: prCreateRes.pullRequest.pullRequestTargets[0].destinationCommit, sourceRepo: config.repository, body: prCreateRes.pullRequest.description, }; } async function updatePr({ number: prNo, prTitle: title, prBody: body, state, }) { logger_1.logger.debug(`updatePr(${prNo}, ${title}, body)`); let cachedPr = undefined; const cachedPrs = config.prList ?? []; for (const p of cachedPrs) { if (p.number === prNo) { cachedPr = p; } } if (body && cachedPr?.body !== body) { await client.updatePrDescription(`${prNo}`, (0, pr_body_1.smartTruncate)((0, sanitize_1.sanitize)(body), AMAZON_MAX_BODY_LENGTH)); } if (title && cachedPr?.title !== title) { await client.updatePrTitle(`${prNo}`, title); } const prStatusInput = state === 'closed' ? client_codecommit_1.PullRequestStatusEnum.CLOSED : client_codecommit_1.PullRequestStatusEnum.OPEN; if (cachedPr?.state !== prStatusInput) { try { await client.updatePrStatus(`${prNo}`, prStatusInput); } catch { // safety check // do nothing, it's ok to fail sometimes when trying to update from open to open or from closed to closed. } } } // Auto-Merge not supported currently. /* v8 ignore start */ async function mergePr({ branchName, id: prNo, strategy, }) { logger_1.logger.debug(`mergePr(${prNo}, ${branchName})`); await client.getPr(`${prNo}`); return Promise.resolve(false); // // /* v8 ignore start */ // if (!prOut) { // return false; // } /* v8 ignore stop */ // const pReq = prOut.pullRequest; // const targets = pReq?.pullRequestTargets; // // /* v8 ignore start */ // if (!targets) { // return false; // } /* v8 ignore stop */ // // if (strategy === 'rebase') { // logger.warn('CodeCommit does not support a "rebase" strategy.'); // return false; // } // // try { // if (strategy === 'auto' || strategy === 'squash') { // await client.squashMerge( // targets[0].repositoryName!, // targets[0].sourceReference!, // targets[0].destinationReference!, // pReq?.title // ); // } else if (strategy === 'fast-forward') { // await client.fastForwardMerge( // targets[0].repositoryName!, // targets[0].sourceReference!, // targets[0].destinationReference! // ); // } else { // logger.debug(`unsupported strategy`); // return false; // } // } catch (err) { // logger.debug({ err }, `PR merge error`); // logger.info({ pr: prNo }, 'PR automerge failed'); // return false; // } // // logger.trace(`Updating PR ${prNo} to status ${PullRequestStatusEnum.CLOSED}`); // // try { // const response = await client.updatePrStatus( // `${prNo}`, // PullRequestStatusEnum.CLOSED // ); // const isClosed = // response.pullRequest?.pullRequestStatus === PullRequestStatusEnum.CLOSED; // // if (!isClosed) { // logger.warn( // { // pullRequestId: prNo, // status: response.pullRequest?.pullRequestStatus, // }, // `Expected PR to have status` // ); // } // return true; // } catch (err) { // logger.debug({ err }, 'Failed to set the PR as Closed.'); // return false; // } } /* v8 ignore stop */ async function addReviewers(prNo, reviewers) { const numberOfApprovers = reviewers.length; const approvalRuleContents = `{"Version":"2018-11-08","Statements": [{"Type": "Approvers","NumberOfApprovalsNeeded":${numberOfApprovers},"ApprovalPoolMembers": ${JSON.stringify(reviewers)}}]}`; const res = await client.createPrApprovalRule(`${prNo}`, approvalRuleContents); if (res) { const approvalRule = res.approvalRule; logger_1.logger.debug({ approvalRule }, `Approval Rule Added to PR #${prNo}:`); } } /* v8 ignore start */ function addAssignees(iid, assignees) { // CodeCommit does not support adding reviewers return Promise.resolve(); } /* v8 ignore stop */ /* v8 ignore start */ function findIssue(title) { // CodeCommit does not have issues return Promise.resolve(null); } /* v8 ignore stop */ /* v8 ignore start */ function ensureIssue({ title, }) { // CodeCommit does not have issues return Promise.resolve(null); } /* v8 ignore stop */ /* v8 ignore start */ function getIssueList() { // CodeCommit does not have issues return Promise.resolve([]); } /* v8 ignore stop */ /* v8 ignore start */ function ensureIssueClosing(title) { // CodeCommit does not have issues return Promise.resolve(); } /* v8 ignore stop */ /* v8 ignore start */ function deleteLabel(prNumber, label) { return Promise.resolve(); } /* v8 ignore stop */ // Returns the combined status for a branch. /* v8 ignore start */ function getBranchStatus(branchName) { logger_1.logger.debug(`getBranchStatus(${branchName})`); logger_1.logger.debug('returning branch status yellow, because getBranchStatus isnt supported on aws yet'); return Promise.resolve('yellow'); } /* v8 ignore stop */ /* v8 ignore start */ function getBranchStatusCheck(branchName, context) { logger_1.logger.debug(`getBranchStatusCheck(${branchName}, context=${context})`); logger_1.logger.debug('returning null, because getBranchStatusCheck is not supported on aws yet'); return Promise.resolve(null); } /* v8 ignore stop */ /* v8 ignore start */ function setBranchStatus({ branchName, context, description, state, url: targetUrl, }) { return Promise.resolve(); } /* v8 ignore stop */ 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)(content)}`; let prCommentsResponse; try { prCommentsResponse = await client.getPrComments(`${number}`); } catch (err) { logger_1.logger.debug({ err }, 'Unable to retrieve pr comments'); return false; } let commentId = undefined; let commentNeedsUpdating = false; if (!prCommentsResponse?.commentsForPullRequestData) { return false; } for (const commentObj of prCommentsResponse.commentsForPullRequestData) { if (!commentObj?.comments) { continue; } const firstCommentContent = commentObj.comments[0].content; if ((topic && firstCommentContent?.startsWith(header)) === true || (!topic && firstCommentContent === body)) { commentId = commentObj.comments[0].commentId; commentNeedsUpdating = firstCommentContent !== body; break; } } if (!commentId) { const prs = await getPrList(); const thisPr = prs.filter((item) => item.number === number); if (!thisPr[0].sourceCommit || !thisPr[0].destinationCommit) { return false; } await client.createPrComment(`${number}`, config.repository, body, thisPr[0].destinationCommit, thisPr[0].sourceCommit); logger_1.logger.info({ repository: config.repository, prNo: number, topic }, 'Comment added'); } else if (commentNeedsUpdating && commentId) { await client.updateComment(commentId, body); logger_1.logger.debug({ repository: config.repository, prNo: number, topic }, 'Comment updated'); } else { logger_1.logger.debug({ repository: config.repository, prNo: number, topic }, 'Comment is already update-to-date'); } return true; } async function ensureCommentRemoval(removeConfig) { const { number: prNo } = removeConfig; const key = removeConfig.type === 'by-topic' ? removeConfig.topic : removeConfig.content; logger_1.logger.debug(`Ensuring comment "${key}" in #${prNo} is removed`); let prCommentsResponse; try { prCommentsResponse = await client.getPrComments(`${prNo}`); } catch (err) { logger_1.logger.debug({ err }, 'Unable to retrieve pr comments'); return; } if (!prCommentsResponse?.commentsForPullRequestData) { logger_1.logger.debug('commentsForPullRequestData not found'); return; } let commentIdToRemove; for (const commentObj of prCommentsResponse.commentsForPullRequestData) { if (!commentObj?.comments) { logger_1.logger.debug('comments object not found under commentsForPullRequestData'); continue; } for (const comment of commentObj.comments) { if ((removeConfig.type === 'by-topic' && comment.content?.startsWith(`### ${removeConfig.topic}\n\n`)) === true || (removeConfig.type === 'by-content' && removeConfig.content === comment.content?.trim())) { commentIdToRemove = comment.commentId; break; } } if (commentIdToRemove) { await client.deleteComment(commentIdToRemove); logger_1.logger.debug(`comment "${key}" in PR #${prNo} was removed`); break; } } } //# sourceMappingURL=index.js.map