From 04e20dce8eaffbfdaeb919b3595c19e67af5ef17 Mon Sep 17 00:00:00 2001 From: Rhys Arkins <rhys@keylocation.sg> Date: Fri, 7 Jul 2017 06:25:38 +0200 Subject: [PATCH] feat: Prune all orphan branches (#461) Schedule logic has been refactored to enable the pruning of all orphan branches. Now, schedules aren't checked at package-time, instead package upgrades are queued regardless of schedule. At branch time, it is checked whether the *branch* is scheduled and then the branch is not created/updated if it's off-schedule. This enables the repository worker to know all possible branches and hence be able to determine which remaining branches in the repository are "orphans" to be deleted. Closes #428, Closes #426 --- lib/workers/branch/index.js | 28 ++++++------ lib/workers/{package => branch}/schedule.js | 0 lib/workers/package-file/index.js | 26 ++++++----- lib/workers/package/index.js | 11 ----- lib/workers/repository/cleanup.js | 45 +++---------------- lib/workers/repository/index.js | 8 ++-- lib/workers/repository/onboarding.js | 22 ++++----- lib/workers/repository/upgrades.js | 15 ++++++- test/workers/branch/index.spec.js | 8 ++++ .../{package => branch}/schedule.spec.js | 4 +- test/workers/package-file/index.spec.js | 17 +++---- test/workers/package/index.spec.js | 12 +---- .../__snapshots__/onboarding.spec.js.snap | 38 ++++++++++++++++ .../__snapshots__/upgrades.spec.js.snap | 29 ++++++++++++ test/workers/repository/cleanup.spec.js | 45 +------------------ test/workers/repository/onboarding.spec.js | 18 ++++++++ test/workers/repository/upgrades.spec.js | 22 +++++++++ 17 files changed, 188 insertions(+), 160 deletions(-) rename lib/workers/{package => branch}/schedule.js (100%) rename test/workers/{package => branch}/schedule.spec.js (97%) diff --git a/lib/workers/branch/index.js b/lib/workers/branch/index.js index c6fcf2d42f..da900af45b 100644 --- a/lib/workers/branch/index.js +++ b/lib/workers/branch/index.js @@ -2,6 +2,7 @@ const handlebars = require('handlebars'); const packageJsonHelper = require('./package-json'); const npm = require('./npm'); const yarn = require('./yarn'); +const schedule = require('./schedule'); const prWorker = require('../pr'); let logger = require('../../logger'); @@ -191,23 +192,22 @@ async function ensureBranch(config) { } async function processBranchUpgrades(branchUpgrades, errors, warnings) { + logger = branchUpgrades.logger || logger; logger.trace({ config: branchUpgrades }, 'processBranchUpgrades'); - let createdBranch; const config = Object.assign({}, branchUpgrades); - // Delete the semanticPrefix for this branch if feature is not enabled - if (config.semanticCommits) { - logger.debug('Branch has semantic commits enabled'); - } else { - logger.debug('Branch has semantic commits disabled'); - delete config.semanticPrefix; + // Check schedule + if ( + config.schedule && + config.schedule.length && + schedule.isScheduledNow(config) === false + ) { + logger.info('Skipping branch as it is not scheduled'); + return; } - // Use templates to generate strings - const branchName = handlebars.compile(config.branchName)(config); - const prTitle = handlebars.compile(config.prTitle)(config); logger = logger.child({ repository: config.repository, - branch: branchName, + branch: config.branchName, }); config.logger = logger; @@ -218,14 +218,13 @@ async function processBranchUpgrades(branchUpgrades, errors, warnings) { if ( // Groups and lock file maintenance should set this to true config.recreateClosed === false && - (await config.api.checkForClosedPr(branchName, prTitle)) + (await config.api.checkForClosedPr(config.branchName, config.prTitle)) ) { logger.info(`Skipping branch as matching closed PR already existed`); - return null; + return; } const branchCreated = await module.exports.ensureBranch(config); if (branchCreated) { - createdBranch = branchName; const pr = await prWorker.ensurePr( config.upgrades, logger, @@ -241,5 +240,4 @@ async function processBranchUpgrades(branchUpgrades, errors, warnings) { logger.debug(JSON.stringify(err)); // Don't throw here - we don't want to stop the other renovations } - return createdBranch; } diff --git a/lib/workers/package/schedule.js b/lib/workers/branch/schedule.js similarity index 100% rename from lib/workers/package/schedule.js rename to lib/workers/branch/schedule.js diff --git a/lib/workers/package-file/index.js b/lib/workers/package-file/index.js index adddc6b59d..baf473fe25 100644 --- a/lib/workers/package-file/index.js +++ b/lib/workers/package-file/index.js @@ -1,6 +1,6 @@ +const path = require('path'); const configParser = require('../../config'); const depTypeWorker = require('../dep-type'); -const schedule = require('../package/schedule'); let logger = require('../../logger'); @@ -55,16 +55,22 @@ async function findUpgrades(packageFileConfig) { ); } - // Maintain lock files - const lockFileMaintenanceConf = Object.assign( - {}, - config, - config.lockFileMaintenance + // Detect if a yarn.lock file is in use + const yarnLockFileName = path.join( + path.dirname(config.packageFile), + 'yarn.lock' ); - if (lockFileMaintenanceConf.enabled) { - logger.debug('lockFileMaintenance enabled'); - lockFileMaintenanceConf.type = 'lockFileMaintenance'; - if (schedule.isScheduledNow(lockFileMaintenanceConf)) { + if (await config.api.getFileContent(yarnLockFileName)) { + config.hasYarnLock = true; + // Maintain lock files + const lockFileMaintenanceConf = Object.assign( + {}, + config, + config.lockFileMaintenance + ); + if (lockFileMaintenanceConf.enabled) { + logger.debug('lockFileMaintenance enabled'); + lockFileMaintenanceConf.type = 'lockFileMaintenance'; logger.debug(`lock config=${JSON.stringify(lockFileMaintenanceConf)}`); upgrades.push(lockFileMaintenanceConf); } diff --git a/lib/workers/package/index.js b/lib/workers/package/index.js index 4411d3ed4a..3ab2679844 100644 --- a/lib/workers/package/index.js +++ b/lib/workers/package/index.js @@ -1,6 +1,5 @@ const npmApi = require('../../api/npm'); const versions = require('./versions'); -const schedule = require('./schedule'); const configParser = require('../../config'); let logger = require('../../logger'); @@ -16,16 +15,6 @@ async function findUpgrades(config) { logger.debug('package is disabled'); return []; } - // Check schedule - if ( - config.repoIsOnboarded && - config.schedule && - config.schedule.length && - schedule.isScheduledNow(config) === false - ) { - logger.debug('Skipping package as it is not scheduled'); - return []; - } let results = []; const npmDep = await npmApi.getDependency(config.depName, logger); if (npmDep) { diff --git a/lib/workers/repository/cleanup.js b/lib/workers/repository/cleanup.js index bd78486b4e..5771c6b1f5 100644 --- a/lib/workers/repository/cleanup.js +++ b/lib/workers/repository/cleanup.js @@ -2,64 +2,29 @@ module.exports = { pruneStaleBranches, }; -async function pruneStaleBranches(config, branchUpgradeNames) { +async function pruneStaleBranches(config, branchList) { const logger = config.logger; logger.debug('Removing any stale branches'); logger.trace( { config }, - `pruneStaleBranches:\n${JSON.stringify(branchUpgradeNames)}` + `pruneStaleBranches:\n${JSON.stringify(branchList)}` ); if (config.platform !== 'github') { logger.debug('Platform is not GitHub - returning'); return; } - if (branchUpgradeNames.length === 0) { - logger.debug('No branchUpgradeNames - returning'); - return; - } const renovateBranches = await config.api.getAllRenovateBranches(); logger.debug(`renovateBranches=${renovateBranches}`); const remainingBranches = renovateBranches.filter( - branch => branchUpgradeNames.indexOf(branch) === -1 + branch => branchList.indexOf(branch) === -1 ); logger.debug(`remainingBranches=${remainingBranches}`); if (remainingBranches.length === 0) { logger.debug('No branches to clean up'); return; } - const allPrs = await config.api.getAllPrs(); for (const branchName of remainingBranches) { - logger.debug({ branch: branchName }, `Checking orphan branch for deletion`); - // Default to deleting the branch if we don't find a PR - let deleteBranch = true; - let foundPr = false; - for (const pr of allPrs) { - if (pr.state === 'open' && pr.branchName === branchName) { - // We have a matching PR - foundPr = true; - logger.debug({ branch: branchName }, `Found matching PR#${pr.number}`); - const prDetails = config.api.getPr(pr.number); - if (prDetails.mergeable) { - // Don't delete the branch if we found a mergeable PR - logger.debug( - { branch: branchName }, - 'PR is mergeable, so do not delete' - ); - deleteBranch = false; - } else { - logger.debug( - { branch: branchName }, - 'PR is not mergeable, we will delete' - ); - } - } - } - if (deleteBranch) { - if (!foundPr) { - logger.debug({ branch: branchName }, 'Orphan branch has no PR'); - } - logger.info({ branch: branchName }, `Deleting orphan branch`); - await config.api.deleteBranch(branchName); - } + logger.debug({ branch: branchName }, `Deleting orphan branch`); + await config.api.deleteBranch(branchName); } } diff --git a/lib/workers/repository/index.js b/lib/workers/repository/index.js index 10d73db1df..983a634d07 100644 --- a/lib/workers/repository/index.js +++ b/lib/workers/repository/index.js @@ -78,17 +78,15 @@ async function renovateRepository(packageFileConfig) { config.logger.debug(`Updating ${branchUpgrades.length} branch(es)`); config.logger.trace({ config: branchUpgrades }, 'branchUpgrades'); if (config.repoIsOnboarded) { - const branchList = []; for (const branchUpgrade of branchUpgrades) { - const createdBranchName = await branchWorker.processBranchUpgrades( + await branchWorker.processBranchUpgrades( branchUpgrade, config.errors, config.warnings ); - if (createdBranchName) { - branchList.push(createdBranchName); - } } + const branchList = branchUpgrades.map(upgrade => upgrade.branchName); + config.logger.debug(`branchList=${branchList}`); await cleanup.pruneStaleBranches(config, branchList); } else { await onboarding.ensurePr(config, branchUpgrades); diff --git a/lib/workers/repository/onboarding.js b/lib/workers/repository/onboarding.js index 28a483de06..96017a1347 100644 --- a/lib/workers/repository/onboarding.js +++ b/lib/workers/repository/onboarding.js @@ -1,4 +1,3 @@ -const handlebars = require('handlebars'); const stringify = require('json-stringify-pretty-compact'); const configParser = require('../../config'); @@ -95,21 +94,24 @@ With your current configuration, renovate will initially create the following Pu | ------ | `; branchUpgrades.forEach(branch => { - const branchName = handlebars.compile(branch.branchName)(branch); - const prTitle = handlebars.compile(branch.prTitle)(branch); - prDesc += `| **${prTitle}**<ul>`; + prDesc += `| **${branch.prTitle}**<ul>`; if (branch.schedule && branch.schedule.length) { prDesc += `<li>Schedule: ${JSON.stringify(branch.schedule)}</li>`; } - prDesc += `<li>Branch name: \`${branchName}\`</li>`; + prDesc += `<li>Branch name: \`${branch.branchName}\`</li>`; branch.upgrades.forEach(upgrade => { - if (upgrade.isPin) { - prDesc += '<li>Pins '; + if (upgrade.type === 'lockFileMaintenance') { + prDesc += + '<li>Regenerates `yarn.lock` file to use latest dependency versions</li>'; } else { - prDesc += '<li>Upgrades '; + if (upgrade.isPin) { + prDesc += '<li>Pins '; + } else { + prDesc += '<li>Upgrades '; + } + prDesc += `[${upgrade.depName}](${upgrade.repositoryUrl}) in \`${upgrade.depType}\` from \`${upgrade.currentVersion}\` to \`${upgrade.newVersion}\``; + prDesc += '</li>'; } - prDesc += `[${upgrade.depName}](${upgrade.repositoryUrl}) in \`${upgrade.depType}\` from \`${upgrade.currentVersion}\` to \`${upgrade.newVersion}\``; - prDesc += '</li>'; }); prDesc += '</ul> |\n'; }); diff --git a/lib/workers/repository/upgrades.js b/lib/workers/repository/upgrades.js index abb416efec..c258102f69 100644 --- a/lib/workers/repository/upgrades.js +++ b/lib/workers/repository/upgrades.js @@ -54,6 +54,18 @@ function generateConfig(branchUpgrades) { // Delete group config regardless of whether it was applied delete upgrade.group; delete upgrade.lazyGrouping; + // Delete the semanticPrefix for this upgrade if not enabled + if (upgrade.semanticCommits) { + logger.debug('Upgrade has semantic commits enabled'); + } else { + logger.debug('Upgrade has semantic commits disabled'); + delete upgrade.semanticPrefix; + } + // Use templates to generate strings + logger.debug('Compiling branchName and prTitle'); + upgrade.branchName = handlebars.compile(upgrade.branchName)(upgrade); + upgrade.prTitle = handlebars.compile(upgrade.prTitle)(upgrade); + logger.debug(`${upgrade.branchName}, ${upgrade.prTitle}`); config.upgrades.push(upgrade); } // Now assign first upgrade's config as branch config @@ -61,7 +73,7 @@ function generateConfig(branchUpgrades) { } function groupByBranch(upgrades) { - logger.trace({ config: upgrades }, 'groupUpgrades'); + logger.trace({ config: upgrades }, 'groupByBranch'); logger.info(`Processing ${upgrades.length} dependency upgrade(s)`); const result = { errors: [], @@ -115,6 +127,7 @@ async function branchifyUpgrades(upgrades, parentLogger) { logger = logger.child({ branch: branchName }); const branchUpgrades = res.branchUpgrades[branchName]; const branchConfig = module.exports.generateConfig(branchUpgrades); + branchConfig.branchName = branchName; branchConfig.logger = logger; branchConfigs.push(branchConfig); } diff --git a/test/workers/branch/index.spec.js b/test/workers/branch/index.spec.js index c68af96f72..6fb49994fe 100644 --- a/test/workers/branch/index.spec.js +++ b/test/workers/branch/index.spec.js @@ -1,5 +1,6 @@ const branchWorker = require('../../../lib/workers/branch'); const prWorker = require('../../../lib/workers/pr'); +const schedule = require('../../../lib/workers/branch/schedule'); const npm = require('../../../lib/workers/branch/npm'); const yarn = require('../../../lib/workers/branch/yarn'); const defaultConfig = require('../../../lib/config/defaults').getConfig(); @@ -15,6 +16,7 @@ describe('workers/branch', () => { let config; const branchName = 'foo'; beforeEach(() => { + schedule.isScheduledNow = jest.fn(); config = { api: { branchExists: jest.fn(() => true), @@ -333,6 +335,12 @@ describe('workers/branch', () => { prWorker.ensurePr = jest.fn(() => true); config.upgrades = [{ depName: 'a' }]; }); + it('skips branch if not scheduled', async () => { + config.schedule = ['some-schedule']; + schedule.isScheduledNow.mockReturnValueOnce(false); + await branchWorker.processBranchUpgrades(config); + expect(branchWorker.ensureBranch.mock.calls.length).toBe(0); + }); it('returns immediately if closed PR found', async () => { config.api.checkForClosedPr.mockReturnValue(true); await branchWorker.processBranchUpgrades(config); diff --git a/test/workers/package/schedule.spec.js b/test/workers/branch/schedule.spec.js similarity index 97% rename from test/workers/package/schedule.spec.js rename to test/workers/branch/schedule.spec.js index 1e09b482d2..742a72cb22 100644 --- a/test/workers/package/schedule.spec.js +++ b/test/workers/branch/schedule.spec.js @@ -1,8 +1,8 @@ const mockDate = require('mockdate'); -const schedule = require('../../../lib/workers/package/schedule'); +const schedule = require('../../../lib/workers/branch/schedule'); const logger = require('../../_fixtures/logger'); -describe('workers/package/schedule', () => { +describe('workers/branch/schedule', () => { describe('hasValidSchedule(schedule)', () => { beforeEach(() => { jest.resetAllMocks(); diff --git a/test/workers/package-file/index.spec.js b/test/workers/package-file/index.spec.js index 5e85b27d11..47513f2fdc 100644 --- a/test/workers/package-file/index.spec.js +++ b/test/workers/package-file/index.spec.js @@ -1,20 +1,21 @@ const packageFileWorker = require('../../../lib/workers/package-file'); const depTypeWorker = require('../../../lib/workers/dep-type'); -const schedule = require('../../../lib/workers/package/schedule'); const defaultConfig = require('../../../lib/config/defaults').getConfig(); const logger = require('../../_fixtures/logger'); jest.mock('../../../lib/workers/dep-type'); -jest.mock('../../../lib/workers/package/schedule'); +jest.mock('../../../lib/workers/branch/schedule'); describe('packageFileWorker', () => { describe('findUpgrades(config)', () => { let config; beforeEach(() => { config = Object.assign({}, defaultConfig, { + packageFile: 'package.json', repoIsOnboarded: true, api: { + getFileContent: jest.fn(), getFileJson: jest.fn(), }, depTypes: ['dependencies', 'devDependencies'], @@ -62,17 +63,11 @@ describe('packageFileWorker', () => { }); it('maintains yarn.lock', async () => { config.api.getFileJson.mockReturnValueOnce({}); - depTypeWorker.findUpgrades.mockReturnValue([]); - schedule.isScheduledNow.mockReturnValueOnce(true); + config.api.getFileContent.mockReturnValueOnce('some-content'); + depTypeWorker.findUpgrades.mockReturnValueOnce([]); + depTypeWorker.findUpgrades.mockReturnValueOnce([]); const res = await packageFileWorker.findUpgrades(config); expect(res).toHaveLength(1); }); - it('skips yarn.lock', async () => { - config.api.getFileJson.mockReturnValueOnce({}); - depTypeWorker.findUpgrades.mockReturnValue([]); - schedule.isScheduledNow.mockReturnValueOnce(false); - const res = await packageFileWorker.findUpgrades(config); - expect(res).toHaveLength(0); - }); }); }); diff --git a/test/workers/package/index.spec.js b/test/workers/package/index.spec.js index 164e325051..01596fdf12 100644 --- a/test/workers/package/index.spec.js +++ b/test/workers/package/index.spec.js @@ -1,11 +1,10 @@ const npmApi = require('../../../lib/api/npm'); -const schedule = require('../../../lib/workers/package/schedule'); const versions = require('../../../lib/workers/package/versions'); const pkgWorker = require('../../../lib/workers/package/index'); const defaultConfig = require('../../../lib/config/defaults').getConfig(); const configParser = require('../../../lib/config'); -jest.mock('../../../lib/workers/package/schedule'); +jest.mock('../../../lib/workers/branch/schedule'); jest.mock('../../../lib/workers/package/versions'); jest.mock('../../../lib/api/npm'); @@ -21,18 +20,9 @@ describe('lib/workers/package/index', () => { const res = await pkgWorker.findUpgrades(config); expect(res).toMatchObject([]); }); - it('returns empty if package is not scheduled', async () => { - config.repoIsOnboarded = true; - config.schedule = 'some schedule'; - schedule.isScheduledNow.mockReturnValueOnce(false); - const res = await pkgWorker.findUpgrades(config); - expect(res).toMatchObject([]); - expect(npmApi.getDependency.mock.calls.length).toBe(0); - }); it('returns error if no npm dep found', async () => { config.repoIsOnboarded = true; config.schedule = 'some schedule'; - schedule.isScheduledNow.mockReturnValueOnce(true); const res = await pkgWorker.findUpgrades(config); expect(res).toHaveLength(1); expect(res[0].type).toEqual('error'); diff --git a/test/workers/repository/__snapshots__/onboarding.spec.js.snap b/test/workers/repository/__snapshots__/onboarding.spec.js.snap index 3e4e295b1e..bbe4ded354 100644 --- a/test/workers/repository/__snapshots__/onboarding.spec.js.snap +++ b/test/workers/repository/__snapshots__/onboarding.spec.js.snap @@ -149,6 +149,44 @@ With your current configuration, renovate will initially create the following Pu | **Pin a**<ul><li>Branch name: \`branch-a\`</li><li>Pins [a](https://a) in \`undefined\` from \`^1.0.0\` to \`1.1.0\`</li><li>Upgrades [b](https://b) in \`undefined\` from \`1.0.0\` to \`2.0.0\`</li></ul> | +Would you like to change this? Simply edit the \`renovate.json\` in this branch and Renovate will update this Pull Request description the next time it runs. + +The [Configuration](https://github.com/singapore/renovate/blob/master/docs/configuration.md) and [Configuration FAQ](https://github.com/singapore/renovate/blob/master/docs/faq.md) documents should be helpful if you wish to modify this behaviour. + +--- + +#### Important! + +You do not need to *merge* this Pull Request - renovate will begin even if it's closed *unmerged*. +In fact, you only need to add a \`renovate.json\` file to your repository if you wish to override any default settings. The file is included as part of this PR only in case you wish to change default settings before you start. + +Alternatively, you can add the same configuration settings into a \\"renovate\\" section of \`package.json\`, which might be more convenient if you have only one. + +If the default settings are all suitable for you, simply close this Pull Request unmerged and your first renovation will begin the next time the program is run. +", + ], +] +`; + +exports[`lib/workers/repository/onboarding ensurePr(config, branchUpgrades) maintains yarn.lock 1`] = ` +Array [ + Array [ + "renovate/configure", + "Configure Renovate", + "Welcome to [Renovate](https://keylocation.sg/our-tech/renovate)! + +This is an onboarding PR to help you understand and configure Renovate before any changes are made to any \`package.json\` files. Once you close this Pull Request, we will begin keeping your dependencies up-to-date via automated Pull Requests. + +--- + + +With your current configuration, renovate will initially create the following Pull Requests: + +| Pull Requests (1) | +| ------ | +| **Lock File Maintenance**<ul><li>Schedule: \\"before 5am on monday\\"</li><li>Branch name: \`renovate/lock-files\`</li><li>Regenerates \`yarn.lock\` file to use latest dependency versions</li></ul> | + + Would you like to change this? Simply edit the \`renovate.json\` in this branch and Renovate will update this Pull Request description the next time it runs. The [Configuration](https://github.com/singapore/renovate/blob/master/docs/configuration.md) and [Configuration FAQ](https://github.com/singapore/renovate/blob/master/docs/faq.md) documents should be helpful if you wish to modify this behaviour. diff --git a/test/workers/repository/__snapshots__/upgrades.spec.js.snap b/test/workers/repository/__snapshots__/upgrades.spec.js.snap index 8701980587..b76c0d02a9 100644 --- a/test/workers/repository/__snapshots__/upgrades.spec.js.snap +++ b/test/workers/repository/__snapshots__/upgrades.spec.js.snap @@ -2,12 +2,18 @@ exports[`workers/repository/upgrades generateConfig(branchUpgrades) does not group single upgrade 1`] = ` Object { + "branchName": "some-branch", "depName": "some-dep", "foo": 1, + "prTitle": "some-title", + "semanticCommits": true, "upgrades": Array [ Object { + "branchName": "some-branch", "depName": "some-dep", "foo": 1, + "prTitle": "some-title", + "semanticCommits": true, }, ], } @@ -15,14 +21,18 @@ Object { exports[`workers/repository/upgrades generateConfig(branchUpgrades) groups single upgrade if not lazyGrouping 1`] = ` Object { + "branchName": "some-branch", "depName": "some-dep", "foo": 2, "groupName": "some-group", + "prTitle": "some-title", "upgrades": Array [ Object { + "branchName": "some-branch", "depName": "some-dep", "foo": 2, "groupName": "some-group", + "prTitle": "some-title", }, ], } @@ -34,18 +44,21 @@ Object { "bar-1.1.0": Array [ Object { "branchName": "bar-{{version}}", + "prTitle": "some-title", "version": "1.1.0", }, ], "foo-1.1.0": Array [ Object { "branchName": "foo-{{version}}", + "prTitle": "some-title", "version": "1.1.0", }, ], "foo-2.0.0": Array [ Object { "branchName": "foo-{{version}}", + "prTitle": "some-title", "version": "2.0.0", }, ], @@ -61,16 +74,19 @@ Object { "bar-1.1.0": Array [ Object { "branchName": "bar-{{version}}", + "prTitle": "some-title", "version": "1.1.0", }, ], "foo": Array [ Object { "branchName": "foo", + "prTitle": "some-title", "version": "2.0.0", }, Object { "branchName": "foo", + "prTitle": "some-title", "version": "1.1.0", }, ], @@ -86,6 +102,7 @@ Object { "foo": Array [ Object { "branchName": "foo", + "prTitle": "some-title", "version": "2.0.0", }, ], @@ -97,6 +114,7 @@ Object { }, "groupName": "My Group", "groupSlug": "my-group", + "prTitle": "some-title", "version": "1.1.0", }, Object { @@ -106,6 +124,7 @@ Object { }, "groupName": "My Group", "groupSlug": "my-group", + "prTitle": "some-title", "version": "1.1.0", }, ], @@ -121,12 +140,14 @@ Object { "bar-1.1.0": Array [ Object { "branchName": "bar-{{version}}", + "prTitle": "some-title", "version": "1.1.0", }, ], "foo-1.1.0": Array [ Object { "branchName": "foo-{{version}}", + "prTitle": "some-title", "version": "1.1.0", }, ], @@ -139,6 +160,7 @@ Object { "warnings": Array [ Object { "branchName": "foo-{{version}}", + "prTitle": "some-title", "type": "warning", "version": "2.0.0", }, @@ -160,6 +182,7 @@ Object { "foo-1.1.0": Array [ Object { "branchName": "foo-{{version}}", + "prTitle": "some-title", "version": "1.1.0", }, ], @@ -171,19 +194,25 @@ Object { exports[`workers/repository/upgrades groups multiple upgrades 1`] = ` Object { + "branchName": "some-branch", "depName": "some-dep", "foo": 2, "groupName": "some-group", + "prTitle": "some-title", "upgrades": Array [ Object { + "branchName": "some-branch", "depName": "some-dep", "foo": 2, "groupName": "some-group", + "prTitle": "some-title", }, Object { + "branchName": "some-branch", "depName": "some-other-dep", "foo": 2, "groupName": "some-group", + "prTitle": "some-title", }, ], } diff --git a/test/workers/repository/cleanup.spec.js b/test/workers/repository/cleanup.spec.js index 492e58b7ba..bf711aa34a 100644 --- a/test/workers/repository/cleanup.spec.js +++ b/test/workers/repository/cleanup.spec.js @@ -22,10 +22,6 @@ describe('workers/repository/cleanup', () => { await cleanup.pruneStaleBranches(config, branchNames); expect(config.api.getAllRenovateBranches.mock.calls).toHaveLength(0); }); - it('returns if no branch names', async () => { - await cleanup.pruneStaleBranches(config, branchNames); - expect(config.api.getAllRenovateBranches.mock.calls).toHaveLength(0); - }); it('returns if no remaining branches', async () => { branchNames = ['renovate/a', 'renovate/b']; config.api.getAllRenovateBranches.mockReturnValueOnce(branchNames); @@ -33,52 +29,13 @@ describe('workers/repository/cleanup', () => { expect(config.api.getAllRenovateBranches.mock.calls).toHaveLength(1); expect(config.api.getAllPrs.mock.calls).toHaveLength(0); }); - it('returns if remaining branch has mergeable PR', async () => { - branchNames = ['renovate/a', 'renovate/b']; - config.api.getAllRenovateBranches.mockReturnValueOnce( - branchNames.concat(['renovate/c']) - ); - config.api.getAllPrs.mockReturnValueOnce([ - { number: 4, state: 'open', branchName: 'test-a' }, - { number: 5, state: 'open', branchName: 'renovate/c' }, - ]); - config.api.getPr.mockReturnValueOnce({ mergeable: true }); - await cleanup.pruneStaleBranches(config, branchNames); - expect(config.api.getAllRenovateBranches.mock.calls).toHaveLength(1); - expect(config.api.getAllPrs.mock.calls).toHaveLength(1); - expect(config.api.getPr.mock.calls).toHaveLength(1); - expect(config.api.deleteBranch.mock.calls).toHaveLength(0); - }); - it('deletes if remaining branch has non-mergeable PR', async () => { - branchNames = ['renovate/a', 'renovate/b']; - config.api.getAllRenovateBranches.mockReturnValueOnce( - branchNames.concat(['renovate/c']) - ); - config.api.getAllPrs.mockReturnValueOnce([ - { number: 4, state: 'open', branchName: 'test-a' }, - { number: 5, state: 'open', branchName: 'renovate/c' }, - ]); - config.api.getPr.mockReturnValueOnce({ mergeable: false }); - await cleanup.pruneStaleBranches(config, branchNames); - expect(config.api.getAllRenovateBranches.mock.calls).toHaveLength(1); - expect(config.api.getAllPrs.mock.calls).toHaveLength(1); - expect(config.api.getPr.mock.calls).toHaveLength(1); - expect(config.api.deleteBranch.mock.calls).toHaveLength(1); - }); - it('deletes if no matching PR', async () => { + it('deletes remaining branch', async () => { branchNames = ['renovate/a', 'renovate/b']; config.api.getAllRenovateBranches.mockReturnValueOnce( branchNames.concat(['renovate/c']) ); - config.api.getAllPrs.mockReturnValueOnce([ - { number: 4, state: 'open', branchName: 'test-a' }, - { number: 5, state: 'open', branchName: 'renovate/a' }, - ]); - config.api.getPr.mockReturnValueOnce({ mergeable: false }); await cleanup.pruneStaleBranches(config, branchNames); expect(config.api.getAllRenovateBranches.mock.calls).toHaveLength(1); - expect(config.api.getAllPrs.mock.calls).toHaveLength(1); - expect(config.api.getPr.mock.calls).toHaveLength(0); expect(config.api.deleteBranch.mock.calls).toHaveLength(1); }); }); diff --git a/test/workers/repository/onboarding.spec.js b/test/workers/repository/onboarding.spec.js index a3653dbbd4..3d64d5a9f1 100644 --- a/test/workers/repository/onboarding.spec.js +++ b/test/workers/repository/onboarding.spec.js @@ -98,6 +98,24 @@ If the default settings are all suitable for you, simply close this Pull Request expect(config.api.updatePr.mock.calls.length).toBe(0); expect(config.api.createPr.mock.calls).toMatchSnapshot(); }); + it('maintains yarn.lock', async () => { + branchUpgrades = [ + { + branchName: 'renovate/lock-files', + prTitle: 'Lock File Maintenance', + schedule: 'before 5am on monday', + upgrades: [ + { + type: 'lockFileMaintenance', + }, + ], + }, + ]; + await onboarding.ensurePr(config, branchUpgrades); + expect(config.api.createPr.mock.calls.length).toBe(1); + expect(config.api.updatePr.mock.calls.length).toBe(0); + expect(config.api.createPr.mock.calls).toMatchSnapshot(); + }); it('handles groups', async () => { branchUpgrades = [ { diff --git a/test/workers/repository/upgrades.spec.js b/test/workers/repository/upgrades.spec.js index 4dfe450eaa..cf8579582d 100644 --- a/test/workers/repository/upgrades.spec.js +++ b/test/workers/repository/upgrades.spec.js @@ -50,6 +50,9 @@ describe('workers/repository/upgrades', () => { { depName: 'some-dep', groupName: 'some-group', + branchName: 'some-branch', + prTitle: 'some-title', + semanticCommits: true, lazyGrouping: true, foo: 1, group: { @@ -67,6 +70,8 @@ describe('workers/repository/upgrades', () => { { depName: 'some-dep', groupName: 'some-group', + branchName: 'some-branch', + prTitle: 'some-title', lazyGrouping: false, foo: 1, group: { @@ -85,6 +90,8 @@ describe('workers/repository/upgrades', () => { { depName: 'some-dep', groupName: 'some-group', + branchName: 'some-branch', + prTitle: 'some-title', lazyGrouping: true, foo: 1, group: { @@ -94,6 +101,8 @@ describe('workers/repository/upgrades', () => { { depName: 'some-other-dep', groupName: 'some-group', + branchName: 'some-branch', + prTitle: 'some-title', lazyGrouping: true, foo: 1, group: { @@ -116,6 +125,7 @@ describe('workers/repository/upgrades', () => { { branchName: 'foo-{{version}}', version: '1.1.0', + prTitle: 'some-title', }, ]; const res = await upgrades.groupByBranch(input); @@ -127,14 +137,17 @@ describe('workers/repository/upgrades', () => { { branchName: 'foo-{{version}}', version: '1.1.0', + prTitle: 'some-title', }, { branchName: 'foo-{{version}}', version: '2.0.0', + prTitle: 'some-title', }, { branchName: 'bar-{{version}}', version: '1.1.0', + prTitle: 'some-title', }, ]; const res = await upgrades.groupByBranch(input); @@ -146,14 +159,17 @@ describe('workers/repository/upgrades', () => { { branchName: 'foo', version: '1.1.0', + prTitle: 'some-title', }, { branchName: 'foo', version: '2.0.0', + prTitle: 'some-title', }, { branchName: 'bar-{{version}}', version: '1.1.0', + prTitle: 'some-title', }, ]; const res = await upgrades.groupByBranch(input); @@ -164,16 +180,19 @@ describe('workers/repository/upgrades', () => { const input = [ { branchName: 'foo', + prTitle: 'some-title', version: '1.1.0', groupName: 'My Group', group: { branchName: 'renovate/{{groupSlug}}' }, }, { branchName: 'foo', + prTitle: 'some-title', version: '2.0.0', }, { branchName: 'bar-{{version}}', + prTitle: 'some-title', version: '1.1.0', groupName: 'My Group', group: { branchName: 'renovate/my-group' }, @@ -190,15 +209,18 @@ describe('workers/repository/upgrades', () => { }, { branchName: 'foo-{{version}}', + prTitle: 'some-title', version: '1.1.0', }, { type: 'warning', branchName: 'foo-{{version}}', + prTitle: 'some-title', version: '2.0.0', }, { branchName: 'bar-{{version}}', + prTitle: 'some-title', version: '1.1.0', }, ]; -- GitLab