diff --git a/lib/manager/composer/__snapshots__/extract.spec.ts.snap b/lib/manager/composer/__snapshots__/extract.spec.ts.snap index 22d84372354b698dffa317fd1e86701597842fca..8e7f636a648c7c9e4e4fdbd0cac6d3795a7fe2e0 100644 --- a/lib/manager/composer/__snapshots__/extract.spec.ts.snap +++ b/lib/manager/composer/__snapshots__/extract.spec.ts.snap @@ -2,10 +2,6 @@ exports[`manager/composer/extract extractPackageFile() extracts dependencies with lock file 1`] = ` Object { - "constraints": Object { - "composer": "^1.10.0", - "php": ">=5.3.2", - }, "deps": Array [ Object { "currentValue": ">=5.3.2", @@ -216,10 +212,6 @@ Object { exports[`manager/composer/extract extractPackageFile() extracts dependencies with no lock file 1`] = ` Object { - "constraints": Object { - "composer": "^1.10.0", - "php": ">=5.3.2", - }, "deps": Array [ Object { "currentValue": ">=5.3.2", @@ -427,10 +419,6 @@ Object { exports[`manager/composer/extract extractPackageFile() extracts object registryUrls 1`] = ` Object { - "constraints": Object { - "composer": "1.*", - "php": ">=5.5", - }, "deps": Array [ Object { "currentValue": ">=5.5", @@ -532,9 +520,6 @@ Object { exports[`manager/composer/extract extractPackageFile() extracts object repositories and registryUrls with lock file 1`] = ` Object { - "constraints": Object { - "composer": "1.*", - }, "deps": Array [ Object { "currentValue": "*", @@ -572,9 +557,6 @@ Object { exports[`manager/composer/extract extractPackageFile() extracts registryUrls 1`] = ` Object { - "constraints": Object { - "composer": "^1.10.0", - }, "deps": Array [ Object { "currentValue": "*", @@ -617,9 +599,6 @@ Object { exports[`manager/composer/extract extractPackageFile() extracts repositories and registryUrls 1`] = ` Object { - "constraints": Object { - "composer": "1.*", - }, "deps": Array [ Object { "currentValue": "*", diff --git a/lib/manager/composer/artifacts.spec.ts b/lib/manager/composer/artifacts.spec.ts index f160b108c915950bbc46f611a700d3675cf8b7dc..012e7c345854e8b35e2b90e8d51015a4c7a6074a 100644 --- a/lib/manager/composer/artifacts.spec.ts +++ b/lib/manager/composer/artifacts.spec.ts @@ -1,6 +1,5 @@ -import { exec as _exec } from 'child_process'; import { join } from 'upath'; -import { envMock, mockExecAll } from '../../../test/exec-util'; +import { envMock, exec, mockExecAll } from '../../../test/exec-util'; import { env, fs, git, mocked, partial } from '../../../test/util'; import { setAdminConfig } from '../../config/admin'; import type { RepoAdminConfig } from '../../config/types'; @@ -22,7 +21,6 @@ jest.mock('../../../lib/datasource'); jest.mock('../../util/fs'); jest.mock('../../util/git'); -const exec: jest.Mock<typeof _exec> = _exec as any; const datasource = mocked(_datasource); const config: UpdateArtifactsConfig = { @@ -55,9 +53,11 @@ describe('.updateArtifacts()', () => { join(adminConfig.cacheDir, './others/composer') ); }); + afterEach(() => { setAdminConfig(); }); + it('returns if no composer.lock found', async () => { expect( await composer.updateArtifacts({ @@ -68,10 +68,11 @@ describe('.updateArtifacts()', () => { }) ).toBeNull(); }); + it('returns null if unchanged', async () => { - fs.readLocalFile.mockResolvedValueOnce('Current composer.lock' as any); + fs.readLocalFile.mockResolvedValueOnce('{}'); const execSnapshots = mockExecAll(exec); - fs.readLocalFile.mockReturnValueOnce('Current composer.lock' as any); + fs.readLocalFile.mockResolvedValueOnce('{}'); git.getRepoStatus.mockResolvedValue(repoStatus); setAdminConfig({ ...adminConfig, allowScripts: true }); expect( @@ -84,6 +85,7 @@ describe('.updateArtifacts()', () => { ).toBeNull(); expect(execSnapshots).toMatchSnapshot(); }); + it('uses hostRules to set COMPOSER_AUTH', async () => { hostRules.add({ hostType: PLATFORM_TYPE_GITHUB, @@ -112,9 +114,9 @@ describe('.updateArtifacts()', () => { username: 'some-other-username', password: 'some-other-password', }); - fs.readLocalFile.mockResolvedValueOnce('Current composer.lock' as any); + fs.readLocalFile.mockResolvedValueOnce('{}'); const execSnapshots = mockExecAll(exec); - fs.readLocalFile.mockReturnValueOnce('Current composer.lock' as any); + fs.readLocalFile.mockResolvedValueOnce('{}'); const authConfig = { ...config, registryUrls: ['https://packagist.renovatebot.com'], @@ -130,10 +132,11 @@ describe('.updateArtifacts()', () => { ).toBeNull(); expect(execSnapshots).toMatchSnapshot(); }); + it('returns updated composer.lock', async () => { - fs.readLocalFile.mockResolvedValueOnce('Current composer.lock' as any); + fs.readLocalFile.mockResolvedValueOnce('{}'); const execSnapshots = mockExecAll(exec); - fs.readLocalFile.mockReturnValueOnce('New composer.lock' as any); + fs.readLocalFile.mockResolvedValueOnce('{}'); git.getRepoStatus.mockResolvedValue({ ...repoStatus, modified: ['composer.lock'], @@ -148,12 +151,13 @@ describe('.updateArtifacts()', () => { ).not.toBeNull(); expect(execSnapshots).toMatchSnapshot(); }); + it('supports vendor directory update', async () => { const foo = join('vendor/foo/Foo.php'); const bar = join('vendor/bar/Bar.php'); const baz = join('vendor/baz/Baz.php'); fs.localPathExists.mockResolvedValueOnce(true); - fs.readLocalFile.mockResolvedValueOnce('Current composer.lock' as any); + fs.readLocalFile.mockResolvedValueOnce('{}'); const execSnapshots = mockExecAll(exec); git.getRepoStatus.mockResolvedValueOnce({ ...repoStatus, @@ -161,10 +165,10 @@ describe('.updateArtifacts()', () => { not_added: [bar], deleted: [baz], }); - fs.readLocalFile.mockResolvedValueOnce('New composer.lock' as any); - fs.readLocalFile.mockResolvedValueOnce('Foo' as any); - fs.readLocalFile.mockResolvedValueOnce('Bar' as any); - fs.getSiblingFileName.mockReturnValueOnce('vendor' as any); + fs.readLocalFile.mockResolvedValueOnce('{ }'); + fs.readLocalFile.mockResolvedValueOnce('Foo'); + fs.readLocalFile.mockResolvedValueOnce('Bar'); + fs.getSiblingFileName.mockReturnValueOnce('vendor'); const res = await composer.updateArtifacts({ packageFileName: 'composer.json', updatedDeps: [], @@ -173,17 +177,18 @@ describe('.updateArtifacts()', () => { }); expect(res).not.toBeNull(); expect(res?.map(({ file }) => file)).toEqual([ - { contents: 'New composer.lock', name: 'composer.lock' }, + { contents: '{ }', name: 'composer.lock' }, { contents: 'Foo', name: foo }, { contents: 'Bar', name: bar }, { contents: baz, name: '|delete|' }, ]); expect(execSnapshots).toMatchSnapshot(); }); + it('performs lockFileMaintenance', async () => { - fs.readLocalFile.mockResolvedValueOnce('Current composer.lock' as any); + fs.readLocalFile.mockResolvedValueOnce('{}'); const execSnapshots = mockExecAll(exec); - fs.readLocalFile.mockReturnValueOnce('New composer.lock' as any); + fs.readLocalFile.mockResolvedValueOnce('{ }'); git.getRepoStatus.mockResolvedValue({ ...repoStatus, modified: ['composer.lock'], @@ -201,13 +206,14 @@ describe('.updateArtifacts()', () => { ).not.toBeNull(); expect(execSnapshots).toMatchSnapshot(); }); + it('supports docker mode', async () => { setAdminConfig({ ...adminConfig, binarySource: 'docker' }); - fs.readLocalFile.mockResolvedValueOnce('Current composer.lock' as any); + fs.readLocalFile.mockResolvedValueOnce('{}'); const execSnapshots = mockExecAll(exec); - fs.readLocalFile.mockReturnValueOnce('New composer.lock' as any); + fs.readLocalFile.mockResolvedValueOnce('{ }'); datasource.getPkgReleases.mockResolvedValueOnce({ releases: [ { version: '1.10.0' }, @@ -232,11 +238,12 @@ describe('.updateArtifacts()', () => { ).not.toBeNull(); expect(execSnapshots).toMatchSnapshot(); }); + it('supports global mode', async () => { setAdminConfig({ ...adminConfig, binarySource: 'global' }); - fs.readLocalFile.mockResolvedValueOnce('Current composer.lock' as any); + fs.readLocalFile.mockResolvedValueOnce('{}'); const execSnapshots = mockExecAll(exec); - fs.readLocalFile.mockReturnValueOnce('New composer.lock' as any); + fs.readLocalFile.mockResolvedValueOnce('{ }'); git.getRepoStatus.mockResolvedValue({ ...repoStatus, modified: ['composer.lock'], @@ -251,8 +258,9 @@ describe('.updateArtifacts()', () => { ).not.toBeNull(); expect(execSnapshots).toMatchSnapshot(); }); + it('catches errors', async () => { - fs.readLocalFile.mockResolvedValueOnce('Current composer.lock' as any); + fs.readLocalFile.mockResolvedValueOnce('{}'); fs.writeLocalFile.mockImplementationOnce(() => { throw new Error('not found'); }); @@ -265,8 +273,9 @@ describe('.updateArtifacts()', () => { }) ).toMatchSnapshot(); }); + it('catches unmet requirements errors', async () => { - fs.readLocalFile.mockResolvedValueOnce('Current composer.lock' as any); + fs.readLocalFile.mockResolvedValueOnce('{}'); fs.writeLocalFile.mockImplementationOnce(() => { throw new Error( 'fooYour requirements could not be resolved to an installable set of packages.bar' @@ -281,8 +290,9 @@ describe('.updateArtifacts()', () => { }) ).toMatchSnapshot(); }); + it('throws for disk space', async () => { - fs.readLocalFile.mockResolvedValueOnce('Current composer.lock' as any); + fs.readLocalFile.mockResolvedValueOnce('{}'); fs.writeLocalFile.mockImplementationOnce(() => { throw new Error( 'vendor/composer/07fe2366/sebastianbergmann-php-code-coverage-c896779/src/Report/Html/Renderer/Template/js/d3.min.js: write error (disk full?). Continue? (y/n/^C) ' @@ -297,10 +307,11 @@ describe('.updateArtifacts()', () => { }) ).rejects.toThrow(); }); + it('disables ignorePlatformReqs', async () => { - fs.readLocalFile.mockResolvedValueOnce('Current composer.lock' as any); + fs.readLocalFile.mockResolvedValueOnce('{}'); const execSnapshots = mockExecAll(exec); - fs.readLocalFile.mockReturnValueOnce('New composer.lock' as any); + fs.readLocalFile.mockResolvedValueOnce('{ }'); git.getRepoStatus.mockResolvedValue({ ...repoStatus, modified: ['composer.lock'], diff --git a/lib/manager/composer/artifacts.ts b/lib/manager/composer/artifacts.ts index 9d6af8df8c6abbc3fb6ce337d93209bc5ecec927..9beef4f7de72780e71bbc6472e684a963e9c596c 100644 --- a/lib/manager/composer/artifacts.ts +++ b/lib/manager/composer/artifacts.ts @@ -25,7 +25,11 @@ import { getRepoStatus } from '../../util/git'; import * as hostRules from '../../util/host-rules'; import type { UpdateArtifact, UpdateArtifactsResult } from '../types'; import type { AuthJson } from './types'; -import { composerVersioningId, getConstraint } from './utils'; +import { + composerVersioningId, + extractContraints, + getConstraint, +} from './utils'; function getAuthJson(): string | null { const authJson: AuthJson = {}; @@ -82,7 +86,7 @@ export async function updateArtifacts({ ); const lockFileName = packageFileName.replace(/\.json$/, '.lock'); - const existingLockFileContent = await readLocalFile(lockFileName); + const existingLockFileContent = await readLocalFile(lockFileName, 'utf8'); if (!existingLockFileContent) { logger.debug('No composer.lock found'); return null; @@ -93,6 +97,15 @@ export async function updateArtifacts({ await ensureLocalDir(vendorDir); try { await writeLocalFile(packageFileName, newPackageFileContent); + + const constraints = { + ...extractContraints( + JSON.parse(newPackageFileContent), + JSON.parse(existingLockFileContent) + ), + ...config.constraints, + }; + if (config.isLockFileMaintenance) { await deleteLocalFile(lockFileName); } @@ -105,7 +118,7 @@ export async function updateArtifacts({ }, docker: { image: 'composer', - tagConstraint: getConstraint(config), + tagConstraint: getConstraint(constraints), tagScheme: composerVersioningId, }, }; diff --git a/lib/manager/composer/extract.ts b/lib/manager/composer/extract.ts index e0faa9fed445a9491cb708f91c9169499b379cfa..e651bd9d81662071eaff0bdfa868b6d7fa2e9664 100644 --- a/lib/manager/composer/extract.ts +++ b/lib/manager/composer/extract.ts @@ -12,7 +12,6 @@ import type { ComposerManagerData, Repo, } from './types'; -import { extractContraints } from './utils'; /** * The regUrl is expected to be a base URL. GitLab composer repository installation guide specifies @@ -116,8 +115,6 @@ export async function extractPackageFile( res.registryUrls = registryUrls; } - res.constraints = extractContraints(composerJson, lockParsed); - const deps = []; const depTypes = ['require', 'require-dev']; for (const depType of depTypes) { diff --git a/lib/manager/composer/utils.spec.ts b/lib/manager/composer/utils.spec.ts index 824751e824f607f29acb3d22f3361c45c65ff288..6c488a0d0c85cb92cb55b070e42838a42aa4f7e0 100644 --- a/lib/manager/composer/utils.spec.ts +++ b/lib/manager/composer/utils.spec.ts @@ -4,9 +4,7 @@ import { extractContraints, getConstraint } from './utils'; describe(getName(), () => { describe('getConstraint', () => { it('returns from config', () => { - expect(getConstraint({ constraints: { composer: '1.1.0' } })).toEqual( - '1.1.0' - ); + expect(getConstraint({ composer: '1.1.0' })).toEqual('1.1.0'); }); it('returns from null', () => { diff --git a/lib/manager/composer/utils.ts b/lib/manager/composer/utils.ts index d5f516e1e8fb7b88d5f97392ff1dc5a97660ec81..8a77b5c75fc367512b38ec5becc5e7b59638ab72 100644 --- a/lib/manager/composer/utils.ts +++ b/lib/manager/composer/utils.ts @@ -1,12 +1,10 @@ import { logger } from '../../logger'; import { api, id as composerVersioningId } from '../../versioning/composer'; -import type { UpdateArtifactsConfig } from '../types'; import type { ComposerConfig, ComposerLock } from './types'; export { composerVersioningId }; -export function getConstraint(config: UpdateArtifactsConfig): string { - const { constraints = {} } = config; +export function getConstraint(constraints: Record<string, string>): string { const { composer } = constraints; if (composer) {