From 3c83075fb899332ae65361608ba4c9a004a3d75d Mon Sep 17 00:00:00 2001
From: Ika <ikatyang@gmail.com>
Date: Fri, 28 Jul 2017 04:17:28 +0800
Subject: [PATCH] feat: add 'patch' option to automerge (#539)

* feat: add 'patch' option to automerge

* test: add tests for automerge = patch

* docs: add patch option to automerge

* refactor(isAutomergeEnabled): write code verbosely

* feat: export isAutomergeEnabled

* test(isAutomergeEnabled): add tests
---
 docs/configuration.md                         |  4 +-
 lib/config/definitions.js                     |  2 +-
 lib/workers/package/versions.js               | 28 +++++++--
 .../__snapshots__/versions.spec.js.snap       | 58 ++++++++++++++++++-
 test/workers/package/versions.spec.js         | 31 ++++++++++
 5 files changed, 113 insertions(+), 10 deletions(-)

diff --git a/docs/configuration.md b/docs/configuration.md
index 8683cc05b8..25a10f35e3 100644
--- a/docs/configuration.md
+++ b/docs/configuration.md
@@ -96,7 +96,7 @@ $ node renovate --help
     --recreate-closed [boolean]          Recreate PRs even if same ones were closed previously
     --rebase-stale-prs [boolean]         Rebase stale PRs (GitHub only)
     --pr-creation <string>               When to create the PR for a branch. Values: immediate, not-pending, status-success.
-    --automerge <string>                 What types of upgrades to merge to base branch automatically. Values: none, minor or any
+    --automerge <string>                 What types of upgrades to merge to base branch automatically. Values: none, patch, minor or any
     --automerge-type <string>            How to automerge - "branch-merge-commit", "branch-push" or "pr". Branch support is GitHub-only
     --lazy-grouping [boolean]            Use group names only when multiple dependencies upgraded
     --group-name <string>                Human understandable name for the dependency group
@@ -398,7 +398,7 @@ Obviously, you can't set repository or package file location with this method.
 </tr>
 <tr>
   <td>`automerge`</td>
-  <td>What types of upgrades to merge to base branch automatically. Values: none, minor or any</td>
+  <td>What types of upgrades to merge to base branch automatically. Values: none, patch, minor or any</td>
   <td>string</td>
   <td><pre>"none"</pre></td>
   <td>`RENOVATE_AUTOMERGE`</td>
diff --git a/lib/config/definitions.js b/lib/config/definitions.js
index 389f016e25..024377e006 100644
--- a/lib/config/definitions.js
+++ b/lib/config/definitions.js
@@ -239,7 +239,7 @@ const options = [
   {
     name: 'automerge',
     description:
-      'What types of upgrades to merge to base branch automatically. Values: none, minor or any',
+      'What types of upgrades to merge to base branch automatically. Values: none, patch, minor or any',
     type: 'string',
     default: 'none',
   },
diff --git a/lib/workers/package/versions.js b/lib/workers/package/versions.js
index 174179f46e..7afbda5eaa 100644
--- a/lib/workers/package/versions.js
+++ b/lib/workers/package/versions.js
@@ -10,6 +10,7 @@ module.exports = {
   isValidVersion,
   isFuture,
   isPastLatest,
+  isAutomergeEnabled,
 };
 
 function determineUpgrades(npmDep, config) {
@@ -102,18 +103,20 @@ function determineUpgrades(npmDep, config) {
         !allUpgrades[upgradeKey] ||
         semver.gt(newVersion, allUpgrades[upgradeKey].newVersion)
       ) {
+        const newVersionMinor = semver.minor(newVersion);
         const type =
-          newVersionMajor > semver.major(changeLogFromVersion)
+          newVersionMajor > semver.major(changeLogFromVersion) // eslint-disable-line no-nested-ternary
             ? 'major'
-            : 'minor';
+            : newVersionMinor > semver.minor(changeLogFromVersion)
+              ? 'minor'
+              : 'patch';
         const changeLogToVersion = newVersion;
-        const automergeEnabled =
-          config.automerge === 'any' ||
-          (config.automerge === 'minor' && type === 'minor');
+        const automergeEnabled = isAutomergeEnabled(config.automerge, type);
         allUpgrades[upgradeKey] = {
           type,
           newVersion,
           newVersionMajor,
+          newVersionMinor,
           changeLogFromVersion,
           changeLogToVersion,
           automergeEnabled,
@@ -122,6 +125,8 @@ function determineUpgrades(npmDep, config) {
           allUpgrades[upgradeKey].isMajor = true;
         } else if (type === 'minor') {
           allUpgrades[upgradeKey].isMinor = true;
+        } else if (type === 'patch') {
+          allUpgrades[upgradeKey].isPatch = true;
         }
       }
     });
@@ -223,3 +228,16 @@ function isPastLatest(npmDep, version) {
   logger.warn(`No dist-tags.latest for ${npmDep.name}`);
   return false;
 }
+
+function isAutomergeEnabled(automerge, type) {
+  if (automerge === 'any') {
+    return true;
+  }
+  if (automerge === 'minor' && type !== 'major') {
+    return true;
+  }
+  if (automerge === 'patch' && type === 'patch') {
+    return true;
+  }
+  return false;
+}
diff --git a/test/workers/package/__snapshots__/versions.spec.js.snap b/test/workers/package/__snapshots__/versions.spec.js.snap
index 07aa9261c9..64966888be 100644
--- a/test/workers/package/__snapshots__/versions.spec.js.snap
+++ b/test/workers/package/__snapshots__/versions.spec.js.snap
@@ -21,6 +21,7 @@ Array [
     "isMajor": true,
     "newVersion": "1.4.1",
     "newVersionMajor": 1,
+    "newVersionMinor": 4,
     "type": "major",
   },
 ]
@@ -35,6 +36,7 @@ Array [
     "isMinor": true,
     "newVersion": "1.4.1",
     "newVersionMajor": 1,
+    "newVersionMinor": 4,
     "type": "minor",
   },
 ]
@@ -56,6 +58,7 @@ Array [
     "isMajor": true,
     "newVersion": "1.4.1",
     "newVersionMajor": 1,
+    "newVersionMinor": 4,
     "type": "major",
   },
   Object {
@@ -136,6 +139,7 @@ Array [
     "isMinor": true,
     "newVersion": "0.9.7",
     "newVersionMajor": 0,
+    "newVersionMinor": 9,
     "type": "minor",
   },
   Object {
@@ -145,6 +149,7 @@ Array [
     "isMajor": true,
     "newVersion": "1.4.1",
     "newVersionMajor": 1,
+    "newVersionMinor": 4,
     "type": "major",
   },
   Object {
@@ -162,6 +167,31 @@ Array [
 ]
 `;
 
+exports[`workers/package/versions .determineUpgrades(npmDep, config) returns both updates if automerging patch 1`] = `
+Array [
+  Object {
+    "automergeEnabled": true,
+    "changeLogFromVersion": "0.9.0",
+    "changeLogToVersion": "0.9.7",
+    "isPatch": true,
+    "newVersion": "0.9.7",
+    "newVersionMajor": 0,
+    "newVersionMinor": 9,
+    "type": "patch",
+  },
+  Object {
+    "automergeEnabled": false,
+    "changeLogFromVersion": "0.9.0",
+    "changeLogToVersion": "1.4.1",
+    "isMajor": true,
+    "newVersion": "1.4.1",
+    "newVersionMajor": 1,
+    "newVersionMinor": 4,
+    "type": "major",
+  },
+]
+`;
+
 exports[`workers/package/versions .determineUpgrades(npmDep, config) returns only one update if automerging any 1`] = `
 Array [
   Object {
@@ -183,6 +213,7 @@ Array [
     "isMajor": true,
     "newVersion": "1.4.1",
     "newVersionMajor": 1,
+    "newVersionMinor": 4,
     "type": "major",
   },
 ]
@@ -209,6 +240,7 @@ Array [
     "isMajor": true,
     "newVersion": "1.4.1",
     "newVersionMajor": 1,
+    "newVersionMinor": 4,
     "type": "major",
   },
 ]
@@ -223,6 +255,7 @@ Array [
     "isMinor": true,
     "newVersion": "1.1.0-beta",
     "newVersionMajor": 1,
+    "newVersionMinor": 1,
     "type": "minor",
   },
 ]
@@ -245,11 +278,12 @@ Array [
     "automergeEnabled": false,
     "changeLogFromVersion": "0.0.34",
     "changeLogToVersion": "0.0.35",
-    "isMinor": true,
+    "isPatch": true,
     "isRange": true,
     "newVersion": "^0.0.35",
     "newVersionMajor": 0,
-    "type": "minor",
+    "newVersionMinor": 0,
+    "type": "patch",
   },
 ]
 `;
@@ -263,6 +297,7 @@ Array [
     "isMajor": true,
     "newVersion": "2.0.1",
     "newVersionMajor": 2,
+    "newVersionMinor": 0,
     "type": "major",
   },
 ]
@@ -294,6 +329,7 @@ Array [
     "isMajor": true,
     "newVersion": "2.0.3",
     "newVersionMajor": 2,
+    "newVersionMinor": 0,
     "type": "major",
   },
 ]
@@ -308,6 +344,7 @@ Array [
     "isMinor": true,
     "newVersion": "0.9.7",
     "newVersionMajor": 0,
+    "newVersionMinor": 9,
     "type": "minor",
   },
   Object {
@@ -317,6 +354,7 @@ Array [
     "isMajor": true,
     "newVersion": "1.4.1",
     "newVersionMajor": 1,
+    "newVersionMinor": 4,
     "type": "major",
   },
   Object {
@@ -343,6 +381,7 @@ Array [
     "isMinor": true,
     "newVersion": "0.9.7",
     "newVersionMajor": 0,
+    "newVersionMinor": 9,
     "type": "minor",
   },
   Object {
@@ -352,6 +391,7 @@ Array [
     "isMajor": true,
     "newVersion": "1.4.1",
     "newVersionMajor": 1,
+    "newVersionMinor": 4,
     "type": "major",
   },
   Object {
@@ -379,6 +419,7 @@ Array [
     "isRange": true,
     "newVersion": "1.x",
     "newVersionMajor": 1,
+    "newVersionMinor": 4,
     "type": "major",
   },
 ]
@@ -393,6 +434,7 @@ Array [
     "isMinor": true,
     "newVersion": "1.4.1",
     "newVersionMajor": 1,
+    "newVersionMinor": 4,
     "type": "minor",
   },
   Object {
@@ -420,6 +462,7 @@ Array [
     "isRange": true,
     "newVersion": "1.4.x",
     "newVersionMajor": 1,
+    "newVersionMinor": 4,
     "type": "minor",
   },
 ]
@@ -435,6 +478,7 @@ Array [
     "isRange": true,
     "newVersion": "<= 0.9.7",
     "newVersionMajor": 0,
+    "newVersionMinor": 9,
     "type": "minor",
   },
   Object {
@@ -445,6 +489,7 @@ Array [
     "isRange": true,
     "newVersion": "<= 1.4.1",
     "newVersionMajor": 1,
+    "newVersionMinor": 4,
     "type": "major",
   },
 ]
@@ -459,6 +504,7 @@ Array [
     "isMinor": true,
     "newVersion": "1.4.1",
     "newVersionMajor": 1,
+    "newVersionMinor": 4,
     "type": "minor",
   },
   Object {
@@ -486,6 +532,7 @@ Array [
     "isRange": true,
     "newVersion": "^0.9.0",
     "newVersionMajor": 0,
+    "newVersionMinor": 9,
     "type": "minor",
   },
   Object {
@@ -496,6 +543,7 @@ Array [
     "isRange": true,
     "newVersion": "^1.0.0",
     "newVersionMajor": 1,
+    "newVersionMinor": 4,
     "type": "major",
   },
 ]
@@ -511,6 +559,7 @@ Array [
     "isRange": true,
     "newVersion": "~0.9.0",
     "newVersionMajor": 0,
+    "newVersionMinor": 9,
     "type": "minor",
   },
   Object {
@@ -521,6 +570,7 @@ Array [
     "isRange": true,
     "newVersion": "~1.4.0",
     "newVersionMajor": 1,
+    "newVersionMinor": 4,
     "type": "major",
   },
 ]
@@ -536,6 +586,7 @@ Array [
     "isRange": true,
     "newVersion": "1",
     "newVersionMajor": 1,
+    "newVersionMinor": 4,
     "type": "major",
   },
 ]
@@ -551,6 +602,7 @@ Array [
     "isRange": true,
     "newVersion": "1.4",
     "newVersionMajor": 1,
+    "newVersionMinor": 4,
     "type": "minor",
   },
 ]
@@ -565,6 +617,7 @@ Array [
     "isMinor": true,
     "newVersion": "1.4.1",
     "newVersionMajor": 1,
+    "newVersionMinor": 4,
     "type": "minor",
   },
   Object {
@@ -592,6 +645,7 @@ Array [
     "isRange": true,
     "newVersion": "~1.4.0",
     "newVersionMajor": 1,
+    "newVersionMinor": 4,
     "type": "minor",
   },
 ]
diff --git a/test/workers/package/versions.spec.js b/test/workers/package/versions.spec.js
index c1c2dbd473..ecd4468a12 100644
--- a/test/workers/package/versions.spec.js
+++ b/test/workers/package/versions.spec.js
@@ -61,6 +61,11 @@ describe('workers/package/versions', () => {
       config.currentVersion = '^0.4.0';
       expect(versions.determineUpgrades(qJson, config)).toMatchSnapshot();
     });
+    it('returns both updates if automerging patch', () => {
+      config.automerge = 'patch';
+      config.currentVersion = '0.9.0';
+      expect(versions.determineUpgrades(qJson, config)).toMatchSnapshot();
+    });
     it('disables major release separation (major)', () => {
       config.separateMajorReleases = false;
       config.currentVersion = '^0.4.0';
@@ -264,4 +269,30 @@ describe('workers/package/versions', () => {
       versions.isPastLatest(qJson, '2.0.3').should.eql(true);
     });
   });
+  describe('.isAutomergeEnabled(automerge, type)', () => {
+    it('should return true for automerge = any', () => {
+      versions.isAutomergeEnabled('any', 'whatever').should.eql(true);
+    });
+    it('should return true for automerge = minor and type = minor', () => {
+      versions.isAutomergeEnabled('minor', 'minor').should.eql(true);
+    });
+    it('should return true for automerge = minor and type = patch', () => {
+      versions.isAutomergeEnabled('minor', 'patch').should.eql(true);
+    });
+    it('should return true for automerge = patch and type = patch', () => {
+      versions.isAutomergeEnabled('patch', 'patch').should.eql(true);
+    });
+    it('should return false for automerge = minor and type = major', () => {
+      versions.isAutomergeEnabled('minor', 'major').should.eql(false);
+    });
+    it('should return false for automerge = patch and type = minor', () => {
+      versions.isAutomergeEnabled('patch', 'minor').should.eql(false);
+    });
+    it('should return false for automerge = patch and type = major', () => {
+      versions.isAutomergeEnabled('patch', 'major').should.eql(false);
+    });
+    it('should return false for automerge = none', () => {
+      versions.isAutomergeEnabled('none', 'whatever').should.eql(false);
+    });
+  });
 });
-- 
GitLab