diff --git a/lib/manager/poetry/artifacts.ts b/lib/manager/poetry/artifacts.ts index e935f71194daf5bc234b75dbfedbba2f1b6fc28a..7e46960ccba077d53c1cfed62ea7301eed2b4dc3 100644 --- a/lib/manager/poetry/artifacts.ts +++ b/lib/manager/poetry/artifacts.ts @@ -1,49 +1,51 @@ import is from '@sindresorhus/is'; -import { parse, join } from 'upath'; -import { outputFile, readFile } from 'fs-extra'; import { exec, ExecOptions } from '../../util/exec'; import { logger } from '../../logger'; import { UpdateArtifact, UpdateArtifactsResult } from '../common'; -import { platform } from '../../platform'; +import { + getSiblingFileName, + getSubDirectory, + readLocalFile, + writeLocalFile, +} from '../../util/fs'; export async function updateArtifacts({ packageFileName, updatedDeps, newPackageFileContent, - config, }: UpdateArtifact): Promise<UpdateArtifactsResult[] | null> { logger.debug(`poetry.updateArtifacts(${packageFileName})`); if (!is.nonEmptyArray(updatedDeps)) { logger.debug('No updated poetry deps - returning null'); return null; } - const subDirectory = parse(packageFileName).dir; - let lockFileName = join(subDirectory, 'poetry.lock'); - let existingLockFileContent = await platform.getFile(lockFileName); + const subDirectory = getSubDirectory(packageFileName); + // Try poetry.lock first + let lockFileName = getSiblingFileName(packageFileName, 'poetry.lock'); + let existingLockFileContent = await readLocalFile(lockFileName); if (!existingLockFileContent) { - lockFileName = join(subDirectory, 'pyproject.lock'); - existingLockFileContent = await platform.getFile(lockFileName); + // Try pyproject.lock next + lockFileName = getSiblingFileName(packageFileName, 'pyproject.lock'); + existingLockFileContent = await readLocalFile(lockFileName); if (!existingLockFileContent) { logger.debug(`No lock file found`); return null; } } logger.debug(`Updating ${lockFileName}`); - const localPackageFileName = join(config.localDir, packageFileName); - const localLockFileName = join(config.localDir, lockFileName); try { - await outputFile(localPackageFileName, newPackageFileContent); + await writeLocalFile(packageFileName, newPackageFileContent); const cmd: string[] = []; for (let i = 0; i < updatedDeps.length; i += 1) { const dep = updatedDeps[i]; cmd.push(`poetry update --lock --no-interaction ${dep}`); } const execOptions: ExecOptions = { - cwd: join(config.localDir, subDirectory), + subDirectory, docker: { image: 'renovate/poetry' }, }; await exec(cmd, execOptions); - const newPoetryLockContent = await readFile(localLockFileName, 'utf8'); + const newPoetryLockContent = await readLocalFile(lockFileName); if (existingLockFileContent === newPoetryLockContent) { logger.debug(`${lockFileName} is unchanged`); return null; diff --git a/lib/util/exec/index.ts b/lib/util/exec/index.ts index d47e37954ce956ef83a69386ae9c972215ec6ee0..e618acebe24ce00994612eb451d9ebb382ffb187 100644 --- a/lib/util/exec/index.ts +++ b/lib/util/exec/index.ts @@ -1,3 +1,4 @@ +import { join } from 'path'; import { hrtime } from 'process'; import { promisify } from 'util'; import { @@ -32,6 +33,7 @@ const pExec: ( type ExtraEnv<T = unknown> = Record<string, T>; export interface ExecOptions extends ChildProcessExecOptions { + subDirectory?: string; extraEnv?: Opt<ExtraEnv>; docker?: Opt<DockerOptions>; } @@ -77,13 +79,19 @@ export async function exec( cmd: string | string[], opts: ExecOptions = {} ): Promise<ExecResult> { - const { env, extraEnv, docker } = opts; - const cwd = opts.cwd || execConfig.localDir; + const { env, extraEnv, docker, subDirectory } = opts; + let cwd; + // istanbul ignore if + if (subDirectory) { + cwd = join(execConfig.localDir, subDirectory); + } + cwd = cwd || opts.cwd || execConfig.localDir; const childEnv = createChildEnv(env, extraEnv); const execOptions: ExecOptions = { ...opts }; delete execOptions.extraEnv; delete execOptions.docker; + delete execOptions.subDirectory; const pExecOptions: ChildProcessExecOptions & { encoding: string } = { encoding: 'utf-8', diff --git a/lib/util/fs.ts b/lib/util/fs.ts new file mode 100644 index 0000000000000000000000000000000000000000..c2975955053f900ce7d3d071ee6a296c1d28f970 --- /dev/null +++ b/lib/util/fs.ts @@ -0,0 +1,40 @@ +import { parse, join } from 'upath'; +import { outputFile, readFile } from 'fs-extra'; +import { logger } from '../logger'; + +let localDir = ''; + +export function setFsConfig(config: any): void { + localDir = config.localDir; +} + +export function getSubDirectory(fileName: string): string { + return parse(fileName).dir; +} + +export function getSiblingFileName( + existingFileNameWithPath: string, + otherFileName: string +): string { + const subDirectory = getSubDirectory(existingFileNameWithPath); + return join(subDirectory, otherFileName); +} + +export async function readLocalFile(fileName: string): Promise<string> { + const localFileName = join(localDir, fileName); + try { + const fileContent = await readFile(localFileName, 'utf8'); + return fileContent; + } catch (err) /* istanbul ignore next */ { + logger.trace({ err }, 'Error reading local file'); + return null; + } +} + +export async function writeLocalFile( + fileName: string, + fileContent: string +): Promise<void> { + const localFileName = join(localDir, fileName); + await outputFile(localFileName, fileContent); +} diff --git a/lib/util/index.ts b/lib/util/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..181fa5ee30c1403baf389a03a14ac8480b2878f2 --- /dev/null +++ b/lib/util/index.ts @@ -0,0 +1,7 @@ +import { setExecConfig } from './exec'; +import { setFsConfig } from './fs'; + +export function setUtilConfig(config: any): void { + setExecConfig(config); + setFsConfig(config); +} diff --git a/lib/workers/global/index.ts b/lib/workers/global/index.ts index 0762814b5e859b8bda071fd8568d1f4c00caee30..409b7e255a204fe3bc5445dccdd9c8f6d01371b4 100644 --- a/lib/workers/global/index.ts +++ b/lib/workers/global/index.ts @@ -12,7 +12,7 @@ import { initPlatform } from '../../platform'; import * as hostRules from '../../util/host-rules'; import { printStats } from '../../util/got/stats'; import * as limits from './limits'; -import { setExecConfig } from '../../util/exec'; +import { setUtilConfig } from '../../util'; type RenovateConfig = configParser.RenovateConfig; type RenovateRepository = configParser.RenovateRepository; @@ -77,7 +77,7 @@ export async function start(): Promise<0 | 1> { break; } const repoConfig = await getRepositoryConfig(config, repository); - setExecConfig(repoConfig); + setUtilConfig(repoConfig); if (repoConfig.hostRules) { hostRules.clear(); repoConfig.hostRules.forEach(rule => hostRules.add(rule)); diff --git a/test/manager/poetry/artifacts.spec.ts b/test/manager/poetry/artifacts.spec.ts index c894f631e9d3bbe0c6be47c643bbd89f2c528b05..bb6097ee639224fbb9d3300de88b24635f29b222 100644 --- a/test/manager/poetry/artifacts.spec.ts +++ b/test/manager/poetry/artifacts.spec.ts @@ -2,7 +2,6 @@ import { join } from 'upath'; import _fs from 'fs-extra'; import { exec as _exec } from 'child_process'; import { updateArtifacts } from '../../../lib/manager/poetry/artifacts'; -import { platform as _platform } from '../../../lib/platform'; import { mocked } from '../../util'; import { envMock, mockExecAll } from '../../execUtil'; import * as _env from '../../../lib/util/exec/env'; @@ -16,7 +15,6 @@ jest.mock('../../../lib/util/exec/env'); const fs: jest.Mocked<typeof _fs> = _fs as any; const exec: jest.Mock<typeof _exec> = _exec as any; const env = mocked(_env); -const platform = mocked(_platform); const config = { localDir: join('/tmp/github/some/repo'), @@ -50,7 +48,7 @@ describe('.updateArtifacts()', () => { ).toBeNull(); }); it('returns null if unchanged', async () => { - platform.getFile.mockResolvedValueOnce('Current poetry.lock'); + fs.readFile.mockReturnValueOnce('Current poetry.lock' as any); const execSnapshots = mockExecAll(exec); fs.readFile.mockReturnValueOnce('Current poetry.lock' as any); const updatedDeps = ['dep1']; @@ -65,7 +63,7 @@ describe('.updateArtifacts()', () => { expect(execSnapshots).toMatchSnapshot(); }); it('returns updated poetry.lock', async () => { - platform.getFile.mockResolvedValueOnce('Old poetry.lock'); + fs.readFile.mockResolvedValueOnce('Old poetry.lock' as any); const execSnapshots = mockExecAll(exec); fs.readFile.mockReturnValueOnce('New poetry.lock' as any); const updatedDeps = ['dep1']; @@ -85,7 +83,7 @@ describe('.updateArtifacts()', () => { binarySource: BinarySource.Docker, dockerUser: 'foobar', }); - platform.getFile.mockResolvedValueOnce('Old poetry.lock'); + fs.readFile.mockResolvedValueOnce('Old poetry.lock' as any); const execSnapshots = mockExecAll(exec); fs.readFile.mockReturnValueOnce('New poetry.lock' as any); const updatedDeps = ['dep1']; @@ -102,7 +100,7 @@ describe('.updateArtifacts()', () => { expect(execSnapshots).toMatchSnapshot(); }); it('catches errors', async () => { - platform.getFile.mockResolvedValueOnce('Current poetry.lock'); + fs.readFile.mockResolvedValueOnce('Current poetry.lock' as any); fs.outputFile.mockImplementationOnce(() => { throw new Error('not found'); });