diff --git a/lib/config/validation.js b/lib/config/validation.js
index 445295bfff7efe6abf5fafc1b4ec2dd849ad31b6..2c9ae276776bcfc6a5a242ad6da98718b4e3de5b 100644
--- a/lib/config/validation.js
+++ b/lib/config/validation.js
@@ -1,5 +1,5 @@
-const semver = require('semver');
 const options = require('./definitions').getOptions();
+const { isValidSemver } = require('../util/semver');
 const { resolveConfigPresets } = require('./presets');
 const {
   hasValidSchedule,
@@ -74,7 +74,7 @@ async function validateConfig(config) {
           });
         }
       } else if (key === 'allowedVersions' && val !== null) {
-        if (!semver.validRange(val)) {
+        if (!isValidSemver(val)) {
           errors.push({
             depName: 'Configuration Error',
             message: `Invalid semver range for allowedVersions: \`${val}\``,
diff --git a/lib/datasource/github.js b/lib/datasource/github.js
index 8cb2d437ce386e6f23470ae3c4c830c2ea9b2176..d04d68ace1fdc68dfb7a09dad0f40508b26fae86 100644
--- a/lib/datasource/github.js
+++ b/lib/datasource/github.js
@@ -1,16 +1,11 @@
 const ghGot = require('../platform/github/gh-got-wrapper');
-const semver = require('semver');
+const { isPinnedVersion, semverSort } = require('../util/semver');
 
 module.exports = {
   getRepoTags,
   getRepoReleases,
-  semverSort,
 };
 
-function semverSort(a, b) {
-  return semver.compare(a, b);
-}
-
 async function getRepoTags(repo) {
   try {
     const url = `repos/${repo}/git/refs/tags?per_page=100`;
@@ -20,7 +15,7 @@ async function getRepoTags(repo) {
     return res
       .filter(o => o.ref.startsWith(tagPrefix))
       .map(o => o.ref.replace(tagPrefix, ''))
-      .filter(o => semver.valid(o))
+      .filter(isPinnedVersion)
       .sort(semverSort);
   } catch (err) /* istanbul ignore next */ {
     logger.warn({ repo }, 'Could not fetch repo tags');
@@ -34,7 +29,7 @@ async function getRepoReleases(repo) {
     const res = (await ghGot(url, { paginate: true })).body;
     return res
       .map(o => o.tag_name)
-      .filter(semver.valid)
+      .filter(isPinnedVersion)
       .sort(semverSort);
   } catch (err) /* istanbul ignore next */ {
     logger.warn({ repo }, 'Could not fetch repo releases');
diff --git a/lib/manager/_helpers/node/package.js b/lib/manager/_helpers/node/package.js
index 2ba2ab57dedb4b7f5904e6ac66b8ed1f1a8b5fef..974b03c47098ff58375446bbd983f4deac6ee3a1 100644
--- a/lib/manager/_helpers/node/package.js
+++ b/lib/manager/_helpers/node/package.js
@@ -1,4 +1,9 @@
-const semver = require('semver');
+const {
+  getMajor,
+  getMinor,
+  isGreaterThan,
+  isPinnedVersion,
+} = require('../../../util/semver');
 const { getRepoTags, semverSort } = require('../../../datasource/github');
 
 async function getPackageUpdates(config) {
@@ -6,7 +11,7 @@ async function getPackageUpdates(config) {
   logger.trace({ config });
   const { currentVersion } = config;
   logger.info('Checking for nvmrc updates');
-  if (!semver.valid(currentVersion)) {
+  if (!isPinnedVersion(currentVersion)) {
     logger.info({ currentVersion }, 'Skipping non-pinned node version');
     return [];
   }
@@ -22,8 +27,8 @@ async function getPackageUpdates(config) {
   }
   const newReleases = (await getRepoTags('nodejs/node'))
     .map(release => release.replace(/^v/, ''))
-    .filter(release => semver.major(currentVersion) === semver.major(release))
-    .filter(release => semver.gt(release, currentVersion))
+    .filter(release => getMajor(currentVersion) === getMajor(release))
+    .filter(release => isGreaterThan(release, currentVersion))
     .sort(semverSort);
   // istanbul ignore if
   if (endpoint) {
@@ -39,13 +44,10 @@ async function getPackageUpdates(config) {
   const newVersion = newReleases.pop();
   return [
     {
-      type:
-        semver.major(currentVersion) !== semver.major(newVersion)
-          ? 'major'
-          : 'minor',
+      type: getMajor(newVersion) > getMajor(currentVersion) ? 'major' : 'minor',
       newVersion,
-      newVersionMajor: semver.major(newVersion),
-      newVersionMinor: semver.minor(newVersion),
+      newVersionMajor: getMajor(newVersion),
+      newVersionMinor: getMinor(newVersion),
       changeLogFromVersion: currentVersion,
       changeLogToVersion: newVersion,
       repositoryUrl: 'https://github.com/nodejs/node',
diff --git a/lib/manager/bazel/package.js b/lib/manager/bazel/package.js
index df1cfb33217b346045f8dd45475956757a0100c7..79596241980487d0bd628f19ffe62bf12a5a740e 100644
--- a/lib/manager/bazel/package.js
+++ b/lib/manager/bazel/package.js
@@ -1,5 +1,9 @@
-const semver = require('semver');
-const stable = require('semver-stable');
+const {
+  getMajor,
+  isGreaterThan,
+  isStable,
+  isPinnedVersion,
+} = require('../../util/semver');
 const { getRepoTags, getRepoReleases } = require('../../datasource/github');
 
 module.exports = {
@@ -16,7 +20,7 @@ async function getPackageUpdates(config) {
       logger.info({ remote }, 'Bazel warning: Unsupported remote');
       return [];
     }
-    if (!semver.valid(currentVersion)) {
+    if (!isPinnedVersion(currentVersion)) {
       logger.info({ currentVersion }, 'Bazel warning: Unsupported tag');
       return [];
     }
@@ -28,39 +32,34 @@ async function getPackageUpdates(config) {
       logger.warn({ repo }, 'No tags found');
       return [];
     }
-    if (stable.is(currentVersion)) {
-      tags = tags.filter(tag => stable.is(tag));
+    if (isStable(currentVersion)) {
+      tags = tags.filter(tag => isStable(tag));
     }
     const [newestTag] = tags.slice(-1);
-    if (newestTag && semver.gt(newestTag, currentVersion)) {
+    if (newestTag && isGreaterThan(newestTag, currentVersion)) {
       logger.debug({ newestTag }, 'Found newer tag');
       return [
         {
           newVersion: newestTag,
-          newVersionMajor: semver.major(newestTag),
+          newVersionMajor: getMajor(newestTag),
           type:
-            semver.major(newestTag) > semver.major(currentVersion)
-              ? 'major'
-              : 'minor',
+            getMajor(newestTag) > getMajor(currentVersion) ? 'major' : 'minor',
         },
       ];
     }
   } else if (config.depType === 'http_archive') {
     const { repo, currentVersion } = config;
-    if (!semver.valid(currentVersion)) {
+    if (!isPinnedVersion(currentVersion)) {
       logger.info({ currentVersion }, 'Bazel warning: Unsupported tag');
       return [];
     }
     const releases = await getRepoReleases(repo);
     const [latest] = releases.slice(-1);
-    if (semver.gt(latest, currentVersion)) {
+    if (isGreaterThan(latest, currentVersion)) {
       const upgrade = {
         newVersion: latest,
-        newVersionMajor: semver.major(latest),
-        type:
-          semver.major(latest) > semver.major(currentVersion)
-            ? 'major'
-            : 'minor',
+        newVersionMajor: getMajor(latest),
+        type: getMajor(latest) > getMajor(currentVersion) ? 'major' : 'minor',
       };
       logger.info({ upgrade });
       return [upgrade];
diff --git a/lib/manager/docker/package.js b/lib/manager/docker/package.js
index c4a3a9f274a079ee3a75bd5800846683f3355612..2acd4481ec92fee7c4b88fa6b7634341ee94cfda 100644
--- a/lib/manager/docker/package.js
+++ b/lib/manager/docker/package.js
@@ -1,6 +1,5 @@
-const semver = require('semver');
+const { getMajor, isValidSemver } = require('../../util/semver');
 const dockerApi = require('../../datasource/docker');
-const versions = require('../../workers/package/versions');
 const compareVersions = require('compare-versions');
 
 module.exports = {
@@ -59,14 +58,14 @@ async function getPackageUpdates(config) {
   if (currentTag) {
     const tagVersion = getVersion(currentTag);
     const tagSuffix = getSuffix(currentTag);
-    if (!versions.isValidVersion(tagVersion)) {
+    if (!isValidSemver(tagVersion)) {
       logger.info(
         { currentDepTag },
         'Docker tag is not valid semver - skipping'
       );
       return upgrades.map(upgrade => ({ ...upgrade, isRange: true }));
     }
-    const currentMajor = semver.major(padRange(tagVersion));
+    const currentMajor = getMajor(tagVersion);
     const currentlyStable = isStable(tagVersion, unstablePattern);
     let versionList = [];
     const allTags = await dockerApi.getTags(dockerRegistry, config.depName);
@@ -74,7 +73,7 @@ async function getPackageUpdates(config) {
       versionList = allTags
         .filter(tag => getSuffix(tag) === tagSuffix)
         .map(getVersion)
-        .filter(versions.isValidVersion)
+        .filter(isValidSemver)
         .filter(
           version =>
             // All stable are allowed
@@ -82,8 +81,7 @@ async function getPackageUpdates(config) {
             // All unstable are allowed if we aren't ignoring them
             !ignoreUnstable ||
             // Allow unstable of same major version
-            (!currentlyStable &&
-              semver.major(padRange(version)) === currentMajor)
+            (!currentlyStable && getMajor(version) === currentMajor)
         )
         .filter(
           prefix => prefix.split('.').length === tagVersion.split('.').length
@@ -93,14 +91,8 @@ async function getPackageUpdates(config) {
     logger.trace({ versionList }, 'upgrades versionList');
     const versionUpgrades = {};
     for (const version of versionList) {
-      const paddedVersion = padRange(version);
-      const newVersionMajor = semver.major(paddedVersion);
-      let type;
-      if (newVersionMajor > currentMajor) {
-        type = 'major';
-      } else {
-        type = 'minor';
-      }
+      const newVersionMajor = getMajor(version);
+      const type = newVersionMajor > currentMajor ? 'major' : 'minor';
       let upgradeKey;
       if (
         !config.separateMajorReleases ||
@@ -125,9 +117,7 @@ async function getPackageUpdates(config) {
     logger.debug({ versionUpgrades }, 'Docker versionUpgrades');
     for (const upgradeKey of Object.keys(versionUpgrades)) {
       let newTag = versionUpgrades[upgradeKey];
-      const newVersionMajor = `${semver.major(
-        padRange(versionUpgrades[upgradeKey])
-      )}`;
+      const newVersionMajor = `${getMajor(versionUpgrades[upgradeKey])}`;
       if (tagSuffix) {
         newTag += `-${tagSuffix}`;
       }
@@ -192,7 +182,3 @@ function getSuffix(tag) {
   const split = tag.indexOf('-');
   return split > 0 ? tag.slice(split + 1) : '';
 }
-
-function padRange(range) {
-  return range + '.0'.repeat(3 - range.split('.').length);
-}
diff --git a/lib/manager/npm/package.js b/lib/manager/npm/package.js
index e9e5f2963485665ce4e822d8a78f7a6057e655d8..e6c40e62deb755c784ceeb524969c32671c7f5b4 100644
--- a/lib/manager/npm/package.js
+++ b/lib/manager/npm/package.js
@@ -1,5 +1,6 @@
 const npmApi = require('../../datasource/npm');
-const versions = require('../../workers/package/versions');
+const versions = require('./versions');
+const { isValidSemver } = require('../../util/semver');
 const nodeManager = require('../_helpers/node/package');
 
 module.exports = {
@@ -23,7 +24,7 @@ async function getPackageUpdates(config) {
     );
     return [];
   }
-  if (!versions.isValidVersion(config.currentVersion)) {
+  if (!isValidSemver(config.currentVersion)) {
     results.push({
       depName: config.depName,
       type: 'warning',
diff --git a/lib/workers/package/versions.js b/lib/manager/npm/versions.js
similarity index 86%
rename from lib/workers/package/versions.js
rename to lib/manager/npm/versions.js
index a6278934ce40e8a89358ee7a17bf64c952237ee0..36abd86d73a624eba5fd52d2117274148de719ae 100644
--- a/lib/workers/package/versions.js
+++ b/lib/manager/npm/versions.js
@@ -1,13 +1,24 @@
-const semver = require('semver');
-const stable = require('semver-stable');
 const _ = require('lodash');
-const semverUtils = require('semver-utils');
+const {
+  getMajor,
+  getMinor,
+  getPatch,
+  isGreaterThan,
+  isRange,
+  isStable,
+  isUnstable,
+  isPinnedVersion,
+  matchesSemver,
+  maxSatisfyingVersion,
+  minSatisfyingVersion,
+  parseRange,
+  parseVersion,
+  stringifyRange,
+} = require('../../util/semver');
 const moment = require('moment');
 
 module.exports = {
   determineUpgrades,
-  isRange,
-  isValidVersion,
   isPastLatest,
 };
 
@@ -31,7 +42,7 @@ function determineUpgrades(npmDep, config) {
   // filter out versions past latest
   const currentIsPastLatest = isPastLatest(
     npmDep,
-    semver.minSatisfying(versionList, currentVersion)
+    minSatisfyingVersion(versionList, currentVersion)
   );
   if (currentIsPastLatest) {
     logger.debug({ name: npmDep.name, currentVersion }, 'currentIsPastLatest');
@@ -43,9 +54,9 @@ function determineUpgrades(npmDep, config) {
       isPastLatest(npmDep, version) === false // if the version is less than or equal to latest
   );
   let rangeOperator;
-  if (config.upgradeInRange && semver.validRange(currentVersion)) {
+  if (config.upgradeInRange && isRange(currentVersion)) {
     logger.debug({ currentVersion }, 'upgradeInRange is true');
-    const parsedRange = semverUtils.parseRange(currentVersion);
+    const parsedRange = parseRange(currentVersion);
     if (parsedRange && parsedRange.length === 1) {
       const [range] = parsedRange;
       if (range.major && range.minor && range.patch) {
@@ -69,12 +80,12 @@ function determineUpgrades(npmDep, config) {
   // Check for a current range and pin it
   if (isRange(currentVersion)) {
     let newVersion;
-    if (pinVersions && lockedVersion && semver.valid(lockedVersion)) {
+    if (pinVersions && lockedVersion && isPinnedVersion(lockedVersion)) {
       newVersion = lockedVersion;
     } else {
       // Pin ranges to their maximum satisfying version
       logger.debug({ dependency }, 'currentVersion is range, not locked');
-      const maxSatisfying = semver.maxSatisfying(versionList, currentVersion);
+      const maxSatisfying = maxSatisfyingVersion(versionList, currentVersion);
       if (!maxSatisfying) {
         result.message = `No satisfying version found for existing dependency range "${currentVersion}"`;
         logger.info(
@@ -91,13 +102,13 @@ function determineUpgrades(npmDep, config) {
       type: 'pin',
       isPin: true,
       newVersion,
-      newVersionMajor: semver.major(newVersion),
+      newVersionMajor: getMajor(newVersion),
     };
     changeLogFromVersion = newVersion;
   } else if (versionList.indexOf(currentVersion) === -1 && !rangeOperator) {
     logger.debug({ dependency }, 'Cannot find currentVersion');
     try {
-      const rollbackVersion = semver.maxSatisfying(
+      const rollbackVersion = maxSatisfyingVersion(
         versionList,
         `<${currentVersion}`
       );
@@ -105,7 +116,7 @@ function determineUpgrades(npmDep, config) {
         type: 'rollback',
         isRollback: true,
         newVersion: rollbackVersion,
-        newVersionMajor: semver.major(rollbackVersion),
+        newVersionMajor: getMajor(rollbackVersion),
         semanticCommitType: 'fix',
         branchName:
           '{{{branchPrefix}}}rollback-{{{depNameSanitized}}}-{{{newVersionMajor}}}.x',
@@ -119,39 +130,39 @@ function determineUpgrades(npmDep, config) {
   }
   _(versionList)
     // Filter out older versions as we can't upgrade to those
-    .filter(version => semver.gt(version, changeLogFromVersion))
+    .filter(version => isGreaterThan(version, changeLogFromVersion))
     // fillter out non-allowed versions if preference is set
     .reject(
-      version => allowedVersions && !semver.satisfies(version, allowedVersions)
+      version => allowedVersions && !matchesSemver(version, allowedVersions)
     )
     // Ignore unstable versions, unless the current version is unstable
     .reject(
       version =>
         config.ignoreUnstable &&
-        stable.is(changeLogFromVersion) &&
-        !stable.is(version)
+        isStable(changeLogFromVersion) &&
+        isUnstable(version)
     )
     // Do not jump to a new major unstable just because the current is unstable
     .reject(
       version =>
         config.ignoreUnstable &&
-        !stable.is(version) &&
-        semver.major(version) > semver.major(changeLogFromVersion)
+        isUnstable(version) &&
+        getMajor(version) > getMajor(changeLogFromVersion)
     )
     // Loop through all possible versions
     .forEach(newVersion => {
       // Group by major versions
-      const newVersionMajor = semver.major(newVersion);
-      const newVersionMinor = semver.minor(newVersion);
+      const newVersionMajor = getMajor(newVersion);
+      const newVersionMinor = getMinor(newVersion);
       const hasPatchOnlyAutomerge =
         config.patch &&
         config.patch.automerge === true &&
         (config.minor && config.minor.automerge !== true);
       let type;
-      if (newVersionMajor > semver.major(changeLogFromVersion)) {
+      if (newVersionMajor > getMajor(changeLogFromVersion)) {
         type = 'major';
       } else if (
-        newVersionMinor === semver.minor(changeLogFromVersion) &&
+        newVersionMinor === getMinor(changeLogFromVersion) &&
         (config.separatePatchReleases || hasPatchOnlyAutomerge)
       ) {
         // Only use patch if configured to
@@ -178,7 +189,7 @@ function determineUpgrades(npmDep, config) {
       // Save this, if it's a new major version or greater than the previous greatest
       if (
         !allUpgrades[upgradeKey] ||
-        semver.gt(newVersion, allUpgrades[upgradeKey].newVersion)
+        isGreaterThan(newVersion, allUpgrades[upgradeKey].newVersion)
       ) {
         const changeLogToVersion = newVersion;
         allUpgrades[upgradeKey] = {
@@ -227,7 +238,7 @@ function determineUpgrades(npmDep, config) {
   }
 
   // Check if it's a range type we support
-  const semverParsed = semverUtils.parseRange(currentVersion);
+  const semverParsed = parseRange(currentVersion);
   // Check the "last" part, which is also the first and only if it's a simple semver
   const [lastSemver] = semverParsed.slice(-1);
   const secondLastSemver = semverParsed[semverParsed.length - 2];
@@ -255,7 +266,7 @@ function determineUpgrades(npmDep, config) {
   const rangedUpgrades = _(upgrades)
     .map(upgrade => ({ ...upgrade, ...{ isRange: true } }))
     .map(upgrade => {
-      const { major, minor } = semverUtils.parse(upgrade.newVersion);
+      const { major, minor } = parseVersion(upgrade.newVersion);
       const canReplace = config.versionStrategy !== 'widen';
       const forceReplace = config.versionStrategy === 'replace';
       const canWiden = config.versionStrategy !== 'replace';
@@ -266,7 +277,7 @@ function determineUpgrades(npmDep, config) {
         (semverParsed.length === 1 || forceReplace)
       ) {
         // Utilise that a.b is the same as ~a.b.0
-        const minSatisfying = semver.minSatisfying(
+        const minSatisfying = minSatisfyingVersion(
           versionList,
           `${major}.${minor}`
         );
@@ -278,7 +289,7 @@ function determineUpgrades(npmDep, config) {
         (semverParsed.length > 1 || forceWiden)
       ) {
         // Utilise that a.b is the same as ~a.b.0
-        const minSatisfying = semver.minSatisfying(
+        const minSatisfying = minSatisfyingVersion(
           versionList,
           `${major}.${minor}`
         );
@@ -300,7 +311,7 @@ function determineUpgrades(npmDep, config) {
         } else {
           // If version is < 1, then semver treats ^ same as ~
           const newRange = major === '0' ? `${major}.${minor}` : `${major}`;
-          const minSatisfying = semver.minSatisfying(versionList, newRange);
+          const minSatisfying = minSatisfyingVersion(versionList, newRange);
           // Add in the caret
           newVersion = `^${minSatisfying}`;
         }
@@ -312,7 +323,7 @@ function determineUpgrades(npmDep, config) {
       ) {
         // If version is < 1, then semver treats ^ same as ~
         const newRange = major === '0' ? `${major}.${minor}` : `${major}`;
-        const minSatisfying = semver.minSatisfying(versionList, newRange);
+        const minSatisfying = minSatisfyingVersion(versionList, newRange);
         // Add in the caret
         const newVersion = `${currentVersion} || ^${minSatisfying}`;
         return {
@@ -339,10 +350,10 @@ function determineUpgrades(npmDep, config) {
           newRange[newRange.length - 1].major = String(upgrade.newVersionMajor);
           newRange[newRange.length - 1].minor = String(upgrade.newVersionMinor);
           newRange[newRange.length - 1].patch = String(
-            semver.patch(upgrade.newVersion)
+            getPatch(upgrade.newVersion)
           );
         }
-        let newVersion = semverUtils.stringifyRange(newRange);
+        let newVersion = stringifyRange(newRange);
         newVersion = fixRange(newVersion, lastSemver, currentVersion);
         return { ...upgrade, newVersion };
       } else if (lastSemver.operator === '<') {
@@ -365,10 +376,10 @@ function determineUpgrades(npmDep, config) {
           newRange[newRange.length - 1].major = String(upgrade.newVersionMajor);
           newRange[newRange.length - 1].minor = String(upgrade.newVersionMinor);
           newRange[newRange.length - 1].patch = String(
-            semver.patch(upgrade.newVersion) + 1
+            getPatch(upgrade.newVersion) + 1
           );
         }
-        let newVersion = semverUtils.stringifyRange(newRange);
+        let newVersion = stringifyRange(newRange);
         newVersion = fixRange(newVersion, lastSemver, currentVersion);
         return { ...upgrade, newVersion };
       } else if (lastSemver.minor === undefined) {
@@ -380,8 +391,8 @@ function determineUpgrades(npmDep, config) {
         if (secondLastSemver && secondLastSemver.operator === '||') {
           newVersion = `${currentVersion} || ${upgrade.newVersionMajor}`;
         } else {
-          newVersion = semverUtils.stringifyRange(newRange);
-          // Fixes a bug with semver-utils
+          newVersion = stringifyRange(newRange);
+          // Fixes a bug with stringifyRange
           newVersion = newVersion.replace(/\.0/g, '');
         }
         return { ...upgrade, newVersion };
@@ -394,8 +405,8 @@ function determineUpgrades(npmDep, config) {
         if (secondLastSemver && secondLastSemver.operator === '||') {
           newVersion = `${currentVersion} || ${upgrade.newVersionMajor}.x`;
         } else {
-          newVersion = semverUtils.stringifyRange(newRange);
-          // Fixes a bug with semver-utils
+          newVersion = stringifyRange(newRange);
+          // Fixes a bug with stringifyRange
           newVersion = newVersion.replace(/\.0/g, '');
         }
         return { ...upgrade, newVersion };
@@ -443,22 +454,12 @@ function fixRange(version, lastSemver, currentVersion) {
   return newVersion;
 }
 
-function isRange(input) {
-  // Pinned versions also return true for semver.validRange
-  // We need to check first that they're not 'valid' to get only ranges
-  return semver.valid(input) === null && semver.validRange(input) !== null;
-}
-
-function isValidVersion(input) {
-  return (semver.valid(input) || semver.validRange(input)) !== null;
-}
-
 function isPastLatest(npmDep, version) {
   if (!version) {
     return false;
   }
   if (npmDep['dist-tags'] && npmDep['dist-tags'].latest) {
-    return semver.gt(version, npmDep['dist-tags'].latest);
+    return isGreaterThan(version, npmDep['dist-tags'].latest);
   }
   logger.warn(`No dist-tags.latest for ${npmDep.name}`);
   return false;
diff --git a/lib/manager/travis/package.js b/lib/manager/travis/package.js
index 48c74294c05ae6b0f6e2a43aca0cc057254c14ec..edc2c94206cfe84a5cf30072f12bc75cee33884d 100644
--- a/lib/manager/travis/package.js
+++ b/lib/manager/travis/package.js
@@ -1,6 +1,6 @@
 const { isEqual } = require('lodash');
-const semver = require('semver');
 const { getRepoReleases } = require('../../datasource/github');
+const { isPinnedVersion, maxSatisfyingVersion } = require('../../util/semver');
 
 module.exports = {
   getPackageUpdates,
@@ -34,10 +34,10 @@ async function getPackageUpdates(config) {
     .sort() // sort combined array
     .reverse() // we want to order latest to oldest
     .map(version => `${version}`); // convert to strings
-  if (config.pinVersions || semver.valid(config.currentVersion[0])) {
+  if (config.pinVersions || isPinnedVersion(config.currentVersion[0])) {
     const releases = await getRepoReleases('nodejs/node');
     newVersion = newVersion.map(version =>
-      semver.maxSatisfying(releases, version).replace(/^v/, '')
+      maxSatisfyingVersion(releases, version).replace(/^v/, '')
     );
   }
   if (isEqual([...config.currentVersion].sort(), [...newVersion].sort())) {
diff --git a/lib/util/semver.js b/lib/util/semver.js
new file mode 100644
index 0000000000000000000000000000000000000000..cacfa2bf65673dc36eb832874c234025e78ebeab
--- /dev/null
+++ b/lib/util/semver.js
@@ -0,0 +1,48 @@
+const semver = require('semver');
+const stable = require('semver-stable');
+const semverUtils = require('semver-utils');
+
+const { is: isStable } = stable;
+const isUnstable = input => !isStable(input);
+
+const { parseRange, parse: parseVersion, stringifyRange } = semverUtils;
+
+const {
+  compare: semverSort,
+  gt: isGreaterThan,
+  maxSatisfying: maxSatisfyingVersion,
+  minSatisfying: minSatisfyingVersion,
+  minor: getMinor,
+  patch: getPatch,
+  satisfies: matchesSemver,
+  valid: isPinnedVersion,
+  validRange: isValidSemver,
+} = semver;
+
+const padRange = range => range + '.0'.repeat(3 - range.split('.').length);
+
+const getMajor = input => {
+  const version = isPinnedVersion(input) ? input : padRange(input);
+  return semver.major(version);
+};
+
+const isRange = input => isValidSemver(input) && !isPinnedVersion(input);
+
+module.exports = {
+  getMajor,
+  getMinor,
+  getPatch,
+  isGreaterThan,
+  isRange,
+  isStable,
+  isUnstable,
+  isValidSemver,
+  isPinnedVersion,
+  matchesSemver,
+  maxSatisfyingVersion,
+  minSatisfyingVersion,
+  parseRange,
+  parseVersion,
+  semverSort,
+  stringifyRange,
+};
diff --git a/lib/workers/pr/changelog.js b/lib/workers/pr/changelog.js
index 112c512328d18274c0458ce470ccba5148c60e9d..34cc35e7e04e6bec8e75d6aeda620c39bd6d03ba 100644
--- a/lib/workers/pr/changelog.js
+++ b/lib/workers/pr/changelog.js
@@ -3,7 +3,7 @@ const changelog = require('changelog');
 const cacache = require('cacache/en');
 const npmRegistry = require('../../datasource/npm');
 const { addReleaseNotes } = require('./release-notes');
-const semver = require('semver');
+const { matchesSemver } = require('../../util/semver');
 
 module.exports = {
   getChangeLogJSON,
@@ -68,7 +68,7 @@ async function getChangeLogJSON(depName, fromVersion, newVersion) {
             github,
           },
           versions: Object.keys(dep.versions)
-            .filter(v => semver.satisfies(v, semverString))
+            .filter(v => matchesSemver(v, semverString))
             .map(version => ({ version, changes: [] })),
         };
         logger.debug({ res }, 'Manual res');
diff --git a/test/workers/package/__snapshots__/versions.spec.js.snap b/test/manager/npm/__snapshots__/versions.spec.js.snap
similarity index 73%
rename from test/workers/package/__snapshots__/versions.spec.js.snap
rename to test/manager/npm/__snapshots__/versions.spec.js.snap
index 97fba9d5d280d665ccdc4bb2bab98799e02bdd20..ef2d8ea89f544a0e457acdeb4a050e2231d1569b 100644
--- a/test/workers/package/__snapshots__/versions.spec.js.snap
+++ b/test/manager/npm/__snapshots__/versions.spec.js.snap
@@ -1,6 +1,6 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
-exports[`workers/package/versions .determineUpgrades(npmDep, config) disables major release separation (major) 1`] = `
+exports[`manager/npm/versions .determineUpgrades(npmDep, config) disables major release separation (major) 1`] = `
 Array [
   Object {
     "isPin": true,
@@ -22,7 +22,7 @@ Array [
 ]
 `;
 
-exports[`workers/package/versions .determineUpgrades(npmDep, config) disables major release separation (minor) 1`] = `
+exports[`manager/npm/versions .determineUpgrades(npmDep, config) disables major release separation (minor) 1`] = `
 Array [
   Object {
     "changeLogFromVersion": "1.0.0",
@@ -37,7 +37,7 @@ Array [
 ]
 `;
 
-exports[`workers/package/versions .determineUpgrades(npmDep, config) ignores pinning for ranges when other upgrade exists 1`] = `
+exports[`manager/npm/versions .determineUpgrades(npmDep, config) ignores pinning for ranges when other upgrade exists 1`] = `
 Array [
   Object {
     "isPin": true,
@@ -59,7 +59,7 @@ Array [
 ]
 `;
 
-exports[`workers/package/versions .determineUpgrades(npmDep, config) pins minor ranged versions 1`] = `
+exports[`manager/npm/versions .determineUpgrades(npmDep, config) pins minor ranged versions 1`] = `
 Array [
   Object {
     "isPin": true,
@@ -71,13 +71,13 @@ Array [
 ]
 `;
 
-exports[`workers/package/versions .determineUpgrades(npmDep, config) rejects complex range in-range updates 1`] = `Array []`;
+exports[`manager/npm/versions .determineUpgrades(npmDep, config) rejects complex range in-range updates 1`] = `Array []`;
 
-exports[`workers/package/versions .determineUpgrades(npmDep, config) rejects in-range unsupported operator 1`] = `Array []`;
+exports[`manager/npm/versions .determineUpgrades(npmDep, config) rejects in-range unsupported operator 1`] = `Array []`;
 
-exports[`workers/package/versions .determineUpgrades(npmDep, config) rejects non-fully specified in-range updates 1`] = `Array []`;
+exports[`manager/npm/versions .determineUpgrades(npmDep, config) rejects non-fully specified in-range updates 1`] = `Array []`;
 
-exports[`workers/package/versions .determineUpgrades(npmDep, config) rejects non-range in-range updates 1`] = `
+exports[`manager/npm/versions .determineUpgrades(npmDep, config) rejects non-range in-range updates 1`] = `
 Array [
   Object {
     "changeLogFromVersion": "1.0.0",
@@ -92,7 +92,7 @@ Array [
 ]
 `;
 
-exports[`workers/package/versions .determineUpgrades(npmDep, config) rejects reverse ordered less than greater than 1`] = `
+exports[`manager/npm/versions .determineUpgrades(npmDep, config) rejects reverse ordered less than greater than 1`] = `
 Array [
   Object {
     "message": "Complex semver ranges such as \\"<= 0.8.0 >= 0.5.0\\" are not yet supported so will be skipped",
@@ -101,7 +101,7 @@ Array [
 ]
 `;
 
-exports[`workers/package/versions .determineUpgrades(npmDep, config) replaces major complex ranged versions if configured 1`] = `
+exports[`manager/npm/versions .determineUpgrades(npmDep, config) replaces major complex ranged versions if configured 1`] = `
 Array [
   Object {
     "changeLogFromVersion": "2.7.0",
@@ -117,7 +117,7 @@ Array [
 ]
 `;
 
-exports[`workers/package/versions .determineUpgrades(npmDep, config) replaces minor complex ranged versions if configured 1`] = `
+exports[`manager/npm/versions .determineUpgrades(npmDep, config) replaces minor complex ranged versions if configured 1`] = `
 Array [
   Object {
     "changeLogFromVersion": "1.3.0",
@@ -133,21 +133,21 @@ Array [
 ]
 `;
 
-exports[`workers/package/versions .determineUpgrades(npmDep, config) return warning if empty versions 1`] = `
+exports[`manager/npm/versions .determineUpgrades(npmDep, config) return warning if empty versions 1`] = `
 Object {
   "message": "No versions returned from registry for this package",
   "type": "warning",
 }
 `;
 
-exports[`workers/package/versions .determineUpgrades(npmDep, config) return warning if null versions 1`] = `
+exports[`manager/npm/versions .determineUpgrades(npmDep, config) return warning if null versions 1`] = `
 Object {
   "message": "No versions returned from registry for this package",
   "type": "warning",
 }
 `;
 
-exports[`workers/package/versions .determineUpgrades(npmDep, config) returns both updates if automerging minor 1`] = `
+exports[`manager/npm/versions .determineUpgrades(npmDep, config) returns both updates if automerging minor 1`] = `
 Array [
   Object {
     "changeLogFromVersion": "0.4.4",
@@ -179,7 +179,7 @@ Array [
 ]
 `;
 
-exports[`workers/package/versions .determineUpgrades(npmDep, config) returns minor update if automerging both patch and minor 1`] = `
+exports[`manager/npm/versions .determineUpgrades(npmDep, config) returns minor update if automerging both patch and minor 1`] = `
 Array [
   Object {
     "changeLogFromVersion": "0.9.0",
@@ -204,7 +204,7 @@ Array [
 ]
 `;
 
-exports[`workers/package/versions .determineUpgrades(npmDep, config) returns minor update if separate patches not configured 1`] = `
+exports[`manager/npm/versions .determineUpgrades(npmDep, config) returns minor update if separate patches not configured 1`] = `
 Array [
   Object {
     "changeLogFromVersion": "0.9.0",
@@ -229,7 +229,7 @@ Array [
 ]
 `;
 
-exports[`workers/package/versions .determineUpgrades(npmDep, config) returns only one update if automerging major 1`] = `
+exports[`manager/npm/versions .determineUpgrades(npmDep, config) returns only one update if automerging major 1`] = `
 Array [
   Object {
     "isPin": true,
@@ -251,7 +251,7 @@ Array [
 ]
 `;
 
-exports[`workers/package/versions .determineUpgrades(npmDep, config) returns only one update if grouping 1`] = `
+exports[`manager/npm/versions .determineUpgrades(npmDep, config) returns only one update if grouping 1`] = `
 Array [
   Object {
     "isPin": true,
@@ -273,7 +273,7 @@ Array [
 ]
 `;
 
-exports[`workers/package/versions .determineUpgrades(npmDep, config) returns patch minor and major 1`] = `
+exports[`manager/npm/versions .determineUpgrades(npmDep, config) returns patch minor and major 1`] = `
 Array [
   Object {
     "changeLogFromVersion": "0.8.0",
@@ -308,7 +308,7 @@ Array [
 ]
 `;
 
-exports[`workers/package/versions .determineUpgrades(npmDep, config) returns patch update if automerging patch 1`] = `
+exports[`manager/npm/versions .determineUpgrades(npmDep, config) returns patch update if automerging patch 1`] = `
 Array [
   Object {
     "changeLogFromVersion": "0.9.0",
@@ -333,7 +333,7 @@ Array [
 ]
 `;
 
-exports[`workers/package/versions .determineUpgrades(npmDep, config) returns patch update if separatePatchReleases 1`] = `
+exports[`manager/npm/versions .determineUpgrades(npmDep, config) returns patch update if separatePatchReleases 1`] = `
 Array [
   Object {
     "changeLogFromVersion": "0.9.0",
@@ -358,7 +358,7 @@ Array [
 ]
 `;
 
-exports[`workers/package/versions .determineUpgrades(npmDep, config) returns warning if range not found 1`] = `
+exports[`manager/npm/versions .determineUpgrades(npmDep, config) returns warning if range not found 1`] = `
 Array [
   Object {
     "message": "No satisfying version found for existing dependency range \\"^8.4.0\\"",
@@ -367,7 +367,7 @@ Array [
 ]
 `;
 
-exports[`workers/package/versions .determineUpgrades(npmDep, config) should allow unstable versions if the current version is unstable 1`] = `
+exports[`manager/npm/versions .determineUpgrades(npmDep, config) should allow unstable versions if the current version is unstable 1`] = `
 Array [
   Object {
     "changeLogFromVersion": "1.0.0-beta",
@@ -382,7 +382,7 @@ Array [
 ]
 `;
 
-exports[`workers/package/versions .determineUpgrades(npmDep, config) should downgrade from missing versions 1`] = `
+exports[`manager/npm/versions .determineUpgrades(npmDep, config) should downgrade from missing versions 1`] = `
 Object {
   "branchName": "{{{branchPrefix}}}rollback-{{{depNameSanitized}}}-{{{newVersionMajor}}}.x",
   "isRollback": true,
@@ -394,7 +394,7 @@ Object {
 }
 `;
 
-exports[`workers/package/versions .determineUpgrades(npmDep, config) should treat zero zero caret ranges as pinned 1`] = `
+exports[`manager/npm/versions .determineUpgrades(npmDep, config) should treat zero zero caret ranges as pinned 1`] = `
 Array [
   Object {
     "changeLogFromVersion": "0.0.34",
@@ -410,7 +410,7 @@ Array [
 ]
 `;
 
-exports[`workers/package/versions .determineUpgrades(npmDep, config) supports > latest versions if configured 1`] = `
+exports[`manager/npm/versions .determineUpgrades(npmDep, config) supports > latest versions if configured 1`] = `
 Array [
   Object {
     "changeLogFromVersion": "1.4.1",
@@ -425,7 +425,7 @@ Array [
 ]
 `;
 
-exports[`workers/package/versions .determineUpgrades(npmDep, config) supports complex major hyphen ranges 1`] = `
+exports[`manager/npm/versions .determineUpgrades(npmDep, config) supports complex major hyphen ranges 1`] = `
 Array [
   Object {
     "changeLogFromVersion": "2.7.0",
@@ -441,7 +441,7 @@ Array [
 ]
 `;
 
-exports[`workers/package/versions .determineUpgrades(npmDep, config) supports complex major ranges 1`] = `
+exports[`manager/npm/versions .determineUpgrades(npmDep, config) supports complex major ranges 1`] = `
 Array [
   Object {
     "changeLogFromVersion": "2.7.0",
@@ -457,7 +457,7 @@ Array [
 ]
 `;
 
-exports[`workers/package/versions .determineUpgrades(npmDep, config) supports complex ranges 1`] = `
+exports[`manager/npm/versions .determineUpgrades(npmDep, config) supports complex ranges 1`] = `
 Object {
   "changeLogFromVersion": "0.8.12",
   "changeLogToVersion": "0.9.7",
@@ -471,7 +471,7 @@ Object {
 }
 `;
 
-exports[`workers/package/versions .determineUpgrades(npmDep, config) supports complex tilde ranges 1`] = `
+exports[`manager/npm/versions .determineUpgrades(npmDep, config) supports complex tilde ranges 1`] = `
 Array [
   Object {
     "changeLogFromVersion": "1.3.0",
@@ -487,7 +487,7 @@ Array [
 ]
 `;
 
-exports[`workers/package/versions .determineUpgrades(npmDep, config) supports future versions if already future 1`] = `
+exports[`manager/npm/versions .determineUpgrades(npmDep, config) supports future versions if already future 1`] = `
 Array [
   Object {
     "isPin": true,
@@ -499,7 +499,7 @@ Array [
 ]
 `;
 
-exports[`workers/package/versions .determineUpgrades(npmDep, config) supports in-range updates 1`] = `
+exports[`manager/npm/versions .determineUpgrades(npmDep, config) supports in-range updates 1`] = `
 Array [
   Object {
     "changeLogFromVersion": "1.0.0",
@@ -515,7 +515,7 @@ Array [
 ]
 `;
 
-exports[`workers/package/versions .determineUpgrades(npmDep, config) supports minor and major upgrades for ranged versions 1`] = `
+exports[`manager/npm/versions .determineUpgrades(npmDep, config) supports minor and major upgrades for ranged versions 1`] = `
 Array [
   Object {
     "changeLogFromVersion": "0.4.4",
@@ -547,7 +547,7 @@ Array [
 ]
 `;
 
-exports[`workers/package/versions .determineUpgrades(npmDep, config) supports minor and major upgrades for tilde ranges 1`] = `
+exports[`manager/npm/versions .determineUpgrades(npmDep, config) supports minor and major upgrades for tilde ranges 1`] = `
 Array [
   Object {
     "changeLogFromVersion": "0.4.4",
@@ -579,7 +579,7 @@ Array [
 ]
 `;
 
-exports[`workers/package/versions .determineUpgrades(npmDep, config) upgrades .x major ranges without pinning 1`] = `
+exports[`manager/npm/versions .determineUpgrades(npmDep, config) upgrades .x major ranges without pinning 1`] = `
 Array [
   Object {
     "changeLogFromVersion": "0.9.7",
@@ -595,7 +595,7 @@ Array [
 ]
 `;
 
-exports[`workers/package/versions .determineUpgrades(npmDep, config) upgrades .x minor ranges 1`] = `
+exports[`manager/npm/versions .determineUpgrades(npmDep, config) upgrades .x minor ranges 1`] = `
 Array [
   Object {
     "changeLogFromVersion": "1.3.0",
@@ -617,7 +617,7 @@ Array [
 ]
 `;
 
-exports[`workers/package/versions .determineUpgrades(npmDep, config) upgrades .x minor ranges without pinning 1`] = `
+exports[`manager/npm/versions .determineUpgrades(npmDep, config) upgrades .x minor ranges without pinning 1`] = `
 Array [
   Object {
     "changeLogFromVersion": "1.3.0",
@@ -633,7 +633,7 @@ Array [
 ]
 `;
 
-exports[`workers/package/versions .determineUpgrades(npmDep, config) upgrades less than equal ranges without pinning 1`] = `
+exports[`manager/npm/versions .determineUpgrades(npmDep, config) upgrades less than equal ranges without pinning 1`] = `
 Array [
   Object {
     "changeLogFromVersion": "0.7.2",
@@ -660,7 +660,7 @@ Array [
 ]
 `;
 
-exports[`workers/package/versions .determineUpgrades(npmDep, config) upgrades less than ranges without pinning 1`] = `
+exports[`manager/npm/versions .determineUpgrades(npmDep, config) upgrades less than ranges without pinning 1`] = `
 Array [
   Object {
     "changeLogFromVersion": "0.7.1",
@@ -687,7 +687,7 @@ Array [
 ]
 `;
 
-exports[`workers/package/versions .determineUpgrades(npmDep, config) upgrades major greater than less than ranges without pinning 1`] = `
+exports[`manager/npm/versions .determineUpgrades(npmDep, config) upgrades major greater than less than ranges without pinning 1`] = `
 Array [
   Object {
     "changeLogFromVersion": "0.9.7",
@@ -703,7 +703,7 @@ Array [
 ]
 `;
 
-exports[`workers/package/versions .determineUpgrades(npmDep, config) upgrades major less than equal ranges 1`] = `
+exports[`manager/npm/versions .determineUpgrades(npmDep, config) upgrades major less than equal ranges 1`] = `
 Array [
   Object {
     "changeLogFromVersion": "1.0.0",
@@ -719,7 +719,7 @@ Array [
 ]
 `;
 
-exports[`workers/package/versions .determineUpgrades(npmDep, config) upgrades major less than ranges without pinning 1`] = `
+exports[`manager/npm/versions .determineUpgrades(npmDep, config) upgrades major less than ranges without pinning 1`] = `
 Array [
   Object {
     "changeLogFromVersion": "0.9.7",
@@ -735,7 +735,7 @@ Array [
 ]
 `;
 
-exports[`workers/package/versions .determineUpgrades(npmDep, config) upgrades minor greater than less than equals ranges without pinning 1`] = `
+exports[`manager/npm/versions .determineUpgrades(npmDep, config) upgrades minor greater than less than equals ranges without pinning 1`] = `
 Array [
   Object {
     "changeLogFromVersion": "0.8.0",
@@ -762,7 +762,7 @@ Array [
 ]
 `;
 
-exports[`workers/package/versions .determineUpgrades(npmDep, config) upgrades minor greater than less than ranges without pinning 1`] = `
+exports[`manager/npm/versions .determineUpgrades(npmDep, config) upgrades minor greater than less than ranges without pinning 1`] = `
 Array [
   Object {
     "changeLogFromVersion": "0.7.2",
@@ -789,7 +789,7 @@ Array [
 ]
 `;
 
-exports[`workers/package/versions .determineUpgrades(npmDep, config) upgrades minor ranged versions 1`] = `
+exports[`manager/npm/versions .determineUpgrades(npmDep, config) upgrades minor ranged versions 1`] = `
 Array [
   Object {
     "changeLogFromVersion": "1.0.1",
@@ -811,7 +811,7 @@ Array [
 ]
 `;
 
-exports[`workers/package/versions .determineUpgrades(npmDep, config) upgrades multiple caret ranges without pinning 1`] = `
+exports[`manager/npm/versions .determineUpgrades(npmDep, config) upgrades multiple caret ranges without pinning 1`] = `
 Array [
   Object {
     "changeLogFromVersion": "0.7.2",
@@ -838,7 +838,7 @@ Array [
 ]
 `;
 
-exports[`workers/package/versions .determineUpgrades(npmDep, config) upgrades multiple tilde ranges without pinning 1`] = `
+exports[`manager/npm/versions .determineUpgrades(npmDep, config) upgrades multiple tilde ranges without pinning 1`] = `
 Array [
   Object {
     "changeLogFromVersion": "0.7.2",
@@ -865,7 +865,7 @@ Array [
 ]
 `;
 
-exports[`workers/package/versions .determineUpgrades(npmDep, config) upgrades shorthand major ranges without pinning 1`] = `
+exports[`manager/npm/versions .determineUpgrades(npmDep, config) upgrades shorthand major ranges without pinning 1`] = `
 Array [
   Object {
     "changeLogFromVersion": "0.9.7",
@@ -881,7 +881,7 @@ Array [
 ]
 `;
 
-exports[`workers/package/versions .determineUpgrades(npmDep, config) upgrades shorthand minor ranges without pinning 1`] = `
+exports[`manager/npm/versions .determineUpgrades(npmDep, config) upgrades shorthand minor ranges without pinning 1`] = `
 Array [
   Object {
     "changeLogFromVersion": "1.3.0",
@@ -897,7 +897,7 @@ Array [
 ]
 `;
 
-exports[`workers/package/versions .determineUpgrades(npmDep, config) upgrades tilde ranges 1`] = `
+exports[`manager/npm/versions .determineUpgrades(npmDep, config) upgrades tilde ranges 1`] = `
 Array [
   Object {
     "changeLogFromVersion": "1.3.0",
@@ -919,7 +919,7 @@ Array [
 ]
 `;
 
-exports[`workers/package/versions .determineUpgrades(npmDep, config) upgrades tilde ranges without pinning 1`] = `
+exports[`manager/npm/versions .determineUpgrades(npmDep, config) upgrades tilde ranges without pinning 1`] = `
 Array [
   Object {
     "changeLogFromVersion": "1.3.0",
@@ -935,7 +935,7 @@ Array [
 ]
 `;
 
-exports[`workers/package/versions .determineUpgrades(npmDep, config) uses the locked version for pinning 1`] = `
+exports[`manager/npm/versions .determineUpgrades(npmDep, config) uses the locked version for pinning 1`] = `
 Array [
   Object {
     "changeLogFromVersion": "1.0.0",
@@ -957,7 +957,7 @@ Array [
 ]
 `;
 
-exports[`workers/package/versions .determineUpgrades(npmDep, config) widens .x OR ranges 1`] = `
+exports[`manager/npm/versions .determineUpgrades(npmDep, config) widens .x OR ranges 1`] = `
 Array [
   Object {
     "changeLogFromVersion": "2.7.0",
@@ -973,7 +973,7 @@ Array [
 ]
 `;
 
-exports[`workers/package/versions .determineUpgrades(npmDep, config) widens major ranged versions if configured 1`] = `
+exports[`manager/npm/versions .determineUpgrades(npmDep, config) widens major ranged versions if configured 1`] = `
 Array [
   Object {
     "changeLogFromVersion": "2.7.0",
@@ -989,7 +989,7 @@ Array [
 ]
 `;
 
-exports[`workers/package/versions .determineUpgrades(npmDep, config) widens minor ranged versions if configured 1`] = `
+exports[`manager/npm/versions .determineUpgrades(npmDep, config) widens minor ranged versions if configured 1`] = `
 Array [
   Object {
     "changeLogFromVersion": "1.3.0",
@@ -1005,7 +1005,7 @@ Array [
 ]
 `;
 
-exports[`workers/package/versions .determineUpgrades(npmDep, config) widens stanndalone major OR ranges 1`] = `
+exports[`manager/npm/versions .determineUpgrades(npmDep, config) widens stanndalone major OR ranges 1`] = `
 Array [
   Object {
     "changeLogFromVersion": "2.7.0",
diff --git a/test/manager/npm/package.spec.js b/test/manager/npm/package.spec.js
index 79eae531bcf48749662ade748fc79343ee83cdf0..56e4976deba6f3307a29a5e4c96870399a84c10f 100644
--- a/test/manager/npm/package.spec.js
+++ b/test/manager/npm/package.spec.js
@@ -1,5 +1,5 @@
 const npmApi = require('../../../lib/datasource/npm');
-const versions = require('../../../lib/workers/package/versions');
+const versions = require('../../../lib/manager/npm/versions');
 const npm = require('../../../lib/manager/npm/package');
 const defaultConfig = require('../../../lib/config/defaults').getConfig();
 
diff --git a/test/workers/package/versions.spec.js b/test/manager/npm/versions.spec.js
similarity index 92%
rename from test/workers/package/versions.spec.js
rename to test/manager/npm/versions.spec.js
index e5ed164a7c7c7a2142f3d6d9a31a6bcb16f3d0b8..1e67a13bef463d225dec589bc237115072707a58 100644
--- a/test/workers/package/versions.spec.js
+++ b/test/manager/npm/versions.spec.js
@@ -1,4 +1,4 @@
-const versions = require('../../../lib/workers/package/versions');
+const versions = require('../../../lib/manager/npm/versions');
 const qJson = require('../../_fixtures/npm/01.json');
 const helmetJson = require('../../_fixtures/npm/02.json');
 const coffeelintJson = require('../../_fixtures/npm/coffeelint.json');
@@ -7,7 +7,7 @@ const nextJson = require('../../_fixtures/npm/next.json');
 
 let config;
 
-describe('workers/package/versions', () => {
+describe('manager/npm/versions', () => {
   beforeEach(() => {
     config = { ...require('../../../lib/config/defaults').getConfig() };
     config.pinVersions = true;
@@ -397,40 +397,6 @@ describe('workers/package/versions', () => {
       expect(versions.determineUpgrades(qJson, config)).toMatchSnapshot();
     });
   });
-  describe('.isRange(input)', () => {
-    it('rejects simple semver', () => {
-      versions.isRange('1.2.3').should.eql(false);
-    });
-    it('accepts tilde', () => {
-      versions.isRange('~1.2.3').should.eql(true);
-    });
-    it('accepts caret', () => {
-      versions.isRange('^1.2.3').should.eql(true);
-    });
-  });
-  describe('.isValidVersion(input)', () => {
-    it('should support simple semver', () => {
-      versions.isValidVersion('1.2.3').should.eql(true);
-    });
-    it('should support versions with dash', () => {
-      versions.isValidVersion('1.2.3-foo').should.eql(true);
-    });
-    it('should reject versions without dash', () => {
-      versions.isValidVersion('1.2.3foo').should.eql(false);
-    });
-    it('should support ranges', () => {
-      versions.isValidVersion('~1.2.3').should.eql(true);
-      versions.isValidVersion('^1.2.3').should.eql(true);
-      versions.isValidVersion('>1.2.3').should.eql(true);
-    });
-    it('should reject github repositories', () => {
-      versions.isValidVersion('renovateapp/renovate').should.eql(false);
-      versions.isValidVersion('renovateapp/renovate#master').should.eql(false);
-      versions
-        .isValidVersion('https://github.com/renovateapp/renovate.git')
-        .should.eql(false);
-    });
-  });
   describe('.isPastLatest(dep, version)', () => {
     it('should return false for less than', () => {
       versions.isPastLatest(qJson, '1.0.0').should.eql(false);
diff --git a/test/util/semver.spec.js b/test/util/semver.spec.js
new file mode 100644
index 0000000000000000000000000000000000000000..18bd7b70f0769a9604a78f909816f514856c9a47
--- /dev/null
+++ b/test/util/semver.spec.js
@@ -0,0 +1,36 @@
+const semver = require('../../lib/util/semver');
+
+describe('.isValidSemver(input)', () => {
+  it('should support simple semver', () => {
+    expect(!!semver.isValidSemver('1.2.3')).toBe(true);
+  });
+  it('should support semver with dash', () => {
+    expect(!!semver.isValidSemver('1.2.3-foo')).toBe(true);
+  });
+  it('should reject semver without dash', () => {
+    expect(!!semver.isValidSemver('1.2.3foo')).toBe(false);
+  });
+  it('should support ranges', () => {
+    expect(!!semver.isValidSemver('~1.2.3')).toBe(true);
+    expect(!!semver.isValidSemver('^1.2.3')).toBe(true);
+    expect(!!semver.isValidSemver('>1.2.3')).toBe(true);
+  });
+  it('should reject github repositories', () => {
+    expect(!!semver.isValidSemver('renovateapp/renovate')).toBe(false);
+    expect(!!semver.isValidSemver('renovateapp/renovate#master')).toBe(false);
+    expect(
+      !!semver.isValidSemver('https://github.com/renovateapp/renovate.git')
+    ).toBe(false);
+  });
+});
+describe('.isRange(input)', () => {
+  it('rejects simple semver', () => {
+    expect(!!semver.isRange('1.2.3')).toBe(false);
+  });
+  it('accepts tilde', () => {
+    expect(!!semver.isRange('~1.2.3')).toBe(true);
+  });
+  it('accepts caret', () => {
+    expect(!!semver.isRange('^1.2.3')).toBe(true);
+  });
+});