From 0503ad7c7ae5528a028a7c025223f0fe7df1ab0f Mon Sep 17 00:00:00 2001
From: Michael Kriese <michael.kriese@visualon.de>
Date: Tue, 8 Sep 2020 12:59:47 +0200
Subject: [PATCH] feat: add docker image prefix (#7164)

Co-authored-by: Jamie Magee <JamieMagee@users.noreply.github.com>
---
 docs/usage/self-hosted-configuration.md       |  6 +++++
 lib/config/common.ts                          |  4 +++
 lib/config/definitions.ts                     |  8 ++++++
 lib/util/exec/__snapshots__/exec.spec.ts.snap | 18 ++++++-------
 lib/util/exec/common.ts                       |  1 +
 lib/util/exec/docker/index.ts                 | 11 +++++++-
 lib/util/exec/exec.spec.ts                    | 26 ++++++++++++++++++-
 lib/util/exec/index.ts                        |  1 +
 8 files changed, 64 insertions(+), 11 deletions(-)

diff --git a/docs/usage/self-hosted-configuration.md b/docs/usage/self-hosted-configuration.md
index 8ccb4c0122..d36a3e3049 100644
--- a/docs/usage/self-hosted-configuration.md
+++ b/docs/usage/self-hosted-configuration.md
@@ -44,6 +44,12 @@ Configure this directory if you want to change which directory Renovate uses for
 
 Set to `false` to prevent usage of `--ignore-platform-reqs` in the composer package manager.
 
+## dockerImagePrefix
+
+Override the default renovate sidecar docker containers image prefix from `docker.io/renovate` to a custom value, so renovate will pull images from a custom docker registry.
+
+If this is set to `ghcr.io/renovatebot` the final image for `node` would become `ghcr.io/renovatebot/node` instead of currently used `docker.io/renovate/node`.
+
 ## dockerMapDotfiles
 
 This is used if you want to map "dotfiles" from your host computer home directory to containers that Renovate creates, e.g. for updating lock files. Currently applicable to `.npmrc` only.
diff --git a/lib/config/common.ts b/lib/config/common.ts
index 04dcebe326..dd60f538c2 100644
--- a/lib/config/common.ts
+++ b/lib/config/common.ts
@@ -73,6 +73,10 @@ export interface RenovateAdminConfig {
   baseDir?: string;
   cacheDir?: string;
   configWarningReuseIssue?: boolean;
+
+  dockerImagePrefix?: string;
+  dockerUser?: string;
+
   dryRun?: boolean;
 
   endpoint?: string;
diff --git a/lib/config/definitions.ts b/lib/config/definitions.ts
index 856324dffe..6e0338c7a8 100644
--- a/lib/config/definitions.ts
+++ b/lib/config/definitions.ts
@@ -300,6 +300,14 @@ const options: RenovateOptions[] = [
     type: 'boolean',
     default: false,
   },
+  {
+    name: 'dockerImagePrefix',
+    description:
+      'Change this value in order to override the default renovate docker sidecar image name prefix.',
+    type: 'string',
+    default: 'docker.io/renovate',
+    admin: true,
+  },
   {
     name: 'dockerUser',
     description:
diff --git a/lib/util/exec/__snapshots__/exec.spec.ts.snap b/lib/util/exec/__snapshots__/exec.spec.ts.snap
index 920b1627f2..0d0c758fed 100644
--- a/lib/util/exec/__snapshots__/exec.spec.ts.snap
+++ b/lib/util/exec/__snapshots__/exec.spec.ts.snap
@@ -5,17 +5,17 @@ Array [
   "echo hello",
   "echo hello",
   "docker ps --filter label=renovate_child -aq",
-  "docker pull example/image",
-  "docker ps --filter name=example_image -aq",
-  "docker run --rm --name=example_image --label=renovate_child example/image bash -l -c \\"echo hello\\"",
-  "docker ps --filter name=example_image -aq",
-  "docker run --rm --name=example_image --label=renovate_child example/image bash -l -c \\"echo hello\\"",
+  "docker pull renovate/image",
+  "docker ps --filter name=renovate_image -aq",
+  "docker run --rm --name=renovate_image --label=renovate_child renovate/image bash -l -c \\"echo hello\\"",
+  "docker ps --filter name=renovate_image -aq",
+  "docker run --rm --name=renovate_image --label=renovate_child renovate/image bash -l -c \\"echo hello\\"",
   "echo hello",
   "echo hello",
   "docker ps --filter label=renovate_child -aq",
-  "docker ps --filter name=example_image -aq",
-  "docker run --rm --name=example_image --label=renovate_child example/image bash -l -c \\"echo hello\\"",
-  "docker ps --filter name=example_image -aq",
-  "docker run --rm --name=example_image --label=renovate_child example/image bash -l -c \\"echo hello\\"",
+  "docker ps --filter name=renovate_image -aq",
+  "docker run --rm --name=renovate_image --label=renovate_child renovate/image bash -l -c \\"echo hello\\"",
+  "docker ps --filter name=renovate_image -aq",
+  "docker run --rm --name=renovate_image --label=renovate_child renovate/image bash -l -c \\"echo hello\\"",
 ]
 `;
diff --git a/lib/util/exec/common.ts b/lib/util/exec/common.ts
index d5b26dbc58..4ae8f395da 100644
--- a/lib/util/exec/common.ts
+++ b/lib/util/exec/common.ts
@@ -14,6 +14,7 @@ export enum BinarySource {
 
 export interface ExecConfig {
   binarySource: Opt<BinarySource>;
+  dockerImagePrefix: Opt<string>;
   dockerUser: Opt<string>;
   localDir: Opt<string>;
   cacheDir: Opt<string>;
diff --git a/lib/util/exec/docker/index.ts b/lib/util/exec/docker/index.ts
index 7731267947..228277afce 100644
--- a/lib/util/exec/docker/index.ts
+++ b/lib/util/exec/docker/index.ts
@@ -2,6 +2,7 @@ import { SYSTEM_INSUFFICIENT_MEMORY } from '../../../constants/error-messages';
 import { getPkgReleases } from '../../../datasource';
 import { logger } from '../../../logger';
 import * as versioning from '../../../versioning';
+import { ensureTrailingSlash } from '../../url';
 import {
   DockerOptions,
   ExecConfig,
@@ -180,7 +181,8 @@ export async function generateDockerCommand(
   options: DockerOptions,
   config: ExecConfig
 ): Promise<string> {
-  const { image, envVars, cwd, tagScheme, tagConstraint } = options;
+  const { envVars, cwd, tagScheme, tagConstraint } = options;
+  let image = options.image;
   const volumes = options.volumes || [];
   const preCommands = options.preCommands || [];
   const postCommands = options.postCommands || [];
@@ -208,6 +210,13 @@ export async function generateDockerCommand(
     result.push(`-w "${cwd}"`);
   }
 
+  if (config.dockerImagePrefix) {
+    image = image.replace(
+      /^renovate\//,
+      ensureTrailingSlash(config.dockerImagePrefix)
+    );
+  }
+
   let tag: string;
   if (options.tag) {
     tag = options.tag;
diff --git a/lib/util/exec/exec.spec.ts b/lib/util/exec/exec.spec.ts
index 3812580c64..969d8e2cce 100644
--- a/lib/util/exec/exec.spec.ts
+++ b/lib/util/exec/exec.spec.ts
@@ -56,7 +56,7 @@ describe(`Child process execution wrapper`, () => {
     global.trustLevel = trustLevelOrig;
   });
 
-  const image = 'example/image';
+  const image = 'renovate/image';
   const name = image.replace(/\//g, '_');
   const tag = '1.2.3';
   const inCmd = 'echo hello';
@@ -331,6 +331,30 @@ describe(`Child process execution wrapper`, () => {
       },
     ],
 
+    [
+      'Docker image prefix',
+      {
+        execConfig: {
+          ...execConfig,
+          binarySource: BinarySource.Docker,
+          dockerImagePrefix: 'ghcr.io/renovatebot',
+        },
+        processEnv,
+        inCmd,
+        inOpts: { docker },
+        outCmd: [
+          `docker pull ghcr.io/renovatebot/image`,
+          dockerRemoveCmd,
+          `docker run --rm --name=${name} --label=renovate_child ${defaultVolumes} -w "${cwd}" ghcr.io/renovatebot/image bash -l -c "${inCmd}"`,
+        ],
+        outOpts: [
+          dockerPullOpts,
+          dockerRemoveOpts,
+          { cwd, encoding, env: envMock.basic, timeout: 900000 },
+        ],
+      },
+    ],
+
     [
       'Docker extra commands',
       {
diff --git a/lib/util/exec/index.ts b/lib/util/exec/index.ts
index 7f94d0b560..36ea72c407 100644
--- a/lib/util/exec/index.ts
+++ b/lib/util/exec/index.ts
@@ -20,6 +20,7 @@ import { getChildProcessEnv } from './env';
 
 const execConfig: ExecConfig = {
   binarySource: null,
+  dockerImagePrefix: null,
   dockerUser: null,
   localDir: null,
   cacheDir: null,
-- 
GitLab