From 7ad306da4417f81a7f45cc9a210b90c8305c6258 Mon Sep 17 00:00:00 2001
From: Rhys Arkins <rhys@arkins.net>
Date: Tue, 29 May 2018 07:13:44 +0200
Subject: [PATCH] feat: allow rollback + upgrades

If a current version is missing, we now return both rollback as well as upgrades if upgrades are present.
---
 lib/manager/npm/lookup/filter.js              |  3 ++
 lib/manager/npm/lookup/index.js               |  4 +-
 lib/manager/npm/lookup/rollback.js            | 24 +++++-----
 lib/versioning/semver/range.js                |  5 +-
 .../npm/__snapshots__/lookup.spec.js.snap     | 21 +++++---
 test/manager/npm/lookup.spec.js               | 48 +++++++------------
 6 files changed, 53 insertions(+), 52 deletions(-)

diff --git a/lib/manager/npm/lookup/filter.js b/lib/manager/npm/lookup/filter.js
index 2d3b17b10a..1b89d77035 100644
--- a/lib/manager/npm/lookup/filter.js
+++ b/lib/manager/npm/lookup/filter.js
@@ -12,6 +12,9 @@ module.exports = {
 
 function filterVersions(config, fromVersion, latestVersion, versions) {
   const { ignoreUnstable, respectLatest } = config;
+  if (!fromVersion) {
+    return [];
+  }
   // Leave only versions greater than current
   let filteredVersions = versions.filter(version =>
     isGreaterThan(version, fromVersion)
diff --git a/lib/manager/npm/lookup/index.js b/lib/manager/npm/lookup/index.js
index 5bf108c9a6..50394b6df3 100644
--- a/lib/manager/npm/lookup/index.js
+++ b/lib/manager/npm/lookup/index.js
@@ -36,8 +36,9 @@ function lookupUpdates(dependency, config) {
   const allSatisfyingVersions = allVersions.filter(version =>
     matches(version, currentVersion)
   );
+  const updates = [];
   if (!allSatisfyingVersions.length) {
-    return getRollbackUpdate(config, allVersions);
+    updates.push(getRollbackUpdate(config, allVersions));
   }
   const fromVersion = getFromVersion(
     currentVersion,
@@ -45,7 +46,6 @@ function lookupUpdates(dependency, config) {
     lockedVersion,
     allVersions
   );
-  const updates = [];
   if (isRange(currentVersion) && config.rangeStrategy === 'pin') {
     updates.push({
       type: 'pin',
diff --git a/lib/manager/npm/lookup/rollback.js b/lib/manager/npm/lookup/rollback.js
index 92724ed0d8..921207541b 100644
--- a/lib/manager/npm/lookup/rollback.js
+++ b/lib/manager/npm/lookup/rollback.js
@@ -30,17 +30,15 @@ function getRollbackUpdate(config, versions) {
   const toVersion = lessThanVersions.pop();
   let fromVersion;
   const newVersion = rangify(config, currentVersion, fromVersion, toVersion);
-  return [
-    {
-      type: 'rollback',
-      branchName:
-        '{{{branchPrefix}}}rollback-{{{depNameSanitized}}}-{{{newVersionMajor}}}.x',
-      commitMessageAction: 'Roll back',
-      isRollback: true,
-      newVersion,
-      newVersionMajor: getMajor(toVersion),
-      semanticCommitType: 'fix',
-      unpublishable: false,
-    },
-  ];
+  return {
+    type: 'rollback',
+    branchName:
+      '{{{branchPrefix}}}rollback-{{{depNameSanitized}}}-{{{newVersionMajor}}}.x',
+    commitMessageAction: 'Roll back',
+    isRollback: true,
+    newVersion,
+    newVersionMajor: getMajor(toVersion),
+    semanticCommitType: 'fix',
+    unpublishable: false,
+  };
 }
diff --git a/lib/versioning/semver/range.js b/lib/versioning/semver/range.js
index e55ade1c24..3e95cb254a 100644
--- a/lib/versioning/semver/range.js
+++ b/lib/versioning/semver/range.js
@@ -56,7 +56,10 @@ function rangify(config, currentVersion, fromVersion, toVersion) {
     return null;
   }
   if (element.operator === '^') {
-    if (fromVersion && major(toVersion) === major(fromVersion)) {
+    if (!fromVersion) {
+      return `^${toVersion}`;
+    }
+    if (major(toVersion) === major(fromVersion)) {
       if (major(toVersion) === 0) {
         if (minor(toVersion) === 0) {
           return `^${toVersion}`;
diff --git a/test/manager/npm/__snapshots__/lookup.spec.js.snap b/test/manager/npm/__snapshots__/lookup.spec.js.snap
index dbff4230ba..74e9abc556 100644
--- a/test/manager/npm/__snapshots__/lookup.spec.js.snap
+++ b/test/manager/npm/__snapshots__/lookup.spec.js.snap
@@ -352,29 +352,38 @@ Array [
 ]
 `;
 
-exports[`manager/npm/lookup .lookupUpdates() returns rollback if range not found 1`] = `
+exports[`manager/npm/lookup .lookupUpdates() returns rollback for pinned version 1`] = `
 Array [
   Object {
     "branchName": "{{{branchPrefix}}}rollback-{{{depNameSanitized}}}-{{{newVersionMajor}}}.x",
     "commitMessageAction": "Roll back",
     "isRollback": true,
-    "newVersion": "^2.0.0",
-    "newVersionMajor": 2,
+    "newVersion": "0.9.7",
+    "newVersionMajor": 0,
     "semanticCommitType": "fix",
     "type": "rollback",
     "unpublishable": false,
   },
+  Object {
+    "fromVersion": "0.9.99",
+    "newVersion": "1.4.1",
+    "newVersionMajor": 1,
+    "newVersionMinor": 4,
+    "toVersion": "1.4.1",
+    "type": "major",
+    "unpublishable": false,
+  },
 ]
 `;
 
-exports[`manager/npm/lookup .lookupUpdates() should allow unstable versions if ignoreUnstable is false 1`] = `
+exports[`manager/npm/lookup .lookupUpdates() returns rollback for ranged version 1`] = `
 Array [
   Object {
     "branchName": "{{{branchPrefix}}}rollback-{{{depNameSanitized}}}-{{{newVersionMajor}}}.x",
     "commitMessageAction": "Roll back",
     "isRollback": true,
-    "newVersion": "1.0.0-beta",
-    "newVersionMajor": 1,
+    "newVersion": "^0.9.7",
+    "newVersionMajor": 0,
     "semanticCommitType": "fix",
     "type": "rollback",
     "unpublishable": false,
diff --git a/test/manager/npm/lookup.spec.js b/test/manager/npm/lookup.spec.js
index 19f01519aa..95c5f310b4 100644
--- a/test/manager/npm/lookup.spec.js
+++ b/test/manager/npm/lookup.spec.js
@@ -27,8 +27,12 @@ describe('manager/npm/lookup', () => {
       expect(res).toHaveLength(1);
       expect(res[0]).toMatchSnapshot();
     });
-    it('returns rollback if range not found', () => {
-      config.currentVersion = '^8.4.0';
+    it('returns rollback for pinned version', () => {
+      config.currentVersion = '0.9.99';
+      expect(lookup.lookupUpdates(qJson, config)).toMatchSnapshot();
+    });
+    it('returns rollback for ranged version', () => {
+      config.currentVersion = '^0.9.99';
       expect(lookup.lookupUpdates(qJson, config)).toMatchSnapshot();
     });
     it('supports minor and major upgrades for tilde ranges', () => {
@@ -355,19 +359,19 @@ describe('manager/npm/lookup', () => {
     it('should allow unstable versions if the ignoreUnstable=false', () => {
       config.currentVersion = '1.0.0';
       config.ignoreUnstable = false;
-      expect(
-        lookup.lookupUpdates(
-          {
-            name: 'amazing-package',
-            versions: {
-              '1.0.0-beta': {},
-              '1.0.0': {},
-              '1.1.0-beta': {},
-            },
+      const res = lookup.lookupUpdates(
+        {
+          name: 'amazing-package',
+          versions: {
+            '1.0.0-beta': {},
+            '1.0.0': {},
+            '1.1.0-beta': {},
           },
-          config
-        )
-      ).toMatchSnapshot();
+        },
+        config
+      );
+      expect(res).toMatchSnapshot();
+      expect(res).toHaveLength(1);
     });
     it('should allow unstable versions if the current version is unstable', () => {
       config.currentVersion = '1.0.0-beta';
@@ -384,22 +388,6 @@ describe('manager/npm/lookup', () => {
         )
       ).toMatchSnapshot();
     });
-    it('should allow unstable versions if ignoreUnstable is false', () => {
-      config.currentVersion = '1.0.0';
-      config.ignoreUnstable = false;
-      expect(
-        lookup.lookupUpdates(
-          {
-            name: 'amazing-package',
-            versions: {
-              '1.0.0-beta': {},
-              '1.1.0-beta': {},
-            },
-          },
-          config
-        )
-      ).toMatchSnapshot();
-    });
     it('should treat zero zero tilde ranges as 0.0.x', () => {
       config.rangeStrategy = 'replace';
       config.currentVersion = '~0.0.34';
-- 
GitLab