114 lines
3.9 KiB
JavaScript
114 lines
3.9 KiB
JavaScript
const {readFile} = require('fs').promises;
|
|
const {castArray, pickBy, isNil, isString, isPlainObject} = require('lodash');
|
|
const findPkgUp = require('pkg-up');
|
|
const {cosmiconfig} = require('cosmiconfig');
|
|
const resolveFrom = require('resolve-from');
|
|
const findDuplicatedPropertyKeys = require('find-duplicated-property-keys');
|
|
const debug = require('debug')('semantic-release:config');
|
|
const {repoUrl} = require('./git');
|
|
const PLUGINS_DEFINITIONS = require('./definitions/plugins');
|
|
const plugins = require('./plugins');
|
|
const {validatePlugin, parseConfig} = require('./plugins/utils');
|
|
const getError = require('./get-error');
|
|
|
|
const CONFIG_NAME = 'release';
|
|
const CONFIG_FILES = [
|
|
'package.json',
|
|
`.${CONFIG_NAME}rc`,
|
|
`.${CONFIG_NAME}rc.json`,
|
|
`.${CONFIG_NAME}rc.yaml`,
|
|
`.${CONFIG_NAME}rc.yml`,
|
|
`.${CONFIG_NAME}rc.js`,
|
|
`${CONFIG_NAME}.config.js`,
|
|
];
|
|
|
|
module.exports = async (context, cliOptions) => {
|
|
const {cwd, env} = context;
|
|
const {config, filepath} = (await cosmiconfig(CONFIG_NAME, {searchPlaces: CONFIG_FILES}).search(cwd)) || {};
|
|
|
|
debug('load config from: %s', filepath);
|
|
|
|
// Merge config file options and CLI/API options
|
|
let options = {...config, ...cliOptions};
|
|
if (options.ci === false) {
|
|
options.noCi = true;
|
|
}
|
|
|
|
const pluginsPath = {};
|
|
let extendPaths;
|
|
({extends: extendPaths, ...options} = options);
|
|
if (extendPaths) {
|
|
// If `extends` is defined, load and merge each shareable config with `options`
|
|
options = {
|
|
...castArray(extendPaths).reduce((result, extendPath) => {
|
|
const extendsOptions = require(resolveFrom.silent(__dirname, extendPath) || resolveFrom(cwd, extendPath));
|
|
|
|
// For each plugin defined in a shareable config, save in `pluginsPath` the extendable config path,
|
|
// so those plugin will be loaded relatively to the config file
|
|
Object.entries(extendsOptions)
|
|
.filter(([, value]) => Boolean(value))
|
|
.reduce((pluginsPath, [option, value]) => {
|
|
castArray(value).forEach((plugin) => {
|
|
if (option === 'plugins' && validatePlugin(plugin)) {
|
|
pluginsPath[parseConfig(plugin)[0]] = extendPath;
|
|
} else if (
|
|
PLUGINS_DEFINITIONS[option] &&
|
|
(isString(plugin) || (isPlainObject(plugin) && isString(plugin.path)))
|
|
) {
|
|
pluginsPath[isString(plugin) ? plugin : plugin.path] = extendPath;
|
|
}
|
|
});
|
|
return pluginsPath;
|
|
}, pluginsPath);
|
|
|
|
return {...result, ...extendsOptions};
|
|
}, {}),
|
|
...options,
|
|
};
|
|
}
|
|
|
|
// Set default options values if not defined yet
|
|
options = {
|
|
branches: [
|
|
'+([0-9])?(.{+([0-9]),x}).x',
|
|
'master',
|
|
'next',
|
|
'next-major',
|
|
{name: 'beta', prerelease: true},
|
|
{name: 'alpha', prerelease: true},
|
|
],
|
|
repositoryUrl: (await pkgRepoUrl({cwd})) || (await repoUrl({cwd, env})),
|
|
tagFormat: `v\${version}`,
|
|
plugins: [
|
|
'@semantic-release/commit-analyzer',
|
|
'@semantic-release/release-notes-generator',
|
|
'@semantic-release/npm',
|
|
'@semantic-release/github',
|
|
],
|
|
// Remove `null` and `undefined` options so they can be replaced with default ones
|
|
...pickBy(options, (option) => !isNil(option)),
|
|
...(options.branches ? {branches: castArray(options.branches)} : {}),
|
|
};
|
|
|
|
debug('options values: %O', options);
|
|
|
|
return {options, plugins: await plugins({...context, options}, pluginsPath)};
|
|
};
|
|
|
|
async function pkgRepoUrl(options) {
|
|
const packageJsonPath = await findPkgUp(options);
|
|
if (!packageJsonPath) return;
|
|
|
|
const packageJsonString = await readFile(packageJsonPath, 'utf-8');
|
|
const result = findDuplicatedPropertyKeys(packageJsonString);
|
|
|
|
if (result.length > 0) {
|
|
throw getError('EDUPLICATEREPOSITORYKEY', {packageJsonPath});
|
|
}
|
|
|
|
const {repository} = require(packageJsonPath);
|
|
if (!repository) return;
|
|
|
|
return isPlainObject(repository) ? repository.url : repository;
|
|
}
|