diff --git a/src/backport/__tests__/runner.spec.ts b/src/backport/__tests__/runner.spec.ts index b7a3976..7cd6881 100644 --- a/src/backport/__tests__/runner.spec.ts +++ b/src/backport/__tests__/runner.spec.ts @@ -4,7 +4,7 @@ import * as os from 'os'; import * as path from 'path'; import * as simpleGit from 'simple-git/promise'; -import { initRepo, setUpRemotes } from '../runner'; +import { initRepo, setupRemotes } from '../../operations/task-runner'; let dirObject: { dir?: string } | null = null; @@ -57,7 +57,7 @@ describe('runner', () => { }); it('should set new remotes correctly', async () => { - await setUpRemotes({ + await setupRemotes({ dir, remotes: [{ name: 'origin', diff --git a/src/backport/utils.ts b/src/backport/utils.ts index d4541dc..426e326 100644 --- a/src/backport/utils.ts +++ b/src/backport/utils.ts @@ -7,17 +7,15 @@ import * as simpleGit from 'simple-git/promise'; import { Label, PullRequest, TropConfig } from './Probot'; import queue from './Queue'; -import { runCommand } from './runner'; +import { runCommand } from '../operations/task-runner'; import { CHECK_PREFIX } from '../constants'; import { PRChange, TropAction, PRStatus } from '../enums'; +import * as labelUtils from '../utils/label-utils'; + const makeQueue: IQueue = require('queue'); const { parse: parseDiff } = require('what-the-diff'); -export const labelToTargetBranch = (label: Label, prefix: string) => { - return label.name.replace(prefix, ''); -}; - const getGitHub = () => { const g = new GitHub(); g.authenticate({ @@ -36,8 +34,8 @@ export const labelMergedPR = async (context: Context, pr: PullRequest, targetBra const labelToAdd = `${labelPrefixes.merged}${targetBranch}`; const labelToRemove = labelPrefixes.inFlight + targetBranch; - await removeLabel(context, prNumber, labelToRemove); - await addLabel(context, prNumber, [labelToAdd]); + await labelUtils.removeLabel(context, prNumber, labelToRemove); + await labelUtils.addLabel(context, prNumber, [labelToAdd]); } }; @@ -154,6 +152,7 @@ export const backportImpl = async (robot: Application, targetRepoRemote = `https://${process.env.GITHUB_FORK_USER_CLONE_LOGIN}:${process.env.GITHUB_FORK_USER_TOKEN}@github.com/${slug}.git`; } + await runCommand({ what: TropAction.SET_UP_REMOTES, payload: { @@ -269,15 +268,15 @@ export const backportImpl = async (robot: Application, if (labelToRemove) { log(`Removing label '${labelToRemove}'`); - await removeLabel(context, pr.number, labelToRemove); + await labelUtils.removeLabel(context, pr.number, labelToRemove); } if (labelToAdd) { log(`Adding label '${labelToAdd}'`); - await addLabel(context, pr.number, [labelToAdd]); + await labelUtils.addLabel(context, pr.number, [labelToAdd]); } - await addLabel(context, newPr.number!, ['backport', `${targetBranch}`]); + await labelUtils.addLabel(context, newPr.number!, ['backport', `${targetBranch}`]); log('Backport complete'); } @@ -343,10 +342,10 @@ export const backportImpl = async (robot: Application, const labelPrefixes = await getLabelPrefixes(context); const labelToRemove = labelPrefixes.target + targetBranch; - await removeLabel(context, pr.number, labelToRemove); + await labelUtils.removeLabel(context, pr.number, labelToRemove); const labelToAdd = labelPrefixes.needsManual + targetBranch; - await addLabel(context, pr.number, [labelToAdd]); + await labelUtils.addLabel(context, pr.number, [labelToAdd]); } if (purpose === BackportPurpose.Check) { @@ -378,16 +377,6 @@ export const backportImpl = async (robot: Application, ); }; -const labelExistsOnPR = async (context: Context, labelName: string) => { - const labels = await context.github.issues.listLabelsOnIssue(context.repo({ - number: context.payload.pull_request.number, - per_page: 100, - page: 1, - })); - - return labels.data.some(label => label.name === labelName); -}; - export const getLabelPrefixes = async (context: Pick) => { const config = await context.config('config.yml') || {}; const target = config.targetLabelPrefix || PRStatus.TARGET; @@ -411,7 +400,7 @@ export const updateManualBackport = async ( if (type === PRChange.OPEN) { labelToRemove = labelPrefixes.needsManual + pr.base.ref; - if (!await labelExistsOnPR(context, labelToRemove)) { + if (!await labelUtils.labelExistsOnPR(context, labelToRemove)) { labelToRemove = labelPrefixes.target + pr.base.ref; } labelToAdd = labelPrefixes.inFlight + pr.base.ref; @@ -441,8 +430,8 @@ please check out #${pr.number}`; labelToAdd = labelPrefixes.merged + pr.base.ref; } - await removeLabel(context, oldPRNumber, labelToRemove); - await addLabel(context, oldPRNumber, [labelToAdd]); + await labelUtils.removeLabel(context, oldPRNumber, labelToRemove); + await labelUtils.addLabel(context, oldPRNumber, [labelToAdd]); }; export const backportToLabel = async ( @@ -456,7 +445,7 @@ export const backportToLabel = async ( return; } - const targetBranch = labelToTargetBranch(label, labelPrefixes.target); + const targetBranch = labelUtils.labelToTargetBranch(label, labelPrefixes.target); if (!targetBranch) { robot.log('Nothing to do'); return; @@ -482,22 +471,3 @@ export const backportToBranch = async ( robot, context, targetBranch, BackportPurpose.ExecuteBackport, labelToRemove, labelToAdd, ); }; - -// HELPERS - -const addLabel = async (context: Context, prNumber: number, labelsToAdd: string[]) => { - return context.github.issues.addLabels(context.repo({ - number: prNumber, - labels: labelsToAdd, - })); -}; - -const removeLabel = async (context: Context, prNumber: number, labelToRemove: string) => { - // If the issue does not have the label, don't try remove it - if (!await labelExistsOnPR(context, labelToRemove)) return; - - return context.github.issues.removeLabel(context.repo({ - number: prNumber, - name: labelToRemove, - })); -}; diff --git a/src/index.ts b/src/index.ts index 14a754d..f9b3f85 100644 --- a/src/index.ts +++ b/src/index.ts @@ -4,13 +4,13 @@ import { backportToBranch, backportToLabel, getLabelPrefixes, - labelToTargetBranch, backportImpl, BackportPurpose, labelMergedPR, updateManualBackport, } from './backport/utils'; +import { labelToTargetBranch } from './utils/label-utils'; import { PullRequest, TropConfig } from './backport/Probot'; import { CHECK_PREFIX } from './constants'; import { PRChange } from './enums'; diff --git a/src/backport/runner.ts b/src/operations/task-runner.ts similarity index 66% rename from src/backport/runner.ts rename to src/operations/task-runner.ts index 0ef394f..a2974c3 100644 --- a/src/backport/runner.ts +++ b/src/operations/task-runner.ts @@ -24,6 +24,12 @@ export type RunnerOptions = { const baseDir = path.resolve(os.tmpdir(), 'trop-working'); +/* +* Initializes the cloned repo trop will use to run backports +* +* @param {InitRepoOptions} repo and payload for repo initialization +* @returns {Object} - an object containing the repo initialization directory +*/ export const initRepo = async (options: InitRepoOptions) => { const slug = `${options.owner}/${options.repo}`; await fs.mkdirp(path.resolve(baseDir, slug)); @@ -33,10 +39,15 @@ export const initRepo = async (options: InitRepoOptions) => { await fs.remove(dir); await fs.mkdirp(dir); const git = simpleGit(dir); - // This adds support for the target_repo being private as long as the fork user has read access - if (process.env.GITHUB_FORK_USER_CLONE_LOGIN) { + + const forkLogin = process.env.GITHUB_FORK_USER_CLONE_LOGIN; + const forkToken = process.env.GITHUB_FORK_USER_TOKEN; + + // Adds support for the target_repo being private as + // long as the fork user has read access + if (forkLogin) { await git.clone( - `https://${process.env.GITHUB_FORK_USER_CLONE_LOGIN}:${process.env.GITHUB_FORK_USER_TOKEN}@github.com/${slug}.git`, + `https://${forkLogin}:${forkToken}@github.com/${slug}.git`, '.', ); } else { @@ -62,7 +73,15 @@ export const initRepo = async (options: InitRepoOptions) => { return { dir }; }; -export const setUpRemotes = async (options: RemotesOptions) => { +/* +* Sets up remotes that trop will run backports with. +* +* @param {RemotesOptions} - an object containing: +* 1) dir - the repo directory +* 2) remotes - the list of remotes to set on the initialized git repository +* @returns {Object} - an object containing the repo initialization directory +*/ +export const setupRemotes = async (options: RemotesOptions) => { const git = simpleGit(options.dir); // Add remotes @@ -77,6 +96,17 @@ export const setUpRemotes = async (options: RemotesOptions) => { return { dir: options.dir }; }; +/* +* Runs the git commands to apply backports in a series of cherry-picked commits. +* +* @param {BackportOptions} - an object containing: +* 1) dir - the repo directory, +* 2) targetBranch - the target branch +* 3) patches - a list of patches to apply to the target branch +* 3) tempBranch - the temporary branch to PR against the target branch +* 4) tempRemote - the temporary remote for use in backporting +* @returns {Object} - an object containing the repo initialization directory +*/ export const backportCommitsToBranch = async (options: BackportOptions) => { const git = simpleGit(options.dir); // Create branch @@ -99,12 +129,13 @@ export const backportCommitsToBranch = async (options: BackportOptions) => { return { dir: options.dir }; }; +// Helper method for running one of three primary git action sets export const runCommand = async (options: RunnerOptions): Promise<{ dir: string }> => { switch (options.what) { case TropAction.INIT_REPO: return await initRepo(options.payload); case TropAction.SET_UP_REMOTES: - return await setUpRemotes(options.payload); + return await setupRemotes(options.payload); case TropAction.BACKPORT: return await backportCommitsToBranch(options.payload); default: diff --git a/src/utils/label-utils.ts b/src/utils/label-utils.ts new file mode 100644 index 0000000..e010037 --- /dev/null +++ b/src/utils/label-utils.ts @@ -0,0 +1,33 @@ +import { Context } from 'probot'; +import { Label } from '../backport/Probot'; + +export const addLabel = async (context: Context, prNumber: number, labelsToAdd: string[]) => { + return context.github.issues.addLabels(context.repo({ + number: prNumber, + labels: labelsToAdd, + })); +}; + +export const removeLabel = async (context: Context, prNumber: number, labelToRemove: string) => { + // If the issue does not have the label, don't try remove it + if (!await labelExistsOnPR(context, labelToRemove)) return; + + return context.github.issues.removeLabel(context.repo({ + number: prNumber, + name: labelToRemove, + })); +}; + +export const labelToTargetBranch = (label: Label, prefix: string) => { + return label.name.replace(prefix, ''); +}; + +export const labelExistsOnPR = async (context: Context, labelName: string) => { + const labels = await context.github.issues.listLabelsOnIssue(context.repo({ + number: context.payload.pull_request.number, + per_page: 100, + page: 1, + })); + + return labels.data.some(label => label.name === labelName); +};