2023-03-28 21:05:36 +00:00
|
|
|
import fs from 'node:fs';
|
2021-12-22 07:38:49 +00:00
|
|
|
import fsExtra from 'fs-extra';
|
2024-08-19 13:15:27 +00:00
|
|
|
import type { DirectoryResult } from 'tmp-promise';
|
|
|
|
import { dir } from 'tmp-promise';
|
2020-11-12 20:46:08 +00:00
|
|
|
import upath from 'upath';
|
2021-12-09 12:45:48 +00:00
|
|
|
import { logger } from '../../../../logger';
|
2021-12-13 21:51:36 +00:00
|
|
|
import customConfig from './__fixtures__/config';
|
2020-05-01 16:03:48 +00:00
|
|
|
import * as file from './file';
|
2017-01-20 13:03:18 +00:00
|
|
|
|
2021-08-18 05:46:56 +00:00
|
|
|
describe('workers/global/config/parse/file', () => {
|
2023-06-20 21:01:25 +00:00
|
|
|
const processExitSpy = jest.spyOn(process, 'exit');
|
|
|
|
const fsPathExistsSpy = jest.spyOn(fsExtra, 'pathExists');
|
|
|
|
const fsRemoveSpy = jest.spyOn(fsExtra, 'remove');
|
|
|
|
|
2021-03-02 16:16:05 +00:00
|
|
|
let tmp: DirectoryResult;
|
|
|
|
|
|
|
|
beforeAll(async () => {
|
|
|
|
tmp = await dir({ unsafeCleanup: true });
|
|
|
|
});
|
|
|
|
|
|
|
|
afterAll(async () => {
|
|
|
|
await tmp.cleanup();
|
|
|
|
});
|
|
|
|
|
2017-01-20 13:03:18 +00:00
|
|
|
describe('.getConfig()', () => {
|
2021-12-09 12:45:48 +00:00
|
|
|
it.each([
|
2021-12-13 21:51:36 +00:00
|
|
|
['custom js config file', 'config.js'],
|
2023-12-01 13:07:10 +00:00
|
|
|
['custom js config file', 'config.cjs'],
|
2021-12-13 21:51:36 +00:00
|
|
|
['custom js config file exporting a Promise', 'config-promise.js'],
|
|
|
|
['custom js config file exporting a function', 'config-function.js'],
|
|
|
|
// The next two are different syntactic ways of expressing the same thing
|
|
|
|
[
|
|
|
|
'custom js config file exporting a function returning a Promise',
|
|
|
|
'config-function-promise.js',
|
|
|
|
],
|
|
|
|
[
|
|
|
|
'custom js config file exporting an async function',
|
|
|
|
'config-async-function.js',
|
|
|
|
],
|
2023-08-25 08:16:30 +00:00
|
|
|
['.renovaterc', '.renovaterc'],
|
2021-12-09 12:45:48 +00:00
|
|
|
['JSON5 config file', 'config.json5'],
|
|
|
|
['YAML config file', 'config.yaml'],
|
2023-09-06 13:35:52 +00:00
|
|
|
])('parses %s', async (_fileType, filePath) => {
|
2021-12-09 12:45:48 +00:00
|
|
|
const configFile = upath.resolve(__dirname, './__fixtures__/', filePath);
|
2021-11-17 06:49:53 +00:00
|
|
|
expect(
|
2023-11-07 15:50:29 +00:00
|
|
|
await file.getConfig({ RENOVATE_CONFIG_FILE: configFile }),
|
2021-11-17 06:49:53 +00:00
|
|
|
).toEqual(customConfig);
|
2018-11-16 10:47:44 +00:00
|
|
|
});
|
2021-12-09 12:45:48 +00:00
|
|
|
|
2021-11-17 06:49:53 +00:00
|
|
|
it('migrates', async () => {
|
2021-12-13 21:51:36 +00:00
|
|
|
const configFile = upath.resolve(__dirname, './__fixtures__/config2.js');
|
2021-11-17 06:49:53 +00:00
|
|
|
const res = await file.getConfig({ RENOVATE_CONFIG_FILE: configFile });
|
2018-11-16 10:47:44 +00:00
|
|
|
expect(res).toMatchSnapshot();
|
2021-11-08 12:16:58 +00:00
|
|
|
expect(res.rangeStrategy).toBe('bump');
|
2017-01-20 13:03:18 +00:00
|
|
|
});
|
2021-08-29 11:23:49 +00:00
|
|
|
|
2024-04-13 08:52:28 +00:00
|
|
|
it('warns if config is invalid', async () => {
|
|
|
|
const configFile = upath.resolve(tmp.path, 'config.js');
|
|
|
|
const fileContent = `module.exports = {
|
|
|
|
"enabled": "invalid-value",
|
|
|
|
"prTitle":"something",
|
|
|
|
};`;
|
|
|
|
fs.writeFileSync(configFile, fileContent, { encoding: 'utf8' });
|
|
|
|
await file.getConfig({ RENOVATE_CONFIG_FILE: configFile });
|
|
|
|
expect(logger.warn).toHaveBeenCalledTimes(2);
|
|
|
|
fs.unlinkSync(configFile);
|
|
|
|
});
|
|
|
|
|
2021-11-17 06:49:53 +00:00
|
|
|
it('parse and returns empty config if there is no RENOVATE_CONFIG_FILE in env', async () => {
|
|
|
|
expect(await file.getConfig({})).toBeDefined();
|
2021-08-29 11:23:49 +00:00
|
|
|
});
|
|
|
|
|
2021-12-09 12:45:48 +00:00
|
|
|
it.each([
|
|
|
|
[
|
|
|
|
'config.js',
|
|
|
|
`module.exports = {
|
2019-12-26 09:08:43 +00:00
|
|
|
"platform": "github",
|
|
|
|
"token":"abcdef",
|
|
|
|
"onboarding": false,
|
2020-07-22 18:11:22 +00:00
|
|
|
"gitAuthor": "Renovate Bot <renovate@whitesourcesoftware.com>"
|
2019-12-26 09:08:43 +00:00
|
|
|
"onboardingConfig": {
|
2023-06-27 12:26:57 +00:00
|
|
|
"extends": ["config:recommended"],
|
2019-12-26 09:08:43 +00:00
|
|
|
},
|
|
|
|
"repositories": [ "test/test" ],
|
2021-12-09 12:45:48 +00:00
|
|
|
};`,
|
|
|
|
],
|
|
|
|
['config.json5', `"invalid":`],
|
|
|
|
['config.yaml', `invalid: -`],
|
|
|
|
])(
|
|
|
|
'fatal error and exit if error in parsing %s',
|
|
|
|
async (fileName, fileContent) => {
|
2023-06-20 21:01:25 +00:00
|
|
|
processExitSpy.mockImplementationOnce(() => undefined as never);
|
2021-12-09 12:45:48 +00:00
|
|
|
const configFile = upath.resolve(tmp.path, fileName);
|
|
|
|
fs.writeFileSync(configFile, fileContent, { encoding: 'utf8' });
|
|
|
|
await file.getConfig({ RENOVATE_CONFIG_FILE: configFile });
|
2023-06-20 21:01:25 +00:00
|
|
|
expect(processExitSpy).toHaveBeenCalledWith(1);
|
2021-12-09 12:45:48 +00:00
|
|
|
fs.unlinkSync(configFile);
|
2023-11-07 15:50:29 +00:00
|
|
|
},
|
2021-12-09 12:45:48 +00:00
|
|
|
);
|
2021-08-29 11:23:49 +00:00
|
|
|
|
2021-11-17 06:49:53 +00:00
|
|
|
it('fatal error and exit if custom config file does not exist', async () => {
|
2023-06-20 21:01:25 +00:00
|
|
|
processExitSpy
|
|
|
|
.mockImplementationOnce(() => undefined as never)
|
|
|
|
.mockImplementationOnce(() => undefined as never);
|
2021-08-29 11:23:49 +00:00
|
|
|
const configFile = upath.resolve(tmp.path, './file4.js');
|
2023-06-20 21:01:25 +00:00
|
|
|
|
2021-11-17 06:49:53 +00:00
|
|
|
await file.getConfig({ RENOVATE_CONFIG_FILE: configFile });
|
2021-08-29 11:23:49 +00:00
|
|
|
|
2023-06-20 21:01:25 +00:00
|
|
|
expect(processExitSpy).toHaveBeenCalledWith(1);
|
2021-08-29 11:23:49 +00:00
|
|
|
});
|
2021-12-09 12:45:48 +00:00
|
|
|
|
2021-12-22 07:38:49 +00:00
|
|
|
it('fatal error and exit if config.js contains unresolved env var', async () => {
|
2023-06-20 21:01:25 +00:00
|
|
|
processExitSpy.mockImplementationOnce(() => undefined as never);
|
2021-12-22 07:38:49 +00:00
|
|
|
|
|
|
|
const configFile = upath.resolve(
|
|
|
|
__dirname,
|
2023-11-07 15:50:29 +00:00
|
|
|
'./__fixtures__/config-ref-error.js-invalid',
|
2021-12-22 07:38:49 +00:00
|
|
|
);
|
|
|
|
const tmpDir = tmp.path;
|
|
|
|
await fsExtra.ensureDir(tmpDir);
|
|
|
|
|
|
|
|
const tmpConfigFile = upath.resolve(tmpDir, 'config-ref-error.js');
|
|
|
|
await fsExtra.copy(configFile, tmpConfigFile);
|
|
|
|
|
|
|
|
await file.getConfig({ RENOVATE_CONFIG_FILE: tmpConfigFile });
|
|
|
|
|
|
|
|
expect(logger.fatal).toHaveBeenCalledWith(
|
2023-11-07 15:50:29 +00:00
|
|
|
`Error parsing config file due to unresolved variable(s): CI_API_V4_URL is not defined`,
|
2021-12-22 07:38:49 +00:00
|
|
|
);
|
2023-06-20 21:01:25 +00:00
|
|
|
expect(processExitSpy).toHaveBeenCalledWith(1);
|
2021-12-22 07:38:49 +00:00
|
|
|
});
|
|
|
|
|
2021-12-09 12:45:48 +00:00
|
|
|
it.each([
|
|
|
|
['invalid config file type', './file.txt'],
|
|
|
|
['missing config file type', './file'],
|
|
|
|
])('fatal error and exit if %s', async (fileType, filePath) => {
|
2023-06-20 21:01:25 +00:00
|
|
|
processExitSpy.mockImplementationOnce(() => undefined as never);
|
2021-12-09 12:45:48 +00:00
|
|
|
const configFile = upath.resolve(tmp.path, filePath);
|
|
|
|
fs.writeFileSync(configFile, `{"token": "abc"}`, { encoding: 'utf8' });
|
|
|
|
await file.getConfig({ RENOVATE_CONFIG_FILE: configFile });
|
2023-06-20 21:01:25 +00:00
|
|
|
expect(processExitSpy).toHaveBeenCalledWith(1);
|
2021-12-09 12:45:48 +00:00
|
|
|
expect(logger.fatal).toHaveBeenCalledWith('Unsupported file type');
|
|
|
|
fs.unlinkSync(configFile);
|
|
|
|
});
|
2023-06-20 21:01:25 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
describe('deleteConfigFile()', () => {
|
|
|
|
it.each([[undefined], [' ']])(
|
|
|
|
'skip when RENOVATE_CONFIG_FILE is not set ("%s")',
|
|
|
|
async (configFile) => {
|
2024-08-07 07:28:34 +00:00
|
|
|
await file.deleteNonDefaultConfig(
|
|
|
|
{ RENOVATE_CONFIG_FILE: configFile },
|
|
|
|
true,
|
|
|
|
);
|
2023-06-20 21:01:25 +00:00
|
|
|
|
|
|
|
expect(fsRemoveSpy).toHaveBeenCalledTimes(0);
|
2023-11-07 15:50:29 +00:00
|
|
|
},
|
2023-06-20 21:01:25 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
it('skip when config file does not exist', async () => {
|
|
|
|
fsPathExistsSpy.mockResolvedValueOnce(false as never);
|
|
|
|
|
2024-08-07 07:28:34 +00:00
|
|
|
await file.deleteNonDefaultConfig(
|
|
|
|
{
|
|
|
|
RENOVATE_CONFIG_FILE: 'path',
|
|
|
|
},
|
|
|
|
true,
|
|
|
|
);
|
2023-06-20 21:01:25 +00:00
|
|
|
|
|
|
|
expect(fsRemoveSpy).toHaveBeenCalledTimes(0);
|
|
|
|
});
|
|
|
|
|
|
|
|
it.each([['false'], [' ']])(
|
2024-08-07 07:28:34 +00:00
|
|
|
'skip if deleteConfigFile is not set ("%s")',
|
2023-06-20 21:01:25 +00:00
|
|
|
async (deleteConfig) => {
|
|
|
|
fsPathExistsSpy.mockResolvedValueOnce(true as never);
|
|
|
|
|
2024-08-07 07:28:34 +00:00
|
|
|
await file.deleteNonDefaultConfig(
|
|
|
|
{
|
|
|
|
RENOVATE_CONFIG_FILE: '/path/to/config.js',
|
|
|
|
},
|
|
|
|
deleteConfig === 'true',
|
|
|
|
);
|
2023-06-20 21:01:25 +00:00
|
|
|
|
|
|
|
expect(fsRemoveSpy).toHaveBeenCalledTimes(0);
|
2023-11-07 15:50:29 +00:00
|
|
|
},
|
2023-06-20 21:01:25 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
it('removes the specified config file', async () => {
|
|
|
|
fsRemoveSpy.mockImplementationOnce(() => {
|
|
|
|
// no-op
|
|
|
|
});
|
|
|
|
fsPathExistsSpy.mockResolvedValueOnce(true as never);
|
|
|
|
const configFile = '/path/to/config.js';
|
|
|
|
|
2024-08-07 07:28:34 +00:00
|
|
|
await file.deleteNonDefaultConfig(
|
|
|
|
{
|
|
|
|
RENOVATE_CONFIG_FILE: configFile,
|
|
|
|
},
|
|
|
|
true,
|
|
|
|
);
|
2023-06-20 21:01:25 +00:00
|
|
|
|
|
|
|
expect(fsRemoveSpy).toHaveBeenCalledTimes(1);
|
|
|
|
expect(fsRemoveSpy).toHaveBeenCalledWith(configFile);
|
|
|
|
expect(logger.trace).toHaveBeenCalledWith(
|
|
|
|
expect.anything(),
|
2023-11-07 15:50:29 +00:00
|
|
|
'config file successfully deleted',
|
2023-06-20 21:01:25 +00:00
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('fails silently when attempting to delete the config file', async () => {
|
|
|
|
fsRemoveSpy.mockImplementationOnce(() => {
|
|
|
|
throw new Error();
|
|
|
|
});
|
|
|
|
fsPathExistsSpy.mockResolvedValueOnce(true as never);
|
|
|
|
const configFile = '/path/to/config.js';
|
|
|
|
|
2024-08-07 07:28:34 +00:00
|
|
|
await file.deleteNonDefaultConfig(
|
|
|
|
{
|
|
|
|
RENOVATE_CONFIG_FILE: configFile,
|
|
|
|
},
|
|
|
|
true,
|
|
|
|
);
|
2023-06-20 21:01:25 +00:00
|
|
|
|
|
|
|
expect(fsRemoveSpy).toHaveBeenCalledTimes(1);
|
|
|
|
expect(fsRemoveSpy).toHaveBeenCalledWith(configFile);
|
|
|
|
expect(logger.warn).toHaveBeenCalledWith(
|
|
|
|
expect.anything(),
|
2023-11-07 15:50:29 +00:00
|
|
|
'error deleting config file',
|
2023-06-20 21:01:25 +00:00
|
|
|
);
|
|
|
|
});
|
2017-01-20 13:03:18 +00:00
|
|
|
});
|
|
|
|
});
|