From 4a19d57c6bb66139fb7f678366fe031b36d3a16b Mon Sep 17 00:00:00 2001
From: Rhys Arkins <rhys@arkins.net>
Date: Wed, 26 Dec 2018 09:36:24 +0200
Subject: [PATCH] feat: datasource versions filter (#3001)

* add filter and sort to datasource
* remove filter and sort from individual datasources
---
 lib/datasource/docker/index.js    |  4 +---
 lib/datasource/github/index.js    |  5 +----
 lib/datasource/index.js           | 24 +++++++++++++++++++++++-
 lib/datasource/npm/index.js       |  6 +-----
 lib/datasource/nuget/index.js     |  6 +-----
 lib/datasource/orb/index.js       |  3 ---
 lib/datasource/packagist/index.js | 28 ++++++++++++----------------
 lib/datasource/pypi/index.js      |  6 ++----
 lib/datasource/terraform/index.js | 10 +++-------
 test/datasource/packagist.spec.js |  1 +
 10 files changed, 45 insertions(+), 48 deletions(-)

diff --git a/lib/datasource/docker/index.js b/lib/datasource/docker/index.js
index c0a237c2c7..f8a5617b73 100644
--- a/lib/datasource/docker/index.js
+++ b/lib/datasource/docker/index.js
@@ -4,7 +4,6 @@ const URL = require('url');
 const is = require('@sindresorhus/is');
 const parseLinkHeader = require('parse-link-header');
 const wwwAuthenticate = require('www-authenticate');
-const { isVersion, sortVersions } = require('../../versioning/docker');
 const hostRules = require('../../util/host-rules');
 
 module.exports = {
@@ -314,8 +313,7 @@ async function getPkgReleases(purl, config = {}) {
   if (!tags) {
     return null;
   }
-  const sortedTags = tags.filter(isVersion).sort(sortVersions);
-  const releases = sortedTags.map(version => ({ version }));
+  const releases = tags.map(version => ({ version }));
   const ret = { releases };
   return ret;
 }
diff --git a/lib/datasource/github/index.js b/lib/datasource/github/index.js
index dc62649666..37495da76f 100644
--- a/lib/datasource/github/index.js
+++ b/lib/datasource/github/index.js
@@ -87,7 +87,6 @@ async function getDigest(config) {
  *
  * This function will:
  *  - Fetch all tags or releases (depending on configuration)
- *  - Filter for only tags that meet the version scheme definition of a version
  *  - Sanitize the versions if desired (e.g. strip out leading 'v')
  *  - Return a dependency object containing sourceUrl string and releases array
  */
@@ -130,9 +129,7 @@ async function getPkgReleases(purl, config) {
     return null;
   }
   // Filter by version scheme
-  const { isVersion, sortVersions } = versioning.get(versionScheme);
-  // Return a sorted list of valid Versions
-  versions = versions.filter(isVersion).sort(sortVersions);
+  const { isVersion } = versioning.get(versionScheme);
   const dependency = {
     sourceUrl: 'https://github.com/' + repo,
   };
diff --git a/lib/datasource/index.js b/lib/datasource/index.js
index 8704697a5a..f0797c9bda 100644
--- a/lib/datasource/index.js
+++ b/lib/datasource/index.js
@@ -1,4 +1,5 @@
 const { parse } = require('../util/purl');
+const versioning = require('../versioning');
 
 const orb = require('./orb');
 const docker = require('./docker');
@@ -30,7 +31,28 @@ const datasources = {
 
 const cacheNamespace = 'datasource-releases';
 
-function getPkgReleases(purlStr, config) {
+async function getPkgReleases(purlStr, config) {
+  const res = await getRawReleases(purlStr, config);
+  if (!res) {
+    return res;
+  }
+  const versionScheme =
+    config && config.versionScheme ? config.versionScheme : 'semver';
+  // Filter by version scheme
+  const { isVersion, sortVersions } = versioning.get(versionScheme);
+  // Return a sorted list of valid Versions
+  function sortReleases(release1, release2) {
+    return sortVersions(release1.version, release2.version);
+  }
+  if (res.releases) {
+    res.releases = res.releases
+      .filter(release => isVersion(release.version))
+      .sort(sortReleases);
+  }
+  return res;
+}
+
+function getRawReleases(purlStr, config) {
   const cacheKey = cacheNamespace + purlStr;
   // The repoCache is initialized for each repo
   // By returning a Promise and reusing it, we should only fetch each package at most once
diff --git a/lib/datasource/npm/index.js b/lib/datasource/npm/index.js
index 3411d0ab4b..424feff12b 100644
--- a/lib/datasource/npm/index.js
+++ b/lib/datasource/npm/index.js
@@ -8,7 +8,6 @@ const getRegistryUrl = require('registry-auth-token/registry-url');
 const registryAuthToken = require('registry-auth-token');
 const parse = require('github-url-from-git');
 const { isBase64 } = require('validator');
-const { isVersion, sortVersions } = require('../../versioning/semver');
 const hostRules = require('../../util/host-rules');
 
 module.exports = {
@@ -322,10 +321,7 @@ async function getDependency(name, maxRetries = 5) {
       }\`\n\nMarking the latest version of an npm package as deprecated results in the entire package being considered deprecated, so contact the package author you think this is a mistake.`;
       dep.deprecationSource = 'npm';
     }
-    const versions = Object.keys(res.versions)
-      .filter(isVersion)
-      .sort(sortVersions);
-    dep.releases = versions.map(version => {
+    dep.releases = Object.keys(res.versions).map(version => {
       const release = {
         version,
         gitRef: res.versions[version].gitHead,
diff --git a/lib/datasource/nuget/index.js b/lib/datasource/nuget/index.js
index b368f2e18a..4db719b232 100644
--- a/lib/datasource/nuget/index.js
+++ b/lib/datasource/nuget/index.js
@@ -1,7 +1,6 @@
 const parse = require('github-url-from-git');
 const got = require('got');
 const xmlParser = require('fast-xml-parser');
-const { isVersion, sortVersions } = require('../../versioning/semver');
 
 module.exports = {
   getPkgReleases,
@@ -19,10 +18,7 @@ async function getPkgReleases(purl) {
     const dep = {
       name,
     };
-    dep.releases = (res.versions || [])
-      .filter(isVersion)
-      .sort(sortVersions)
-      .map(version => ({ version }));
+    dep.releases = (res.versions || []).map(version => ({ version }));
     // look up nuspec for latest release to get repository
     const url = `https://api.nuget.org/v3-flatcontainer/${name.toLowerCase()}/${res.versions.pop()}/${name.toLowerCase()}.nuspec`;
     try {
diff --git a/lib/datasource/orb/index.js b/lib/datasource/orb/index.js
index 1d07eaa301..6f7f6b22f8 100644
--- a/lib/datasource/orb/index.js
+++ b/lib/datasource/orb/index.js
@@ -1,5 +1,4 @@
 const got = require('got');
-const { isVersion, sortVersions } = require('../../versioning/semver');
 
 module.exports = {
   getPkgReleases,
@@ -43,8 +42,6 @@ async function getPkgReleases(purl) {
     dep.homepage =
       dep.homepage || `https://circleci.com/orbs/registry/orb/${dependency}`;
     dep.releases = res.versions.map(v => v.version);
-    dep.releases = dep.releases.filter(v => isVersion(v));
-    dep.releases = dep.releases.sort(sortVersions);
     dep.releases = dep.releases.map(version => ({
       version,
     }));
diff --git a/lib/datasource/packagist/index.js b/lib/datasource/packagist/index.js
index 9d9bc9626e..0dc04609c5 100644
--- a/lib/datasource/packagist/index.js
+++ b/lib/datasource/packagist/index.js
@@ -4,7 +4,6 @@ const delay = require('delay');
 const got = require('got');
 const parse = require('github-url-from-git');
 const pAll = require('p-all');
-const { isVersion, sortVersions } = require('../../versioning/semver-composer');
 const hostRules = require('../../util/host-rules');
 
 module.exports = {
@@ -103,21 +102,18 @@ function extractDepReleases(versions) {
     dep.releases = [];
     return dep;
   }
-  dep.releases = Object.keys(versions)
-    .filter(isVersion)
-    .sort(sortVersions)
-    .map(version => {
-      const release = versions[version];
-      dep.homepage = release.homepage || dep.homepage;
-      if (release.source && release.source.url) {
-        dep.sourceUrl = parse(release.source.url) || release.source.url;
-      }
-      return {
-        version: version.replace(/^v/, ''),
-        gitRef: version,
-        releaseTimestamp: release.time,
-      };
-    });
+  dep.releases = Object.keys(versions).map(version => {
+    const release = versions[version];
+    dep.homepage = release.homepage || dep.homepage;
+    if (release.source && release.source.url) {
+      dep.sourceUrl = parse(release.source.url) || release.source.url;
+    }
+    return {
+      version: version.replace(/^v/, ''),
+      gitRef: version,
+      releaseTimestamp: release.time,
+    };
+  });
   return dep;
 }
 
diff --git a/lib/datasource/pypi/index.js b/lib/datasource/pypi/index.js
index a3326b33c3..dde5da30ec 100644
--- a/lib/datasource/pypi/index.js
+++ b/lib/datasource/pypi/index.js
@@ -1,7 +1,7 @@
 const got = require('got');
 const url = require('url');
 const is = require('@sindresorhus/is');
-const { isVersion, sortVersions, matches } = require('../../versioning/pep440');
+const { matches } = require('../../versioning/pep440');
 
 module.exports = {
   getPkgReleases,
@@ -12,9 +12,7 @@ function normalizeName(input) {
 }
 
 function compatibleVersions(releases, compatibility) {
-  const versions = Object.keys(releases)
-    .filter(isVersion)
-    .sort(sortVersions);
+  const versions = Object.keys(releases);
   if (!(compatibility && compatibility.python)) {
     return versions;
   }
diff --git a/lib/datasource/terraform/index.js b/lib/datasource/terraform/index.js
index c82863f00b..f9587a821a 100644
--- a/lib/datasource/terraform/index.js
+++ b/lib/datasource/terraform/index.js
@@ -1,6 +1,5 @@
 const got = require('got');
 const parse = require('github-url-from-git');
-const { isVersion, sortVersions } = require('../../versioning/semver');
 
 module.exports = {
   getPkgReleases,
@@ -43,12 +42,9 @@ async function getPkgReleases(purl) {
     if (res.source) {
       dep.sourceUrl = parse(res.source);
     }
-    dep.releases = res.versions
-      .filter(v => isVersion(v))
-      .sort(sortVersions)
-      .map(version => ({
-        version,
-      }));
+    dep.releases = res.versions.map(version => ({
+      version,
+    }));
     if (pkgUrl.startsWith('https://registry.terraform.io/')) {
       dep.homepage = `https://registry.terraform.io/modules/${dependency}`;
     }
diff --git a/test/datasource/packagist.spec.js b/test/datasource/packagist.spec.js
index c9a516b5f6..4136104666 100644
--- a/test/datasource/packagist.spec.js
+++ b/test/datasource/packagist.spec.js
@@ -20,6 +20,7 @@ describe('datasource/packagist', () => {
       hostRules.find = jest.fn(input => input);
       global.repoCache = {};
       config = {
+        versionScheme: 'semver-composer',
         registryUrls: [
           {
             type: 'composer',
-- 
GitLab