From 0887c0cdfc2aa8be383b046f81a30a0b3feac759 Mon Sep 17 00:00:00 2001
From: Rhys Arkins <rhys@arkins.net>
Date: Sun, 18 Jun 2023 12:13:32 +0200
Subject: [PATCH] fix: use install-tool for all (#21506)

---
 lib/modules/manager/gradle-wrapper/utils.ts   |  2 +-
 .../manager/pip-compile/artifacts.spec.ts     | 26 +++++++++++++++----
 lib/modules/manager/pip-compile/artifacts.ts  |  7 ++---
 .../pip_requirements/artifacts.spec.ts        | 17 +++++++++---
 .../manager/pip_requirements/artifacts.ts     |  2 +-
 .../__snapshots__/artifacts.spec.ts.snap      |  8 +++---
 lib/modules/manager/pipenv/artifacts.spec.ts  | 22 ++++++++++++++--
 lib/modules/manager/pipenv/artifacts.ts       |  6 +++--
 lib/util/exec/containerbase.ts                |  2 +-
 9 files changed, 70 insertions(+), 22 deletions(-)

diff --git a/lib/modules/manager/gradle-wrapper/utils.ts b/lib/modules/manager/gradle-wrapper/utils.ts
index b4322f3fcc..b0edcccdc0 100644
--- a/lib/modules/manager/gradle-wrapper/utils.ts
+++ b/lib/modules/manager/gradle-wrapper/utils.ts
@@ -60,7 +60,7 @@ export async function prepareGradleCommand(
  */
 export function getJavaConstraint(
   gradleVersion: string | null | undefined
-): string | null {
+): string {
   const major = gradleVersion ? gradleVersioning.getMajor(gradleVersion) : null;
   const minor = gradleVersion ? gradleVersioning.getMinor(gradleVersion) : null;
   if (major && (major > 7 || (major >= 7 && minor && minor >= 3))) {
diff --git a/lib/modules/manager/pip-compile/artifacts.spec.ts b/lib/modules/manager/pip-compile/artifacts.spec.ts
index 94c8463f29..b410cbce2a 100644
--- a/lib/modules/manager/pip-compile/artifacts.spec.ts
+++ b/lib/modules/manager/pip-compile/artifacts.spec.ts
@@ -1,21 +1,25 @@
 import { join } from 'upath';
 import { envMock, mockExecAll } from '../../../../test/exec-util';
 import { Fixtures } from '../../../../test/fixtures';
-import { env, fs, git, partial } from '../../../../test/util';
+import { env, fs, git, mocked, partial } from '../../../../test/util';
 import { GlobalConfig } from '../../../config/global';
 import type { RepoGlobalConfig } from '../../../config/types';
 import { logger } from '../../../logger';
 import * as docker from '../../../util/exec/docker';
 import type { StatusResult } from '../../../util/git/types';
+import * as _datasource from '../../datasource';
 import type { UpdateArtifactsConfig } from '../types';
 import { constructPipCompileCmd, extractResolver } from './artifacts';
 import { updateArtifacts } from '.';
 
+const datasource = mocked(_datasource);
+
 jest.mock('../../../util/exec/env');
 jest.mock('../../../util/fs');
 jest.mock('../../../util/git');
 jest.mock('../../../util/host-rules');
 jest.mock('../../../util/http');
+jest.mock('../../datasource');
 
 const adminConfig: RepoGlobalConfig = {
   // `join` fixes Windows CI
@@ -96,6 +100,10 @@ describe('modules/manager/pip-compile/artifacts', () => {
 
   it('supports docker mode', async () => {
     GlobalConfig.set(dockerAdminConfig);
+    // pip-tools
+    datasource.getPkgReleases.mockResolvedValueOnce({
+      releases: [{ version: '6.13.0' }],
+    });
     const execSnapshots = mockExecAll();
     git.getRepoStatus.mockResolvedValue(
       partial<StatusResult>({
@@ -129,7 +137,7 @@ describe('modules/manager/pip-compile/artifacts', () => {
           'bash -l -c "' +
           'install-tool python 3.10.2 ' +
           '&& ' +
-          'pip install --user pip-tools ' +
+          'install-tool pip-tools 6.13.0 ' +
           '&& ' +
           'pip-compile requirements.in' +
           '"',
@@ -139,6 +147,10 @@ describe('modules/manager/pip-compile/artifacts', () => {
 
   it('supports install mode', async () => {
     GlobalConfig.set({ ...adminConfig, binarySource: 'install' });
+    // pip-tools
+    datasource.getPkgReleases.mockResolvedValueOnce({
+      releases: [{ version: '6.13.0' }],
+    });
     const execSnapshots = mockExecAll();
     git.getRepoStatus.mockResolvedValue(
       partial<StatusResult>({
@@ -157,7 +169,7 @@ describe('modules/manager/pip-compile/artifacts', () => {
 
     expect(execSnapshots).toMatchObject([
       { cmd: 'install-tool python 3.10.2' },
-      { cmd: 'pip install --user pip-tools' },
+      { cmd: 'install-tool pip-tools 6.13.0' },
       {
         cmd: 'pip-compile requirements.in',
         options: { cwd: '/tmp/github/some/repo' },
@@ -210,6 +222,10 @@ describe('modules/manager/pip-compile/artifacts', () => {
 
   it('uses pip-compile version from config', async () => {
     GlobalConfig.set(dockerAdminConfig);
+    // pip-tools
+    datasource.getPkgReleases.mockResolvedValueOnce({
+      releases: [{ version: '6.13.0' }],
+    });
     const execSnapshots = mockExecAll();
     git.getRepoStatus.mockResolvedValue(
       partial<StatusResult>({
@@ -225,7 +241,7 @@ describe('modules/manager/pip-compile/artifacts', () => {
         newPackageFileContent: 'some new content',
         config: {
           ...config,
-          constraints: { python: '3.10.2', pipTools: '==1.2.3' },
+          constraints: { python: '3.10.2', pipTools: '6.13.0' },
         },
       })
     ).not.toBeNull();
@@ -246,7 +262,7 @@ describe('modules/manager/pip-compile/artifacts', () => {
           'bash -l -c "' +
           'install-tool python 3.10.2 ' +
           '&& ' +
-          'pip install --user pip-tools==1.2.3 ' +
+          'install-tool pip-tools 6.13.0 ' +
           '&& ' +
           'pip-compile requirements.in' +
           '"',
diff --git a/lib/modules/manager/pip-compile/artifacts.ts b/lib/modules/manager/pip-compile/artifacts.ts
index 495cf342c9..66d1c2d257 100644
--- a/lib/modules/manager/pip-compile/artifacts.ts
+++ b/lib/modules/manager/pip-compile/artifacts.ts
@@ -126,14 +126,15 @@ export async function updateArtifacts({
     const execOptions: ExecOptions = {
       cwdFile: inputFileName,
       docker: {},
-      preCommands: [
-        `pip install --user ${quote(`pip-tools${pipToolsConstraint}`)}`,
-      ],
       toolConstraints: [
         {
           toolName: 'python',
           constraint,
         },
+        {
+          toolName: 'pip-tools',
+          constraint: pipToolsConstraint,
+        },
       ],
       extraEnv: {
         PIP_CACHE_DIR: await ensureCacheDir('pip'),
diff --git a/lib/modules/manager/pip_requirements/artifacts.spec.ts b/lib/modules/manager/pip_requirements/artifacts.spec.ts
index 69e2152cea..adaeacf6d3 100644
--- a/lib/modules/manager/pip_requirements/artifacts.spec.ts
+++ b/lib/modules/manager/pip_requirements/artifacts.spec.ts
@@ -1,11 +1,14 @@
 import { join } from 'upath';
 import { mockExecAll } from '../../../../test/exec-util';
-import { fs } from '../../../../test/util';
+import { fs, mocked } from '../../../../test/util';
 import { GlobalConfig } from '../../../config/global';
 import type { RepoGlobalConfig } from '../../../config/types';
+import * as _datasource from '../../datasource';
 import type { UpdateArtifactsConfig } from '../types';
 import { updateArtifacts } from '.';
 
+const datasource = mocked(_datasource);
+
 jest.mock('../../../util/exec/common');
 jest.mock('../../../util/fs');
 jest.mock('../../datasource');
@@ -187,6 +190,10 @@ describe('modules/manager/pip_requirements/artifacts', () => {
     GlobalConfig.set({ ...adminConfig, binarySource: 'docker' });
     fs.readLocalFile.mockResolvedValueOnce('new content');
     fs.ensureCacheDir.mockResolvedValueOnce('/tmp/cache');
+    // hashin
+    datasource.getPkgReleases.mockResolvedValueOnce({
+      releases: [{ version: '0.1.7' }],
+    });
     const execSnapshots = mockExecAll();
 
     expect(
@@ -222,7 +229,7 @@ describe('modules/manager/pip_requirements/artifacts', () => {
           'bash -l -c "' +
           'install-tool python 3.10.2 ' +
           '&& ' +
-          'pip install --user hashin ' +
+          'install-tool hashin 0.1.7 ' +
           '&& ' +
           'hashin atomicwrites==1.4.0 -r requirements.txt' +
           '"',
@@ -233,6 +240,10 @@ describe('modules/manager/pip_requirements/artifacts', () => {
   it('supports install mode', async () => {
     GlobalConfig.set({ ...adminConfig, binarySource: 'install' });
     fs.readLocalFile.mockResolvedValueOnce('new content');
+    // hashin
+    datasource.getPkgReleases.mockResolvedValueOnce({
+      releases: [{ version: '0.1.7' }],
+    });
     const execSnapshots = mockExecAll();
 
     expect(
@@ -253,7 +264,7 @@ describe('modules/manager/pip_requirements/artifacts', () => {
     ]);
     expect(execSnapshots).toMatchObject([
       { cmd: 'install-tool python 3.10.2' },
-      { cmd: 'pip install --user hashin' },
+      { cmd: 'install-tool hashin 0.1.7' },
       {
         cmd: 'hashin atomicwrites==1.4.0 -r requirements.txt',
         options: { cwd: '/tmp/github/some/repo' },
diff --git a/lib/modules/manager/pip_requirements/artifacts.ts b/lib/modules/manager/pip_requirements/artifacts.ts
index bde3664994..53fd1587f4 100644
--- a/lib/modules/manager/pip_requirements/artifacts.ts
+++ b/lib/modules/manager/pip_requirements/artifacts.ts
@@ -69,9 +69,9 @@ export async function updateArtifacts({
     const execOptions: ExecOptions = {
       cwdFile: '.',
       docker: {},
-      preCommands: ['pip install --user hashin'],
       toolConstraints: [
         { toolName: 'python', constraint: config.constraints?.python },
+        { toolName: 'hashin' },
       ],
       extraEnv: {
         PIP_CACHE_DIR: await ensureCacheDir('pip'),
diff --git a/lib/modules/manager/pipenv/__snapshots__/artifacts.spec.ts.snap b/lib/modules/manager/pipenv/__snapshots__/artifacts.spec.ts.snap
index 81581b65c5..184fb21b84 100644
--- a/lib/modules/manager/pipenv/__snapshots__/artifacts.spec.ts.snap
+++ b/lib/modules/manager/pipenv/__snapshots__/artifacts.spec.ts.snap
@@ -111,7 +111,7 @@ exports[`modules/manager/pipenv/artifacts supports docker mode 1`] = `
     },
   },
   {
-    "cmd": "docker run --rm --name=renovate_sidecar --label=renovate_child -v "/tmp/github/some/repo":"/tmp/github/some/repo" -v "/tmp/renovate/cache":"/tmp/renovate/cache" -e PIPENV_CACHE_DIR -e PIP_CACHE_DIR -e BUILDPACK_CACHE_DIR -e CONTAINERBASE_CACHE_DIR -w "/tmp/github/some/repo" containerbase/sidecar bash -l -c "install-tool python 3.7.6 && pip install --user pipenv && pipenv lock"",
+    "cmd": "docker run --rm --name=renovate_sidecar --label=renovate_child -v "/tmp/github/some/repo":"/tmp/github/some/repo" -v "/tmp/renovate/cache":"/tmp/renovate/cache" -e PIPENV_CACHE_DIR -e PIP_CACHE_DIR -e BUILDPACK_CACHE_DIR -e CONTAINERBASE_CACHE_DIR -w "/tmp/github/some/repo" containerbase/sidecar bash -l -c "install-tool python 3.7.6 && install-tool pipenv 2023.1.2 && pipenv lock"",
     "options": {
       "cwd": "/tmp/github/some/repo",
       "encoding": "utf-8",
@@ -150,7 +150,7 @@ exports[`modules/manager/pipenv/artifacts uses pipenv version from Pipfile 1`] =
     },
   },
   {
-    "cmd": "docker run --rm --name=renovate_sidecar --label=renovate_child -v "/tmp/github/some/repo":"/tmp/github/some/repo" -v "/tmp/renovate/cache":"/tmp/renovate/cache" -e PIPENV_CACHE_DIR -e BUILDPACK_CACHE_DIR -e CONTAINERBASE_CACHE_DIR -w "/tmp/github/some/repo" containerbase/sidecar bash -l -c "install-tool python 3.10.2 && pip install --user pipenv==2020.8.13 && pipenv lock"",
+    "cmd": "docker run --rm --name=renovate_sidecar --label=renovate_child -v "/tmp/github/some/repo":"/tmp/github/some/repo" -v "/tmp/renovate/cache":"/tmp/renovate/cache" -e PIPENV_CACHE_DIR -e BUILDPACK_CACHE_DIR -e CONTAINERBASE_CACHE_DIR -w "/tmp/github/some/repo" containerbase/sidecar bash -l -c "install-tool python 3.10.2 && install-tool pipenv 2020.8.13 && pipenv lock"",
     "options": {
       "cwd": "/tmp/github/some/repo",
       "encoding": "utf-8",
@@ -188,7 +188,7 @@ exports[`modules/manager/pipenv/artifacts uses pipenv version from Pipfile dev p
     },
   },
   {
-    "cmd": "docker run --rm --name=renovate_sidecar --label=renovate_child -v "/tmp/github/some/repo":"/tmp/github/some/repo" -v "/tmp/renovate/cache":"/tmp/renovate/cache" -e PIPENV_CACHE_DIR -e BUILDPACK_CACHE_DIR -e CONTAINERBASE_CACHE_DIR -w "/tmp/github/some/repo" containerbase/sidecar bash -l -c "install-tool python 3.10.2 && pip install --user pipenv==2020.8.13 && pipenv lock"",
+    "cmd": "docker run --rm --name=renovate_sidecar --label=renovate_child -v "/tmp/github/some/repo":"/tmp/github/some/repo" -v "/tmp/renovate/cache":"/tmp/renovate/cache" -e PIPENV_CACHE_DIR -e BUILDPACK_CACHE_DIR -e CONTAINERBASE_CACHE_DIR -w "/tmp/github/some/repo" containerbase/sidecar bash -l -c "install-tool python 3.10.2 && install-tool pipenv 2020.8.13 && pipenv lock"",
     "options": {
       "cwd": "/tmp/github/some/repo",
       "encoding": "utf-8",
@@ -226,7 +226,7 @@ exports[`modules/manager/pipenv/artifacts uses pipenv version from config 1`] =
     },
   },
   {
-    "cmd": "docker run --rm --name=renovate_sidecar --label=renovate_child -v "/tmp/github/some/repo":"/tmp/github/some/repo" -v "/tmp/renovate/cache":"/tmp/renovate/cache" -e PIPENV_CACHE_DIR -e BUILDPACK_CACHE_DIR -e CONTAINERBASE_CACHE_DIR -w "/tmp/github/some/repo" containerbase/sidecar bash -l -c "install-tool python 3.10.2 && pip install --user pipenv==2020.1.1 && pipenv lock"",
+    "cmd": "docker run --rm --name=renovate_sidecar --label=renovate_child -v "/tmp/github/some/repo":"/tmp/github/some/repo" -v "/tmp/renovate/cache":"/tmp/renovate/cache" -e PIPENV_CACHE_DIR -e BUILDPACK_CACHE_DIR -e CONTAINERBASE_CACHE_DIR -w "/tmp/github/some/repo" containerbase/sidecar bash -l -c "install-tool python 3.10.2 && install-tool pipenv 2020.1.1 && pipenv lock"",
     "options": {
       "cwd": "/tmp/github/some/repo",
       "encoding": "utf-8",
diff --git a/lib/modules/manager/pipenv/artifacts.spec.ts b/lib/modules/manager/pipenv/artifacts.spec.ts
index 55718c39d6..5f8d14acd0 100644
--- a/lib/modules/manager/pipenv/artifacts.spec.ts
+++ b/lib/modules/manager/pipenv/artifacts.spec.ts
@@ -1,14 +1,24 @@
 import { join } from 'upath';
 import { envMock, mockExecAll } from '../../../../test/exec-util';
-import { env, fs, git, mockedFunction, partial } from '../../../../test/util';
+import {
+  env,
+  fs,
+  git,
+  mocked,
+  mockedFunction,
+  partial,
+} from '../../../../test/util';
 import { GlobalConfig } from '../../../config/global';
 import type { RepoGlobalConfig } from '../../../config/types';
 import * as docker from '../../../util/exec/docker';
 import type { StatusResult } from '../../../util/git/types';
 import { getPkgReleases as _getPkgReleases } from '../../datasource';
+import * as _datasource from '../../datasource';
 import type { UpdateArtifactsConfig } from '../types';
 import * as pipenv from '.';
 
+const datasource = mocked(_datasource);
+
 jest.mock('../../../util/exec/env');
 jest.mock('../../../util/git');
 jest.mock('../../../util/fs');
@@ -141,6 +151,10 @@ describe('modules/manager/pipenv/artifacts', () => {
     );
     fs.ensureCacheDir.mockResolvedValueOnce('/tmp/renovate/cache/others/pip');
     fs.readLocalFile.mockResolvedValueOnce(JSON.stringify(pipFileLock));
+    // pipenv
+    datasource.getPkgReleases.mockResolvedValueOnce({
+      releases: [{ version: '2023.1.2' }],
+    });
     const execSnapshots = mockExecAll();
     git.getRepoStatus.mockResolvedValue(
       partial<StatusResult>({
@@ -167,6 +181,10 @@ describe('modules/manager/pipenv/artifacts', () => {
     );
     fs.ensureCacheDir.mockResolvedValueOnce('/tmp/renovate/cache/others/pip');
     fs.readLocalFile.mockResolvedValueOnce(JSON.stringify(pipFileLock));
+    // pipenv
+    datasource.getPkgReleases.mockResolvedValueOnce({
+      releases: [{ version: '2023.1.2' }],
+    });
     const execSnapshots = mockExecAll();
     git.getRepoStatus.mockResolvedValue(
       partial<StatusResult>({
@@ -184,7 +202,7 @@ describe('modules/manager/pipenv/artifacts', () => {
     ).not.toBeNull();
     expect(execSnapshots).toMatchObject([
       { cmd: 'install-tool python 3.7.6' },
-      { cmd: 'pip install --user pipenv' },
+      { cmd: 'install-tool pipenv 2023.1.2' },
       { cmd: 'pipenv lock', options: { cwd: '/tmp/github/some/repo' } },
     ]);
   });
diff --git a/lib/modules/manager/pipenv/artifacts.ts b/lib/modules/manager/pipenv/artifacts.ts
index 50692ec341..d2f3ea3363 100644
--- a/lib/modules/manager/pipenv/artifacts.ts
+++ b/lib/modules/manager/pipenv/artifacts.ts
@@ -1,4 +1,3 @@
-import { quote } from 'shlex';
 import { TEMPORARY_ERROR } from '../../../constants/error-messages';
 import { logger } from '../../../logger';
 import { exec } from '../../../util/exec';
@@ -114,12 +113,15 @@ export async function updateArtifacts({
         PIP_CACHE_DIR: await ensureCacheDir('pip'),
       },
       docker: {},
-      preCommands: [`pip install --user ${quote(`pipenv${pipenvConstraint}`)}`],
       toolConstraints: [
         {
           toolName: 'python',
           constraint: tagConstraint,
         },
+        {
+          toolName: 'pipenv',
+          constraint: pipenvConstraint,
+        },
       ],
     };
     logger.trace({ cmd }, 'pipenv lock command');
diff --git a/lib/util/exec/containerbase.ts b/lib/util/exec/containerbase.ts
index d9efe6285a..165d362288 100644
--- a/lib/util/exec/containerbase.ts
+++ b/lib/util/exec/containerbase.ts
@@ -254,7 +254,7 @@ export async function resolveConstraint(
   if (constraint) {
     if (versioning.isValid(constraint)) {
       if (versioning.isSingleVersion(constraint)) {
-        return constraint;
+        return constraint.replace(/^=+/, '').trim();
       }
     } else {
       logger.warn(
-- 
GitLab