feat(composer): extract contraints on update artifacts (#11022)

This commit is contained in:
Michael Kriese 2021-07-30 13:07:28 +02:00 committed by GitHub
parent f0e1070299
commit ab800f46d1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 55 additions and 59 deletions

View file

@ -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": "*",

View file

@ -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'],

View file

@ -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,
},
};

View file

@ -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) {

View file

@ -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', () => {

View file

@ -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) {