mirror of
https://github.com/renovatebot/renovate.git
synced 2026-10-26 07:20:39 +00:00
1700 lines
46 KiB
TypeScript
1700 lines
46 KiB
TypeScript
// oxlint-disable renovate/prefer-stub-env -- these tests assert on the *whole*
|
|
// env handed to the child process, so they need `process.env` to hold exactly
|
|
// the fixture and nothing else. `vi.stubEnv()` cannot express that: it refuses
|
|
// to delete `PROD`, `DEV` and `SSR`, setting them to '' instead, and those
|
|
// would then show up in the `exposeAllEnv` expectations. Nothing here stubs,
|
|
// so the two styles are not mixed.
|
|
import { mockDeep } from 'vitest-mock-extended';
|
|
import { exec as cpExec, envMock } from '~test/exec-util.ts';
|
|
import { logger } from '~test/util.ts';
|
|
import { GlobalConfig } from '../../config/global.ts';
|
|
import type { RepoGlobalConfig } from '../../config/types.ts';
|
|
import { TEMPORARY_ERROR } from '../../constants/error-messages.ts';
|
|
import * as _datasource from '../../modules/datasource/index.ts';
|
|
import type { UpdateArtifactsConfig } from '../../modules/manager/types.ts';
|
|
import * as memCache from '../cache/memory/index.ts';
|
|
import { setCustomEnv } from '../env.ts';
|
|
import { coerceObject } from '../object.ts';
|
|
import * as dockerModule from './docker/index.ts';
|
|
import { hardcodedProcessEnv } from './env.ts';
|
|
import { getHermitEnvs } from './hermit.ts';
|
|
import { exec, getToolSettingsOptions, gradleJvmArg } from './index.ts';
|
|
import type {
|
|
CommandWithOptions,
|
|
ExecOptions,
|
|
ExecResult,
|
|
RawExecOptions,
|
|
VolumeOption,
|
|
} from './types.ts';
|
|
import { asRawCommand } from './utils.ts';
|
|
|
|
const getHermitEnvsMock = vi.mocked(getHermitEnvs);
|
|
|
|
vi.mock('./hermit.ts', async () => ({
|
|
...(await vi.importActual<typeof import('./hermit.ts')>('./hermit.ts')),
|
|
getHermitEnvs: vi.fn(),
|
|
}));
|
|
vi.mock('../../modules/datasource/index.ts', () => mockDeep());
|
|
const datasource = vi.mocked(_datasource);
|
|
|
|
interface TestInput {
|
|
processEnv: Record<string, string>;
|
|
inCmd: string | string[];
|
|
inOpts?: ExecOptions;
|
|
outCmd: string[];
|
|
outOpts: RawExecOptions[];
|
|
adminConfig?: Partial<RepoGlobalConfig>;
|
|
hermitEnvs?: Record<string, string>;
|
|
}
|
|
|
|
describe('util/exec/index', () => {
|
|
let processEnvOrig: NodeJS.ProcessEnv;
|
|
|
|
const cacheDir = '/tmp/renovate/cache/';
|
|
const containerbaseDir = '/tmp/renovate/cache/containerbase';
|
|
const cwd = '/tmp/renovate/github/some/repo/';
|
|
|
|
const defaultCwd = `-w "${cwd}"`;
|
|
const defaultCacheVolume = `-v "${cacheDir}":"${cacheDir}"`;
|
|
const defaultVolumes = `-v "${cwd}":"${cwd}" ${defaultCacheVolume}`;
|
|
|
|
const globalConfig: RepoGlobalConfig = {
|
|
cacheDir,
|
|
containerbaseDir,
|
|
dockerSidecarImage: 'ghcr.io/renovatebot/base-image',
|
|
};
|
|
|
|
beforeEach(() => {
|
|
dockerModule.resetPrefetchedImages();
|
|
vi.restoreAllMocks();
|
|
processEnvOrig = process.env;
|
|
GlobalConfig.reset();
|
|
setCustomEnv({});
|
|
});
|
|
|
|
afterEach(() => {
|
|
process.env = processEnvOrig;
|
|
memCache.reset();
|
|
});
|
|
|
|
const sideCarName = dockerModule.sideCarName;
|
|
const fullImage = `ghcr.io/renovatebot/base-image`;
|
|
const name = `renovate_${sideCarName}`;
|
|
const inCmd = 'echo hello';
|
|
const outCmd = ['echo hello'];
|
|
const volume_1 = '/path/to/volume-1';
|
|
const volume_2_from = '/path/to/volume-2';
|
|
const volume_2_to = '/path/to/volume-3';
|
|
const volumes: VolumeOption[] = [
|
|
volume_1,
|
|
null,
|
|
undefined,
|
|
[volume_2_from, volume_2_to],
|
|
];
|
|
const docker = {};
|
|
const processEnv = envMock.full;
|
|
const dockerPullCmd = `docker pull ${fullImage}`;
|
|
const dockerRemoveCmd = `docker ps --filter name=${name} -aq`;
|
|
const dockerPullOpts = {};
|
|
const dockerRemoveOpts = dockerPullOpts;
|
|
|
|
const containerbaseEnv = {
|
|
...envMock.basic,
|
|
CONTAINERBASE_CACHE_DIR: `${cacheDir}containerbase`,
|
|
};
|
|
const containerbaseEnvFiltered = {
|
|
...envMock.filtered,
|
|
CONTAINERBASE_CACHE_DIR: `${cacheDir}containerbase`,
|
|
};
|
|
|
|
const testInputs: [string, TestInput][] = [
|
|
[
|
|
'Single command',
|
|
{
|
|
processEnv,
|
|
inCmd,
|
|
inOpts: {},
|
|
outCmd,
|
|
outOpts: [
|
|
{
|
|
cwd,
|
|
env: envMock.basic,
|
|
timeout: 900000,
|
|
maxBuffer: 10485760,
|
|
stdin: 'pipe',
|
|
stdout: 'pipe',
|
|
stderr: 'pipe',
|
|
},
|
|
],
|
|
},
|
|
],
|
|
|
|
[
|
|
'Command without options',
|
|
{
|
|
processEnv,
|
|
inCmd,
|
|
inOpts: undefined,
|
|
outCmd,
|
|
outOpts: [
|
|
{
|
|
cwd,
|
|
env: envMock.basic,
|
|
timeout: 900000,
|
|
maxBuffer: 10485760,
|
|
stdin: 'pipe',
|
|
stdout: 'pipe',
|
|
stderr: 'pipe',
|
|
},
|
|
],
|
|
},
|
|
],
|
|
|
|
[
|
|
'Multiple commands',
|
|
{
|
|
processEnv,
|
|
inCmd: ['echo "begin"', inCmd, "echo 'end'"],
|
|
inOpts: {},
|
|
outCmd: ['echo "begin"', ...outCmd, "echo 'end'"],
|
|
outOpts: [
|
|
{
|
|
cwd,
|
|
env: envMock.basic,
|
|
timeout: 900000,
|
|
maxBuffer: 10485760,
|
|
stdin: 'pipe',
|
|
stdout: 'pipe',
|
|
stderr: 'pipe',
|
|
},
|
|
{
|
|
cwd,
|
|
env: envMock.basic,
|
|
timeout: 900000,
|
|
maxBuffer: 10485760,
|
|
stdin: 'pipe',
|
|
stdout: 'pipe',
|
|
stderr: 'pipe',
|
|
},
|
|
{
|
|
cwd,
|
|
env: envMock.basic,
|
|
timeout: 900000,
|
|
maxBuffer: 10485760,
|
|
stdin: 'pipe',
|
|
stdout: 'pipe',
|
|
stderr: 'pipe',
|
|
},
|
|
],
|
|
},
|
|
],
|
|
|
|
[
|
|
'Explicit env option',
|
|
{
|
|
processEnv,
|
|
inCmd,
|
|
inOpts: { env: { FOO: 'BAR' } },
|
|
outCmd,
|
|
outOpts: [
|
|
{
|
|
cwd,
|
|
env: { ...envMock.basic, FOO: 'BAR' },
|
|
timeout: 900000,
|
|
maxBuffer: 10485760,
|
|
stdin: 'pipe',
|
|
stdout: 'pipe',
|
|
stderr: 'pipe',
|
|
},
|
|
],
|
|
},
|
|
],
|
|
|
|
[
|
|
'By default, exposeAllEnv=false',
|
|
{
|
|
processEnv,
|
|
inCmd,
|
|
inOpts: {},
|
|
outCmd,
|
|
outOpts: [
|
|
{
|
|
cwd,
|
|
env: envMock.basic,
|
|
timeout: 900000,
|
|
maxBuffer: 10485760,
|
|
stdin: 'pipe',
|
|
stdout: 'pipe',
|
|
stderr: 'pipe',
|
|
},
|
|
],
|
|
},
|
|
],
|
|
|
|
[
|
|
'When exposeAllEnv=true, all environment variables are passed to child',
|
|
{
|
|
processEnv: envMock.full,
|
|
inCmd,
|
|
inOpts: {},
|
|
outCmd,
|
|
outOpts: [
|
|
{
|
|
cwd,
|
|
env: envMock.full,
|
|
timeout: 900000,
|
|
maxBuffer: 10485760,
|
|
stdin: 'pipe',
|
|
stdout: 'pipe',
|
|
stderr: 'pipe',
|
|
},
|
|
],
|
|
adminConfig: { exposeAllEnv: true },
|
|
},
|
|
],
|
|
|
|
[
|
|
'Docker',
|
|
{
|
|
processEnv,
|
|
inCmd,
|
|
inOpts: { docker, cwd },
|
|
outCmd: [
|
|
dockerPullCmd,
|
|
dockerRemoveCmd,
|
|
`docker run --rm --name=${name} --label=renovate_child ${defaultVolumes} -e CI -e CONTAINERBASE_CACHE_DIR ${defaultCwd} ${fullImage} bash -l -c '${inCmd}'`,
|
|
],
|
|
outOpts: [
|
|
dockerPullOpts,
|
|
dockerRemoveOpts,
|
|
{
|
|
cwd,
|
|
env: containerbaseEnv,
|
|
timeout: 900000,
|
|
maxBuffer: 10485760,
|
|
stdin: 'pipe',
|
|
stdout: 'pipe',
|
|
stderr: 'pipe',
|
|
},
|
|
],
|
|
adminConfig: { binarySource: 'docker' },
|
|
},
|
|
],
|
|
|
|
[
|
|
'Explicit Docker env vars',
|
|
{
|
|
processEnv,
|
|
inCmd,
|
|
inOpts: {
|
|
docker: { envVars: ['FORCED_ENV_VAR'] },
|
|
env: { FORCED_ENV_VAR: 'forced' },
|
|
cwd,
|
|
},
|
|
outCmd: [
|
|
dockerPullCmd,
|
|
dockerRemoveCmd,
|
|
`docker run --rm --name=${name} --label=renovate_child ${defaultVolumes} -e CI -e FORCED_ENV_VAR -e CONTAINERBASE_CACHE_DIR ${defaultCwd} ${fullImage} bash -l -c '${inCmd}'`,
|
|
],
|
|
outOpts: [
|
|
dockerPullOpts,
|
|
dockerRemoveOpts,
|
|
{
|
|
cwd,
|
|
env: {
|
|
...containerbaseEnv,
|
|
FORCED_ENV_VAR: 'forced',
|
|
},
|
|
timeout: 900000,
|
|
maxBuffer: 10485760,
|
|
stdin: 'pipe',
|
|
stdout: 'pipe',
|
|
stderr: 'pipe',
|
|
},
|
|
],
|
|
adminConfig: { binarySource: 'docker' },
|
|
},
|
|
],
|
|
|
|
[
|
|
'Extra env vars',
|
|
{
|
|
processEnv,
|
|
inCmd,
|
|
inOpts: {
|
|
extraEnv: {
|
|
SELECTED_ENV_VAR: envMock.full.SELECTED_ENV_VAR,
|
|
FILTERED_ENV_VAR: null,
|
|
FOO: null,
|
|
BAR: undefined,
|
|
},
|
|
},
|
|
outCmd,
|
|
outOpts: [
|
|
{
|
|
cwd,
|
|
env: containerbaseEnvFiltered,
|
|
timeout: 900000,
|
|
maxBuffer: 10485760,
|
|
stdin: 'pipe',
|
|
stdout: 'pipe',
|
|
stderr: 'pipe',
|
|
},
|
|
],
|
|
adminConfig: { binarySource: 'docker' },
|
|
},
|
|
],
|
|
|
|
[
|
|
'Extra env vars (Docker)',
|
|
{
|
|
processEnv,
|
|
inCmd,
|
|
inOpts: {
|
|
docker,
|
|
extraEnv: {
|
|
SELECTED_ENV_VAR: envMock.full.SELECTED_ENV_VAR,
|
|
FILTERED_ENV_VAR: null,
|
|
FOO: null,
|
|
BAR: undefined,
|
|
},
|
|
cwd,
|
|
},
|
|
outCmd: [
|
|
dockerPullCmd,
|
|
dockerRemoveCmd,
|
|
`docker run --rm --name=${name} --label=renovate_child ${defaultVolumes} -e CI -e SELECTED_ENV_VAR -e CONTAINERBASE_CACHE_DIR ${defaultCwd} ${fullImage} bash -l -c '${inCmd}'`,
|
|
],
|
|
outOpts: [
|
|
dockerPullOpts,
|
|
dockerRemoveOpts,
|
|
{
|
|
cwd,
|
|
env: containerbaseEnvFiltered,
|
|
timeout: 900000,
|
|
maxBuffer: 10485760,
|
|
stdin: 'pipe',
|
|
stdout: 'pipe',
|
|
stderr: 'pipe',
|
|
},
|
|
],
|
|
adminConfig: { binarySource: 'docker' },
|
|
},
|
|
],
|
|
|
|
[
|
|
'Extra env vars defaults',
|
|
{
|
|
processEnv: envMock.basic,
|
|
inCmd,
|
|
inOpts: { cwd, extraEnv: { SELECTED_ENV_VAR: 'Default value' } },
|
|
outCmd,
|
|
outOpts: [
|
|
{
|
|
cwd,
|
|
env: { ...containerbaseEnv, SELECTED_ENV_VAR: 'Default value' },
|
|
timeout: 900000,
|
|
maxBuffer: 10485760,
|
|
stdin: 'pipe',
|
|
stdout: 'pipe',
|
|
stderr: 'pipe',
|
|
},
|
|
],
|
|
adminConfig: { binarySource: 'docker' },
|
|
},
|
|
],
|
|
|
|
[
|
|
'Extra env vars defaults (Docker)',
|
|
{
|
|
processEnv: envMock.basic,
|
|
inCmd,
|
|
inOpts: {
|
|
docker,
|
|
extraEnv: { SELECTED_ENV_VAR: 'Default value' },
|
|
cwd,
|
|
},
|
|
outCmd: [
|
|
dockerPullCmd,
|
|
dockerRemoveCmd,
|
|
`docker run --rm --name=${name} --label=renovate_child ${defaultVolumes} -e CI -e SELECTED_ENV_VAR -e CONTAINERBASE_CACHE_DIR ${defaultCwd} ${fullImage} bash -l -c '${inCmd}'`,
|
|
],
|
|
outOpts: [
|
|
dockerPullOpts,
|
|
dockerRemoveOpts,
|
|
{
|
|
cwd,
|
|
env: { ...containerbaseEnv, SELECTED_ENV_VAR: 'Default value' },
|
|
timeout: 900000,
|
|
maxBuffer: 10485760,
|
|
stdin: 'pipe',
|
|
stdout: 'pipe',
|
|
stderr: 'pipe',
|
|
},
|
|
],
|
|
adminConfig: { binarySource: 'docker' },
|
|
},
|
|
],
|
|
|
|
[
|
|
'Docker volumes',
|
|
{
|
|
processEnv,
|
|
inCmd,
|
|
inOpts: { cwd, docker: { volumes } },
|
|
outCmd: [
|
|
dockerPullCmd,
|
|
dockerRemoveCmd,
|
|
`docker run --rm --name=${name} --label=renovate_child ${defaultVolumes} -v "${volume_1}":"${volume_1}" -v "${volume_2_from}":"${volume_2_to}" -e CI -e CONTAINERBASE_CACHE_DIR -w "${cwd}" ${fullImage} bash -l -c '${inCmd}'`,
|
|
],
|
|
outOpts: [
|
|
dockerPullOpts,
|
|
dockerRemoveOpts,
|
|
{
|
|
cwd,
|
|
env: containerbaseEnv,
|
|
timeout: 900000,
|
|
maxBuffer: 10485760,
|
|
stdin: 'pipe',
|
|
stdout: 'pipe',
|
|
stderr: 'pipe',
|
|
},
|
|
],
|
|
adminConfig: { binarySource: 'docker' },
|
|
},
|
|
],
|
|
|
|
[
|
|
'Docker user',
|
|
{
|
|
processEnv,
|
|
inCmd,
|
|
inOpts: { docker },
|
|
outCmd: [
|
|
dockerPullCmd,
|
|
dockerRemoveCmd,
|
|
`docker run --rm --name=${name} --label=renovate_child --user=foobar ${defaultVolumes} -e CI -e CONTAINERBASE_CACHE_DIR -w "${cwd}" ${fullImage} bash -l -c '${inCmd}'`,
|
|
],
|
|
outOpts: [
|
|
dockerPullOpts,
|
|
dockerRemoveOpts,
|
|
{
|
|
cwd,
|
|
env: containerbaseEnv,
|
|
timeout: 900000,
|
|
maxBuffer: 10485760,
|
|
stdin: 'pipe',
|
|
stdout: 'pipe',
|
|
stderr: 'pipe',
|
|
},
|
|
],
|
|
adminConfig: {
|
|
dockerUser: 'foobar',
|
|
binarySource: 'docker',
|
|
},
|
|
},
|
|
],
|
|
|
|
[
|
|
'Docker image prefix',
|
|
{
|
|
processEnv,
|
|
inCmd,
|
|
inOpts: { docker },
|
|
outCmd: [
|
|
`docker pull ghcr.io/renovatebot/base-image`,
|
|
dockerRemoveCmd,
|
|
`docker run --rm --name=${name} --label=renovate_child ${defaultVolumes} -e CI -e CONTAINERBASE_CACHE_DIR -w "${cwd}" ghcr.io/renovatebot/base-image bash -l -c '${inCmd}'`,
|
|
],
|
|
outOpts: [
|
|
dockerPullOpts,
|
|
dockerRemoveOpts,
|
|
{
|
|
cwd,
|
|
env: containerbaseEnv,
|
|
timeout: 900000,
|
|
maxBuffer: 10485760,
|
|
stdin: 'pipe',
|
|
stdout: 'pipe',
|
|
stderr: 'pipe',
|
|
},
|
|
],
|
|
adminConfig: {
|
|
dockerSidecarImage: 'ghcr.io/renovatebot/base-image',
|
|
binarySource: 'docker',
|
|
},
|
|
},
|
|
],
|
|
|
|
[
|
|
'Docker child prefix',
|
|
{
|
|
processEnv,
|
|
inCmd,
|
|
inOpts: { docker },
|
|
outCmd: [
|
|
dockerPullCmd,
|
|
`docker ps --filter name=myprefix_${sideCarName} -aq`,
|
|
`docker run --rm --name=myprefix_${sideCarName} --label=myprefix_child ${defaultVolumes} -e CI -e CONTAINERBASE_CACHE_DIR -w "${cwd}" ${fullImage} bash -l -c '${inCmd}'`,
|
|
],
|
|
outOpts: [
|
|
dockerPullOpts,
|
|
dockerRemoveOpts,
|
|
{
|
|
cwd,
|
|
env: containerbaseEnv,
|
|
timeout: 900000,
|
|
maxBuffer: 10485760,
|
|
stdin: 'pipe',
|
|
stdout: 'pipe',
|
|
stderr: 'pipe',
|
|
},
|
|
],
|
|
adminConfig: {
|
|
dockerChildPrefix: 'myprefix_',
|
|
binarySource: 'docker',
|
|
},
|
|
},
|
|
],
|
|
|
|
[
|
|
'Docker extra commands',
|
|
{
|
|
processEnv,
|
|
inCmd,
|
|
inOpts: {
|
|
docker,
|
|
preCommands: ['preCommand1', 'preCommand2', null as never],
|
|
},
|
|
outCmd: [
|
|
dockerPullCmd,
|
|
dockerRemoveCmd,
|
|
`docker run --rm --name=${name} --label=renovate_child ${defaultVolumes} -e CI -e CONTAINERBASE_CACHE_DIR -w "${cwd}" ${fullImage} bash -l -c 'preCommand1 && preCommand2 && ${inCmd}'`,
|
|
],
|
|
outOpts: [
|
|
dockerPullOpts,
|
|
dockerRemoveOpts,
|
|
{
|
|
cwd,
|
|
env: containerbaseEnv,
|
|
timeout: 900000,
|
|
maxBuffer: 10485760,
|
|
stdin: 'pipe',
|
|
stdout: 'pipe',
|
|
stderr: 'pipe',
|
|
},
|
|
],
|
|
adminConfig: { binarySource: 'docker' },
|
|
},
|
|
],
|
|
|
|
[
|
|
'Docker commands are nullable',
|
|
{
|
|
processEnv,
|
|
inCmd,
|
|
inOpts: { docker },
|
|
outCmd: [
|
|
dockerPullCmd,
|
|
dockerRemoveCmd,
|
|
`docker run --rm --name=${name} --label=renovate_child ${defaultVolumes} -e CI -e CONTAINERBASE_CACHE_DIR -w "${cwd}" ${fullImage} bash -l -c '${inCmd}'`,
|
|
],
|
|
outOpts: [
|
|
dockerPullOpts,
|
|
dockerRemoveOpts,
|
|
{
|
|
cwd,
|
|
env: containerbaseEnv,
|
|
timeout: 900000,
|
|
maxBuffer: 10485760,
|
|
stdin: 'pipe',
|
|
stdout: 'pipe',
|
|
stderr: 'pipe',
|
|
},
|
|
],
|
|
adminConfig: { binarySource: 'docker' },
|
|
},
|
|
],
|
|
|
|
[
|
|
'Explicit timeout',
|
|
{
|
|
processEnv,
|
|
inCmd,
|
|
inOpts: {
|
|
timeout: 20 * 60 * 1000,
|
|
},
|
|
outCmd,
|
|
outOpts: [
|
|
{
|
|
cwd,
|
|
env: containerbaseEnv,
|
|
timeout: 20 * 60 * 1000,
|
|
maxBuffer: 10485760,
|
|
stdin: 'pipe',
|
|
stdout: 'pipe',
|
|
stderr: 'pipe',
|
|
},
|
|
],
|
|
adminConfig: { binarySource: 'docker' },
|
|
},
|
|
],
|
|
|
|
[
|
|
'Default timeout from executionTimeout config option',
|
|
{
|
|
processEnv,
|
|
inCmd,
|
|
inOpts: {},
|
|
outCmd,
|
|
outOpts: [
|
|
{
|
|
cwd,
|
|
env: envMock.basic,
|
|
timeout: 30 * 60 * 1000,
|
|
maxBuffer: 10485760,
|
|
stdin: 'pipe',
|
|
stdout: 'pipe',
|
|
stderr: 'pipe',
|
|
},
|
|
],
|
|
adminConfig: { executionTimeout: 30 },
|
|
},
|
|
],
|
|
|
|
[
|
|
'Explicit maxBuffer',
|
|
{
|
|
processEnv,
|
|
inCmd,
|
|
inOpts: {
|
|
maxBuffer: 1024,
|
|
},
|
|
outCmd,
|
|
outOpts: [
|
|
{
|
|
cwd,
|
|
env: containerbaseEnv,
|
|
timeout: 900000,
|
|
maxBuffer: 1024,
|
|
stdin: 'pipe',
|
|
stdout: 'pipe',
|
|
stderr: 'pipe',
|
|
},
|
|
],
|
|
adminConfig: { binarySource: 'docker' },
|
|
},
|
|
],
|
|
|
|
[
|
|
'Custom environment variables for child',
|
|
{
|
|
processEnv: envMock.basic,
|
|
inCmd,
|
|
inOpts: {},
|
|
outCmd,
|
|
outOpts: [
|
|
{
|
|
cwd,
|
|
env: {
|
|
...containerbaseEnv,
|
|
CUSTOM_KEY: 'CUSTOM_VALUE',
|
|
},
|
|
timeout: 900000,
|
|
maxBuffer: 10485760,
|
|
stdin: 'pipe',
|
|
stdout: 'pipe',
|
|
stderr: 'pipe',
|
|
},
|
|
],
|
|
adminConfig: {
|
|
customEnvVariables: {
|
|
CUSTOM_KEY: 'CUSTOM_VALUE',
|
|
},
|
|
binarySource: 'docker',
|
|
},
|
|
},
|
|
],
|
|
|
|
[
|
|
'Custom environment variables for child should override',
|
|
{
|
|
processEnv: { ...envMock.basic, CUSTOM_KEY: 'CUSTOM_VALUE' },
|
|
inCmd,
|
|
inOpts: {},
|
|
outCmd,
|
|
outOpts: [
|
|
{
|
|
cwd,
|
|
env: {
|
|
...containerbaseEnv,
|
|
CUSTOM_KEY: 'CUSTOM_OVERRIDEN_VALUE',
|
|
},
|
|
timeout: 900000,
|
|
maxBuffer: 10485760,
|
|
stdin: 'pipe',
|
|
stdout: 'pipe',
|
|
stderr: 'pipe',
|
|
},
|
|
],
|
|
adminConfig: {
|
|
customEnvVariables: {
|
|
CUSTOM_KEY: 'CUSTOM_OVERRIDEN_VALUE',
|
|
},
|
|
binarySource: 'docker',
|
|
},
|
|
},
|
|
],
|
|
|
|
[
|
|
'Custom environment variables for child (Docker)',
|
|
{
|
|
processEnv,
|
|
inCmd,
|
|
inOpts: { docker, cwd },
|
|
outCmd: [
|
|
dockerPullCmd,
|
|
dockerRemoveCmd,
|
|
`docker run --rm --name=${name} --label=renovate_child ${defaultVolumes} -e CI -e CUSTOM_KEY -e CONTAINERBASE_CACHE_DIR ${defaultCwd} ${fullImage} bash -l -c '${inCmd}'`,
|
|
],
|
|
outOpts: [
|
|
dockerPullOpts,
|
|
dockerRemoveOpts,
|
|
{
|
|
cwd,
|
|
env: {
|
|
...containerbaseEnv,
|
|
CUSTOM_KEY: 'CUSTOM_VALUE',
|
|
},
|
|
timeout: 900000,
|
|
maxBuffer: 10485760,
|
|
stdin: 'pipe',
|
|
stdout: 'pipe',
|
|
stderr: 'pipe',
|
|
},
|
|
],
|
|
adminConfig: {
|
|
customEnvVariables: {
|
|
CUSTOM_KEY: 'CUSTOM_VALUE',
|
|
},
|
|
binarySource: 'docker',
|
|
},
|
|
},
|
|
],
|
|
|
|
[
|
|
'Custom environment variables for child should override (Docker)',
|
|
{
|
|
processEnv: { ...envMock.basic, CUSTOM_KEY: 'CUSTOM_VALUE' },
|
|
inCmd,
|
|
inOpts: { docker, cwd },
|
|
outCmd: [
|
|
dockerPullCmd,
|
|
dockerRemoveCmd,
|
|
`docker run --rm --name=${name} --label=renovate_child ${defaultVolumes} -e CI -e CUSTOM_KEY -e CONTAINERBASE_CACHE_DIR ${defaultCwd} ${fullImage} bash -l -c '${inCmd}'`,
|
|
],
|
|
outOpts: [
|
|
dockerPullOpts,
|
|
dockerRemoveOpts,
|
|
{
|
|
cwd,
|
|
env: {
|
|
...containerbaseEnv,
|
|
CUSTOM_KEY: 'CUSTOM_OVERRIDEN_VALUE',
|
|
},
|
|
timeout: 900000,
|
|
maxBuffer: 10485760,
|
|
stdin: 'pipe',
|
|
stdout: 'pipe',
|
|
stderr: 'pipe',
|
|
},
|
|
],
|
|
adminConfig: {
|
|
customEnvVariables: {
|
|
CUSTOM_KEY: 'CUSTOM_OVERRIDEN_VALUE',
|
|
},
|
|
binarySource: 'docker',
|
|
},
|
|
},
|
|
],
|
|
|
|
[
|
|
'Discarded stdout if ignoreStdout=true',
|
|
{
|
|
processEnv,
|
|
inCmd,
|
|
inOpts: {
|
|
ignoreStdout: true,
|
|
cwdFile: '/somefile',
|
|
},
|
|
outCmd,
|
|
outOpts: [
|
|
{
|
|
cwd,
|
|
env: envMock.basic,
|
|
timeout: 900000,
|
|
maxBuffer: 10485760,
|
|
stdin: 'pipe',
|
|
stdout: 'ignore',
|
|
stderr: 'pipe',
|
|
},
|
|
],
|
|
},
|
|
],
|
|
|
|
[
|
|
'Shell is not set if not specified',
|
|
{
|
|
processEnv,
|
|
inCmd,
|
|
inOpts: {},
|
|
outCmd,
|
|
outOpts: [
|
|
{
|
|
cwd,
|
|
env: envMock.basic,
|
|
timeout: 900000,
|
|
maxBuffer: 10485760,
|
|
stdin: 'pipe',
|
|
stdout: 'pipe',
|
|
stderr: 'pipe',
|
|
},
|
|
],
|
|
},
|
|
],
|
|
|
|
[
|
|
'Shell=true is set if specified',
|
|
{
|
|
processEnv,
|
|
inCmd,
|
|
inOpts: {
|
|
shell: true,
|
|
},
|
|
outCmd,
|
|
outOpts: [
|
|
{
|
|
cwd,
|
|
env: envMock.basic,
|
|
timeout: 900000,
|
|
maxBuffer: 10485760,
|
|
shell: true,
|
|
stdin: 'pipe',
|
|
stdout: 'pipe',
|
|
stderr: 'pipe',
|
|
},
|
|
],
|
|
},
|
|
],
|
|
|
|
[
|
|
'Shell={string} is set if specified',
|
|
{
|
|
processEnv,
|
|
inCmd,
|
|
inOpts: {
|
|
shell: '/usr/bin/another-shell',
|
|
},
|
|
outCmd,
|
|
outOpts: [
|
|
{
|
|
cwd,
|
|
env: envMock.basic,
|
|
timeout: 900000,
|
|
maxBuffer: 10485760,
|
|
shell: '/usr/bin/another-shell',
|
|
stdin: 'pipe',
|
|
stdout: 'pipe',
|
|
stderr: 'pipe',
|
|
},
|
|
],
|
|
},
|
|
],
|
|
|
|
[
|
|
'Hermit',
|
|
{
|
|
processEnv: {
|
|
...envMock.basic,
|
|
CUSTOM_KEY: 'CUSTOM_VALUE',
|
|
PATH: '/home/user-a/bin;/usr/local/bin',
|
|
},
|
|
inCmd,
|
|
inOpts: {
|
|
cwd,
|
|
},
|
|
outCmd: [inCmd],
|
|
outOpts: [
|
|
{
|
|
cwd,
|
|
env: {
|
|
...envMock.basic,
|
|
CUSTOM_KEY: 'CUSTOM_OVERRIDEN_VALUE',
|
|
GOBIN: '/usr/src/app/repository-a/.hermit/go/bin',
|
|
PATH: '/usr/src/app/repository-a/bin/;/home/user-a/bin;/usr/local/bin;',
|
|
},
|
|
timeout: 900000,
|
|
maxBuffer: 10485760,
|
|
stdin: 'pipe',
|
|
stdout: 'pipe',
|
|
stderr: 'pipe',
|
|
},
|
|
],
|
|
hermitEnvs: {
|
|
GOBIN: '/usr/src/app/repository-a/.hermit/go/bin',
|
|
PATH: '/usr/src/app/repository-a/bin/;/home/user-a/bin;/usr/local/bin;',
|
|
},
|
|
adminConfig: {
|
|
customEnvVariables: {
|
|
CUSTOM_KEY: 'CUSTOM_OVERRIDEN_VALUE',
|
|
},
|
|
binarySource: 'hermit',
|
|
},
|
|
},
|
|
],
|
|
];
|
|
|
|
it.each(testInputs)('%s', async (_msg, testOpts) => {
|
|
const {
|
|
processEnv: procEnv,
|
|
inCmd: cmd,
|
|
inOpts,
|
|
outCmd: outCommand,
|
|
outOpts,
|
|
adminConfig = {},
|
|
hermitEnvs,
|
|
} = testOpts;
|
|
|
|
process.env = procEnv;
|
|
|
|
const actualCmd: string[] = [];
|
|
const actualOpts: RawExecOptions[] = [];
|
|
cpExec.mockImplementation((execCmd, execOpts) => {
|
|
actualCmd.push(asRawCommand(execCmd));
|
|
actualOpts.push(execOpts);
|
|
|
|
return Promise.resolve({ stdout: '', stderr: '' });
|
|
});
|
|
GlobalConfig.set({ ...globalConfig, localDir: cwd, ...adminConfig });
|
|
setCustomEnv(coerceObject(adminConfig.customEnvVariables));
|
|
if (hermitEnvs !== undefined) {
|
|
getHermitEnvsMock.mockResolvedValue(hermitEnvs);
|
|
}
|
|
|
|
await exec(cmd, inOpts);
|
|
|
|
expect(actualCmd).toEqual(outCommand);
|
|
expect(actualOpts).toEqual(outOpts);
|
|
});
|
|
|
|
it('Supports image prefetch', async () => {
|
|
process.env = processEnv;
|
|
|
|
const actualCmd: string[] = [];
|
|
cpExec.mockImplementation((execCmd) => {
|
|
actualCmd.push(asRawCommand(execCmd));
|
|
return Promise.resolve({ stdout: '', stderr: '' });
|
|
});
|
|
|
|
GlobalConfig.set({ ...globalConfig, binarySource: 'global' });
|
|
await exec(inCmd, { docker });
|
|
await exec(inCmd, { docker });
|
|
|
|
GlobalConfig.set({ ...globalConfig, binarySource: 'docker' });
|
|
await exec(inCmd, { docker });
|
|
await exec(inCmd, { docker });
|
|
|
|
GlobalConfig.set({ ...globalConfig, binarySource: 'global' });
|
|
await exec(inCmd, { docker });
|
|
await exec(inCmd, { docker });
|
|
|
|
GlobalConfig.set({ ...globalConfig, binarySource: 'docker' });
|
|
await exec(inCmd, { docker });
|
|
await exec(inCmd, { docker });
|
|
|
|
expect(actualCmd).toEqual([
|
|
`echo hello`,
|
|
`echo hello`,
|
|
`docker pull ${fullImage}`,
|
|
`docker ps --filter name=renovate_${sideCarName} -aq`,
|
|
`docker run --rm --name=renovate_${sideCarName} --label=renovate_child ${defaultCacheVolume} -e CI -e CONTAINERBASE_CACHE_DIR ${fullImage} bash -l -c 'echo hello'`,
|
|
`docker ps --filter name=renovate_${sideCarName} -aq`,
|
|
`docker run --rm --name=renovate_${sideCarName} --label=renovate_child ${defaultCacheVolume} -e CI -e CONTAINERBASE_CACHE_DIR ${fullImage} bash -l -c 'echo hello'`,
|
|
`echo hello`,
|
|
`echo hello`,
|
|
`docker ps --filter name=renovate_${sideCarName} -aq`,
|
|
`docker run --rm --name=renovate_${sideCarName} --label=renovate_child ${defaultCacheVolume} -e CI -e CONTAINERBASE_CACHE_DIR ${fullImage} bash -l -c 'echo hello'`,
|
|
`docker ps --filter name=renovate_${sideCarName} -aq`,
|
|
`docker run --rm --name=renovate_${sideCarName} --label=renovate_child ${defaultCacheVolume} -e CI -e CONTAINERBASE_CACHE_DIR ${fullImage} bash -l -c 'echo hello'`,
|
|
]);
|
|
});
|
|
|
|
it.each(Object.keys(hardcodedProcessEnv))(
|
|
'always forwards %s into the Docker sidecar',
|
|
async (envVar) => {
|
|
process.env = processEnv;
|
|
|
|
const actualCmd: string[] = [];
|
|
cpExec.mockImplementation((execCmd) => {
|
|
actualCmd.push(asRawCommand(execCmd));
|
|
return Promise.resolve({ stdout: '', stderr: '' });
|
|
});
|
|
|
|
GlobalConfig.set({
|
|
...globalConfig,
|
|
localDir: cwd,
|
|
binarySource: 'docker',
|
|
});
|
|
await exec(inCmd, { docker });
|
|
|
|
expect(actualCmd.at(-1)).toContain(`-e ${envVar} `);
|
|
},
|
|
);
|
|
|
|
it('throws when an error is thrown', async () => {
|
|
process.env = processEnv;
|
|
cpExec.mockImplementation(() => {
|
|
throw new Error('some error occurred');
|
|
});
|
|
GlobalConfig.set({ ...globalConfig, binarySource: 'install' });
|
|
const promise = exec('foobar');
|
|
await expect(promise).rejects.toThrow('some error occurred');
|
|
});
|
|
|
|
it('rejects and throws if an error is thrown, even if we specify ignoreFailure=true', async () => {
|
|
process.env = processEnv;
|
|
cpExec.mockImplementation(() => {
|
|
throw new Error('some error occurred');
|
|
});
|
|
GlobalConfig.set({ ...globalConfig });
|
|
const promise = exec([
|
|
{
|
|
command: ['foobar'],
|
|
ignoreFailure: true,
|
|
},
|
|
]);
|
|
await expect(promise).rejects.toThrow('some error occurred');
|
|
});
|
|
|
|
it('does not reject and throw if rawExec returns an exit code, and we specify ignoreFailure=true', async () => {
|
|
process.env = processEnv;
|
|
const stdout = 'out';
|
|
const stderr = 'err';
|
|
cpExec.mockImplementation((): Promise<ExecResult> =>
|
|
// NOTE that this only makes sense as a return value when `ignoreFailure=true` is set
|
|
Promise.resolve({
|
|
stdout,
|
|
stderr,
|
|
exitCode: 10,
|
|
}),
|
|
);
|
|
GlobalConfig.set({ ...globalConfig });
|
|
const promise = exec([
|
|
{
|
|
command: ['foobar'],
|
|
// NOTE that the implementation would only work if `ignoreFailure: true`
|
|
ignoreFailure: true,
|
|
},
|
|
]);
|
|
await expect(promise).resolves.toEqual({
|
|
stdout,
|
|
stderr,
|
|
exitCode: 10,
|
|
});
|
|
});
|
|
|
|
it('exec takes an array with both `string`s and `CommandWithOptions` as an argument', async () => {
|
|
const command: CommandWithOptions = {
|
|
command: ['exit 1'],
|
|
ignoreFailure: true,
|
|
};
|
|
|
|
cpExec.mockImplementationOnce(() => {
|
|
return Promise.resolve({ stdout: '', stderr: '' });
|
|
});
|
|
|
|
cpExec.mockImplementationOnce(() => {
|
|
return Promise.resolve({ stdout: 'out', stderr: 'err', exitCode: 5 });
|
|
});
|
|
|
|
await expect(exec(['ls', command])).resolves.toEqual({
|
|
stdout: 'out',
|
|
stderr: 'err',
|
|
exitCode: 5,
|
|
});
|
|
});
|
|
|
|
it('exec takes CommandWithOptions as an argument', async () => {
|
|
const command: CommandWithOptions = {
|
|
command: ['exit 1'],
|
|
ignoreFailure: true,
|
|
};
|
|
|
|
cpExec.mockImplementation(() => {
|
|
return Promise.resolve({ stdout: 'out', stderr: 'err', exitCode: 5 });
|
|
});
|
|
|
|
await expect(exec([command])).resolves.toEqual({
|
|
stdout: 'out',
|
|
stderr: 'err',
|
|
exitCode: 5,
|
|
});
|
|
});
|
|
|
|
it('Supports binarySource=install', async () => {
|
|
process.env = processEnv;
|
|
cpExec.mockImplementation(() => {
|
|
throw new Error('some error occurred');
|
|
});
|
|
GlobalConfig.set({ ...globalConfig, binarySource: 'install' });
|
|
process.env.CONTAINERBASE = 'true';
|
|
const promise = exec('foobar', { toolConstraints: [{ toolName: 'npm' }] });
|
|
await expect(promise).rejects.toThrow('No tool releases found.');
|
|
});
|
|
|
|
it('logs "Executing command" with the command and environment variable names', async () => {
|
|
process.env = processEnv;
|
|
cpExec.mockResolvedValue({ stdout: '', stderr: '' });
|
|
GlobalConfig.set({ ...globalConfig, localDir: cwd });
|
|
|
|
await exec(inCmd);
|
|
|
|
expect(logger.logger.debug).toHaveBeenCalledWith(
|
|
{
|
|
command: inCmd,
|
|
env: [
|
|
'HTTP_PROXY',
|
|
'HTTPS_PROXY',
|
|
'NO_PROXY',
|
|
'HOME',
|
|
'PATH',
|
|
'LC_ALL',
|
|
'LANG',
|
|
'CI',
|
|
],
|
|
},
|
|
'Executing command',
|
|
);
|
|
});
|
|
|
|
it('logs ignored tool constraints for binarySource=global', async () => {
|
|
process.env = processEnv;
|
|
cpExec.mockResolvedValue({ stdout: '', stderr: '' });
|
|
GlobalConfig.set({ ...globalConfig, binarySource: 'global' });
|
|
await exec('foobar', { toolConstraints: [{ toolName: 'npm' }] });
|
|
expect(logger.logger.once.debug).toHaveBeenCalledWith(
|
|
{ toolConstraints: [{ toolName: 'npm' }] },
|
|
'Ignoring tool constraints because of `binarySource=global`',
|
|
);
|
|
});
|
|
|
|
it('Supports binarySource=install preCommands', async () => {
|
|
process.env = processEnv;
|
|
const actualCmd: string[] = [];
|
|
cpExec.mockImplementation((execCmd) => {
|
|
actualCmd.push(asRawCommand(execCmd));
|
|
return Promise.resolve({ stdout: '', stderr: '' });
|
|
});
|
|
|
|
GlobalConfig.set({ ...globalConfig, binarySource: 'install' });
|
|
process.env.CONTAINERBASE = 'true';
|
|
await exec('foobar', { preCommands: ['install-pip foobar'] });
|
|
expect(actualCmd).toEqual([`install-pip foobar`, `foobar`]);
|
|
});
|
|
|
|
it('only installs a tool once per run for binarySource=install', async () => {
|
|
process.env = processEnv;
|
|
memCache.init();
|
|
datasource.getPkgReleases.mockResolvedValue({
|
|
releases: [{ version: '1.2.3' }],
|
|
});
|
|
const actualCmds: string[] = [];
|
|
cpExec.mockImplementation((execCmd) => {
|
|
actualCmds.push(asRawCommand(execCmd));
|
|
return Promise.resolve({ stdout: '', stderr: '' });
|
|
});
|
|
|
|
GlobalConfig.set({ ...globalConfig, binarySource: 'install' });
|
|
process.env.CONTAINERBASE = 'true';
|
|
const toolConstraints = [{ toolName: 'npm' as const }];
|
|
await exec('foobar', { toolConstraints });
|
|
await exec('foobar', { toolConstraints });
|
|
|
|
expect(actualCmds).toEqual([`install-tool npm 1.2.3`, `foobar`, `foobar`]);
|
|
});
|
|
|
|
it('installs a tool on every exec for binarySource=docker', async () => {
|
|
process.env = processEnv;
|
|
memCache.init();
|
|
datasource.getPkgReleases.mockResolvedValue({
|
|
releases: [{ version: '1.2.3' }],
|
|
});
|
|
cpExec.mockResolvedValue({ stdout: '', stderr: '' });
|
|
|
|
GlobalConfig.set({ ...globalConfig, binarySource: 'docker' });
|
|
const toolConstraints = [{ toolName: 'npm' as const }];
|
|
await exec('foobar', { docker, toolConstraints });
|
|
await exec('foobar', { docker, toolConstraints });
|
|
|
|
const dockerRunCmds = cpExec.mock.calls
|
|
.map((call) => asRawCommand(call[0]))
|
|
.filter((cmd) => cmd.startsWith('docker run'));
|
|
expect(dockerRunCmds).toHaveLength(2);
|
|
expect(dockerRunCmds.every((cmd) => cmd.includes('install-tool npm'))).toBe(
|
|
true,
|
|
);
|
|
});
|
|
|
|
it('only calls removeDockerContainer in catch block is useDocker is set', async () => {
|
|
cpExec.mockImplementation(() => {
|
|
throw new Error('some error occurred');
|
|
});
|
|
|
|
const removeDockerContainerSpy = vi.spyOn(
|
|
dockerModule,
|
|
'removeDockerContainer',
|
|
);
|
|
|
|
const promise = exec('foobar', {});
|
|
await expect(promise).rejects.toThrow('some error occurred');
|
|
expect(removeDockerContainerSpy).toHaveBeenCalledTimes(0);
|
|
});
|
|
|
|
it('wraps error if removeDockerContainer throws an error', async () => {
|
|
GlobalConfig.set({ ...globalConfig, binarySource: 'docker' });
|
|
cpExec.mockImplementation(() => {
|
|
throw new Error('some error occurred');
|
|
});
|
|
vi.spyOn(dockerModule, 'generateDockerCommand').mockImplementation(
|
|
(): any => 'asdf',
|
|
);
|
|
|
|
// The `removeDockerContainer` function is called once before it's used in the `catch` block.
|
|
// We want it to fail in the catch block so we can assert the error is wrapped.
|
|
let calledOnce = false;
|
|
const removeDockerContainerSpy = vi.spyOn(
|
|
dockerModule,
|
|
'removeDockerContainer',
|
|
);
|
|
removeDockerContainerSpy.mockImplementation((): Promise<void> => {
|
|
if (!calledOnce) {
|
|
calledOnce = true;
|
|
return Promise.resolve();
|
|
}
|
|
|
|
return Promise.reject(new Error('removeDockerContainer failed'));
|
|
});
|
|
|
|
const promise = exec('foobar', { docker });
|
|
await expect(promise).rejects.toThrow(
|
|
new Error(
|
|
'Error: "removeDockerContainer failed" - Original Error: "some error occurred"',
|
|
),
|
|
);
|
|
expect(removeDockerContainerSpy).toHaveBeenCalledTimes(2);
|
|
});
|
|
|
|
it('converts to TEMPORARY_ERROR', async () => {
|
|
cpExec.mockImplementation(() => {
|
|
class ErrorSignal extends Error {
|
|
signal?: string;
|
|
}
|
|
const error = new ErrorSignal();
|
|
error.signal = 'SIGTERM';
|
|
throw error;
|
|
});
|
|
const removeDockerContainerSpy = vi.spyOn(
|
|
dockerModule,
|
|
'removeDockerContainer',
|
|
);
|
|
const promise = exec('foobar', {});
|
|
await expect(promise).rejects.toThrow(TEMPORARY_ERROR);
|
|
expect(removeDockerContainerSpy).toHaveBeenCalledTimes(0);
|
|
});
|
|
|
|
describe('getToolSettingsOptions()', () => {
|
|
const config: UpdateArtifactsConfig = {
|
|
newValue: '5.6.4',
|
|
};
|
|
|
|
describe('for JVM settings', () => {
|
|
beforeEach(() => {
|
|
GlobalConfig.set({
|
|
toolSettings: { jvmMemory: 768, jvmMaxMemory: 800 },
|
|
});
|
|
|
|
// remove any test-specific overrides
|
|
delete config.toolSettings;
|
|
});
|
|
|
|
it('returns default values if no global or repo config', () => {
|
|
GlobalConfig.set({});
|
|
|
|
const res = getToolSettingsOptions(undefined);
|
|
|
|
expect(res).toMatchObject({
|
|
jvmMemory: 512,
|
|
jvmMaxMemory: 512,
|
|
});
|
|
});
|
|
|
|
it('returns default values if empty repo config', () => {
|
|
GlobalConfig.set({});
|
|
|
|
const res = getToolSettingsOptions({});
|
|
|
|
expect(res).toMatchObject({
|
|
jvmMemory: 512,
|
|
jvmMaxMemory: 512,
|
|
});
|
|
});
|
|
|
|
it('returns default values if empty global config', () => {
|
|
GlobalConfig.set({
|
|
toolSettings: {},
|
|
});
|
|
|
|
const res = getToolSettingsOptions(undefined);
|
|
|
|
expect(res).toMatchObject({
|
|
jvmMemory: 512,
|
|
jvmMaxMemory: 512,
|
|
});
|
|
});
|
|
|
|
describe('does not allow floating point numbers', () => {
|
|
it('in global config', () => {
|
|
GlobalConfig.set({
|
|
toolSettings: { jvmMemory: 512.5, jvmMaxMemory: 600.2 },
|
|
});
|
|
|
|
const res = getToolSettingsOptions(undefined);
|
|
|
|
expect(res).toMatchObject({
|
|
jvmMemory: 512,
|
|
jvmMaxMemory: 600,
|
|
});
|
|
});
|
|
|
|
it('in repo config', () => {
|
|
GlobalConfig.set({
|
|
toolSettings: { jvmMemory: 1024, jvmMaxMemory: 1024 },
|
|
});
|
|
|
|
config.toolSettings = {
|
|
jvmMemory: 556.8,
|
|
jvmMaxMemory: 600.4,
|
|
};
|
|
|
|
const res = getToolSettingsOptions(config.toolSettings);
|
|
|
|
expect(res).toMatchObject({
|
|
jvmMemory: 556,
|
|
jvmMaxMemory: 600,
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('when using repo config to override memory limits', () => {
|
|
it('when below global settings, repo settings are used', () => {
|
|
config.toolSettings = {
|
|
jvmMemory: 512,
|
|
jvmMaxMemory: 700,
|
|
};
|
|
|
|
const res = getToolSettingsOptions(config.toolSettings);
|
|
|
|
expect(res).toMatchObject({
|
|
jvmMemory: 512,
|
|
jvmMaxMemory: 700,
|
|
});
|
|
});
|
|
|
|
it('when repo settings are the same as global settings, they are used', () => {
|
|
config.toolSettings = {
|
|
jvmMemory: 512,
|
|
jvmMaxMemory: 600,
|
|
};
|
|
|
|
const res = getToolSettingsOptions(config.toolSettings);
|
|
|
|
expect(res).toMatchObject({
|
|
jvmMemory: 512,
|
|
jvmMaxMemory: 600,
|
|
});
|
|
});
|
|
|
|
it('when repo jvmMemory setting is higher than global setting, but lower than global jvmMaxMemory, the repo config is used', () => {
|
|
config.toolSettings = {
|
|
jvmMemory: 600,
|
|
};
|
|
|
|
const res = getToolSettingsOptions(config.toolSettings);
|
|
|
|
expect(res).toMatchObject({
|
|
jvmMemory: 600,
|
|
});
|
|
});
|
|
|
|
it('when repo jvmMaxMemory setting is lower than global settings, it is applied', () => {
|
|
config.toolSettings = {
|
|
jvmMaxMemory: 680,
|
|
};
|
|
|
|
const res = getToolSettingsOptions(config.toolSettings);
|
|
|
|
expect(res).toMatchObject({
|
|
jvmMaxMemory: 680,
|
|
});
|
|
});
|
|
|
|
it('when repo jvmMaxMemory setting is lower than global jvmMemory, jvmMemory is set to the same value', () => {
|
|
config.toolSettings = {
|
|
jvmMaxMemory: 600,
|
|
};
|
|
|
|
const res = getToolSettingsOptions(config.toolSettings);
|
|
|
|
expect(res).toMatchObject({
|
|
jvmMemory: 600,
|
|
jvmMaxMemory: 600,
|
|
});
|
|
});
|
|
|
|
it('when repo jvmMaxMemory setting is lower than repo jvmMemory, jvmMemory is set to the same value', () => {
|
|
config.toolSettings = {
|
|
jvmMemory: 600,
|
|
jvmMaxMemory: 600,
|
|
};
|
|
|
|
const res = getToolSettingsOptions(config.toolSettings);
|
|
|
|
expect(res).toMatchObject({
|
|
jvmMemory: 600,
|
|
jvmMaxMemory: 600,
|
|
});
|
|
});
|
|
|
|
it('when repo jvmMaxMemory setting is higher than global settings, they are ignored', () => {
|
|
config.toolSettings = {
|
|
jvmMaxMemory: 8192,
|
|
};
|
|
|
|
const res = getToolSettingsOptions(config.toolSettings);
|
|
|
|
expect(res).toMatchObject({
|
|
jvmMemory: 768,
|
|
jvmMaxMemory: 800,
|
|
});
|
|
});
|
|
|
|
it('when repo jvmMaxMemory setting is higher than global settings, a debug log is logged', () => {
|
|
config.toolSettings = {
|
|
jvmMaxMemory: 8192,
|
|
};
|
|
|
|
const res = getToolSettingsOptions(config.toolSettings);
|
|
|
|
expect(logger.logger.once.debug).toHaveBeenCalledWith(
|
|
'A higher jvmMaxMemory (8192) than the global configuration (800) is not permitted for Java VM invocations. Using global configuration instead',
|
|
);
|
|
|
|
expect(res).toMatchObject({
|
|
jvmMemory: 768,
|
|
jvmMaxMemory: 800,
|
|
});
|
|
});
|
|
});
|
|
|
|
// to provide a bit more safety to users, so they can't specify too little memory for Gradle
|
|
describe('a minimum of 512M is enforced', () => {
|
|
it('when global settings are lower than 512M, they are overridden to 512M', () => {
|
|
GlobalConfig.set({
|
|
toolSettings: { jvmMemory: 100, jvmMaxMemory: 127 },
|
|
});
|
|
|
|
const res = getToolSettingsOptions(undefined);
|
|
|
|
expect(res).toMatchObject({
|
|
jvmMemory: 512,
|
|
jvmMaxMemory: 512,
|
|
});
|
|
});
|
|
|
|
it('when global settings are lower than 512M, a debug log is logged', () => {
|
|
GlobalConfig.set({
|
|
toolSettings: { jvmMemory: 200, jvmMaxMemory: 255 },
|
|
});
|
|
|
|
getToolSettingsOptions(undefined);
|
|
|
|
expect(logger.logger.once.debug).toHaveBeenCalledWith(
|
|
'Overriding low memory settings for Java VM invocations to a minimum of 512M',
|
|
);
|
|
});
|
|
|
|
it('when repo settings are lower than 512M, they are overridden to 512M', () => {
|
|
config.toolSettings = {
|
|
jvmMemory: 500,
|
|
jvmMaxMemory: 511,
|
|
};
|
|
|
|
const res = getToolSettingsOptions(config.toolSettings);
|
|
|
|
expect(res).toMatchObject({
|
|
jvmMemory: 512,
|
|
jvmMaxMemory: 512,
|
|
});
|
|
});
|
|
|
|
it('when repo settings are lower than 512M, a debug log is logged', () => {
|
|
config.toolSettings = {
|
|
jvmMemory: 500,
|
|
jvmMaxMemory: 511,
|
|
};
|
|
|
|
getToolSettingsOptions(config.toolSettings);
|
|
|
|
expect(logger.logger.once.debug).toHaveBeenCalledWith(
|
|
'Overriding low memory settings for Java VM invocations to a minimum of 512M',
|
|
);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('for Node settings', () => {
|
|
beforeEach(() => {
|
|
GlobalConfig.set({
|
|
toolSettings: { nodeMaxMemory: 1024 },
|
|
});
|
|
|
|
// remove any test-specific overrides
|
|
delete config.toolSettings;
|
|
});
|
|
|
|
it('does not return a default value if no global or repo config', () => {
|
|
GlobalConfig.set({});
|
|
|
|
const res = getToolSettingsOptions(undefined);
|
|
|
|
expect(res).toMatchObject({
|
|
nodeMaxMemory: undefined,
|
|
});
|
|
});
|
|
|
|
it('does not return default values if empty global config', () => {
|
|
GlobalConfig.set({
|
|
toolSettings: {},
|
|
});
|
|
|
|
const res = getToolSettingsOptions(undefined);
|
|
|
|
expect(res).toMatchObject({
|
|
nodeMaxMemory: undefined,
|
|
});
|
|
});
|
|
|
|
describe('does not allow floating point numbers', () => {
|
|
it('in global config', () => {
|
|
GlobalConfig.set({
|
|
toolSettings: { nodeMaxMemory: 1024.1536 },
|
|
});
|
|
|
|
const res = getToolSettingsOptions(undefined);
|
|
|
|
expect(res).toMatchObject({
|
|
nodeMaxMemory: 1024,
|
|
});
|
|
});
|
|
|
|
it('in repo config', () => {
|
|
GlobalConfig.set({});
|
|
|
|
config.toolSettings = {
|
|
nodeMaxMemory: 1024.1536,
|
|
};
|
|
|
|
const res = getToolSettingsOptions(config.toolSettings);
|
|
|
|
expect(res).toMatchObject({
|
|
nodeMaxMemory: 1024,
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('when using repo config to override memory limits', () => {
|
|
it('when below global settings, repo settings are used', () => {
|
|
config.toolSettings = {
|
|
nodeMaxMemory: 700,
|
|
};
|
|
|
|
const res = getToolSettingsOptions(config.toolSettings);
|
|
|
|
expect(res).toMatchObject({
|
|
nodeMaxMemory: 700,
|
|
});
|
|
});
|
|
|
|
it('when repo settings are the same as global settings, they are used', () => {
|
|
config.toolSettings = {
|
|
nodeMaxMemory: 1024,
|
|
};
|
|
|
|
const res = getToolSettingsOptions(config.toolSettings);
|
|
|
|
expect(res).toMatchObject({
|
|
nodeMaxMemory: 1024,
|
|
});
|
|
});
|
|
|
|
it('when repo nodeMaxMemory setting is lower than global settings, it is applied', () => {
|
|
config.toolSettings = {
|
|
nodeMaxMemory: 128,
|
|
};
|
|
|
|
const res = getToolSettingsOptions(config.toolSettings);
|
|
|
|
expect(res).toMatchObject({
|
|
nodeMaxMemory: 128,
|
|
});
|
|
});
|
|
|
|
it('when repo nodeMaxMemory setting is higher than global settings, they are ignored', () => {
|
|
config.toolSettings = {
|
|
nodeMaxMemory: 8192,
|
|
};
|
|
|
|
const res = getToolSettingsOptions(config.toolSettings);
|
|
|
|
expect(res).toMatchObject({
|
|
nodeMaxMemory: 1024,
|
|
});
|
|
});
|
|
|
|
it('when repo nodeMaxMemory setting is higher than global settings, a debug log is logged', () => {
|
|
config.toolSettings = {
|
|
nodeMaxMemory: 8192,
|
|
};
|
|
|
|
const res = getToolSettingsOptions(config.toolSettings);
|
|
|
|
expect(logger.logger.once.debug).toHaveBeenCalledWith(
|
|
'A higher nodeMaxMemory (8192) than the global configuration (1024) is not permitted for Node invocations. Using global configuration instead',
|
|
);
|
|
|
|
expect(res).toMatchObject({
|
|
nodeMaxMemory: 1024,
|
|
});
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('gradleJvmArg()', () => {
|
|
it('takes the values given to it, and returns the JVM arguments', () => {
|
|
const result = gradleJvmArg({ jvmMemory: 256, jvmMaxMemory: 768 });
|
|
expect(result).toBe(' -Dorg.gradle.jvmargs="-Xms256m -Xmx768m"');
|
|
});
|
|
});
|
|
});
|