const GitHub = require('github-api'); let program = require('commander'); const ORGANISATION = 'Alfresco'; const ORIGIN_REPO = 'alfresco-content-app'; const ATTEMPT_MSG = [ `Could you check it please? 🤖`, `Emm did you forget? 🤡`, `Where are you? 🤷`, `We are going to die!! 👻`, `I guess the Apocalypse happened and I am alone 👽` ]; GIVE_UP_MSG = `I gave up, it will be fixed eventually 🔴`; class PrCreator { constructor(githubUser, githubRepo, token, commit) { this.github = new GitHub({token}); this.repoOrigin = this.github.getRepo(githubUser, ORIGIN_REPO); this.repoDestination = this.github.getRepo(githubUser, githubRepo); this.issue = this.github.getIssues(githubUser, githubRepo); this.commit = commit; } async createOrUpdate(title, head, base, commit) { const { data: prs } = await this.repoDestination.listPullRequests({ state: 'open', head: `${ORGANISATION}:${head}`, base }); if (prs.length < 1) { const { data: pr } = await this.repoDestination.createPullRequest({ title, head, base, body: `sha:${commit}` }); return pr.number; } else { const upstreamPrOpen = prs[0]; // override the title to contains the latest aca dep number await this.repoDestination.updatePullRequest(upstreamPrOpen.number, { title, body: `sha:${commit}` }); return upstreamPrOpen.number; } } async fetchContributors(shaFrom, shaTo) { const mapAuthors = new Map(); let upstreamShaFound = shaTo !== null; const listCommits = await this.repoOrigin.listCommits(({sha: shaFrom})); let index = 0; while(upstreamShaFound) { if (listCommits.data[index].sha === shaTo ) { upstreamShaFound = false; } else { mapAuthors.set(listCommits.data[index].author.login, listCommits.data[index].commit.author.name); } index++; } return mapAuthors; } async createComment(issueOrPrNumber, head, base, shaOriginHead ) { const shaTo = await this.getShaTo(head, base); const contributors = await this.fetchContributors(shaOriginHead, shaTo); const attemptCount = await this.getCommentAmount(issueOrPrNumber); const commentMsg = this.createCommentBody(contributors, attemptCount); await this.issue.createIssueComment(issueOrPrNumber, commentMsg); } createCommentBody(contributors, attemptCount) { const flattenedContributors = this.flattenContributors(contributors); const attemptMsg = attemptCount <= 5 ? ATTEMPT_MSG[attemptCount] : GIVE_UP_MSG const tmpl = ` Attempt: ${attemptCount+1} you are part of the contributors: ${flattenedContributors} ${attemptMsg} `; return tmpl; } flattenContributors(contributors) { let names = []; for (let key of contributors.keys()) { names.push(`@${key}`) } return names.join(', '); } async getShaTo(head, base) { const { data: closedUpstreamPRs } = await this.repoDestination.listPullRequests({ state: 'closed', head: `${ORGANISATION}:${head}`, base }); if (closedUpstreamPRs && closedUpstreamPRs.length > 0) { const latestClosedUpstream = closedUpstreamPRs[0]; const shaTo = latestClosedUpstream.body.split(':')[1].trim(); return shaTo; } return null; } async getCommentAmount(issueOrPrNumber) { const { data: listComments } = await this.issue.listIssueComments(issueOrPrNumber); return listComments.length; } } async function main() { program .version('0.1.0') .option('-t, --token [type]', 'token') .option('-h, --head [type]', 'head') .option('-r, --repo [type]', 'repo') .option('-c, --commit [type]', 'commit') .option('-title, --title [type]', 'title') .parse(process.argv); const { token, title, head, repo, commit } = program, prCreator = new PrCreator(ORGANISATION, repo, token, commit); if (!token || !head || !title) { throw new Error('Each of the parameters have to be provided. --token, --title, --head'); } const baseBranchName = 'develop'; const prNumber = await prCreator.createOrUpdate(title, head, baseBranchName, commit); await prCreator.createComment(prNumber, head, baseBranchName, commit); return prNumber; } main() .then(prNumber => { console.log("======= PR Created ========="); console.log(prNumber) process.exit(0); }) .catch(error => { console.error("======= Impossible create PR ========="); console.error(error.response.status); console.error(error.response.statusText); process.exit(1); });