From 1a174226ed0c8083ca6ca40b0d61af2dac4ca5c1 Mon Sep 17 00:00:00 2001
From: Rhys Arkins <rhys@keylocation.sg>
Date: Fri, 13 Oct 2017 06:42:17 +0200
Subject: [PATCH] feat: keep branches/PRs updated when out of schedule (#926)

By default, Renovate will now continue *updating* - but not creating - branches even if off-schedule. This applies to features such as (a) rebasing branches if master changes, (b) updating if new version comes, (c) creating PRs if tests pass, (d) automerging. It is planned that (b) will be configurable in a future feature. To disable this behaviour altogether, set updateNotScheduled to false.

Closes #879
---
 docs/configuration.md                            | 12 ++++++++++--
 lib/config/definitions.js                        |  7 +++++++
 lib/workers/branch/index.js                      | 16 +++++++++++++---
 test/workers/branch/index.spec.js                | 11 ++++++++++-
 .../package/__snapshots__/index.spec.js.snap     |  1 +
 5 files changed, 41 insertions(+), 6 deletions(-)

diff --git a/docs/configuration.md b/docs/configuration.md
index 5df4725291..264f2992c2 100644
--- a/docs/configuration.md
+++ b/docs/configuration.md
@@ -77,6 +77,7 @@ $ node renovate --help
     --private-key <string>               Server-side private key
     --encrypted <json>                   A configuration object containing configuration encrypted with project key
     --timezone <string>                  [IANA Time Zone](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones)
+    --update-not-scheduled [boolean]     Whether to update (but not create) branches when not scheduled
     --onboarding [boolean]               Require a Configuration PR first
     --platform <string>                  Platform type of repository
     --endpoint <string>                  Custom endpoint to use
@@ -242,6 +243,14 @@ Obviously, you can't set repository or package file location with this method.
   <td></td>
   <td><td>
 </tr>
+<tr>
+  <td>`updateNotScheduled`</td>
+  <td>Whether to update (but not create) branches when not scheduled</td>
+  <td>boolean</td>
+  <td><pre>true</pre></td>
+  <td>`RENOVATE_UPDATE_NOT_SCHEDULED`</td>
+  <td>`--update-not-scheduled`<td>
+</tr>
 <tr>
   <td>`onboarding`</td>
   <td>Require a Configuration PR first</td>
@@ -529,7 +538,6 @@ Obviously, you can't set repository or package file location with this method.
   <td>Configuration to apply when an update type is pin.</td>
   <td>json</td>
   <td><pre>{
-  "automerge": true,
   "unpublishSafe": false,
   "groupName": "Pin Dependencies",
   "group": {
@@ -736,7 +744,7 @@ Obviously, you can't set repository or package file location with this method.
   <td>`npm`</td>
   <td>Configuration object for npm package.json renovation</td>
   <td>json</td>
-  <td><pre>{}</pre></td>
+  <td><pre>{"enabled": true}</pre></td>
   <td>`RENOVATE_NPM`</td>
   <td>`--npm`<td>
 </tr>
diff --git a/lib/config/definitions.js b/lib/config/definitions.js
index 457fd67324..00aa9e9959 100644
--- a/lib/config/definitions.js
+++ b/lib/config/definitions.js
@@ -102,6 +102,13 @@ const options = [
     cli: false,
     env: false,
   },
+  {
+    name: 'updateNotScheduled',
+    description:
+      'Whether to update (but not create) branches when not scheduled',
+    stage: 'branch',
+    type: 'boolean',
+  },
   {
     name: 'onboarding',
     description: 'Require a Configuration PR first',
diff --git a/lib/workers/branch/index.js b/lib/workers/branch/index.js
index 1ea8aa0bf4..e39457929d 100644
--- a/lib/workers/branch/index.js
+++ b/lib/workers/branch/index.js
@@ -28,9 +28,19 @@ async function processBranch(branchConfig) {
   logger.trace({ config }, 'processBranch');
   try {
     // Check schedule
-    if (!isScheduledNow(config)) {
-      logger.info('Skipping branch as it is not scheduled');
-      return 'not-scheduled';
+    config.isScheduledNow = isScheduledNow(config);
+    if (!config.isScheduledNow) {
+      if (!await config.api.branchExists(config.branchName)) {
+        logger.info('Skipping branch creation as not within schedule');
+        return 'not-scheduled';
+      }
+      if (config.updateNotScheduled === false) {
+        logger.debug('Skipping branch update as not within schedule');
+        return 'not-scheduled';
+      }
+      logger.debug(
+        'Branch exists but is not scheduled -- will update if necessary'
+      );
     }
 
     logger.info(`Branch has ${dependencies.length} upgrade(s)`);
diff --git a/test/workers/branch/index.spec.js b/test/workers/branch/index.spec.js
index eda5c4ed22..8f1a56eac5 100644
--- a/test/workers/branch/index.spec.js
+++ b/test/workers/branch/index.spec.js
@@ -37,12 +37,21 @@ describe('workers/branch', () => {
       };
       schedule.isScheduledNow.mockReturnValue(true);
     });
-    it('skips branch if not scheduled', async () => {
+    it('skips branch if not scheduled and branch does not exist', async () => {
       schedule.isScheduledNow.mockReturnValueOnce(false);
       await branchWorker.processBranch(config);
       expect(checkExisting.prAlreadyExisted.mock.calls).toHaveLength(0);
     });
+    it('skips branch if not scheduled and not updating out of schedule', async () => {
+      schedule.isScheduledNow.mockReturnValueOnce(false);
+      config.updateNotScheduled = false;
+      config.api.branchExists.mockReturnValueOnce(true);
+      await branchWorker.processBranch(config);
+      expect(checkExisting.prAlreadyExisted.mock.calls).toHaveLength(0);
+    });
     it('skips branch if closed PR found', async () => {
+      schedule.isScheduledNow.mockReturnValueOnce(false);
+      config.api.branchExists.mockReturnValueOnce(true);
       checkExisting.prAlreadyExisted.mockReturnValueOnce(true);
       await branchWorker.processBranch(config);
       expect(parent.getParentBranch.mock.calls.length).toBe(0);
diff --git a/test/workers/package/__snapshots__/index.spec.js.snap b/test/workers/package/__snapshots__/index.spec.js.snap
index ac5e5ed737..f6d66af39c 100644
--- a/test/workers/package/__snapshots__/index.spec.js.snap
+++ b/test/workers/package/__snapshots__/index.spec.js.snap
@@ -158,6 +158,7 @@ This {{#if isGitHub}}PR{{else}}MR{{/if}} has been generated by [Renovate Bot](ht
   "timezone": null,
   "type": "pin",
   "unpublishSafe": false,
+  "updateNotScheduled": true,
   "yarnrc": null,
 }
 `;
-- 
GitLab