From a372e10beeff5899f6c46821b85a0537dfc21469 Mon Sep 17 00:00:00 2001 From: Sergei Zharinov Date: Mon, 17 Jun 2024 21:11:59 -0300 Subject: [PATCH 01/18] test(pipenv): Rewrite test mocks --- lib/modules/manager/pipenv/artifacts.spec.ts | 345 +++++++++++++------ 1 file changed, 245 insertions(+), 100 deletions(-) diff --git a/lib/modules/manager/pipenv/artifacts.spec.ts b/lib/modules/manager/pipenv/artifacts.spec.ts index a3975ef6c31603..7602db9f0a71cf 100644 --- a/lib/modules/manager/pipenv/artifacts.spec.ts +++ b/lib/modules/manager/pipenv/artifacts.spec.ts @@ -1,10 +1,10 @@ +import * as _fsExtra from 'fs-extra'; import { mockDeep } from 'jest-mock-extended'; import { join } from 'upath'; import { envMock, mockExecAll } from '../../../../test/exec-util'; import { Fixtures } from '../../../../test/fixtures'; import { env, - fs, git, mocked, mockedFunction, @@ -30,9 +30,11 @@ import { updateArtifacts } from '.'; const datasource = mocked(_datasource); const find = mockedFunction(_find); +jest.mock('fs-extra'); +const fsExtra = mocked(_fsExtra); + jest.mock('../../../util/exec/env'); jest.mock('../../../util/git'); -jest.mock('../../../util/fs'); jest.mock('../../../util/host-rules', () => mockDeep()); jest.mock('../../../util/http'); jest.mock('../../datasource', () => mockDeep()); @@ -58,8 +60,6 @@ const pipCacheDir = '/tmp/renovate/cache/others/pip'; const virtualenvsCacheDir = '/tmp/renovate/cache/others/virtualenvs'; describe('modules/manager/pipenv/artifacts', () => { - let pipFileLock: PipfileLockSchema; - beforeEach(() => { env.getChildProcessEnv.mockReturnValue({ ...envMock.basic, @@ -69,11 +69,6 @@ describe('modules/manager/pipenv/artifacts', () => { GlobalConfig.set(adminConfig); docker.resetPrefetchedImages(); - pipFileLock = { - _meta: { requires: {} }, - default: { pipenv: {} }, - develop: { pipenv: {} }, - }; // python datasource.getPkgReleases.mockResolvedValueOnce({ @@ -109,12 +104,15 @@ describe('modules/manager/pipenv/artifacts', () => { }); it('returns null if unchanged', async () => { - pipFileLock._meta!.requires!.python_full_version = '3.7.6'; - fs.ensureCacheDir.mockResolvedValueOnce(pipenvCacheDir); - fs.ensureCacheDir.mockResolvedValueOnce(virtualenvsCacheDir); - fs.readLocalFile.mockResolvedValueOnce(JSON.stringify(pipFileLock)); + const pipFileLock = JSON.stringify({ + _meta: { requires: { python_full_version: '3.7.6' } }, + } satisfies PipfileLockSchema); + + fsExtra.ensureDir.mockResolvedValue(undefined as never); + + fsExtra.readFile.mockResolvedValueOnce(pipFileLock as never); const execSnapshots = mockExecAll(); - fs.readLocalFile.mockResolvedValueOnce(JSON.stringify(pipFileLock)); + fsExtra.readFile.mockResolvedValueOnce(pipFileLock as never); expect( await updateArtifacts({ @@ -136,16 +134,28 @@ describe('modules/manager/pipenv/artifacts', () => { }, }, ]); + + expect(fsExtra.ensureDir.mock.calls).toEqual([ + ['/tmp/renovate/cache/others/pipenv'], + ['/tmp/renovate/cache/others/pip'], + ['/tmp/renovate/cache/others/virtualenvs'], + ]); + expect(fsExtra.readFile.mock.calls).toEqual([ + ['/tmp/github/some/repo/Pipfile.lock', 'utf8'], + ]); }); it('gets python full version from Pipfile', async () => { GlobalConfig.set({ ...adminConfig, binarySource: 'install' }); - pipFileLock._meta!.requires!.python_full_version = '3.7.6'; - fs.ensureCacheDir.mockResolvedValueOnce(pipenvCacheDir); - fs.ensureCacheDir.mockResolvedValueOnce(virtualenvsCacheDir); - fs.readLocalFile.mockResolvedValueOnce(JSON.stringify(pipFileLock)); + const pipFileLock = JSON.stringify({ + _meta: { requires: { python_full_version: '3.7.6' } }, + } satisfies PipfileLockSchema); + + fsExtra.ensureDir.mockResolvedValue(undefined as never); + + fsExtra.readFile.mockResolvedValueOnce(pipFileLock as never); const execSnapshots = mockExecAll(); - fs.readLocalFile.mockResolvedValueOnce(JSON.stringify(pipFileLock)); + fsExtra.readFile.mockResolvedValueOnce(pipFileLock as never); expect( await updateArtifacts({ @@ -169,16 +179,29 @@ describe('modules/manager/pipenv/artifacts', () => { }, }, ]); + + expect(fsExtra.ensureDir.mock.calls).toEqual([ + ['/tmp/renovate/cache/others/pipenv'], + ['/tmp/renovate/cache/others/pip'], + ['/tmp/renovate/cache/others/virtualenvs'], + ]); + expect(fsExtra.readFile.mock.calls).toEqual([ + ['/tmp/github/some/repo/Pipfile.lock', 'utf8'], + ]); }); it('gets python version from Pipfile', async () => { GlobalConfig.set({ ...adminConfig, binarySource: 'install' }); - pipFileLock._meta!.requires!.python_full_version = '3.7.6'; - fs.ensureCacheDir.mockResolvedValueOnce(pipenvCacheDir); - fs.ensureCacheDir.mockResolvedValueOnce(virtualenvsCacheDir); - fs.readLocalFile.mockResolvedValueOnce(JSON.stringify(pipFileLock)); + + const pipFileLock = JSON.stringify({ + _meta: { requires: { python_full_version: '3.7.6' } }, + } satisfies PipfileLockSchema); + + fsExtra.ensureDir.mockResolvedValue(undefined as never); + + fsExtra.readFile.mockResolvedValueOnce(pipFileLock as never); const execSnapshots = mockExecAll(); - fs.readLocalFile.mockResolvedValueOnce(JSON.stringify(pipFileLock)); + fsExtra.readFile.mockResolvedValueOnce(pipFileLock as never); expect( await updateArtifacts({ @@ -202,16 +225,25 @@ describe('modules/manager/pipenv/artifacts', () => { }, }, ]); + + expect(fsExtra.ensureDir.mock.calls).toEqual([ + ['/tmp/renovate/cache/others/pipenv'], + ['/tmp/renovate/cache/others/pip'], + ['/tmp/renovate/cache/others/virtualenvs'], + ]); + expect(fsExtra.readFile.mock.calls).toEqual([ + ['/tmp/github/some/repo/Pipfile.lock', 'utf8'], + ]); }); it('gets full python version from .python-version', async () => { GlobalConfig.set({ ...adminConfig, binarySource: 'install' }); - fs.ensureCacheDir.mockResolvedValueOnce(pipenvCacheDir); - fs.ensureCacheDir.mockResolvedValueOnce(virtualenvsCacheDir); - fs.readLocalFile.mockResolvedValueOnce(JSON.stringify(pipFileLock)); + + fsExtra.ensureDir.mockResolvedValue(undefined as never); + + fsExtra.readFile.mockResolvedValueOnce('{}' as never); const execSnapshots = mockExecAll(); - fs.getSiblingFileName.mockResolvedValueOnce('.python-version' as never); - fs.readLocalFile.mockResolvedValueOnce('3.7.6'); + fsExtra.readFile.mockResolvedValueOnce('3.7.6' as never); expect( await updateArtifacts({ @@ -235,16 +267,26 @@ describe('modules/manager/pipenv/artifacts', () => { }, }, ]); + + expect(fsExtra.ensureDir.mock.calls).toEqual([ + ['/tmp/renovate/cache/others/pipenv'], + ['/tmp/renovate/cache/others/pip'], + ['/tmp/renovate/cache/others/virtualenvs'], + ]); + expect(fsExtra.readFile.mock.calls).toEqual([ + ['/tmp/github/some/repo/Pipfile.lock', 'utf8'], + ['/tmp/github/some/repo/.python-version', 'utf8'], + ]); }); it('gets python stream, from .python-version', async () => { GlobalConfig.set({ ...adminConfig, binarySource: 'install' }); - fs.ensureCacheDir.mockResolvedValueOnce(pipenvCacheDir); - fs.ensureCacheDir.mockResolvedValueOnce(virtualenvsCacheDir); - fs.readLocalFile.mockResolvedValueOnce(JSON.stringify(pipFileLock)); + + fsExtra.ensureDir.mockResolvedValue(undefined as never); + + fsExtra.readFile.mockResolvedValueOnce('{}' as never); const execSnapshots = mockExecAll(); - fs.getSiblingFileName.mockResolvedValueOnce('.python-version' as never); - fs.readLocalFile.mockResolvedValueOnce('3.8'); + fsExtra.readFile.mockResolvedValueOnce('3.8' as never); expect( await updateArtifacts({ @@ -268,15 +310,24 @@ describe('modules/manager/pipenv/artifacts', () => { }, }, ]); + + expect(fsExtra.ensureDir.mock.calls).toEqual([ + ['/tmp/renovate/cache/others/pipenv'], + ['/tmp/renovate/cache/others/pip'], + ['/tmp/renovate/cache/others/virtualenvs'], + ]); + expect(fsExtra.readFile.mock.calls).toEqual([ + ['/tmp/github/some/repo/Pipfile.lock', 'utf8'], + ['/tmp/github/some/repo/.python-version', 'utf8'], + ]); }); it('handles no constraint', async () => { - fs.ensureCacheDir.mockResolvedValueOnce(pipenvCacheDir); - fs.ensureCacheDir.mockResolvedValueOnce(pipCacheDir); - fs.ensureCacheDir.mockResolvedValueOnce(virtualenvsCacheDir); - fs.readLocalFile.mockResolvedValueOnce('unparseable pipfile lock'); + fsExtra.ensureDir.mockResolvedValue(undefined as never); + + fsExtra.readFile.mockResolvedValueOnce('unparseable pipfile lock' as never); const execSnapshots = mockExecAll(); - fs.readLocalFile.mockResolvedValueOnce('unparseable pipfile lock'); + fsExtra.readFile.mockResolvedValueOnce('unparseable pipfile lock' as never); expect( await updateArtifacts({ @@ -300,20 +351,28 @@ describe('modules/manager/pipenv/artifacts', () => { }, }, ]); + + expect(fsExtra.ensureDir.mock.calls).toEqual([ + ['/tmp/renovate/cache/others/pipenv'], + ['/tmp/renovate/cache/others/pip'], + ['/tmp/renovate/cache/others/virtualenvs'], + ]); + expect(fsExtra.readFile.mock.calls).toEqual([ + ['/tmp/github/some/repo/Pipfile.lock', 'utf8'], + ]); }); it('returns updated Pipfile.lock', async () => { - fs.ensureCacheDir.mockResolvedValueOnce(pipenvCacheDir); - fs.ensureCacheDir.mockResolvedValueOnce(pipCacheDir); - fs.ensureCacheDir.mockResolvedValueOnce(virtualenvsCacheDir); - fs.readLocalFile.mockResolvedValueOnce('current pipfile.lock'); + fsExtra.ensureDir.mockResolvedValue(undefined as never); + + fsExtra.readFile.mockResolvedValueOnce('current pipfile.lock' as never); const execSnapshots = mockExecAll(); git.getRepoStatus.mockResolvedValue( partial({ modified: ['Pipfile.lock'], }), ); - fs.readLocalFile.mockResolvedValueOnce('New Pipfile.lock'); + fsExtra.readFile.mockResolvedValueOnce('New Pipfile.lock' as never); expect( await updateArtifacts({ @@ -337,15 +396,27 @@ describe('modules/manager/pipenv/artifacts', () => { }, }, ]); + + expect(fsExtra.ensureDir.mock.calls).toEqual([ + ['/tmp/renovate/cache/others/pipenv'], + ['/tmp/renovate/cache/others/pip'], + ['/tmp/renovate/cache/others/virtualenvs'], + ]); + expect(fsExtra.readFile.mock.calls).toEqual([ + ['/tmp/github/some/repo/Pipfile.lock', 'utf8'], + ['/tmp/github/some/repo/Pipfile.lock', 'utf8'], + ]); }); it('supports docker mode', async () => { GlobalConfig.set(dockerAdminConfig); - pipFileLock._meta!.requires!.python_version = '3.7'; - fs.ensureCacheDir.mockResolvedValueOnce(pipenvCacheDir); - fs.ensureCacheDir.mockResolvedValueOnce(pipCacheDir); - fs.ensureCacheDir.mockResolvedValueOnce(virtualenvsCacheDir); - fs.readLocalFile.mockResolvedValueOnce(JSON.stringify(pipFileLock)); + const pipFileLock = JSON.stringify({ + _meta: { requires: { python_version: '3.7' } }, + } satisfies PipfileLockSchema); + + fsExtra.ensureDir.mockResolvedValue(undefined as never); + + fsExtra.readFile.mockResolvedValueOnce(pipFileLock as never); // pipenv datasource.getPkgReleases.mockResolvedValueOnce({ releases: [{ version: '2023.1.2' }], @@ -356,7 +427,7 @@ describe('modules/manager/pipenv/artifacts', () => { modified: ['Pipfile.lock'], }), ); - fs.readLocalFile.mockResolvedValueOnce('new lock'); + fsExtra.readFile.mockResolvedValueOnce('new lock' as never); expect( await updateArtifacts({ @@ -398,15 +469,27 @@ describe('modules/manager/pipenv/artifacts', () => { }, }, ]); + + expect(fsExtra.ensureDir.mock.calls).toEqual([ + ['/tmp/renovate/cache/others/pipenv'], + ['/tmp/renovate/cache/others/pip'], + ['/tmp/renovate/cache/others/virtualenvs'], + ]); + expect(fsExtra.readFile.mock.calls).toEqual([ + ['/tmp/github/some/repo/Pipfile.lock', 'utf8'], + ['/tmp/github/some/repo/Pipfile.lock', 'utf8'], + ]); }); it('supports install mode', async () => { GlobalConfig.set({ ...adminConfig, binarySource: 'install' }); - pipFileLock._meta!.requires!.python_version = '3.6'; - fs.ensureCacheDir.mockResolvedValueOnce(pipenvCacheDir); - fs.ensureCacheDir.mockResolvedValueOnce(pipCacheDir); - fs.ensureCacheDir.mockResolvedValueOnce(virtualenvsCacheDir); - fs.readLocalFile.mockResolvedValueOnce(JSON.stringify(pipFileLock)); + const pipFileLock = JSON.stringify({ + _meta: { requires: { python_version: '3.6' } }, + } satisfies PipfileLockSchema); + + fsExtra.ensureDir.mockResolvedValue(undefined as never); + + fsExtra.readFile.mockResolvedValueOnce(pipFileLock as never); // pipenv datasource.getPkgReleases.mockResolvedValueOnce({ releases: [{ version: '2023.1.2' }], @@ -417,7 +500,7 @@ describe('modules/manager/pipenv/artifacts', () => { modified: ['Pipfile.lock'], }), ); - fs.readLocalFile.mockResolvedValueOnce('new lock'); + fsExtra.readFile.mockResolvedValueOnce('new lock' as never); expect( await updateArtifacts({ @@ -443,14 +526,23 @@ describe('modules/manager/pipenv/artifacts', () => { }, }, ]); + + expect(fsExtra.ensureDir.mock.calls).toEqual([ + ['/tmp/renovate/cache/others/pipenv'], + ['/tmp/renovate/cache/others/pip'], + ['/tmp/renovate/cache/others/virtualenvs'], + ]); + expect(fsExtra.readFile.mock.calls).toEqual([ + ['/tmp/github/some/repo/Pipfile.lock', 'utf8'], + ['/tmp/github/some/repo/Pipfile.lock', 'utf8'], + ]); }); it('defaults to latest if no lock constraints', async () => { GlobalConfig.set({ ...adminConfig, binarySource: 'install' }); - fs.ensureCacheDir.mockResolvedValueOnce(pipenvCacheDir); - fs.ensureCacheDir.mockResolvedValueOnce(pipCacheDir); - fs.ensureCacheDir.mockResolvedValueOnce(virtualenvsCacheDir); - fs.readLocalFile.mockResolvedValueOnce(JSON.stringify(pipFileLock)); + fsExtra.ensureDir.mockResolvedValue(undefined as never); + + fsExtra.readFile.mockResolvedValueOnce('{}' as never); // pipenv datasource.getPkgReleases.mockResolvedValueOnce({ releases: [{ version: '2023.1.2' }], @@ -461,7 +553,7 @@ describe('modules/manager/pipenv/artifacts', () => { modified: ['Pipfile.lock'], }), ); - fs.readLocalFile.mockResolvedValueOnce('new lock'); + fsExtra.readFile.mockResolvedValueOnce('new lock' as never); expect( await updateArtifacts({ @@ -487,16 +579,26 @@ describe('modules/manager/pipenv/artifacts', () => { }, }, ]); + + expect(fsExtra.ensureDir.mock.calls).toEqual([ + ['/tmp/renovate/cache/others/pipenv'], + ['/tmp/renovate/cache/others/pip'], + ['/tmp/renovate/cache/others/virtualenvs'], + ]); + expect(fsExtra.readFile.mock.calls).toEqual([ + ['/tmp/github/some/repo/Pipfile.lock', 'utf8'], + ['/tmp/github/some/repo/.python-version', 'utf8'], + ['/tmp/github/some/repo/Pipfile.lock', 'utf8'], + ]); }); it('catches errors', async () => { - fs.ensureCacheDir.mockResolvedValueOnce(pipenvCacheDir); - fs.ensureCacheDir.mockResolvedValueOnce(pipCacheDir); - fs.ensureCacheDir.mockResolvedValueOnce(virtualenvsCacheDir); - fs.readLocalFile.mockResolvedValueOnce('Current Pipfile.lock'); - fs.writeLocalFile.mockImplementationOnce(() => { + fsExtra.ensureDir.mockResolvedValue(undefined as never); + + fsExtra.readFile.mockResolvedValueOnce('Current Pipfile.lock' as never); + fsExtra.outputFile.mockImplementationOnce((() => { throw new Error('not found'); - }); + }) as never); expect( await updateArtifacts({ @@ -508,20 +610,24 @@ describe('modules/manager/pipenv/artifacts', () => { ).toEqual([ { artifactError: { lockFile: 'Pipfile.lock', stderr: 'not found' } }, ]); + + expect(fsExtra.ensureDir.mock.calls).toEqual([]); + expect(fsExtra.readFile.mock.calls).toEqual([ + ['/tmp/github/some/repo/Pipfile.lock', 'utf8'], + ]); }); it('returns updated Pipenv.lock when doing lockfile maintenance', async () => { - fs.ensureCacheDir.mockResolvedValueOnce(pipenvCacheDir); - fs.ensureCacheDir.mockResolvedValueOnce(pipCacheDir); - fs.ensureCacheDir.mockResolvedValueOnce(virtualenvsCacheDir); - fs.readLocalFile.mockResolvedValueOnce('Current Pipfile.lock'); + fsExtra.ensureDir.mockResolvedValue(undefined as never); + + fsExtra.readFile.mockResolvedValueOnce('Current Pipfile.lock' as never); const execSnapshots = mockExecAll(); git.getRepoStatus.mockResolvedValue( partial({ modified: ['Pipfile.lock'], }), ); - fs.readLocalFile.mockResolvedValueOnce('New Pipfile.lock'); + fsExtra.readFile.mockResolvedValueOnce('New Pipfile.lock' as never); expect( await updateArtifacts({ @@ -548,19 +654,22 @@ describe('modules/manager/pipenv/artifacts', () => { }); it('uses pipenv version from Pipfile', async () => { - fs.ensureCacheDir.mockResolvedValueOnce(pipenvCacheDir); - fs.ensureCacheDir.mockResolvedValueOnce(pipCacheDir); - fs.ensureCacheDir.mockResolvedValueOnce(virtualenvsCacheDir); + fsExtra.ensureDir.mockResolvedValue(undefined as never); + GlobalConfig.set(dockerAdminConfig); - pipFileLock.default!['pipenv'].version = '==2020.8.13'; - fs.readLocalFile.mockResolvedValueOnce(JSON.stringify(pipFileLock)); + + fsExtra.readFile.mockResolvedValueOnce( + JSON.stringify({ + default: { pipenv: { version: '==2020.8.13' } }, + } satisfies PipfileLockSchema) as never, + ); const execSnapshots = mockExecAll(); git.getRepoStatus.mockResolvedValue( partial({ modified: ['Pipfile.lock'], }), ); - fs.readLocalFile.mockResolvedValueOnce('new lock'); + fsExtra.readFile.mockResolvedValueOnce('new lock' as never); expect( await updateArtifacts({ @@ -601,22 +710,36 @@ describe('modules/manager/pipenv/artifacts', () => { }, }, ]); + + expect(fsExtra.ensureDir.mock.calls).toEqual([ + ['/tmp/renovate/cache/others/pipenv'], + ['/tmp/renovate/cache/others/pip'], + ['/tmp/renovate/cache/others/virtualenvs'], + ]); + expect(fsExtra.readFile.mock.calls).toEqual([ + ['/tmp/github/some/repo/Pipfile.lock', 'utf8'], + ['/tmp/github/some/repo/.python-version', 'utf8'], + ['/tmp/github/some/repo/Pipfile.lock', 'utf8'], + ]); }); it('uses pipenv version from Pipfile dev packages', async () => { GlobalConfig.set(dockerAdminConfig); - pipFileLock.develop!['pipenv'].version = '==2020.8.13'; - fs.ensureCacheDir.mockResolvedValueOnce(pipenvCacheDir); - fs.ensureCacheDir.mockResolvedValueOnce(pipCacheDir); - fs.ensureCacheDir.mockResolvedValueOnce(virtualenvsCacheDir); - fs.readLocalFile.mockResolvedValueOnce(JSON.stringify(pipFileLock)); + + fsExtra.ensureDir.mockResolvedValue(undefined as never); + + fsExtra.readFile.mockResolvedValueOnce( + JSON.stringify({ + develop: { pipenv: { version: '==2020.8.13' } }, + } satisfies PipfileLockSchema) as never, + ); const execSnapshots = mockExecAll(); git.getRepoStatus.mockResolvedValue( partial({ modified: ['Pipfile.lock'], }), ); - fs.readLocalFile.mockResolvedValueOnce('new lock'); + fsExtra.readFile.mockResolvedValueOnce('new lock' as never); expect( await updateArtifacts({ @@ -657,22 +780,35 @@ describe('modules/manager/pipenv/artifacts', () => { }, }, ]); + + expect(fsExtra.ensureDir.mock.calls).toEqual([ + ['/tmp/renovate/cache/others/pipenv'], + ['/tmp/renovate/cache/others/pip'], + ['/tmp/renovate/cache/others/virtualenvs'], + ]); + expect(fsExtra.readFile.mock.calls).toEqual([ + ['/tmp/github/some/repo/Pipfile.lock', 'utf8'], + ['/tmp/github/some/repo/.python-version', 'utf8'], + ['/tmp/github/some/repo/Pipfile.lock', 'utf8'], + ]); }); it('uses pipenv version from config', async () => { GlobalConfig.set(dockerAdminConfig); - pipFileLock.default!['pipenv'].version = '==2020.8.13'; - fs.ensureCacheDir.mockResolvedValueOnce(pipenvCacheDir); - fs.ensureCacheDir.mockResolvedValueOnce(pipCacheDir); - fs.ensureCacheDir.mockResolvedValueOnce(virtualenvsCacheDir); - fs.readLocalFile.mockResolvedValueOnce(JSON.stringify(pipFileLock)); + fsExtra.ensureDir.mockResolvedValue(undefined as never); + + fsExtra.readFile.mockResolvedValueOnce( + JSON.stringify({ + default: { pipenv: { version: '==2020.8.13' } }, + } satisfies PipfileLockSchema) as never, + ); const execSnapshots = mockExecAll(); git.getRepoStatus.mockResolvedValue( partial({ modified: ['Pipfile.lock'], }), ); - fs.readLocalFile.mockResolvedValueOnce('new lock'); + fsExtra.readFile.mockResolvedValueOnce('new lock' as never); expect( await updateArtifacts({ @@ -713,20 +849,30 @@ describe('modules/manager/pipenv/artifacts', () => { }, }, ]); + + expect(fsExtra.ensureDir.mock.calls).toEqual([ + ['/tmp/renovate/cache/others/pipenv'], + ['/tmp/renovate/cache/others/pip'], + ['/tmp/renovate/cache/others/virtualenvs'], + ]); + expect(fsExtra.readFile.mock.calls).toEqual([ + ['/tmp/github/some/repo/Pipfile.lock', 'utf8'], + ['/tmp/github/some/repo/.python-version', 'utf8'], + ['/tmp/github/some/repo/Pipfile.lock', 'utf8'], + ]); }); it('passes private credential environment vars', async () => { - fs.ensureCacheDir.mockResolvedValueOnce(pipenvCacheDir); - fs.ensureCacheDir.mockResolvedValueOnce(pipCacheDir); - fs.ensureCacheDir.mockResolvedValueOnce(virtualenvsCacheDir); - fs.readLocalFile.mockResolvedValueOnce('current pipfile.lock'); + fsExtra.ensureDir.mockResolvedValue(undefined as never); + + fsExtra.readFile.mockResolvedValueOnce('current pipfile.lock' as never); const execSnapshots = mockExecAll(); git.getRepoStatus.mockResolvedValue( partial({ modified: ['Pipfile.lock'], }), ); - fs.readLocalFile.mockResolvedValueOnce('New Pipfile.lock'); + fsExtra.readFile.mockResolvedValueOnce('New Pipfile.lock' as never); find.mockReturnValueOnce({ username: 'usernameOne', @@ -791,17 +937,16 @@ describe('modules/manager/pipenv/artifacts', () => { }); it('updates extraEnv if variable names differ from default', async () => { - fs.ensureCacheDir.mockResolvedValueOnce(pipenvCacheDir); - fs.ensureCacheDir.mockResolvedValueOnce(pipCacheDir); - fs.ensureCacheDir.mockResolvedValueOnce(virtualenvsCacheDir); - fs.readLocalFile.mockResolvedValueOnce('current pipfile.lock'); + fsExtra.ensureDir.mockResolvedValue(undefined as never); + + fsExtra.readFile.mockResolvedValueOnce('current pipfile.lock' as never); const execSnapshots = mockExecAll(); git.getRepoStatus.mockResolvedValue( partial({ modified: ['Pipfile.lock'], }), ); - fs.readLocalFile.mockResolvedValueOnce('New Pipfile.lock'); + fsExtra.readFile.mockResolvedValueOnce('New Pipfile.lock' as never); find.mockReturnValueOnce({ username: 'usernameOne', From a40af9e041f734146e15774cef8bc7adb94a5fcf Mon Sep 17 00:00:00 2001 From: Sergei Zharinov Date: Tue, 18 Jun 2024 15:59:52 -0300 Subject: [PATCH 02/18] Specify which files to mock --- lib/modules/manager/pipenv/artifacts.spec.ts | 167 ++++++++++++------- 1 file changed, 111 insertions(+), 56 deletions(-) diff --git a/lib/modules/manager/pipenv/artifacts.spec.ts b/lib/modules/manager/pipenv/artifacts.spec.ts index 7602db9f0a71cf..5895e58b8cbdd2 100644 --- a/lib/modules/manager/pipenv/artifacts.spec.ts +++ b/lib/modules/manager/pipenv/artifacts.spec.ts @@ -59,6 +59,21 @@ const pipenvCacheDir = '/tmp/renovate/cache/others/pipenv'; const pipCacheDir = '/tmp/renovate/cache/others/pip'; const virtualenvsCacheDir = '/tmp/renovate/cache/others/virtualenvs'; +interface MockFiles { + [key: string]: string | string[]; +} + +function mockFiles(mockFiles: MockFiles): void { + fsExtra.readFile.mockImplementation(((name: string) => { + for (const [key, value] of Object.entries(mockFiles)) { + if (name.endsWith(key)) { + return Array.isArray(value) ? value.shift() : value; + } + } + return Promise.reject(new Error('File not found')); + }) as never); +} + describe('modules/manager/pipenv/artifacts', () => { beforeEach(() => { env.getChildProcessEnv.mockReturnValue({ @@ -104,15 +119,16 @@ describe('modules/manager/pipenv/artifacts', () => { }); it('returns null if unchanged', async () => { - const pipFileLock = JSON.stringify({ - _meta: { requires: { python_full_version: '3.7.6' } }, - } satisfies PipfileLockSchema); - fsExtra.ensureDir.mockResolvedValue(undefined as never); - fsExtra.readFile.mockResolvedValueOnce(pipFileLock as never); + mockFiles({ + '/Pipfile.lock': JSON.stringify({ + _meta: { + requires: { python_full_version: '3.7.6' }, + }, + } satisfies PipfileLockSchema), + }); const execSnapshots = mockExecAll(); - fsExtra.readFile.mockResolvedValueOnce(pipFileLock as never); expect( await updateArtifacts({ @@ -147,15 +163,18 @@ describe('modules/manager/pipenv/artifacts', () => { it('gets python full version from Pipfile', async () => { GlobalConfig.set({ ...adminConfig, binarySource: 'install' }); - const pipFileLock = JSON.stringify({ - _meta: { requires: { python_full_version: '3.7.6' } }, - } satisfies PipfileLockSchema); + + mockFiles({ + '/Pipfile.lock': JSON.stringify({ + _meta: { + requires: { python_full_version: '3.7.6' }, + }, + } satisfies PipfileLockSchema), + }); fsExtra.ensureDir.mockResolvedValue(undefined as never); - fsExtra.readFile.mockResolvedValueOnce(pipFileLock as never); const execSnapshots = mockExecAll(); - fsExtra.readFile.mockResolvedValueOnce(pipFileLock as never); expect( await updateArtifacts({ @@ -193,15 +212,17 @@ describe('modules/manager/pipenv/artifacts', () => { it('gets python version from Pipfile', async () => { GlobalConfig.set({ ...adminConfig, binarySource: 'install' }); - const pipFileLock = JSON.stringify({ - _meta: { requires: { python_full_version: '3.7.6' } }, - } satisfies PipfileLockSchema); + mockFiles({ + '/Pipfile.lock': JSON.stringify({ + _meta: { + requires: { python_full_version: '3.7.6' }, + }, + } satisfies PipfileLockSchema), + }); fsExtra.ensureDir.mockResolvedValue(undefined as never); - fsExtra.readFile.mockResolvedValueOnce(pipFileLock as never); const execSnapshots = mockExecAll(); - fsExtra.readFile.mockResolvedValueOnce(pipFileLock as never); expect( await updateArtifacts({ @@ -239,11 +260,14 @@ describe('modules/manager/pipenv/artifacts', () => { it('gets full python version from .python-version', async () => { GlobalConfig.set({ ...adminConfig, binarySource: 'install' }); + mockFiles({ + '/Pipfile.lock': '{}', + '/.python-version': '3.7.6', + }); + fsExtra.ensureDir.mockResolvedValue(undefined as never); - fsExtra.readFile.mockResolvedValueOnce('{}' as never); const execSnapshots = mockExecAll(); - fsExtra.readFile.mockResolvedValueOnce('3.7.6' as never); expect( await updateArtifacts({ @@ -284,9 +308,11 @@ describe('modules/manager/pipenv/artifacts', () => { fsExtra.ensureDir.mockResolvedValue(undefined as never); - fsExtra.readFile.mockResolvedValueOnce('{}' as never); + mockFiles({ + '/Pipfile.lock': '{}', + '/.python-version': '3.8', + }); const execSnapshots = mockExecAll(); - fsExtra.readFile.mockResolvedValueOnce('3.8' as never); expect( await updateArtifacts({ @@ -325,9 +351,11 @@ describe('modules/manager/pipenv/artifacts', () => { it('handles no constraint', async () => { fsExtra.ensureDir.mockResolvedValue(undefined as never); - fsExtra.readFile.mockResolvedValueOnce('unparseable pipfile lock' as never); + mockFiles({ + '/Pipfile.lock': 'unparseable pipfile lock', + }); + const execSnapshots = mockExecAll(); - fsExtra.readFile.mockResolvedValueOnce('unparseable pipfile lock' as never); expect( await updateArtifacts({ @@ -365,14 +393,15 @@ describe('modules/manager/pipenv/artifacts', () => { it('returns updated Pipfile.lock', async () => { fsExtra.ensureDir.mockResolvedValue(undefined as never); - fsExtra.readFile.mockResolvedValueOnce('current pipfile.lock' as never); + mockFiles({ + '/Pipfile.lock': ['current pipfile.lock', 'new pipfile.lock'], + }); const execSnapshots = mockExecAll(); git.getRepoStatus.mockResolvedValue( partial({ modified: ['Pipfile.lock'], }), ); - fsExtra.readFile.mockResolvedValueOnce('New Pipfile.lock' as never); expect( await updateArtifacts({ @@ -381,7 +410,15 @@ describe('modules/manager/pipenv/artifacts', () => { newPackageFileContent: 'some new content', config: { ...config, constraints: { python: '== 3.8.*' } }, }), - ).not.toBeNull(); + ).toEqual([ + { + file: { + contents: 'new pipfile.lock', + path: 'Pipfile.lock', + type: 'addition', + }, + }, + ]); expect(execSnapshots).toMatchObject([ { @@ -410,13 +447,16 @@ describe('modules/manager/pipenv/artifacts', () => { it('supports docker mode', async () => { GlobalConfig.set(dockerAdminConfig); + const pipFileLock = JSON.stringify({ _meta: { requires: { python_version: '3.7' } }, } satisfies PipfileLockSchema); + mockFiles({ + '/Pipfile.lock': [pipFileLock, 'new lock'], + }); fsExtra.ensureDir.mockResolvedValue(undefined as never); - fsExtra.readFile.mockResolvedValueOnce(pipFileLock as never); // pipenv datasource.getPkgReleases.mockResolvedValueOnce({ releases: [{ version: '2023.1.2' }], @@ -427,7 +467,6 @@ describe('modules/manager/pipenv/artifacts', () => { modified: ['Pipfile.lock'], }), ); - fsExtra.readFile.mockResolvedValueOnce('new lock' as never); expect( await updateArtifacts({ @@ -483,13 +522,16 @@ describe('modules/manager/pipenv/artifacts', () => { it('supports install mode', async () => { GlobalConfig.set({ ...adminConfig, binarySource: 'install' }); + const pipFileLock = JSON.stringify({ _meta: { requires: { python_version: '3.6' } }, } satisfies PipfileLockSchema); + mockFiles({ + '/Pipfile.lock': [pipFileLock, 'new lock'], + }); fsExtra.ensureDir.mockResolvedValue(undefined as never); - fsExtra.readFile.mockResolvedValueOnce(pipFileLock as never); // pipenv datasource.getPkgReleases.mockResolvedValueOnce({ releases: [{ version: '2023.1.2' }], @@ -500,7 +542,6 @@ describe('modules/manager/pipenv/artifacts', () => { modified: ['Pipfile.lock'], }), ); - fsExtra.readFile.mockResolvedValueOnce('new lock' as never); expect( await updateArtifacts({ @@ -542,7 +583,10 @@ describe('modules/manager/pipenv/artifacts', () => { GlobalConfig.set({ ...adminConfig, binarySource: 'install' }); fsExtra.ensureDir.mockResolvedValue(undefined as never); - fsExtra.readFile.mockResolvedValueOnce('{}' as never); + mockFiles({ + '/Pipfile.lock': ['{}', 'new lock'], + }); + // pipenv datasource.getPkgReleases.mockResolvedValueOnce({ releases: [{ version: '2023.1.2' }], @@ -553,7 +597,6 @@ describe('modules/manager/pipenv/artifacts', () => { modified: ['Pipfile.lock'], }), ); - fsExtra.readFile.mockResolvedValueOnce('new lock' as never); expect( await updateArtifacts({ @@ -595,7 +638,10 @@ describe('modules/manager/pipenv/artifacts', () => { it('catches errors', async () => { fsExtra.ensureDir.mockResolvedValue(undefined as never); - fsExtra.readFile.mockResolvedValueOnce('Current Pipfile.lock' as never); + mockFiles({ + '/Pipfile.lock': 'Current Pipfile.lock', + }); + fsExtra.outputFile.mockImplementationOnce((() => { throw new Error('not found'); }) as never); @@ -620,14 +666,16 @@ describe('modules/manager/pipenv/artifacts', () => { it('returns updated Pipenv.lock when doing lockfile maintenance', async () => { fsExtra.ensureDir.mockResolvedValue(undefined as never); - fsExtra.readFile.mockResolvedValueOnce('Current Pipfile.lock' as never); + mockFiles({ + '/Pipfile.lock': ['Current Pipfile.lock', 'New Pipfile.lock'], + }); + const execSnapshots = mockExecAll(); git.getRepoStatus.mockResolvedValue( partial({ modified: ['Pipfile.lock'], }), ); - fsExtra.readFile.mockResolvedValueOnce('New Pipfile.lock' as never); expect( await updateArtifacts({ @@ -658,18 +706,19 @@ describe('modules/manager/pipenv/artifacts', () => { GlobalConfig.set(dockerAdminConfig); - fsExtra.readFile.mockResolvedValueOnce( - JSON.stringify({ - default: { pipenv: { version: '==2020.8.13' } }, - } satisfies PipfileLockSchema) as never, - ); + const oldLock = JSON.stringify({ + default: { pipenv: { version: '==2020.8.13' } }, + } satisfies PipfileLockSchema); + mockFiles({ + '/Pipfile.lock': [oldLock, 'new lock'], + }); + const execSnapshots = mockExecAll(); git.getRepoStatus.mockResolvedValue( partial({ modified: ['Pipfile.lock'], }), ); - fsExtra.readFile.mockResolvedValueOnce('new lock' as never); expect( await updateArtifacts({ @@ -728,18 +777,19 @@ describe('modules/manager/pipenv/artifacts', () => { fsExtra.ensureDir.mockResolvedValue(undefined as never); - fsExtra.readFile.mockResolvedValueOnce( - JSON.stringify({ - develop: { pipenv: { version: '==2020.8.13' } }, - } satisfies PipfileLockSchema) as never, - ); + const oldLock = JSON.stringify({ + develop: { pipenv: { version: '==2020.8.13' } }, + } satisfies PipfileLockSchema) as never; + mockFiles({ + '/Pipfile.lock': [oldLock, 'new lock'], + }); + const execSnapshots = mockExecAll(); git.getRepoStatus.mockResolvedValue( partial({ modified: ['Pipfile.lock'], }), ); - fsExtra.readFile.mockResolvedValueOnce('new lock' as never); expect( await updateArtifacts({ @@ -797,18 +847,19 @@ describe('modules/manager/pipenv/artifacts', () => { GlobalConfig.set(dockerAdminConfig); fsExtra.ensureDir.mockResolvedValue(undefined as never); - fsExtra.readFile.mockResolvedValueOnce( - JSON.stringify({ - default: { pipenv: { version: '==2020.8.13' } }, - } satisfies PipfileLockSchema) as never, - ); + const oldLock = JSON.stringify({ + default: { pipenv: { version: '==2020.8.13' } }, + } satisfies PipfileLockSchema) as never; + mockFiles({ + '/Pipfile.lock': [oldLock, 'new lock'], + }); + const execSnapshots = mockExecAll(); git.getRepoStatus.mockResolvedValue( partial({ modified: ['Pipfile.lock'], }), ); - fsExtra.readFile.mockResolvedValueOnce('new lock' as never); expect( await updateArtifacts({ @@ -865,14 +916,16 @@ describe('modules/manager/pipenv/artifacts', () => { it('passes private credential environment vars', async () => { fsExtra.ensureDir.mockResolvedValue(undefined as never); - fsExtra.readFile.mockResolvedValueOnce('current pipfile.lock' as never); + mockFiles({ + '/Pipfile.lock': ['current Pipfile.lock', 'New Pipfile.lock'], + }); + const execSnapshots = mockExecAll(); git.getRepoStatus.mockResolvedValue( partial({ modified: ['Pipfile.lock'], }), ); - fsExtra.readFile.mockResolvedValueOnce('New Pipfile.lock' as never); find.mockReturnValueOnce({ username: 'usernameOne', @@ -939,14 +992,16 @@ describe('modules/manager/pipenv/artifacts', () => { it('updates extraEnv if variable names differ from default', async () => { fsExtra.ensureDir.mockResolvedValue(undefined as never); - fsExtra.readFile.mockResolvedValueOnce('current pipfile.lock' as never); + mockFiles({ + '/Pipfile.lock': ['current Pipfile.lock', 'New Pipfile.lock'], + }); + const execSnapshots = mockExecAll(); git.getRepoStatus.mockResolvedValue( partial({ modified: ['Pipfile.lock'], }), ); - fsExtra.readFile.mockResolvedValueOnce('New Pipfile.lock' as never); find.mockReturnValueOnce({ username: 'usernameOne', From eddd30fd3969234008fdbce34d8fef133f250715 Mon Sep 17 00:00:00 2001 From: Sergei Zharinov Date: Tue, 18 Jun 2024 16:08:46 -0300 Subject: [PATCH 03/18] Fix mock helper --- lib/modules/manager/pipenv/artifacts.spec.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/modules/manager/pipenv/artifacts.spec.ts b/lib/modules/manager/pipenv/artifacts.spec.ts index 5895e58b8cbdd2..72b09a02847461 100644 --- a/lib/modules/manager/pipenv/artifacts.spec.ts +++ b/lib/modules/manager/pipenv/artifacts.spec.ts @@ -67,7 +67,8 @@ function mockFiles(mockFiles: MockFiles): void { fsExtra.readFile.mockImplementation(((name: string) => { for (const [key, value] of Object.entries(mockFiles)) { if (name.endsWith(key)) { - return Array.isArray(value) ? value.shift() : value; + const res = Array.isArray(value) ? value.shift() : value; + return Promise.resolve(res); } } return Promise.reject(new Error('File not found')); From 4941ab2a7987549f79fe05a0d02d718e0c6cb820 Mon Sep 17 00:00:00 2001 From: Sergei Zharinov Date: Tue, 18 Jun 2024 16:16:14 -0300 Subject: [PATCH 04/18] Fix mock helper --- lib/modules/manager/pipenv/artifacts.spec.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/modules/manager/pipenv/artifacts.spec.ts b/lib/modules/manager/pipenv/artifacts.spec.ts index 72b09a02847461..951085dc05326c 100644 --- a/lib/modules/manager/pipenv/artifacts.spec.ts +++ b/lib/modules/manager/pipenv/artifacts.spec.ts @@ -68,10 +68,10 @@ function mockFiles(mockFiles: MockFiles): void { for (const [key, value] of Object.entries(mockFiles)) { if (name.endsWith(key)) { const res = Array.isArray(value) ? value.shift() : value; - return Promise.resolve(res); + return res; } } - return Promise.reject(new Error('File not found')); + throw new Error('File not found'); }) as never); } From 510385878200c39575f5c6b48fedfab476011562 Mon Sep 17 00:00:00 2001 From: Sergei Zharinov Date: Thu, 20 Jun 2024 19:34:14 -0300 Subject: [PATCH 05/18] Fix --- lib/modules/manager/pipenv/artifacts.spec.ts | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/lib/modules/manager/pipenv/artifacts.spec.ts b/lib/modules/manager/pipenv/artifacts.spec.ts index 951085dc05326c..4d1471ee6f5608 100644 --- a/lib/modules/manager/pipenv/artifacts.spec.ts +++ b/lib/modules/manager/pipenv/artifacts.spec.ts @@ -67,8 +67,15 @@ function mockFiles(mockFiles: MockFiles): void { fsExtra.readFile.mockImplementation(((name: string) => { for (const [key, value] of Object.entries(mockFiles)) { if (name.endsWith(key)) { - const res = Array.isArray(value) ? value.shift() : value; - return res; + if (!Array.isArray(value)) { + return value; + } + + if (value.length > 1) { + return value.shift(); + } + + return value[0]; } } throw new Error('File not found'); From 59d151e19df286a81ac725af65dfec9e1b90ad2a Mon Sep 17 00:00:00 2001 From: Sergei Zharinov Date: Thu, 20 Jun 2024 20:42:13 -0300 Subject: [PATCH 06/18] feat(pipenv): Use "@renovatebot/detect-tools" for constraints detection --- lib/modules/manager/pipenv/artifacts.spec.ts | 56 +++++- lib/modules/manager/pipenv/artifacts.ts | 178 ++----------------- 2 files changed, 61 insertions(+), 173 deletions(-) diff --git a/lib/modules/manager/pipenv/artifacts.spec.ts b/lib/modules/manager/pipenv/artifacts.spec.ts index 4d1471ee6f5608..6f09292b934a54 100644 --- a/lib/modules/manager/pipenv/artifacts.spec.ts +++ b/lib/modules/manager/pipenv/artifacts.spec.ts @@ -128,6 +128,7 @@ describe('modules/manager/pipenv/artifacts', () => { it('returns null if unchanged', async () => { fsExtra.ensureDir.mockResolvedValue(undefined as never); + fsExtra.stat.mockResolvedValueOnce({} as never); mockFiles({ '/Pipfile.lock': JSON.stringify({ @@ -165,14 +166,18 @@ describe('modules/manager/pipenv/artifacts', () => { ['/tmp/renovate/cache/others/virtualenvs'], ]); expect(fsExtra.readFile.mock.calls).toEqual([ + ['/tmp/github/some/repo/Pipfile', 'utf8'], + ['/tmp/github/some/repo/Pipfile.lock', 'utf8'], ['/tmp/github/some/repo/Pipfile.lock', 'utf8'], ]); }); it('gets python full version from Pipfile', async () => { GlobalConfig.set({ ...adminConfig, binarySource: 'install' }); + fsExtra.stat.mockResolvedValueOnce({} as never); mockFiles({ + '/Pipfile': Fixtures.get('Pipfile1'), '/Pipfile.lock': JSON.stringify({ _meta: { requires: { python_full_version: '3.7.6' }, @@ -213,14 +218,17 @@ describe('modules/manager/pipenv/artifacts', () => { ['/tmp/renovate/cache/others/virtualenvs'], ]); expect(fsExtra.readFile.mock.calls).toEqual([ + ['/tmp/github/some/repo/Pipfile', 'utf8'], ['/tmp/github/some/repo/Pipfile.lock', 'utf8'], ]); }); it('gets python version from Pipfile', async () => { GlobalConfig.set({ ...adminConfig, binarySource: 'install' }); + fsExtra.stat.mockResolvedValueOnce({} as never); mockFiles({ + '/Pipfile': Fixtures.get('Pipfile2'), '/Pipfile.lock': JSON.stringify({ _meta: { requires: { python_full_version: '3.7.6' }, @@ -261,12 +269,14 @@ describe('modules/manager/pipenv/artifacts', () => { ['/tmp/renovate/cache/others/virtualenvs'], ]); expect(fsExtra.readFile.mock.calls).toEqual([ + ['/tmp/github/some/repo/Pipfile', 'utf8'], ['/tmp/github/some/repo/Pipfile.lock', 'utf8'], ]); }); it('gets full python version from .python-version', async () => { GlobalConfig.set({ ...adminConfig, binarySource: 'install' }); + fsExtra.stat.mockResolvedValueOnce({} as never); mockFiles({ '/Pipfile.lock': '{}', @@ -306,13 +316,16 @@ describe('modules/manager/pipenv/artifacts', () => { ['/tmp/renovate/cache/others/virtualenvs'], ]); expect(fsExtra.readFile.mock.calls).toEqual([ + ['/tmp/github/some/repo/Pipfile', 'utf8'], ['/tmp/github/some/repo/Pipfile.lock', 'utf8'], ['/tmp/github/some/repo/.python-version', 'utf8'], + ['/tmp/github/some/repo/Pipfile.lock', 'utf8'], ]); }); it('gets python stream, from .python-version', async () => { GlobalConfig.set({ ...adminConfig, binarySource: 'install' }); + fsExtra.stat.mockResolvedValueOnce({} as never); fsExtra.ensureDir.mockResolvedValue(undefined as never); @@ -351,13 +364,16 @@ describe('modules/manager/pipenv/artifacts', () => { ['/tmp/renovate/cache/others/virtualenvs'], ]); expect(fsExtra.readFile.mock.calls).toEqual([ + ['/tmp/github/some/repo/Pipfile', 'utf8'], ['/tmp/github/some/repo/Pipfile.lock', 'utf8'], ['/tmp/github/some/repo/.python-version', 'utf8'], + ['/tmp/github/some/repo/Pipfile.lock', 'utf8'], ]); }); it('handles no constraint', async () => { fsExtra.ensureDir.mockResolvedValue(undefined as never); + fsExtra.stat.mockResolvedValueOnce({} as never); mockFiles({ '/Pipfile.lock': 'unparseable pipfile lock', @@ -394,12 +410,16 @@ describe('modules/manager/pipenv/artifacts', () => { ['/tmp/renovate/cache/others/virtualenvs'], ]); expect(fsExtra.readFile.mock.calls).toEqual([ + ['/tmp/github/some/repo/Pipfile', 'utf8'], + ['/tmp/github/some/repo/Pipfile.lock', 'utf8'], + ['/tmp/github/some/repo/.python-version', 'utf8'], ['/tmp/github/some/repo/Pipfile.lock', 'utf8'], ]); }); it('returns updated Pipfile.lock', async () => { fsExtra.ensureDir.mockResolvedValue(undefined as never); + fsExtra.stat.mockResolvedValueOnce({} as never); mockFiles({ '/Pipfile.lock': ['current pipfile.lock', 'new pipfile.lock'], @@ -448,6 +468,9 @@ describe('modules/manager/pipenv/artifacts', () => { ['/tmp/renovate/cache/others/virtualenvs'], ]); expect(fsExtra.readFile.mock.calls).toEqual([ + ['/tmp/github/some/repo/Pipfile', 'utf8'], + ['/tmp/github/some/repo/Pipfile.lock', 'utf8'], + ['/tmp/github/some/repo/.python-version', 'utf8'], ['/tmp/github/some/repo/Pipfile.lock', 'utf8'], ['/tmp/github/some/repo/Pipfile.lock', 'utf8'], ]); @@ -455,12 +478,13 @@ describe('modules/manager/pipenv/artifacts', () => { it('supports docker mode', async () => { GlobalConfig.set(dockerAdminConfig); + fsExtra.stat.mockResolvedValueOnce({} as never); const pipFileLock = JSON.stringify({ _meta: { requires: { python_version: '3.7' } }, } satisfies PipfileLockSchema); mockFiles({ - '/Pipfile.lock': [pipFileLock, 'new lock'], + '/Pipfile.lock': [pipFileLock, pipFileLock, 'new lock'], }); fsExtra.ensureDir.mockResolvedValue(undefined as never); @@ -523,6 +547,8 @@ describe('modules/manager/pipenv/artifacts', () => { ['/tmp/renovate/cache/others/virtualenvs'], ]); expect(fsExtra.readFile.mock.calls).toEqual([ + ['/tmp/github/some/repo/Pipfile', 'utf8'], + ['/tmp/github/some/repo/Pipfile.lock', 'utf8'], ['/tmp/github/some/repo/Pipfile.lock', 'utf8'], ['/tmp/github/some/repo/Pipfile.lock', 'utf8'], ]); @@ -530,6 +556,7 @@ describe('modules/manager/pipenv/artifacts', () => { it('supports install mode', async () => { GlobalConfig.set({ ...adminConfig, binarySource: 'install' }); + fsExtra.stat.mockResolvedValueOnce({} as never); const pipFileLock = JSON.stringify({ _meta: { requires: { python_version: '3.6' } }, @@ -582,6 +609,8 @@ describe('modules/manager/pipenv/artifacts', () => { ['/tmp/renovate/cache/others/virtualenvs'], ]); expect(fsExtra.readFile.mock.calls).toEqual([ + ['/tmp/github/some/repo/Pipfile', 'utf8'], + ['/tmp/github/some/repo/Pipfile.lock', 'utf8'], ['/tmp/github/some/repo/Pipfile.lock', 'utf8'], ['/tmp/github/some/repo/Pipfile.lock', 'utf8'], ]); @@ -589,6 +618,7 @@ describe('modules/manager/pipenv/artifacts', () => { it('defaults to latest if no lock constraints', async () => { GlobalConfig.set({ ...adminConfig, binarySource: 'install' }); + fsExtra.stat.mockResolvedValueOnce({} as never); fsExtra.ensureDir.mockResolvedValue(undefined as never); mockFiles({ @@ -637,14 +667,17 @@ describe('modules/manager/pipenv/artifacts', () => { ['/tmp/renovate/cache/others/virtualenvs'], ]); expect(fsExtra.readFile.mock.calls).toEqual([ + ['/tmp/github/some/repo/Pipfile', 'utf8'], ['/tmp/github/some/repo/Pipfile.lock', 'utf8'], ['/tmp/github/some/repo/.python-version', 'utf8'], ['/tmp/github/some/repo/Pipfile.lock', 'utf8'], + ['/tmp/github/some/repo/Pipfile.lock', 'utf8'], ]); }); it('catches errors', async () => { fsExtra.ensureDir.mockResolvedValue(undefined as never); + fsExtra.stat.mockResolvedValueOnce({} as never); mockFiles({ '/Pipfile.lock': 'Current Pipfile.lock', @@ -666,13 +699,12 @@ describe('modules/manager/pipenv/artifacts', () => { ]); expect(fsExtra.ensureDir.mock.calls).toEqual([]); - expect(fsExtra.readFile.mock.calls).toEqual([ - ['/tmp/github/some/repo/Pipfile.lock', 'utf8'], - ]); + expect(fsExtra.readFile.mock.calls).toEqual([]); }); it('returns updated Pipenv.lock when doing lockfile maintenance', async () => { fsExtra.ensureDir.mockResolvedValue(undefined as never); + fsExtra.stat.mockResolvedValueOnce({} as never); mockFiles({ '/Pipfile.lock': ['Current Pipfile.lock', 'New Pipfile.lock'], @@ -711,6 +743,7 @@ describe('modules/manager/pipenv/artifacts', () => { it('uses pipenv version from Pipfile', async () => { fsExtra.ensureDir.mockResolvedValue(undefined as never); + fsExtra.stat.mockResolvedValueOnce({} as never); GlobalConfig.set(dockerAdminConfig); @@ -718,7 +751,7 @@ describe('modules/manager/pipenv/artifacts', () => { default: { pipenv: { version: '==2020.8.13' } }, } satisfies PipfileLockSchema); mockFiles({ - '/Pipfile.lock': [oldLock, 'new lock'], + '/Pipfile.lock': [oldLock, oldLock, 'new lock'], }); const execSnapshots = mockExecAll(); @@ -774,14 +807,17 @@ describe('modules/manager/pipenv/artifacts', () => { ['/tmp/renovate/cache/others/virtualenvs'], ]); expect(fsExtra.readFile.mock.calls).toEqual([ + ['/tmp/github/some/repo/Pipfile', 'utf8'], ['/tmp/github/some/repo/Pipfile.lock', 'utf8'], ['/tmp/github/some/repo/.python-version', 'utf8'], ['/tmp/github/some/repo/Pipfile.lock', 'utf8'], + ['/tmp/github/some/repo/Pipfile.lock', 'utf8'], ]); }); it('uses pipenv version from Pipfile dev packages', async () => { GlobalConfig.set(dockerAdminConfig); + fsExtra.stat.mockResolvedValueOnce({} as never); fsExtra.ensureDir.mockResolvedValue(undefined as never); @@ -789,7 +825,7 @@ describe('modules/manager/pipenv/artifacts', () => { develop: { pipenv: { version: '==2020.8.13' } }, } satisfies PipfileLockSchema) as never; mockFiles({ - '/Pipfile.lock': [oldLock, 'new lock'], + '/Pipfile.lock': [oldLock, oldLock, 'new lock'], }); const execSnapshots = mockExecAll(); @@ -845,14 +881,17 @@ describe('modules/manager/pipenv/artifacts', () => { ['/tmp/renovate/cache/others/virtualenvs'], ]); expect(fsExtra.readFile.mock.calls).toEqual([ + ['/tmp/github/some/repo/Pipfile', 'utf8'], ['/tmp/github/some/repo/Pipfile.lock', 'utf8'], ['/tmp/github/some/repo/.python-version', 'utf8'], ['/tmp/github/some/repo/Pipfile.lock', 'utf8'], + ['/tmp/github/some/repo/Pipfile.lock', 'utf8'], ]); }); it('uses pipenv version from config', async () => { GlobalConfig.set(dockerAdminConfig); + fsExtra.stat.mockResolvedValueOnce({} as never); fsExtra.ensureDir.mockResolvedValue(undefined as never); const oldLock = JSON.stringify({ @@ -915,6 +954,7 @@ describe('modules/manager/pipenv/artifacts', () => { ['/tmp/renovate/cache/others/virtualenvs'], ]); expect(fsExtra.readFile.mock.calls).toEqual([ + ['/tmp/github/some/repo/Pipfile', 'utf8'], ['/tmp/github/some/repo/Pipfile.lock', 'utf8'], ['/tmp/github/some/repo/.python-version', 'utf8'], ['/tmp/github/some/repo/Pipfile.lock', 'utf8'], @@ -923,6 +963,7 @@ describe('modules/manager/pipenv/artifacts', () => { it('passes private credential environment vars', async () => { fsExtra.ensureDir.mockResolvedValue(undefined as never); + fsExtra.stat.mockResolvedValueOnce({} as never); mockFiles({ '/Pipfile.lock': ['current Pipfile.lock', 'New Pipfile.lock'], @@ -985,7 +1026,7 @@ describe('modules/manager/pipenv/artifacts', () => { ${'${USERNAME}'} | ${'USERNAME'} ${'${USERNAME:-default}'} | ${'USERNAME'} ${'${COMPLEX_NAME_1:-default}'} | ${'COMPLEX_NAME_1'} - `('extractEnvironmentVariableName(%p)', ({ credential, result }) => { + `('extractEnvironmentVariableName($credential)', ({ credential, result }) => { expect(extractEnvironmentVariableName(credential)).toEqual(result); }); @@ -999,6 +1040,7 @@ describe('modules/manager/pipenv/artifacts', () => { it('updates extraEnv if variable names differ from default', async () => { fsExtra.ensureDir.mockResolvedValue(undefined as never); + fsExtra.stat.mockResolvedValueOnce({} as never); mockFiles({ '/Pipfile.lock': ['current Pipfile.lock', 'New Pipfile.lock'], diff --git a/lib/modules/manager/pipenv/artifacts.ts b/lib/modules/manager/pipenv/artifacts.ts index 6404c2b9c0a827..cda5e31b70b74d 100644 --- a/lib/modules/manager/pipenv/artifacts.ts +++ b/lib/modules/manager/pipenv/artifacts.ts @@ -1,5 +1,5 @@ +import * as detectTools from '@renovatebot/detect-tools'; import is from '@sindresorhus/is'; -import semver from 'semver'; import { TEMPORARY_ERROR } from '../../../constants/error-messages'; import { logger } from '../../../logger'; import type { HostRule } from '../../../types'; @@ -8,168 +8,19 @@ import type { ExecOptions, ExtraEnv, Opt } from '../../../util/exec/types'; import { deleteLocalFile, ensureCacheDir, - getSiblingFileName, + getParentDir, + localPathExists, readLocalFile, writeLocalFile, } from '../../../util/fs'; +import { ensureLocalPath } from '../../../util/fs/util'; import { getRepoStatus } from '../../../util/git'; import { find } from '../../../util/host-rules'; import { regEx } from '../../../util/regex'; -import { parse as parseToml } from '../../../util/toml'; import { parseUrl } from '../../../util/url'; import { PypiDatasource } from '../../datasource/pypi'; -import pep440 from '../../versioning/pep440'; -import type { - UpdateArtifact, - UpdateArtifactsConfig, - UpdateArtifactsResult, -} from '../types'; +import type { UpdateArtifact, UpdateArtifactsResult } from '../types'; import { extractPackageFile } from './extract'; -import { PipfileLockSchema } from './schema'; - -export async function getPythonConstraint( - pipfileName: string, - pipfileContent: string, - existingLockFileContent: string, - config: UpdateArtifactsConfig, -): Promise { - const { constraints = {} } = config; - const { python } = constraints; - - if (python) { - logger.debug(`Using python constraint ${python} from config`); - return python; - } - - // Try Pipfile first because it may have had its Python version updated - try { - const pipfile = parseToml(pipfileContent) as any; - const pythonFullVersion = pipfile.requires.python_full_version; - if (pythonFullVersion) { - logger.debug( - `Using python full version ${pythonFullVersion} from Pipfile`, - ); - return `== ${pythonFullVersion}`; - } - const pythonVersion = pipfile.requires.python_version; - if (pythonVersion) { - logger.debug(`Using python version ${pythonVersion} from Pipfile`); - return `== ${pythonVersion}.*`; - } - } catch (err) { - logger.warn({ err }, 'Error parsing Pipfile'); - } - - // Try Pipfile.lock next - try { - const result = PipfileLockSchema.safeParse(existingLockFileContent); - // istanbul ignore if: not easily testable - if (!result.success) { - logger.warn({ err: result.error }, 'Invalid Pipfile.lock'); - return undefined; - } - // Exact python version has been included since 2022.10.9. It is more specific than the major.minor version - // https://github.com/pypa/pipenv/blob/main/CHANGELOG.md#2022109-2022-10-09 - const pythonFullVersion = result.data._meta?.requires?.python_full_version; - if (pythonFullVersion) { - logger.debug( - `Using python full version ${pythonFullVersion} from Pipfile.lock`, - ); - return `== ${pythonFullVersion}`; - } - // Before 2022.10.9, only the major.minor version was included - const pythonVersion = result.data._meta?.requires?.python_version; - if (pythonVersion) { - logger.debug(`Using python version ${pythonVersion} from Pipfile.lock`); - return `== ${pythonVersion}.*`; - } - } catch (err) { - // Do nothing - } - - // Try looking for the contents of .python-version - const pythonVersionFileName = getSiblingFileName( - pipfileName, - '.python-version', - ); - try { - const pythonVersion = await readLocalFile(pythonVersionFileName, 'utf8'); - let pythonVersionConstraint; - if (pythonVersion && pep440.isVersion(pythonVersion)) { - if (pythonVersion.split('.').length >= 3) { - pythonVersionConstraint = `== ${pythonVersion}`; - } else { - pythonVersionConstraint = `== ${pythonVersion}.*`; - } - } - if (pythonVersionConstraint) { - logger.debug( - `Using python version ${pythonVersionConstraint} from ${pythonVersionFileName}`, - ); - return pythonVersionConstraint; - } - } catch (err) { - // Do nothing - } - - return undefined; -} - -export function getPipenvConstraint( - existingLockFileContent: string, - config: UpdateArtifactsConfig, -): string { - const { constraints = {} } = config; - const { pipenv } = constraints; - - if (pipenv) { - logger.debug('Using pipenv constraint from config'); - return pipenv; - } - try { - const result = PipfileLockSchema.safeParse(existingLockFileContent); - // istanbul ignore if: not easily testable - if (!result.success) { - logger.warn({ error: result.error }, 'Invalid Pipfile.lock'); - return ''; - } - if (result.data.default?.pipenv?.version) { - return result.data.default.pipenv.version; - } - if (result.data.develop?.pipenv?.version) { - return result.data.develop.pipenv.version; - } - // Exact python version has been included since 2022.10.9 - const pythonFullVersion = result.data._meta?.requires?.python_full_version; - if (is.string(pythonFullVersion) && semver.valid(pythonFullVersion)) { - // python_full_version was added after 3.6 was already deprecated, so it should be impossible to have a 3.6 version - // https://github.com/pypa/pipenv/blob/main/CHANGELOG.md#2022109-2022-10-09 - if (semver.satisfies(pythonFullVersion, '3.7.*')) { - // Python 3.7 support was dropped in pipenv 2023.10.20 - // https://github.com/pypa/pipenv/blob/main/CHANGELOG.md#20231020-2023-10-20 - return '< 2023.10.20'; - } - // Future deprecations will go here - } - // Before 2022.10.9, only the major.minor version was included - const pythonVersion = result.data._meta?.requires?.python_version; - if (pythonVersion) { - if (pythonVersion === '3.6') { - // Python 3.6 was deprecated in 2022.4.20 - // https://github.com/pypa/pipenv/blob/main/CHANGELOG.md#2022420-2022-04-20 - return '< 2022.4.20'; - } - if (pythonVersion === '3.7') { - // Python 3.7 was deprecated in 2023.10.20 but we shouldn't reach here unless we are < 2022.10.9 - // https://github.com/pypa/pipenv/blob/main/CHANGELOG.md#20231020-2023-10-20 - return '< 2022.10.9'; - } - } - } catch (err) { - // Do nothing - } - return ''; -} export function getMatchingHostRule(url: string): HostRule | null { const parsedUrl = parseUrl(url); @@ -278,8 +129,7 @@ export async function updateArtifacts({ logger.debug(`pipenv.updateArtifacts(${pipfileName})`); const lockFileName = pipfileName + '.lock'; - const existingLockFileContent = await readLocalFile(lockFileName, 'utf8'); - if (!existingLockFileContent) { + if (!(await localPathExists(lockFileName))) { logger.debug('No Pipfile.lock found'); return null; } @@ -289,16 +139,12 @@ export async function updateArtifacts({ await deleteLocalFile(lockFileName); } const cmd = 'pipenv lock'; - const tagConstraint = await getPythonConstraint( - pipfileName, - newPipfileContent, - existingLockFileContent, - config, - ); - const pipenvConstraint = getPipenvConstraint( - existingLockFileContent, - config, - ); + const pipfileDir = getParentDir(ensureLocalPath(pipfileName)); + const tagConstraint = + await detectTools.pipenv.getPythonConstraint(pipfileDir); + const pipenvConstraint = + config?.constraints?.pipenv ?? + (await detectTools.pipenv.getPipenvConstraint(pipfileDir)); const extraEnv: Opt = { PIPENV_CACHE_DIR: await ensureCacheDir('pipenv'), PIP_CACHE_DIR: await ensureCacheDir('pip'), From 777842b323f3cb49c3a29baf2693ac695d10da68 Mon Sep 17 00:00:00 2001 From: Sergei Zharinov Date: Mon, 1 Jul 2024 10:45:21 -0300 Subject: [PATCH 07/18] Add package --- package.json | 1 + pnpm-lock.yaml | 14 +++++++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 52f682a8abd08d..19b79ad5ec69fe 100644 --- a/package.json +++ b/package.json @@ -161,6 +161,7 @@ "@opentelemetry/sdk-trace-node": "1.25.0", "@opentelemetry/semantic-conventions": "1.25.0", "@qnighy/marshal": "0.1.3", + "@renovatebot/detect-tools": "1.0.0", "@renovatebot/kbpgp": "3.0.1", "@renovatebot/osv-offline": "1.5.7", "@renovatebot/pep440": "3.0.20", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b3917b1a4d8e35..3a958ef8b9d5b9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -68,6 +68,9 @@ importers: '@qnighy/marshal': specifier: 0.1.3 version: 0.1.3 + '@renovatebot/detect-tools': + specifier: 1.0.0 + version: 1.0.0 '@renovatebot/kbpgp': specifier: 3.0.1 version: 3.0.1 @@ -1193,7 +1196,6 @@ packages: '@ls-lint/ls-lint@2.2.3': resolution: {integrity: sha512-ekM12jNm/7O2I/hsRv9HvYkRdfrHpiV1epVuI2NP+eTIcEgdIdKkKCs9KgQydu/8R5YXTov9aHdOgplmCHLupw==} - cpu: [x64, arm64, s390x] os: [darwin, linux, win32] hasBin: true @@ -1516,6 +1518,9 @@ packages: peerDependencies: '@redis/client': ^1.0.0 + '@renovatebot/detect-tools@1.0.0': + resolution: {integrity: sha512-YgJrH+LqeRSuSyQgAF7YskS8sPnIajlHpJhcL8XhAqWErbJtBYW2QKBKcPo//cdmONOa5JXJ+6Z9MKnx9cqmmg==} + '@renovatebot/eslint-plugin@file:tools/eslint': resolution: {directory: tools/eslint, type: directory} @@ -7706,6 +7711,13 @@ snapshots: dependencies: '@redis/client': 1.5.16 + '@renovatebot/detect-tools@1.0.0': + dependencies: + fs-extra: 11.2.0 + toml-eslint-parser: 0.9.3 + upath: 2.0.1 + zod: 3.23.8 + '@renovatebot/eslint-plugin@file:tools/eslint': {} '@renovatebot/kbpgp@3.0.1': From 2e33fe9b67c880844df2430276ded39450f6d75f Mon Sep 17 00:00:00 2001 From: Sergei Zharinov Date: Mon, 1 Jul 2024 11:03:39 -0300 Subject: [PATCH 08/18] Remove unused schema --- lib/modules/manager/pipenv/artifacts.spec.ts | 17 ++++++----- lib/modules/manager/pipenv/schema.ts | 30 -------------------- 2 files changed, 8 insertions(+), 39 deletions(-) delete mode 100644 lib/modules/manager/pipenv/schema.ts diff --git a/lib/modules/manager/pipenv/artifacts.spec.ts b/lib/modules/manager/pipenv/artifacts.spec.ts index 6f09292b934a54..4c44d010b89950 100644 --- a/lib/modules/manager/pipenv/artifacts.spec.ts +++ b/lib/modules/manager/pipenv/artifacts.spec.ts @@ -24,7 +24,6 @@ import { extractEnvironmentVariableName, getMatchingHostRule, } from './artifacts'; -import type { PipfileLockSchema } from './schema'; import { updateArtifacts } from '.'; const datasource = mocked(_datasource); @@ -135,7 +134,7 @@ describe('modules/manager/pipenv/artifacts', () => { _meta: { requires: { python_full_version: '3.7.6' }, }, - } satisfies PipfileLockSchema), + }), }); const execSnapshots = mockExecAll(); @@ -182,7 +181,7 @@ describe('modules/manager/pipenv/artifacts', () => { _meta: { requires: { python_full_version: '3.7.6' }, }, - } satisfies PipfileLockSchema), + }), }); fsExtra.ensureDir.mockResolvedValue(undefined as never); @@ -233,7 +232,7 @@ describe('modules/manager/pipenv/artifacts', () => { _meta: { requires: { python_full_version: '3.7.6' }, }, - } satisfies PipfileLockSchema), + }), }); fsExtra.ensureDir.mockResolvedValue(undefined as never); @@ -482,7 +481,7 @@ describe('modules/manager/pipenv/artifacts', () => { const pipFileLock = JSON.stringify({ _meta: { requires: { python_version: '3.7' } }, - } satisfies PipfileLockSchema); + }); mockFiles({ '/Pipfile.lock': [pipFileLock, pipFileLock, 'new lock'], }); @@ -560,7 +559,7 @@ describe('modules/manager/pipenv/artifacts', () => { const pipFileLock = JSON.stringify({ _meta: { requires: { python_version: '3.6' } }, - } satisfies PipfileLockSchema); + }); mockFiles({ '/Pipfile.lock': [pipFileLock, 'new lock'], }); @@ -749,7 +748,7 @@ describe('modules/manager/pipenv/artifacts', () => { const oldLock = JSON.stringify({ default: { pipenv: { version: '==2020.8.13' } }, - } satisfies PipfileLockSchema); + }); mockFiles({ '/Pipfile.lock': [oldLock, oldLock, 'new lock'], }); @@ -823,7 +822,7 @@ describe('modules/manager/pipenv/artifacts', () => { const oldLock = JSON.stringify({ develop: { pipenv: { version: '==2020.8.13' } }, - } satisfies PipfileLockSchema) as never; + }) as never; mockFiles({ '/Pipfile.lock': [oldLock, oldLock, 'new lock'], }); @@ -896,7 +895,7 @@ describe('modules/manager/pipenv/artifacts', () => { const oldLock = JSON.stringify({ default: { pipenv: { version: '==2020.8.13' } }, - } satisfies PipfileLockSchema) as never; + }) as never; mockFiles({ '/Pipfile.lock': [oldLock, 'new lock'], }); diff --git a/lib/modules/manager/pipenv/schema.ts b/lib/modules/manager/pipenv/schema.ts deleted file mode 100644 index c7a59de51abe50..00000000000000 --- a/lib/modules/manager/pipenv/schema.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { z } from 'zod'; -import { Json } from '../../../util/schema-utils'; - -const PipfileLockEntrySchema = z - .record( - z.string(), - z.object({ - version: z.string().optional(), - }), - ) - .optional(); - -export const PipfileLockSchema = Json.pipe( - z.object({ - _meta: z - .object({ - requires: z - .object({ - python_version: z.string().optional(), - python_full_version: z.string().optional(), - }) - .optional(), - }) - .optional(), - default: PipfileLockEntrySchema, - develop: PipfileLockEntrySchema, - }), -); - -export type PipfileLockSchema = z.infer; From c25f822822a8ac4caff0588a9d1cc32d8323593f Mon Sep 17 00:00:00 2001 From: Sergei Zharinov Date: Tue, 2 Jul 2024 13:45:12 -0300 Subject: [PATCH 09/18] Revert unrelated changes --- lib/modules/manager/pipenv/artifacts.spec.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/modules/manager/pipenv/artifacts.spec.ts b/lib/modules/manager/pipenv/artifacts.spec.ts index e1fd0eb3e43dd0..315dae7c6a3387 100644 --- a/lib/modules/manager/pipenv/artifacts.spec.ts +++ b/lib/modules/manager/pipenv/artifacts.spec.ts @@ -42,9 +42,9 @@ process.env.CONTAINERBASE = 'true'; const adminConfig: RepoGlobalConfig = { // `join` fixes Windows CI - localDir: join('/tmp/github/some/repo'), - cacheDir: join('/tmp/renovate/cache'), - containerbaseDir: join('/tmp/renovate/cache/containerbase'), + localDir: join(join('/tmp/github/some/repo')), + cacheDir: join(join('/tmp/renovate/cache')), + containerbaseDir: join(join('/tmp/renovate/cache/containerbase')), }; const dockerAdminConfig = { ...adminConfig, From 86d7b8d08f366530f65fd14736181b94854bb047 Mon Sep 17 00:00:00 2001 From: Sergei Zharinov Date: Wed, 3 Jul 2024 17:05:29 -0300 Subject: [PATCH 10/18] Fix lockfile --- pnpm-lock.yaml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 58d7ee93f35220..0218f0761a7115 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1188,7 +1188,6 @@ packages: '@ls-lint/ls-lint@2.2.3': resolution: {integrity: sha512-ekM12jNm/7O2I/hsRv9HvYkRdfrHpiV1epVuI2NP+eTIcEgdIdKkKCs9KgQydu/8R5YXTov9aHdOgplmCHLupw==} - cpu: [x64, arm64, s390x] os: [darwin, linux, win32] hasBin: true @@ -5655,6 +5654,10 @@ packages: resolution: {integrity: sha512-khrZo4buq4qVmsGzS5yQjKe/WsFvV8fGfOjDQN0q4iy9FjRfPWRgTFrU8u1R2iu/SfWLhY9WnCi4Jhdrcbtg+g==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + toml-eslint-parser@0.9.3: + resolution: {integrity: sha512-moYoCvkNUAPCxSW9jmHmRElhm4tVJpHL8ItC/+uYD0EpPSFXbck7yREz9tNdJVTSpHVod8+HoipcpbQ0oE6gsw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + tr46@0.0.3: resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} @@ -12632,6 +12635,10 @@ snapshots: dependencies: eslint-visitor-keys: 3.4.3 + toml-eslint-parser@0.9.3: + dependencies: + eslint-visitor-keys: 3.4.3 + tr46@0.0.3: {} traverse@0.6.9: From ba5d3e9bb0d8e18f2980d7a73c9660e1fe7aca3d Mon Sep 17 00:00:00 2001 From: Sergei Zharinov Date: Mon, 15 Jul 2024 11:47:56 -0300 Subject: [PATCH 11/18] Bump `detect-tools` --- package.json | 2 +- pnpm-lock.yaml | 20 ++++++-------------- 2 files changed, 7 insertions(+), 15 deletions(-) diff --git a/package.json b/package.json index 3ec7642ee44b29..d2964da024136f 100644 --- a/package.json +++ b/package.json @@ -161,7 +161,7 @@ "@opentelemetry/sdk-trace-node": "1.25.1", "@opentelemetry/semantic-conventions": "1.25.1", "@qnighy/marshal": "0.1.3", - "@renovatebot/detect-tools": "1.0.0", + "@renovatebot/detect-tools": "1.0.1", "@renovatebot/kbpgp": "3.0.1", "@renovatebot/osv-offline": "1.5.7", "@renovatebot/pep440": "3.0.20", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 68fde736543cb6..3beb078d85eee6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -70,8 +70,8 @@ importers: specifier: 0.1.3 version: 0.1.3 '@renovatebot/detect-tools': - specifier: 1.0.0 - version: 1.0.0 + specifier: 1.0.1 + version: 1.0.1 '@renovatebot/kbpgp': specifier: 3.0.1 version: 3.0.1 @@ -1528,8 +1528,8 @@ packages: peerDependencies: '@redis/client': ^1.0.0 - '@renovatebot/detect-tools@1.0.0': - resolution: {integrity: sha512-YgJrH+LqeRSuSyQgAF7YskS8sPnIajlHpJhcL8XhAqWErbJtBYW2QKBKcPo//cdmONOa5JXJ+6Z9MKnx9cqmmg==} + '@renovatebot/detect-tools@1.0.1': + resolution: {integrity: sha512-LBKW14n+GA0Tkw5jTjnKd1jl7J3fRloZHHOTWz15yCrS7TbiwUOAC7Dt+JA7k1exHvrXvhM6TD4d624qYv9PWg==} '@renovatebot/eslint-plugin@file:tools/eslint': resolution: {directory: tools/eslint, type: directory} @@ -5653,10 +5653,6 @@ packages: resolution: {integrity: sha512-khrZo4buq4qVmsGzS5yQjKe/WsFvV8fGfOjDQN0q4iy9FjRfPWRgTFrU8u1R2iu/SfWLhY9WnCi4Jhdrcbtg+g==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - toml-eslint-parser@0.9.3: - resolution: {integrity: sha512-moYoCvkNUAPCxSW9jmHmRElhm4tVJpHL8ItC/+uYD0EpPSFXbck7yREz9tNdJVTSpHVod8+HoipcpbQ0oE6gsw==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - tr46@0.0.3: resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} @@ -7792,10 +7788,10 @@ snapshots: dependencies: '@redis/client': 1.5.17 - '@renovatebot/detect-tools@1.0.0': + '@renovatebot/detect-tools@1.0.1': dependencies: fs-extra: 11.2.0 - toml-eslint-parser: 0.9.3 + toml-eslint-parser: 0.10.0 upath: 2.0.1 zod: 3.23.8 @@ -12647,10 +12643,6 @@ snapshots: dependencies: eslint-visitor-keys: 3.4.3 - toml-eslint-parser@0.9.3: - dependencies: - eslint-visitor-keys: 3.4.3 - tr46@0.0.3: {} traverse@0.6.8: {} From 50270642172214e3c5acef64486eef57e375c69a Mon Sep 17 00:00:00 2001 From: Sergei Zharinov Date: Mon, 15 Jul 2024 11:54:22 -0300 Subject: [PATCH 12/18] Fix import --- lib/modules/manager/pipenv/artifacts.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/lib/modules/manager/pipenv/artifacts.ts b/lib/modules/manager/pipenv/artifacts.ts index cda5e31b70b74d..db762822e4ab43 100644 --- a/lib/modules/manager/pipenv/artifacts.ts +++ b/lib/modules/manager/pipenv/artifacts.ts @@ -1,4 +1,4 @@ -import * as detectTools from '@renovatebot/detect-tools'; +import { pipenv as pipenvDetect } from '@renovatebot/detect-tools'; import is from '@sindresorhus/is'; import { TEMPORARY_ERROR } from '../../../constants/error-messages'; import { logger } from '../../../logger'; @@ -140,11 +140,10 @@ export async function updateArtifacts({ } const cmd = 'pipenv lock'; const pipfileDir = getParentDir(ensureLocalPath(pipfileName)); - const tagConstraint = - await detectTools.pipenv.getPythonConstraint(pipfileDir); + const tagConstraint = await pipenvDetect.getPythonConstraint(pipfileDir); const pipenvConstraint = config?.constraints?.pipenv ?? - (await detectTools.pipenv.getPipenvConstraint(pipfileDir)); + (await pipenvDetect.getPipenvConstraint(pipfileDir)); const extraEnv: Opt = { PIPENV_CACHE_DIR: await ensureCacheDir('pipenv'), PIP_CACHE_DIR: await ensureCacheDir('pip'), From 6d9c6823b89c847dc6b6acfc02cbf017af16eb55 Mon Sep 17 00:00:00 2001 From: Sergei Zharinov Date: Mon, 15 Jul 2024 11:56:07 -0300 Subject: [PATCH 13/18] Revert unrelated lockfile change --- pnpm-lock.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3beb078d85eee6..0f626d9174fbd9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1188,6 +1188,7 @@ packages: '@ls-lint/ls-lint@2.2.3': resolution: {integrity: sha512-ekM12jNm/7O2I/hsRv9HvYkRdfrHpiV1epVuI2NP+eTIcEgdIdKkKCs9KgQydu/8R5YXTov9aHdOgplmCHLupw==} + cpu: [x64, arm64, s390x] os: [darwin, linux, win32] hasBin: true From e4f36c2544d08edfffe2c434d79c4d3a0fe3f690 Mon Sep 17 00:00:00 2001 From: Sergei Zharinov Date: Mon, 15 Jul 2024 11:56:07 -0300 Subject: [PATCH 14/18] Revert unrelated lockfile change --- lib/modules/manager/pipenv/artifacts.spec.ts | 3 --- lib/modules/manager/pipenv/artifacts.ts | 4 +++- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/lib/modules/manager/pipenv/artifacts.spec.ts b/lib/modules/manager/pipenv/artifacts.spec.ts index 315dae7c6a3387..8a4986a2e836d8 100644 --- a/lib/modules/manager/pipenv/artifacts.spec.ts +++ b/lib/modules/manager/pipenv/artifacts.spec.ts @@ -467,9 +467,6 @@ describe('modules/manager/pipenv/artifacts', () => { [expect.toEndWith(join('/tmp/renovate/cache/others/virtualenvs'))], ]); expect(fsExtra.readFile.mock.calls).toEqual([ - [expect.toEndWith(join('/tmp/github/some/repo/Pipfile')), 'utf8'], - [expect.toEndWith(join('/tmp/github/some/repo/Pipfile.lock')), 'utf8'], - [expect.toEndWith(join('/tmp/github/some/repo/.python-version')), 'utf8'], [expect.toEndWith(join('/tmp/github/some/repo/Pipfile.lock')), 'utf8'], [expect.toEndWith(join('/tmp/github/some/repo/Pipfile.lock')), 'utf8'], ]); diff --git a/lib/modules/manager/pipenv/artifacts.ts b/lib/modules/manager/pipenv/artifacts.ts index db762822e4ab43..4a8dce7dda4bbc 100644 --- a/lib/modules/manager/pipenv/artifacts.ts +++ b/lib/modules/manager/pipenv/artifacts.ts @@ -140,7 +140,9 @@ export async function updateArtifacts({ } const cmd = 'pipenv lock'; const pipfileDir = getParentDir(ensureLocalPath(pipfileName)); - const tagConstraint = await pipenvDetect.getPythonConstraint(pipfileDir); + const tagConstraint = + config?.constraints?.python ?? + (await pipenvDetect.getPythonConstraint(pipfileDir)); const pipenvConstraint = config?.constraints?.pipenv ?? (await pipenvDetect.getPipenvConstraint(pipfileDir)); From 41bc897bfba17b4e4800e3e05a81e253883af8b2 Mon Sep 17 00:00:00 2001 From: Sergei Zharinov Date: Sun, 4 Aug 2024 15:45:25 -0300 Subject: [PATCH 15/18] Bump to 1.0.3 --- package.json | 2 +- pnpm-lock.yaml | 11 +++++------ 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index d4da39a6a33208..97bfce12b0881e 100644 --- a/package.json +++ b/package.json @@ -157,7 +157,7 @@ "@opentelemetry/sdk-trace-node": "1.25.1", "@opentelemetry/semantic-conventions": "1.25.1", "@qnighy/marshal": "0.1.3", - "@renovatebot/detect-tools": "1.0.1", + "@renovatebot/detect-tools": "1.0.3", "@renovatebot/kbpgp": "3.0.1", "@renovatebot/osv-offline": "1.5.7", "@renovatebot/pep440": "3.0.20", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6c39d09cdfb8b3..e7844dcb03f55c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -70,8 +70,8 @@ importers: specifier: 0.1.3 version: 0.1.3 '@renovatebot/detect-tools': - specifier: 1.0.1 - version: 1.0.1 + specifier: 1.0.3 + version: 1.0.3 '@renovatebot/kbpgp': specifier: 3.0.1 version: 3.0.1 @@ -1185,7 +1185,6 @@ packages: '@ls-lint/ls-lint@2.2.3': resolution: {integrity: sha512-ekM12jNm/7O2I/hsRv9HvYkRdfrHpiV1epVuI2NP+eTIcEgdIdKkKCs9KgQydu/8R5YXTov9aHdOgplmCHLupw==} - cpu: [x64, arm64, s390x] os: [darwin, linux, win32] hasBin: true @@ -1526,8 +1525,8 @@ packages: peerDependencies: '@redis/client': ^1.0.0 - '@renovatebot/detect-tools@1.0.1': - resolution: {integrity: sha512-LBKW14n+GA0Tkw5jTjnKd1jl7J3fRloZHHOTWz15yCrS7TbiwUOAC7Dt+JA7k1exHvrXvhM6TD4d624qYv9PWg==} + '@renovatebot/detect-tools@1.0.3': + resolution: {integrity: sha512-oHK8GXhXbLyWoZxOtx2XXQ5lf7AHoZBP2/ueGq9nyU0+Z9SkwZ/SvtBGY2q7pk6WoPw8pOYthksLhe2g3dgA8Q==} '@renovatebot/eslint-plugin@file:tools/eslint': resolution: {directory: tools/eslint, type: directory} @@ -7835,7 +7834,7 @@ snapshots: dependencies: '@redis/client': 1.5.17 - '@renovatebot/detect-tools@1.0.1': + '@renovatebot/detect-tools@1.0.3': dependencies: fs-extra: 11.2.0 toml-eslint-parser: 0.10.0 From b941708a61a6825064f106c13c8e2edc487bd73d Mon Sep 17 00:00:00 2001 From: Sergei Zharinov Date: Sun, 4 Aug 2024 15:50:36 -0300 Subject: [PATCH 16/18] Return type constraints --- lib/modules/manager/pipenv/artifacts.spec.ts | 17 +++++++++-------- lib/modules/manager/pipenv/types.ts | 11 +++++++++++ 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/lib/modules/manager/pipenv/artifacts.spec.ts b/lib/modules/manager/pipenv/artifacts.spec.ts index 8a4986a2e836d8..09af11a40931a2 100644 --- a/lib/modules/manager/pipenv/artifacts.spec.ts +++ b/lib/modules/manager/pipenv/artifacts.spec.ts @@ -24,6 +24,7 @@ import { extractEnvironmentVariableName, getMatchingHostRule, } from './artifacts'; +import { PipfileLock } from './types'; import { updateArtifacts } from '.'; const datasource = mocked(_datasource); @@ -134,7 +135,7 @@ describe('modules/manager/pipenv/artifacts', () => { _meta: { requires: { python_full_version: '3.7.6' }, }, - }), + } satisfies PipfileLock), }); const execSnapshots = mockExecAll(); @@ -181,7 +182,7 @@ describe('modules/manager/pipenv/artifacts', () => { _meta: { requires: { python_full_version: '3.7.6' }, }, - }), + } satisfies PipfileLock), }); fsExtra.ensureDir.mockResolvedValue(undefined as never); @@ -232,7 +233,7 @@ describe('modules/manager/pipenv/artifacts', () => { _meta: { requires: { python_full_version: '3.7.6' }, }, - }), + } satisfies PipfileLock), }); fsExtra.ensureDir.mockResolvedValue(undefined as never); @@ -478,7 +479,7 @@ describe('modules/manager/pipenv/artifacts', () => { const pipFileLock = JSON.stringify({ _meta: { requires: { python_version: '3.7' } }, - }); + } satisfies PipfileLock); mockFiles({ '/Pipfile.lock': [pipFileLock, pipFileLock, 'new lock'], }); @@ -556,7 +557,7 @@ describe('modules/manager/pipenv/artifacts', () => { const pipFileLock = JSON.stringify({ _meta: { requires: { python_version: '3.6' } }, - }); + } satisfies PipfileLock); mockFiles({ '/Pipfile.lock': [pipFileLock, 'new lock'], }); @@ -745,7 +746,7 @@ describe('modules/manager/pipenv/artifacts', () => { const oldLock = JSON.stringify({ default: { pipenv: { version: '==2020.8.13' } }, - }); + } satisfies PipfileLock); mockFiles({ '/Pipfile.lock': [oldLock, oldLock, 'new lock'], }); @@ -819,7 +820,7 @@ describe('modules/manager/pipenv/artifacts', () => { const oldLock = JSON.stringify({ develop: { pipenv: { version: '==2020.8.13' } }, - }) as never; + } satisfies PipfileLock) as never; mockFiles({ '/Pipfile.lock': [oldLock, oldLock, 'new lock'], }); @@ -892,7 +893,7 @@ describe('modules/manager/pipenv/artifacts', () => { const oldLock = JSON.stringify({ default: { pipenv: { version: '==2020.8.13' } }, - }) as never; + } satisfies PipfileLock) as never; mockFiles({ '/Pipfile.lock': [oldLock, 'new lock'], }); diff --git a/lib/modules/manager/pipenv/types.ts b/lib/modules/manager/pipenv/types.ts index 51cbb9c186ca0e..9e1cbca60579c5 100644 --- a/lib/modules/manager/pipenv/types.ts +++ b/lib/modules/manager/pipenv/types.ts @@ -30,3 +30,14 @@ export type PipRequirement = file?: string; git?: string; }; + +export interface PipfileLock { + _meta?: { + requires?: { + python_version?: string; + python_full_version?: string; + }; + }; + default?: Record; + develop?: Record; +} From 97df9dc9f9aa44960521aa1aa19f7a42d85fa6fe Mon Sep 17 00:00:00 2001 From: Sergei Zharinov Date: Mon, 5 Aug 2024 09:13:01 -0300 Subject: [PATCH 17/18] Apply suggestions from code review Co-authored-by: Michael Kriese --- lib/modules/manager/pipenv/artifacts.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/modules/manager/pipenv/artifacts.ts b/lib/modules/manager/pipenv/artifacts.ts index 4a8dce7dda4bbc..8d2760ce357aa6 100644 --- a/lib/modules/manager/pipenv/artifacts.ts +++ b/lib/modules/manager/pipenv/artifacts.ts @@ -141,10 +141,10 @@ export async function updateArtifacts({ const cmd = 'pipenv lock'; const pipfileDir = getParentDir(ensureLocalPath(pipfileName)); const tagConstraint = - config?.constraints?.python ?? + config.constraints?.python ?? (await pipenvDetect.getPythonConstraint(pipfileDir)); const pipenvConstraint = - config?.constraints?.pipenv ?? + config.constraints?.pipenv ?? (await pipenvDetect.getPipenvConstraint(pipfileDir)); const extraEnv: Opt = { PIPENV_CACHE_DIR: await ensureCacheDir('pipenv'), From bc852b4ecc21d5396291754d031fcf9137885e31 Mon Sep 17 00:00:00 2001 From: Sergei Zharinov Date: Mon, 5 Aug 2024 21:26:41 -0300 Subject: [PATCH 18/18] Use `detect-tools` in the extract phase --- lib/modules/manager/pipenv/artifacts.spec.ts | 18 ++++++++-- lib/modules/manager/pipenv/extract.spec.ts | 35 ++++++++++++++++---- lib/modules/manager/pipenv/extract.ts | 24 ++++++-------- package.json | 2 +- pnpm-lock.yaml | 10 +++--- 5 files changed, 61 insertions(+), 28 deletions(-) diff --git a/lib/modules/manager/pipenv/artifacts.spec.ts b/lib/modules/manager/pipenv/artifacts.spec.ts index 09af11a40931a2..5cdb047e917969 100644 --- a/lib/modules/manager/pipenv/artifacts.spec.ts +++ b/lib/modules/manager/pipenv/artifacts.spec.ts @@ -168,6 +168,7 @@ describe('modules/manager/pipenv/artifacts', () => { expect(fsExtra.readFile.mock.calls).toEqual([ [expect.toEndWith(join('/tmp/github/some/repo/Pipfile')), 'utf8'], [expect.toEndWith(join('/tmp/github/some/repo/Pipfile.lock')), 'utf8'], + [expect.toEndWith(join('/tmp/github/some/repo/Pipfile')), 'utf8'], [expect.toEndWith(join('/tmp/github/some/repo/Pipfile.lock')), 'utf8'], ]); }); @@ -219,7 +220,9 @@ describe('modules/manager/pipenv/artifacts', () => { ]); expect(fsExtra.readFile.mock.calls).toEqual([ [expect.toEndWith(join('/tmp/github/some/repo/Pipfile')), 'utf8'], - [expect.toEndWith(join('/tmp/github/some/repo/Pipfile.lock')), 'utf8'], + [expect.toEndWith(join('/tmp/github/some/repo/Pipfile')), 'utf8'], + [expect.toEndWith(join('/tmp/github/some/repo/Pipfile')), 'utf8'], + [expect.toEndWith(join('/tmp/github/some/repo/Pipfile')), 'utf8'], ]); }); @@ -270,7 +273,9 @@ describe('modules/manager/pipenv/artifacts', () => { ]); expect(fsExtra.readFile.mock.calls).toEqual([ [expect.toEndWith(join('/tmp/github/some/repo/Pipfile')), 'utf8'], - [expect.toEndWith(join('/tmp/github/some/repo/Pipfile.lock')), 'utf8'], + [expect.toEndWith(join('/tmp/github/some/repo/Pipfile')), 'utf8'], + [expect.toEndWith(join('/tmp/github/some/repo/Pipfile')), 'utf8'], + [expect.toEndWith(join('/tmp/github/some/repo/Pipfile')), 'utf8'], ]); }); @@ -319,6 +324,7 @@ describe('modules/manager/pipenv/artifacts', () => { [expect.toEndWith(join('/tmp/github/some/repo/Pipfile')), 'utf8'], [expect.toEndWith(join('/tmp/github/some/repo/Pipfile.lock')), 'utf8'], [expect.toEndWith(join('/tmp/github/some/repo/.python-version')), 'utf8'], + [expect.toEndWith(join('/tmp/github/some/repo/Pipfile')), 'utf8'], [expect.toEndWith(join('/tmp/github/some/repo/Pipfile.lock')), 'utf8'], ]); }); @@ -367,6 +373,7 @@ describe('modules/manager/pipenv/artifacts', () => { [expect.toEndWith(join('/tmp/github/some/repo/Pipfile')), 'utf8'], [expect.toEndWith(join('/tmp/github/some/repo/Pipfile.lock')), 'utf8'], [expect.toEndWith(join('/tmp/github/some/repo/.python-version')), 'utf8'], + [expect.toEndWith(join('/tmp/github/some/repo/Pipfile')), 'utf8'], [expect.toEndWith(join('/tmp/github/some/repo/Pipfile.lock')), 'utf8'], ]); }); @@ -413,6 +420,7 @@ describe('modules/manager/pipenv/artifacts', () => { [expect.toEndWith(join('/tmp/github/some/repo/Pipfile')), 'utf8'], [expect.toEndWith(join('/tmp/github/some/repo/Pipfile.lock')), 'utf8'], [expect.toEndWith(join('/tmp/github/some/repo/.python-version')), 'utf8'], + [expect.toEndWith(join('/tmp/github/some/repo/Pipfile')), 'utf8'], [expect.toEndWith(join('/tmp/github/some/repo/Pipfile.lock')), 'utf8'], ]); }); @@ -468,6 +476,7 @@ describe('modules/manager/pipenv/artifacts', () => { [expect.toEndWith(join('/tmp/renovate/cache/others/virtualenvs'))], ]); expect(fsExtra.readFile.mock.calls).toEqual([ + [expect.toEndWith(join('/tmp/github/some/repo/Pipfile')), 'utf8'], [expect.toEndWith(join('/tmp/github/some/repo/Pipfile.lock')), 'utf8'], [expect.toEndWith(join('/tmp/github/some/repo/Pipfile.lock')), 'utf8'], ]); @@ -546,6 +555,7 @@ describe('modules/manager/pipenv/artifacts', () => { expect(fsExtra.readFile.mock.calls).toEqual([ [expect.toEndWith(join('/tmp/github/some/repo/Pipfile')), 'utf8'], [expect.toEndWith(join('/tmp/github/some/repo/Pipfile.lock')), 'utf8'], + [expect.toEndWith(join('/tmp/github/some/repo/Pipfile')), 'utf8'], [expect.toEndWith(join('/tmp/github/some/repo/Pipfile.lock')), 'utf8'], [expect.toEndWith(join('/tmp/github/some/repo/Pipfile.lock')), 'utf8'], ]); @@ -608,6 +618,7 @@ describe('modules/manager/pipenv/artifacts', () => { expect(fsExtra.readFile.mock.calls).toEqual([ [expect.toEndWith(join('/tmp/github/some/repo/Pipfile')), 'utf8'], [expect.toEndWith(join('/tmp/github/some/repo/Pipfile.lock')), 'utf8'], + [expect.toEndWith(join('/tmp/github/some/repo/Pipfile')), 'utf8'], [expect.toEndWith(join('/tmp/github/some/repo/Pipfile.lock')), 'utf8'], [expect.toEndWith(join('/tmp/github/some/repo/Pipfile.lock')), 'utf8'], ]); @@ -667,6 +678,7 @@ describe('modules/manager/pipenv/artifacts', () => { [expect.toEndWith(join('/tmp/github/some/repo/Pipfile')), 'utf8'], [expect.toEndWith(join('/tmp/github/some/repo/Pipfile.lock')), 'utf8'], [expect.toEndWith(join('/tmp/github/some/repo/.python-version')), 'utf8'], + [expect.toEndWith(join('/tmp/github/some/repo/Pipfile')), 'utf8'], [expect.toEndWith(join('/tmp/github/some/repo/Pipfile.lock')), 'utf8'], [expect.toEndWith(join('/tmp/github/some/repo/Pipfile.lock')), 'utf8'], ]); @@ -807,6 +819,7 @@ describe('modules/manager/pipenv/artifacts', () => { [expect.toEndWith(join('/tmp/github/some/repo/Pipfile')), 'utf8'], [expect.toEndWith(join('/tmp/github/some/repo/Pipfile.lock')), 'utf8'], [expect.toEndWith(join('/tmp/github/some/repo/.python-version')), 'utf8'], + [expect.toEndWith(join('/tmp/github/some/repo/Pipfile')), 'utf8'], [expect.toEndWith(join('/tmp/github/some/repo/Pipfile.lock')), 'utf8'], [expect.toEndWith(join('/tmp/github/some/repo/Pipfile.lock')), 'utf8'], ]); @@ -881,6 +894,7 @@ describe('modules/manager/pipenv/artifacts', () => { [expect.toEndWith(join('/tmp/github/some/repo/Pipfile')), 'utf8'], [expect.toEndWith(join('/tmp/github/some/repo/Pipfile.lock')), 'utf8'], [expect.toEndWith(join('/tmp/github/some/repo/.python-version')), 'utf8'], + [expect.toEndWith(join('/tmp/github/some/repo/Pipfile')), 'utf8'], [expect.toEndWith(join('/tmp/github/some/repo/Pipfile.lock')), 'utf8'], [expect.toEndWith(join('/tmp/github/some/repo/Pipfile.lock')), 'utf8'], ]); diff --git a/lib/modules/manager/pipenv/extract.spec.ts b/lib/modules/manager/pipenv/extract.spec.ts index 2f36c980927bb4..49727aa80075e9 100644 --- a/lib/modules/manager/pipenv/extract.spec.ts +++ b/lib/modules/manager/pipenv/extract.spec.ts @@ -1,9 +1,13 @@ import { codeBlock } from 'common-tags'; +import * as _fsExtra from 'fs-extra'; +import { join } from 'upath'; import { Fixtures } from '../../../../test/fixtures'; -import { fs } from '../../../../test/util'; +import { mocked } from '../../../../test/util'; +import { GlobalConfig } from '../../../config/global'; import { extractPackageFile } from '.'; -jest.mock('../../../util/fs'); +jest.mock('fs-extra'); +const fsExtra = mocked(_fsExtra); const pipfile1 = Fixtures.get('Pipfile1'); const pipfile2 = Fixtures.get('Pipfile2'); @@ -12,6 +16,16 @@ const pipfile4 = Fixtures.get('Pipfile4'); const pipfile5 = Fixtures.get('Pipfile5'); describe('modules/manager/pipenv/extract', () => { + beforeEach(() => { + GlobalConfig.set({ + localDir: join('/tmp/github/some/repo'), + }); + }); + + afterEach(() => { + GlobalConfig.reset(); + }); + describe('extractPackageFile()', () => { it('returns null for empty', async () => { expect(await extractPackageFile('[packages]\r\n', 'Pipfile')).toBeNull(); @@ -22,7 +36,8 @@ describe('modules/manager/pipenv/extract', () => { }); it('extracts dependencies', async () => { - fs.localPathExists.mockResolvedValueOnce(true); + fsExtra.stat.mockResolvedValueOnce({} as never); + fsExtra.readFile.mockResolvedValueOnce(pipfile1 as never); const res = await extractPackageFile(pipfile1, 'Pipfile'); expect(res).toMatchObject({ deps: [ @@ -99,7 +114,7 @@ describe('modules/manager/pipenv/extract', () => { }, ], extractedConstraints: { - python: '== 3.6.*', + python: '== 3.6.2', }, lockFiles: ['Pipfile.lock'], registryUrls: [ @@ -118,7 +133,8 @@ describe('modules/manager/pipenv/extract', () => { }); it('extracts multiple dependencies', async () => { - fs.localPathExists.mockResolvedValueOnce(true); + fsExtra.stat.mockResolvedValueOnce({} as never); + fsExtra.readFile.mockResolvedValueOnce(pipfile2 as never); const res = await extractPackageFile(pipfile2, 'Pipfile'); expect(res).toMatchObject({ deps: [ @@ -222,7 +238,8 @@ describe('modules/manager/pipenv/extract', () => { }); it('extracts example pipfile', async () => { - fs.localPathExists.mockResolvedValueOnce(true); + fsExtra.stat.mockResolvedValueOnce({} as never); + fsExtra.readFile.mockResolvedValueOnce(pipfile4 as never); const res = await extractPackageFile(pipfile4, 'Pipfile'); expect(res).toMatchObject({ deps: [ @@ -287,7 +304,7 @@ describe('modules/manager/pipenv/extract', () => { }); it('supports custom index', async () => { - fs.localPathExists.mockResolvedValueOnce(true); + fsExtra.stat.mockResolvedValueOnce({} as never); const res = await extractPackageFile(pipfile5, 'Pipfile'); expect(res).toMatchObject({ deps: [ @@ -318,6 +335,7 @@ describe('modules/manager/pipenv/extract', () => { [requires] python_version = "3.8" `; + fsExtra.readFile.mockResolvedValueOnce(content as never); const res = await extractPackageFile(content, 'Pipfile'); expect(res?.extractedConstraints?.python).toBe('== 3.8.*'); }); @@ -329,6 +347,7 @@ describe('modules/manager/pipenv/extract', () => { [requires] python_full_version = "3.8.6" `; + fsExtra.readFile.mockResolvedValueOnce(content as never); const res = await extractPackageFile(content, 'Pipfile'); expect(res?.extractedConstraints?.python).toBe('== 3.8.6'); }); @@ -338,6 +357,7 @@ describe('modules/manager/pipenv/extract', () => { [packages] pipenv = "==2020.8.13" `; + fsExtra.readFile.mockResolvedValue(content as never); const res = await extractPackageFile(content, 'Pipfile'); expect(res?.extractedConstraints?.pipenv).toBe('==2020.8.13'); }); @@ -347,6 +367,7 @@ describe('modules/manager/pipenv/extract', () => { [dev-packages] pipenv = "==2020.8.13" `; + fsExtra.readFile.mockResolvedValue(content as never); const res = await extractPackageFile(content, 'Pipfile'); expect(res?.extractedConstraints?.pipenv).toBe('==2020.8.13'); }); diff --git a/lib/modules/manager/pipenv/extract.ts b/lib/modules/manager/pipenv/extract.ts index cfdae749cb879e..a770b5b25c6f8d 100644 --- a/lib/modules/manager/pipenv/extract.ts +++ b/lib/modules/manager/pipenv/extract.ts @@ -1,8 +1,10 @@ +import { pipenv as pipenvDetect } from '@renovatebot/detect-tools'; import { RANGE_PATTERN } from '@renovatebot/pep440'; import is from '@sindresorhus/is'; import { logger } from '../../../logger'; import type { SkipReason } from '../../../types'; -import { localPathExists } from '../../../util/fs'; +import { getParentDir, localPathExists } from '../../../util/fs'; +import { ensureLocalPath } from '../../../util/fs/util'; import { regEx } from '../../../util/regex'; import { parse as parseToml } from '../../../util/toml'; import { PypiDatasource } from '../../datasource/pypi'; @@ -142,8 +144,6 @@ export async function extractPackageFile( res.registryUrls = sources.map((source) => source.url); } - let pipenv_constraint: PipRequirement | undefined; - res.deps = Object.entries(pipfile) .map(([category, section]) => { if ( @@ -154,10 +154,6 @@ export async function extractPackageFile( return []; } - if (section.pipenv && !pipenv_constraint) { - pipenv_constraint = section.pipenv; - } - return extractFromSection(category, section, sources); }) .flat(); @@ -168,14 +164,16 @@ export async function extractPackageFile( const extractedConstraints: Record = {}; - if (is.nonEmptyString(pipfile.requires?.python_version)) { - extractedConstraints.python = `== ${pipfile.requires.python_version}.*`; - } else if (is.nonEmptyString(pipfile.requires?.python_full_version)) { - extractedConstraints.python = `== ${pipfile.requires.python_full_version}`; + const pipfileDir = getParentDir(ensureLocalPath(packageFile)); + + const pythonConstraint = await pipenvDetect.getPythonConstraint(pipfileDir); + if (pythonConstraint) { + extractedConstraints.python = pythonConstraint; } - if (pipenv_constraint) { - extractedConstraints.pipenv = pipenv_constraint; + const pipenvConstraint = await pipenvDetect.getPipenvConstraint(pipfileDir); + if (pipenvConstraint) { + extractedConstraints.pipenv = pipenvConstraint; } const lockFileName = `${packageFile}.lock`; diff --git a/package.json b/package.json index c77e0fee31d0db..c8d42b6d2907e5 100644 --- a/package.json +++ b/package.json @@ -157,7 +157,7 @@ "@opentelemetry/sdk-trace-node": "1.25.1", "@opentelemetry/semantic-conventions": "1.25.1", "@qnighy/marshal": "0.1.3", - "@renovatebot/detect-tools": "1.0.3", + "@renovatebot/detect-tools": "1.0.4", "@renovatebot/kbpgp": "3.0.1", "@renovatebot/osv-offline": "1.5.7", "@renovatebot/pep440": "3.0.20", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index dbad13efee63b7..30cd8abf653823 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -70,8 +70,8 @@ importers: specifier: 0.1.3 version: 0.1.3 '@renovatebot/detect-tools': - specifier: 1.0.3 - version: 1.0.3 + specifier: 1.0.4 + version: 1.0.4 '@renovatebot/kbpgp': specifier: 3.0.1 version: 3.0.1 @@ -1522,8 +1522,8 @@ packages: peerDependencies: '@redis/client': ^1.0.0 - '@renovatebot/detect-tools@1.0.3': - resolution: {integrity: sha512-oHK8GXhXbLyWoZxOtx2XXQ5lf7AHoZBP2/ueGq9nyU0+Z9SkwZ/SvtBGY2q7pk6WoPw8pOYthksLhe2g3dgA8Q==} + '@renovatebot/detect-tools@1.0.4': + resolution: {integrity: sha512-s7RvoGgEolHIZ5IQUVoYJMa3uVDxMuz9TDq9zl+j678wNNygT4CoqaArxkZLbGzDU7Znf+HhhMGV5/etUd3ljQ==} '@renovatebot/eslint-plugin@file:tools/eslint': resolution: {directory: tools/eslint, type: directory} @@ -7824,7 +7824,7 @@ snapshots: dependencies: '@redis/client': 1.5.17 - '@renovatebot/detect-tools@1.0.3': + '@renovatebot/detect-tools@1.0.4': dependencies: fs-extra: 11.2.0 toml-eslint-parser: 0.10.0