diff --git a/docs/usage/getting-started/running.md b/docs/usage/getting-started/running.md index a19079c12ef5f8e8be8bca1045b3f5ab85635520..cff201ef9c54e73c75670293e4baa140a609c3c2 100644 --- a/docs/usage/getting-started/running.md +++ b/docs/usage/getting-started/running.md @@ -92,7 +92,7 @@ WhiteSource Renovate On-Premises and WhiteSource Remediate both run as long-live ### Global config -Renovate's server-side/admin config is referred to as its "global" config, and can be specified using either a config file (`config.js` or `config.json`), environment variables, or CLI parameters. +Renovate's server-side/admin config is referred to as its "global" config, and can be specified using either a config file (`config.js`, `config.json`, `config.json5`, `config.yaml` or `config.yml`), environment variables, or CLI parameters. Some config is global-only, meaning that either it is only applicable to the bot administrator or it can only be controlled by the administrator and not repository users. Those are documented in [Self-hosted Configuration](../self-hosted-configuration.md). @@ -108,6 +108,21 @@ If you combine both of the above then any single config option in the environmen Note: it's also possible to change the default prefix from `RENOVATE_` using `ENV_PREFIX`. e.g. `ENV_PREFIX=RNV_ RNV_TOKEN=abc123 renovate`. +#### Using `config.js` + +If you use a `config.js`, it will be expected to export a configuration via `module.exports`. +The value can be either a plain JavaScript object like in this example where `config.js` exports a plain object: + +```javascript +module.exports = { + token: 'abcdefg', +}; +``` + +`config.js` may also export a `Promise` of such an object, or a function that will return either a plain Javascript object or a `Promise` of such an object. +This allows one to include the results of asynchronous operations in the exported value. +An example of a `config.js` that exports an async function (which is a function that returns a `Promise`) can be seen in a comment for [#10011: Allow autodiscover filtering for repo topic](https://github.com/renovatebot/renovate/issues/10011#issuecomment-992568583) and more examples can be seen in [`file.spec.ts`](https://github.com/renovatebot/renovate/blob/main/lib/workers/global/config/parse/file.spec.ts). + ### Authentication Regardless of platform, you need to select a user account for `renovate` to assume the identity of, and generate a Personal Access Token. diff --git a/lib/workers/global/config/parse/__fixtures__/config-async-function.js b/lib/workers/global/config/parse/__fixtures__/config-async-function.js new file mode 100644 index 0000000000000000000000000000000000000000..5d5b3c5d9244abf8231af66c22ac1d7bba3090f5 --- /dev/null +++ b/lib/workers/global/config/parse/__fixtures__/config-async-function.js @@ -0,0 +1,8 @@ +// This is functionally equivalent to config-function-promise.js but syntactically different + +// @ts-ignore +module.exports = async function () { + return { + token: 'abcdefg', + } +}; diff --git a/lib/workers/global/config/parse/__fixtures__/config-function-promise.js b/lib/workers/global/config/parse/__fixtures__/config-function-promise.js new file mode 100644 index 0000000000000000000000000000000000000000..946c9f62939ef5019afef571403df939e8c88f8e --- /dev/null +++ b/lib/workers/global/config/parse/__fixtures__/config-function-promise.js @@ -0,0 +1,10 @@ +// This is functionally equivalent to config-async-function.js but syntactically different + +// @ts-ignore +module.exports = function () { + return new Promise(resolve => { + resolve({ + token: 'abcdefg', + }) + }); +}; diff --git a/lib/workers/global/config/parse/__fixtures__/config-function.js b/lib/workers/global/config/parse/__fixtures__/config-function.js new file mode 100644 index 0000000000000000000000000000000000000000..e9e2acb9b89f7b8c96a66b349b56ad592a200941 --- /dev/null +++ b/lib/workers/global/config/parse/__fixtures__/config-function.js @@ -0,0 +1,6 @@ +// @ts-ignore +module.exports = function () { + return { + token: 'abcdefg', + } +}; diff --git a/lib/workers/global/config/parse/__fixtures__/config-promise.js b/lib/workers/global/config/parse/__fixtures__/config-promise.js new file mode 100644 index 0000000000000000000000000000000000000000..a8c19e3adae43c79221495e6dc5ab473bf6b6a85 --- /dev/null +++ b/lib/workers/global/config/parse/__fixtures__/config-promise.js @@ -0,0 +1,8 @@ +// @ts-ignore +module.exports = new Promise( resolve => { + resolve( + { + token: 'abcdefg', + } + ); +}); diff --git a/lib/workers/global/config/parse/__fixtures__/file.js b/lib/workers/global/config/parse/__fixtures__/config.js similarity index 100% rename from lib/workers/global/config/parse/__fixtures__/file.js rename to lib/workers/global/config/parse/__fixtures__/config.js diff --git a/lib/workers/global/config/parse/__fixtures__/file2.js b/lib/workers/global/config/parse/__fixtures__/config2.js similarity index 100% rename from lib/workers/global/config/parse/__fixtures__/file2.js rename to lib/workers/global/config/parse/__fixtures__/config2.js diff --git a/lib/workers/global/config/parse/file.spec.ts b/lib/workers/global/config/parse/file.spec.ts index cf497566a42a59308cf81bf249ddbcc1d71d5e07..a0826752160183ceef4526e309618e8d2b07a3f0 100644 --- a/lib/workers/global/config/parse/file.spec.ts +++ b/lib/workers/global/config/parse/file.spec.ts @@ -2,7 +2,7 @@ import fs from 'fs'; import { DirectoryResult, dir } from 'tmp-promise'; import upath from 'upath'; import { logger } from '../../../../logger'; -import customConfig from './__fixtures__/file'; +import customConfig from './__fixtures__/config'; import * as file from './file'; describe('workers/global/config/parse/file', () => { @@ -18,7 +18,18 @@ describe('workers/global/config/parse/file', () => { describe('.getConfig()', () => { it.each([ - ['custom config file with extension', 'file.js'], + ['custom js config file', 'config.js'], + ['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', + ], ['JSON5 config file', 'config.json5'], ['YAML config file', 'config.yaml'], ])('parses %s', async (fileType, filePath) => { @@ -29,7 +40,7 @@ describe('workers/global/config/parse/file', () => { }); it('migrates', async () => { - const configFile = upath.resolve(__dirname, './__fixtures__/file2.js'); + const configFile = upath.resolve(__dirname, './__fixtures__/config2.js'); const res = await file.getConfig({ RENOVATE_CONFIG_FILE: configFile }); expect(res).toMatchSnapshot(); expect(res.rangeStrategy).toBe('bump'); diff --git a/lib/workers/global/config/parse/file.ts b/lib/workers/global/config/parse/file.ts index 103d24d8692f4ec7f0095ebbeff5eabd4aa55565..24a0e31df7d6c0adf61ebff5c9f814f6c1ea24ef 100644 --- a/lib/workers/global/config/parse/file.ts +++ b/lib/workers/global/config/parse/file.ts @@ -1,3 +1,4 @@ +import is from 'is'; import { load } from 'js-yaml'; import JSON5 from 'json5'; import upath from 'upath'; @@ -18,7 +19,12 @@ export async function getParsedContent(file: string): Promise<RenovateConfig> { return JSON5.parse(await readFile(file, 'utf8')); case '.js': { const tmpConfig = await import(file); - return tmpConfig.default ? tmpConfig.default : tmpConfig; + let config = tmpConfig.default ? tmpConfig.default : tmpConfig; + // Allow the config to be a function + if (is.fn(config)) { + config = config(); + } + return config; } default: throw new Error('Unsupported file type');