UNPKG

3.34 kBJavaScriptView Raw
1const {castArray, pickBy, isNil, isString, isPlainObject} = require('lodash');
2const readPkgUp = require('read-pkg-up');
3const {cosmiconfig} = require('cosmiconfig');
4const resolveFrom = require('resolve-from');
5const debug = require('debug')('semantic-release:config');
6const {repoUrl} = require('./git');
7const PLUGINS_DEFINITIONS = require('./definitions/plugins');
8const plugins = require('./plugins');
9const {validatePlugin, parseConfig} = require('./plugins/utils');
10
11const CONFIG_NAME = 'release';
12
13module.exports = async (context, cliOptions) => {
14 const {cwd, env} = context;
15 const {config, filepath} = (await cosmiconfig(CONFIG_NAME).search(cwd)) || {};
16
17 debug('load config from: %s', filepath);
18
19 // Merge config file options and CLI/API options
20 let options = {...config, ...cliOptions};
21
22 const pluginsPath = {};
23 let extendPaths;
24 ({extends: extendPaths, ...options} = options);
25 if (extendPaths) {
26 // If `extends` is defined, load and merge each shareable config with `options`
27 options = {
28 ...castArray(extendPaths).reduce((result, extendPath) => {
29 const extendsOptions = require(resolveFrom.silent(__dirname, extendPath) || resolveFrom(cwd, extendPath));
30
31 // For each plugin defined in a shareable config, save in `pluginsPath` the extendable config path,
32 // so those plugin will be loaded relatively to the config file
33 Object.entries(extendsOptions)
34 .filter(([, value]) => Boolean(value))
35 .reduce((pluginsPath, [option, value]) => {
36 castArray(value).forEach((plugin) => {
37 if (option === 'plugins' && validatePlugin(plugin)) {
38 pluginsPath[parseConfig(plugin)[0]] = extendPath;
39 } else if (
40 PLUGINS_DEFINITIONS[option] &&
41 (isString(plugin) || (isPlainObject(plugin) && isString(plugin.path)))
42 ) {
43 pluginsPath[isString(plugin) ? plugin : plugin.path] = extendPath;
44 }
45 });
46 return pluginsPath;
47 }, pluginsPath);
48
49 return {...result, ...extendsOptions};
50 }, {}),
51 ...options,
52 };
53 }
54
55 // Set default options values if not defined yet
56 options = {
57 branches: [
58 '+([0-9])?(.{+([0-9]),x}).x',
59 'master',
60 'next',
61 'next-major',
62 {name: 'beta', prerelease: true},
63 {name: 'alpha', prerelease: true},
64 ],
65 repositoryUrl: (await pkgRepoUrl({normalize: false, cwd})) || (await repoUrl({cwd, env})),
66 tagFormat: `v\${version}`,
67 plugins: [
68 '@semantic-release/commit-analyzer',
69 '@semantic-release/release-notes-generator',
70 '@semantic-release/npm',
71 '@semantic-release/github',
72 ],
73 // Remove `null` and `undefined` options so they can be replaced with default ones
74 ...pickBy(options, (option) => !isNil(option)),
75 ...(options.branches ? {branches: castArray(options.branches)} : {}),
76 };
77
78 if (options.ci === false) {
79 options.noCi = true;
80 }
81
82 debug('options values: %O', options);
83
84 return {options, plugins: await plugins({...context, options}, pluginsPath)};
85};
86
87async function pkgRepoUrl(options) {
88 const {packageJson} = (await readPkgUp(options)) || {};
89 return packageJson && (isPlainObject(packageJson.repository) ? packageJson.repository.url : packageJson.repository);
90}