From 10ca7b3efebd28be119abf50be4a59b103deea7f Mon Sep 17 00:00:00 2001
From: Michael Kriese <michael.kriese@visualon.de>
Date: Wed, 16 Jun 2021 16:29:33 +0200
Subject: [PATCH] fix(datasource): use greatest version for docker labels
 (#10464)

Co-authored-by: Rhys Arkins <rhys@arkins.net>
---
 .../docker/__snapshots__/index.spec.ts.snap   | 25 ++++++++++++++++---
 lib/datasource/docker/index.spec.ts           | 18 ++++++++++---
 lib/datasource/docker/index.ts                | 17 ++++++++++---
 3 files changed, 50 insertions(+), 10 deletions(-)

diff --git a/lib/datasource/docker/__snapshots__/index.spec.ts.snap b/lib/datasource/docker/__snapshots__/index.spec.ts.snap
index be528e2811..21c6a14bd9 100644
--- a/lib/datasource/docker/__snapshots__/index.spec.ts.snap
+++ b/lib/datasource/docker/__snapshots__/index.spec.ts.snap
@@ -773,7 +773,26 @@ Array [
 exports[`datasource/docker/index getReleases supports labels 1`] = `
 Object {
   "registryUrl": "https://index.docker.io",
-  "releases": Array [],
+  "releases": Array [
+    Object {
+      "version": "1.0.0",
+    },
+    Object {
+      "version": "1.2.3-alpine",
+    },
+    Object {
+      "version": "1.2.3",
+    },
+    Object {
+      "version": "1-alpine",
+    },
+    Object {
+      "version": "2.0.0",
+    },
+    Object {
+      "version": "2-alpine",
+    },
+  ],
   "sourceUrl": "https://github.com/renovatebot/renovate",
 }
 `;
@@ -820,7 +839,7 @@ Array [
       "user-agent": "https://github.com/renovatebot/renovate",
     },
     "method": "GET",
-    "url": "https://registry.company.com/v2/node/manifests/latest",
+    "url": "https://registry.company.com/v2/node/manifests/2-alpine",
   },
   Object {
     "headers": Object {
@@ -895,7 +914,7 @@ Array [
       "user-agent": "https://github.com/renovatebot/renovate",
     },
     "method": "GET",
-    "url": "https://registry.company.com/v2/node/manifests/latest",
+    "url": "https://registry.company.com/v2/node/manifests/abc",
   },
   Object {
     "headers": Object {
diff --git a/lib/datasource/docker/index.spec.ts b/lib/datasource/docker/index.spec.ts
index 99b397c594..7be603c7cb 100644
--- a/lib/datasource/docker/index.spec.ts
+++ b/lib/datasource/docker/index.spec.ts
@@ -623,8 +623,18 @@ describe(getName(), () => {
         .times(3)
         .reply(200)
         .get('/node/tags/list?n=10000')
-        .reply(200, { tags: ['latest'] })
-        .get('/node/manifests/latest')
+        .reply(200, {
+          tags: [
+            '2.0.0',
+            '2-alpine',
+            '1-alpine',
+            '1.0.0',
+            '1.2.3',
+            '1.2.3-alpine',
+            'abc',
+          ],
+        })
+        .get('/node/manifests/2-alpine')
         .reply(200, {
           schemaVersion: 2,
           mediaType: MediaType.manifestV2,
@@ -655,8 +665,8 @@ describe(getName(), () => {
         .times(4)
         .reply(200)
         .get('/node/tags/list?n=10000')
-        .reply(200, { tags: ['latest'] })
-        .get('/node/manifests/latest')
+        .reply(200, { tags: ['abc'] })
+        .get('/node/manifests/abc')
         .reply(200, {
           schemaVersion: 2,
           mediaType: MediaType.manifestListV2,
diff --git a/lib/datasource/docker/index.ts b/lib/datasource/docker/index.ts
index b4aec34b47..843f058ece 100644
--- a/lib/datasource/docker/index.ts
+++ b/lib/datasource/docker/index.ts
@@ -3,7 +3,10 @@ import parseLinkHeader from 'parse-link-header';
 import { logger } from '../../logger';
 import { ExternalHostError } from '../../types/errors/external-host-error';
 import * as packageCache from '../../util/cache/package';
-import * as dockerVersioning from '../../versioning/docker';
+import {
+  api as dockerVersioning,
+  id as dockerVersioningId,
+} from '../../versioning/docker';
 import type { GetReleasesConfig, ReleaseResult } from '../types';
 import {
   defaultRegistryUrls,
@@ -24,7 +27,7 @@ import { getTagsQuayRegistry } from './quay';
 export { id };
 export const customRegistrySupport = true;
 export { defaultRegistryUrls };
-export const defaultVersioning = dockerVersioning.id;
+export const defaultVersioning = dockerVersioningId;
 export const registryStrategy = 'first';
 
 export const defaultConfig = {
@@ -142,6 +145,14 @@ async function getTags(
   }
 }
 
+function findLatestStable(tags: string[]): string {
+  const versions = tags
+    .filter((v) => dockerVersioning.isValid(v) && dockerVersioning.isStable(v))
+    .sort((a, b) => dockerVersioning.sortVersions(a, b));
+
+  return versions.pop() ?? tags.slice(-1).pop();
+}
+
 /**
  * docker.getDigest
  *
@@ -228,7 +239,7 @@ export async function getReleases({
     releases,
   };
 
-  const latestTag = tags.includes('latest') ? 'latest' : tags[tags.length - 1];
+  const latestTag = tags.includes('latest') ? 'latest' : findLatestStable(tags);
   const labels = await getLabels(registryHost, dockerRepository, latestTag);
   if (labels && 'org.opencontainers.image.source' in labels) {
     ret.sourceUrl = labels['org.opencontainers.image.source'];
-- 
GitLab