renovate
Version:
Automated dependency updates. Flexible so you don't need to be.
535 lines • 20.4 kB
JavaScript
;
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