From 8b6d28074187a6ccadb5f5aed6e37264aabebe72 Mon Sep 17 00:00:00 2001
From: Sergio Zharinov <zharinov@users.noreply.github.com>
Date: Wed, 22 Jan 2020 14:45:21 +0400
Subject: [PATCH] feat(exec): Allow for extra docker commands (#5208)

---
 lib/util/exec/common.ts       |  7 ++++++-
 lib/util/exec/docker/index.ts | 24 +++++++++++++++++++++---
 lib/util/exec/index.ts        |  6 ++----
 test/util/exec.spec.ts        | 26 +++++++++++++++++++++++++-
 4 files changed, 54 insertions(+), 9 deletions(-)

diff --git a/lib/util/exec/common.ts b/lib/util/exec/common.ts
index 3c8464e14d..c229cf62dd 100644
--- a/lib/util/exec/common.ts
+++ b/lib/util/exec/common.ts
@@ -20,7 +20,10 @@ export interface ExecConfig {
 }
 
 export type VolumesPair = [string, string];
-export type VolumeOption = string | VolumesPair | null | undefined;
+export type VolumeOption = Opt<string | VolumesPair>;
+
+export type DockerExtraCommand = Opt<string>;
+export type DockerExtraCommands = Opt<DockerExtraCommand[]>;
 
 export interface DockerOptions {
   image: string;
@@ -28,6 +31,8 @@ export interface DockerOptions {
   volumes?: Opt<VolumeOption[]>;
   envVars?: Opt<Opt<string>[]>;
   cwd?: Opt<string>;
+  preCommands?: DockerExtraCommands;
+  postCommands?: DockerExtraCommands;
 }
 
 export interface RawExecOptions extends ChildProcessExecOptions {
diff --git a/lib/util/exec/docker/index.ts b/lib/util/exec/docker/index.ts
index 2aa4b53155..94e4d9f5e9 100644
--- a/lib/util/exec/docker/index.ts
+++ b/lib/util/exec/docker/index.ts
@@ -3,6 +3,7 @@ import {
   VolumesPair,
   DockerOptions,
   ExecConfig,
+  DockerExtraCommands,
   rawExec,
 } from '../common';
 import { logger } from '../../../logger';
@@ -56,12 +57,24 @@ function prepareVolumes(volumes: VolumeOption[] = []): string[] {
   });
 }
 
+function prepareCommands(commands: DockerExtraCommands = []): string[] {
+  return commands.filter(command => command && typeof command === 'string');
+}
+
 export async function generateDockerCommand(
-  cmd: string,
+  commands: string[],
   options: DockerOptions,
   config: ExecConfig
 ): Promise<string> {
-  const { image, tag, envVars, cwd, volumes = [] } = options;
+  const {
+    image,
+    tag,
+    envVars,
+    cwd,
+    volumes = [],
+    preCommands,
+    postCommands,
+  } = options;
   const { localDir, cacheDir, dockerUser } = config;
 
   const result = ['docker run --rm'];
@@ -83,7 +96,12 @@ export async function generateDockerCommand(
   await prefetchDockerImage(taggedImage);
   result.push(taggedImage);
 
-  result.push(cmd);
+  const bashCommand = [
+    ...prepareCommands(preCommands),
+    ...commands,
+    ...prepareCommands(postCommands),
+  ].join(' && ');
+  result.push(`bash -l -c "${bashCommand.replace(/"/g, '\\"')}"`);
 
   return result.join(' ');
 }
diff --git a/lib/util/exec/index.ts b/lib/util/exec/index.ts
index 578e4a326d..a47b6e9323 100644
--- a/lib/util/exec/index.ts
+++ b/lib/util/exec/index.ts
@@ -103,10 +103,8 @@ export async function exec(
       envVars: dockerEnvVars(extraEnv, childEnv),
     };
 
-    let dockerCommand = commands.join(' && ');
-    dockerCommand = `bash -l -c "${dockerCommand.replace(/"/g, '\\"')}"`;
-    dockerCommand = await generateDockerCommand(
-      dockerCommand,
+    const dockerCommand = await generateDockerCommand(
+      commands,
       dockerOptions,
       execConfig
     );
diff --git a/test/util/exec.spec.ts b/test/util/exec.spec.ts
index e182903c8d..453f819358 100644
--- a/test/util/exec.spec.ts
+++ b/test/util/exec.spec.ts
@@ -295,7 +295,7 @@ describe(`Child process execution wrapper`, () => {
         },
         processEnv,
         inCmd,
-        inOpts: { docker: { image } },
+        inOpts: { docker },
         outCmd: [
           dockerPullCmd,
           `docker run --rm --user=foobar ${defaultVolumes} -w "${cwd}" ${image} bash -l -c "${inCmd}"`,
@@ -303,6 +303,30 @@ describe(`Child process execution wrapper`, () => {
         outOpts: [dockerPullOpts, { cwd, encoding, env: envMock.basic }],
       },
     ],
+
+    [
+      'Docker extra commands',
+      {
+        execConfig: {
+          ...execConfig,
+          binarySource: BinarySource.Docker,
+        },
+        processEnv,
+        inCmd,
+        inOpts: {
+          docker: {
+            image,
+            preCommands: ['preCommand1', 'preCommand2', null],
+            postCommands: ['postCommand1', undefined, 'postCommand2'],
+          },
+        },
+        outCmd: [
+          dockerPullCmd,
+          `docker run --rm ${defaultVolumes} -w "${cwd}" ${image} bash -l -c "preCommand1 && preCommand2 && ${inCmd} && postCommand1 && postCommand2"`,
+        ],
+        outOpts: [dockerPullOpts, { cwd, encoding, env: envMock.basic }],
+      },
+    ],
   ];
 
   test.each(testInputs)('%s', async (_msg, testOpts) => {
-- 
GitLab