From af3cd372aa779073284d38b8001889a7b65a8c96 Mon Sep 17 00:00:00 2001 From: Gilbert Gilb's <gilbsgilbs@users.noreply.github.com> Date: Sun, 22 Sep 2019 09:19:26 +0200 Subject: [PATCH] feat(workers): add option to randomize assignees and reviewers. (#4517) Closes #4516 --- docs/usage/configuration-options.md | 8 ++++++++ lib/config/definitions.ts | 12 ++++++++++++ lib/workers/pr/index.js | 20 ++++++++++++++------ renovate-schema.json | 10 ++++++++++ test/workers/pr/index.spec.js | 17 +++++++++++++++++ 5 files changed, 61 insertions(+), 6 deletions(-) diff --git a/docs/usage/configuration-options.md b/docs/usage/configuration-options.md index 05eb77c664..d3b4b10519 100644 --- a/docs/usage/configuration-options.md +++ b/docs/usage/configuration-options.md @@ -33,6 +33,10 @@ By default, Renovate will not assign reviewers and assignees if the PR is to be Must be valid usernames. +## assigneesSampleSize + +Take a random sample of given size from assignees. + ## automerge By default, Renovate raises PRs but leaves them to someone/something else to merge them. By configuring this setting, you can enable Renovate to automerge branches or PRs itself, therefore reducing the amount of human intervention required. @@ -1028,6 +1032,10 @@ Similar to `ignoreUnstable`, this option controls whether to update to versions Must be valid usernames. If on GitHub and assigning a team to review, use the prefix `team:`, e.g. provide a value like `team:someteam`. +## reviewersSampleSize + +Take a random sample of given size from reviewers. + ## rollbackPrs Set this to false either globally, per-language, or per-package if you want to disable Renovate's behaviour of generating rollback PRs when it can't find the current version on the registry anymore. diff --git a/lib/config/definitions.ts b/lib/config/definitions.ts index e139a3876c..1af8fa2f18 100644 --- a/lib/config/definitions.ts +++ b/lib/config/definitions.ts @@ -1241,6 +1241,12 @@ const options: RenovateOptions[] = [ type: 'array', subType: 'string', }, + { + name: 'assigneesSampleSize', + description: 'Take a random sample of given size from assignees.', + type: 'integer', + default: null, + }, { name: 'assignAutomerge', description: @@ -1255,6 +1261,12 @@ const options: RenovateOptions[] = [ type: 'array', subType: 'string', }, + { + name: 'reviewersSampleSize', + description: 'Take a random sample of given size from reviewers.', + type: 'integer', + default: null, + }, { name: 'fileMatch', description: 'JS RegExp pattern for matching manager files', diff --git a/lib/workers/pr/index.js b/lib/workers/pr/index.js index 052d739efb..dc953c5432 100644 --- a/lib/workers/pr/index.js +++ b/lib/workers/pr/index.js @@ -1,3 +1,5 @@ +const sampleSize = require('lodash/sampleSize'); + const { logger } = require('../../logger'); const changelogHelper = require('./changelog'); const { getPrBody } = require('./pr-body'); @@ -339,15 +341,18 @@ async function ensurePr(prConfig) { async function addAssigneesReviewers(config, pr) { if (config.assignees.length > 0) { try { - const assignees = config.assignees.map(assignee => + let assignees = config.assignees.map(assignee => assignee.length && assignee[0] === '@' ? assignee.slice(1) : assignee ); + if (config.assigneesSampleSize !== null) { + assignees = sampleSize(assignees, config.assigneesSampleSize); + } // istanbul ignore if if (config.dryRun) { logger.info('DRY-RUN: Would add assignees to PR #' + pr.number); } else { await platform.addAssignees(pr.number, assignees); - logger.info({ assignees: config.assignees }, 'Added assignees'); + logger.info({ assignees }, 'Added assignees'); } } catch (err) { logger.info( @@ -358,19 +363,22 @@ async function addAssigneesReviewers(config, pr) { } if (config.reviewers.length > 0) { try { - const reviewers = config.reviewers.map(reviewer => + let reviewers = config.reviewers.map(reviewer => reviewer.length && reviewer[0] === '@' ? reviewer.slice(1) : reviewer ); + if (config.reviewersSampleSize !== null) { + reviewers = sampleSize(reviewers, config.reviewersSampleSize); + } // istanbul ignore if if (config.dryRun) { - logger.info('DRY-RUN: Would add assignees to PR #' + pr.number); + logger.info('DRY-RUN: Would add reviewers to PR #' + pr.number); } else { await platform.addReviewers(pr.number, reviewers); - logger.info({ reviewers: config.reviewers }, 'Added reviewers'); + logger.info({ reviewers }, 'Added reviewers'); } } catch (err) { logger.info( - { assignees: config.assignees, err }, + { reviewers: config.reviewers, err }, 'Failed to add reviewers' ); } diff --git a/renovate-schema.json b/renovate-schema.json index 04b676f41c..c06ef58622 100644 --- a/renovate-schema.json +++ b/renovate-schema.json @@ -784,6 +784,11 @@ "type": "string" } }, + "assigneesSampleSize": { + "description": "Take a random sample of given size from assignees.", + "type": "integer", + "default": null + }, "assignAutomerge": { "description": "Assign reviewers and assignees even if the PR is to be automerged", "type": "boolean", @@ -796,6 +801,11 @@ "type": "string" } }, + "reviewersSampleSize": { + "description": "Take a random sample of given size from reviewers.", + "type": "integer", + "default": null + }, "fileMatch": { "description": "JS RegExp pattern for matching manager files", "type": "array", diff --git a/test/workers/pr/index.spec.js b/test/workers/pr/index.spec.js index f766722870..3bbf13823b 100644 --- a/test/workers/pr/index.spec.js +++ b/test/workers/pr/index.spec.js @@ -300,6 +300,23 @@ describe('workers/pr', () => { expect(platform.addAssignees).toHaveBeenCalledTimes(1); expect(platform.addReviewers).toHaveBeenCalledTimes(1); }); + it('should add random sample of assignees and reviewers to new PR', async () => { + config.assignees = ['foo', 'bar', 'baz']; + config.assigneesSampleSize = 2; + config.reviewers = ['baz', 'boo', 'bor']; + config.reviewersSampleSize = 2; + await prWorker.ensurePr(config); + + expect(platform.addAssignees).toHaveBeenCalledTimes(1); + const assignees = platform.addAssignees.mock.calls[0][1]; + expect(assignees.length).toEqual(2); + expect(config.assignees).toEqual(expect.arrayContaining(assignees)); + + expect(platform.addReviewers).toHaveBeenCalledTimes(1); + const reviewers = platform.addReviewers.mock.calls[0][1]; + expect(reviewers.length).toEqual(2); + expect(config.reviewers).toEqual(expect.arrayContaining(reviewers)); + }); it('should return unmodified existing PR', async () => { platform.getBranchPr.mockReturnValueOnce(existingPr); config.semanticCommitScope = null; -- GitLab