Skip to content

Commit

Permalink
feat(composer): Support install before update and configure plugin be…
Browse files Browse the repository at this point in the history
…haviour (#11990)
  • Loading branch information
bobvandevijver committed Nov 14, 2021
1 parent bd3f7cb commit 4e07ddf
Show file tree
Hide file tree
Showing 11 changed files with 346 additions and 20 deletions.
5 changes: 5 additions & 0 deletions docs/usage/configuration-options.md
Expand Up @@ -1090,6 +1090,11 @@ For instance if you have a project with an `"examples/"` directory you wish to i

Useful to know: Renovate's default ignore is `node_modules` and `bower_components` only, however if you are extending the popular `config:base` preset then it adds ignore patterns for `vendor`, `examples`, `test(s)` and `fixtures` directories too.

## ignorePlugins

Set this to `true` if running plugins causes problems.
Applicable for Composer only for now.

## ignorePrAuthor

This is usually needed if someone needs to migrate bot accounts, including from hosted app to self-hosted.
Expand Down
2 changes: 2 additions & 0 deletions docs/usage/self-hosted-configuration.md
Expand Up @@ -11,6 +11,8 @@ Please also see [Self-Hosted Experimental Options](./self-hosted-experimental.md

## allowCustomCrateRegistries

## allowPlugins

## allowPostUpgradeCommandTemplating

Set to true to allow templating of dependency level post-upgrade commands.
Expand Down
1 change: 1 addition & 0 deletions lib/config/global.ts
Expand Up @@ -5,6 +5,7 @@ let repoGlobalConfig: RepoGlobalConfig = {};
// TODO: once global config work is complete, add a test to make sure this list includes all options with globalOnly=true (#9603)
const repoGlobalOptions = [
'allowCustomCrateRegistries',
'allowPlugins',
'allowPostUpgradeCommandTemplating',
'allowScripts',
'allowedPostUpgradeCommands',
Expand Down
15 changes: 15 additions & 0 deletions lib/config/options/index.ts
Expand Up @@ -548,6 +548,14 @@ const options: RenovateOptions[] = [
type: 'boolean',
default: false,
},
{
name: 'allowPlugins',
description:
'Configure this to true if repositories are allowed to run install plugins.',
globalOnly: true,
type: 'boolean',
default: false,
},
{
name: 'allowScripts',
description:
Expand All @@ -564,6 +572,13 @@ const options: RenovateOptions[] = [
type: 'boolean',
default: false,
},
{
name: 'ignorePlugins',
description:
'Configure this to true if allowPlugins=true but you wish to skip running plugins when updating lock files.',
type: 'boolean',
default: false,
},
{
name: 'ignoreScripts',
description:
Expand Down
1 change: 1 addition & 0 deletions lib/config/types.ts
Expand Up @@ -91,6 +91,7 @@ export interface GlobalOnlyConfig {
// The below should contain config options where globalOnly=true
export interface RepoGlobalConfig {
allowCustomCrateRegistries?: boolean;
allowPlugins?: boolean;
allowPostUpgradeCommandTemplating?: boolean;
allowScripts?: boolean;
allowedPostUpgradeCommands?: string[];
Expand Down
134 changes: 134 additions & 0 deletions lib/manager/composer/__snapshots__/artifacts.spec.ts.snap
Expand Up @@ -46,6 +46,30 @@ Array [
]
`;

exports[`manager/composer/artifacts disable plugins when configured locally 1`] = `
Array [
Object {
"cmd": "composer update foo bar --with-dependencies --ignore-platform-reqs --no-ansi --no-interaction --no-scripts --no-autoloader --no-plugins",
"options": Object {
"cwd": "/tmp/github/some/repo",
"encoding": "utf-8",
"env": Object {
"COMPOSER_CACHE_DIR": "/tmp/renovate/cache/others/composer",
"HOME": "/home/user",
"HTTPS_PROXY": "https://example.com",
"HTTP_PROXY": "http://example.com",
"LANG": "en_US.UTF-8",
"LC_ALL": "en_US",
"NO_PROXY": "localhost",
"PATH": "/tmp/path",
},
"maxBuffer": 10485760,
"timeout": 900000,
},
},
]
`;

exports[`manager/composer/artifacts disables ignorePlatformReqs 1`] = `
Array [
Object {
Expand All @@ -70,6 +94,116 @@ Array [
]
`;

exports[`manager/composer/artifacts does not disable plugins when configured globally 1`] = `
Array [
Object {
"cmd": "composer update foo bar --with-dependencies --ignore-platform-reqs --no-ansi --no-interaction --no-scripts --no-autoloader",
"options": Object {
"cwd": "/tmp/github/some/repo",
"encoding": "utf-8",
"env": Object {
"COMPOSER_CACHE_DIR": "/tmp/renovate/cache/others/composer",
"HOME": "/home/user",
"HTTPS_PROXY": "https://example.com",
"HTTP_PROXY": "http://example.com",
"LANG": "en_US.UTF-8",
"LC_ALL": "en_US",
"NO_PROXY": "localhost",
"PATH": "/tmp/path",
},
"maxBuffer": 10485760,
"timeout": 900000,
},
},
]
`;

exports[`manager/composer/artifacts installs before running the update when symfony flex is installed 1`] = `
Array [
Object {
"cmd": "composer install --ignore-platform-reqs --no-ansi --no-interaction --no-scripts --no-autoloader --no-plugins",
"options": Object {
"cwd": "/tmp/github/some/repo",
"encoding": "utf-8",
"env": Object {
"COMPOSER_CACHE_DIR": "/tmp/renovate/cache/others/composer",
"HOME": "/home/user",
"HTTPS_PROXY": "https://example.com",
"HTTP_PROXY": "http://example.com",
"LANG": "en_US.UTF-8",
"LC_ALL": "en_US",
"NO_PROXY": "localhost",
"PATH": "/tmp/path",
},
"maxBuffer": 10485760,
"timeout": 900000,
},
},
Object {
"cmd": "composer update --with-dependencies --ignore-platform-reqs --no-ansi --no-interaction --no-scripts --no-autoloader --no-plugins",
"options": Object {
"cwd": "/tmp/github/some/repo",
"encoding": "utf-8",
"env": Object {
"COMPOSER_CACHE_DIR": "/tmp/renovate/cache/others/composer",
"HOME": "/home/user",
"HTTPS_PROXY": "https://example.com",
"HTTP_PROXY": "http://example.com",
"LANG": "en_US.UTF-8",
"LC_ALL": "en_US",
"NO_PROXY": "localhost",
"PATH": "/tmp/path",
},
"maxBuffer": 10485760,
"timeout": 900000,
},
},
]
`;

exports[`manager/composer/artifacts installs before running the update when symfony flex is installed as dev 1`] = `
Array [
Object {
"cmd": "composer install --ignore-platform-reqs --no-ansi --no-interaction --no-scripts --no-autoloader --no-plugins",
"options": Object {
"cwd": "/tmp/github/some/repo",
"encoding": "utf-8",
"env": Object {
"COMPOSER_CACHE_DIR": "/tmp/renovate/cache/others/composer",
"HOME": "/home/user",
"HTTPS_PROXY": "https://example.com",
"HTTP_PROXY": "http://example.com",
"LANG": "en_US.UTF-8",
"LC_ALL": "en_US",
"NO_PROXY": "localhost",
"PATH": "/tmp/path",
},
"maxBuffer": 10485760,
"timeout": 900000,
},
},
Object {
"cmd": "composer update --with-dependencies --ignore-platform-reqs --no-ansi --no-interaction --no-scripts --no-autoloader --no-plugins",
"options": Object {
"cwd": "/tmp/github/some/repo",
"encoding": "utf-8",
"env": Object {
"COMPOSER_CACHE_DIR": "/tmp/renovate/cache/others/composer",
"HOME": "/home/user",
"HTTPS_PROXY": "https://example.com",
"HTTP_PROXY": "http://example.com",
"LANG": "en_US.UTF-8",
"LC_ALL": "en_US",
"NO_PROXY": "localhost",
"PATH": "/tmp/path",
},
"maxBuffer": 10485760,
"timeout": 900000,
},
},
]
`;

exports[`manager/composer/artifacts performs lockFileMaintenance 1`] = `
Array [
Object {
Expand Down
104 changes: 95 additions & 9 deletions lib/manager/composer/artifacts.spec.ts
Expand Up @@ -26,6 +26,7 @@ const config: UpdateArtifactsConfig = {
};

const adminConfig: RepoGlobalConfig = {
allowPlugins: false,
allowScripts: false,
// `join` fixes Windows CI
localDir: join('/tmp/github/some/repo'),
Expand Down Expand Up @@ -79,8 +80,8 @@ describe('manager/composer/artifacts', () => {
fs.readLocalFile.mockResolvedValueOnce('{}');
const execSnapshots = mockExecAll(exec);
fs.readLocalFile.mockResolvedValueOnce('{}');
git.getRepoStatus.mockResolvedValue(repoStatus);
setGlobalConfig({ ...adminConfig, allowScripts: true });
git.getRepoStatus.mockResolvedValueOnce(repoStatus);
setGlobalConfig({ ...adminConfig, allowScripts: true, allowPlugins: true });
expect(
await composer.updateArtifacts({
packageFileName: 'composer.json',
Expand Down Expand Up @@ -132,7 +133,7 @@ describe('manager/composer/artifacts', () => {
...config,
registryUrls: ['https://packagist.renovatebot.com'],
};
git.getRepoStatus.mockResolvedValue(repoStatus);
git.getRepoStatus.mockResolvedValueOnce(repoStatus);
expect(
await composer.updateArtifacts({
packageFileName: 'composer.json',
Expand All @@ -148,7 +149,7 @@ describe('manager/composer/artifacts', () => {
fs.readLocalFile.mockResolvedValueOnce('{}');
const execSnapshots = mockExecAll(exec);
fs.readLocalFile.mockResolvedValueOnce('{}');
git.getRepoStatus.mockResolvedValue({
git.getRepoStatus.mockResolvedValueOnce({
...repoStatus,
modified: ['composer.lock'],
});
Expand Down Expand Up @@ -200,7 +201,7 @@ describe('manager/composer/artifacts', () => {
fs.readLocalFile.mockResolvedValueOnce('{}');
const execSnapshots = mockExecAll(exec);
fs.readLocalFile.mockResolvedValueOnce('{ }');
git.getRepoStatus.mockResolvedValue({
git.getRepoStatus.mockResolvedValueOnce({
...repoStatus,
modified: ['composer.lock'],
});
Expand Down Expand Up @@ -236,7 +237,7 @@ describe('manager/composer/artifacts', () => {
],
});

git.getRepoStatus.mockResolvedValue({
git.getRepoStatus.mockResolvedValueOnce({
...repoStatus,
modified: ['composer.lock'],
});
Expand All @@ -258,7 +259,7 @@ describe('manager/composer/artifacts', () => {
fs.readLocalFile.mockResolvedValueOnce('{}');
const execSnapshots = mockExecAll(exec);
fs.readLocalFile.mockResolvedValueOnce('{ }');
git.getRepoStatus.mockResolvedValue({
git.getRepoStatus.mockResolvedValueOnce({
...repoStatus,
modified: ['composer.lock'],
});
Expand Down Expand Up @@ -328,7 +329,7 @@ describe('manager/composer/artifacts', () => {
fs.readLocalFile.mockResolvedValueOnce('{}');
const execSnapshots = mockExecAll(exec);
fs.readLocalFile.mockResolvedValueOnce('{ }');
git.getRepoStatus.mockResolvedValue({
git.getRepoStatus.mockResolvedValueOnce({
...repoStatus,
modified: ['composer.lock'],
});
Expand All @@ -350,7 +351,7 @@ describe('manager/composer/artifacts', () => {
fs.readLocalFile.mockResolvedValueOnce('{}');
const execSnapshots = mockExecAll(exec);
fs.readLocalFile.mockResolvedValueOnce('{ }');
git.getRepoStatus.mockResolvedValue({
git.getRepoStatus.mockResolvedValueOnce({
...repoStatus,
modified: ['composer.lock'],
});
Expand All @@ -367,4 +368,89 @@ describe('manager/composer/artifacts', () => {
).not.toBeNull();
expect(execSnapshots).toMatchSnapshot();
});

it('installs before running the update when symfony flex is installed', async () => {
fs.readLocalFile.mockResolvedValueOnce(
'{"packages":[{"name":"symfony/flex","version":"1.17.1"}]}'
);
const execSnapshots = mockExecAll(exec);
fs.readLocalFile.mockResolvedValueOnce('{ }');
git.getRepoStatus.mockResolvedValueOnce({
...repoStatus,
modified: ['composer.lock'],
});
expect(
await composer.updateArtifacts({
packageFileName: 'composer.json',
updatedDeps: [],
newPackageFileContent: '{}',
config: {
...config,
},
})
).not.toBeNull();
expect(execSnapshots).toMatchSnapshot();
expect(execSnapshots).toHaveLength(2);
});

it('installs before running the update when symfony flex is installed as dev', async () => {
fs.readLocalFile.mockResolvedValueOnce(
'{"packages-dev":[{"name":"symfony/flex","version":"1.17.1"}]}'
);
const execSnapshots = mockExecAll(exec);
fs.readLocalFile.mockResolvedValueOnce('{ }');
git.getRepoStatus.mockResolvedValueOnce({
...repoStatus,
modified: ['composer.lock'],
});
expect(
await composer.updateArtifacts({
packageFileName: 'composer.json',
updatedDeps: [],
newPackageFileContent: '{}',
config: {
...config,
},
})
).not.toBeNull();
expect(execSnapshots).toMatchSnapshot();
expect(execSnapshots).toHaveLength(2);
});

it('does not disable plugins when configured globally', async () => {
fs.readLocalFile.mockResolvedValueOnce('{}');
const execSnapshots = mockExecAll(exec);
fs.readLocalFile.mockResolvedValueOnce('{}');
git.getRepoStatus.mockResolvedValueOnce(repoStatus);
setGlobalConfig({ ...adminConfig, allowPlugins: true });
expect(
await composer.updateArtifacts({
packageFileName: 'composer.json',
updatedDeps: [{ depName: 'foo' }, { depName: 'bar' }],
newPackageFileContent: '{}',
config,
})
).toBeNull();
expect(execSnapshots).toMatchSnapshot();
});

it('disable plugins when configured locally', async () => {
fs.readLocalFile.mockResolvedValueOnce('{}');
const execSnapshots = mockExecAll(exec);
fs.readLocalFile.mockResolvedValueOnce('{}');
git.getRepoStatus.mockResolvedValueOnce(repoStatus);
setGlobalConfig({ ...adminConfig, allowPlugins: true });
expect(
await composer.updateArtifacts({
packageFileName: 'composer.json',
updatedDeps: [{ depName: 'foo' }, { depName: 'bar' }],
newPackageFileContent: '{}',
config: {
...config,
ignorePlugins: true,
},
})
).toBeNull();
expect(execSnapshots).toMatchSnapshot();
});
});

0 comments on commit 4e07ddf

Please sign in to comment.