feat: Make semantic-release language agnostic

- Do not rely on `package.json` anymore
- Use `cosmiconfig` to load the configation. `semantic-release` can be configured:
  - via CLI options (including plugin names but not plugin options)
  - in the `release` property of `package.json` (as before)
  - in a `.releaserc.yml` or `.releaserc.js` or `.releaserc.js` or `release.config.js` file
  - in a `.releaserc` file containing `json`, `yaml` or `javascript` module
- Add the `repositoryUrl` options (used across `semantic-release` and plugins). The value is determined from CLi option, or option configuration, or package.json or the git remote url
- Verifies that `semantic-release` runs from a git repository
- `pkg` and `env` are not passed to plugin anymore
- `semantic-release` can be run both locally and globally. If ran globally with non default plugins, the plugins can be installed both globally or locally.

BREAKING CHANGE: `pkg` and `env` are not passed to plugin anymore.
Plugins relying on a `package.json` must verify the presence of a valid `package.json` and load it.
Plugins can use `process.env` instead of `env`.
This commit is contained in:
Pierre Vanduynslager
2017-11-24 21:56:15 -05:00
parent 5bec59b26b
commit 0c67ba517f
11 changed files with 292 additions and 110 deletions
+14 -6
View File
@@ -1,19 +1,27 @@
const {readJson} = require('fs-extra');
const readPkgUp = require('read-pkg-up');
const {defaults} = require('lodash');
const normalizeData = require('normalize-package-data');
const cosmiconfig = require('cosmiconfig');
const debug = require('debug')('semantic-release:config');
const {repoUrl} = require('./git');
const plugins = require('./plugins');
module.exports = async (opts, logger) => {
const pkg = await readJson('./package.json');
normalizeData(pkg);
const options = defaults(opts, pkg.release, {branch: 'master'});
const {config} = (await cosmiconfig('release', {rcExtensions: true}).load(process.cwd())) || {};
const options = defaults(opts, config, {branch: 'master', repositoryUrl: (await pkgRepoUrl()) || (await repoUrl())});
debug('name: %O', options.name);
debug('branch: %O', options.branch);
debug('repositoryUrl: %O', options.repositoryUrl);
debug('analyzeCommits: %O', options.analyzeCommits);
debug('generateNotes: %O', options.generateNotes);
debug('verifyConditions: %O', options.verifyConditions);
debug('verifyRelease: %O', options.verifyRelease);
debug('publish: %O', options.publish);
return {env: process.env, pkg, options, plugins: await plugins(options, logger), logger};
return {options, plugins: await plugins(options, logger)};
};
async function pkgRepoUrl() {
const {pkg} = await readPkgUp();
return pkg && pkg.repository ? pkg.repository.url : null;
}
+17 -1
View File
@@ -72,4 +72,20 @@ async function gitHead() {
}
}
module.exports = {gitTagHead, gitCommitTag, isCommitInHistory, unshallow, gitHead};
/**
* @return {string|null} The value of the remote git URL.
*/
async function repoUrl() {
return (await execa.stdout('git', ['remote', 'get-url', 'origin'], {reject: false})) || null;
}
/**
* @return {Boolean} `true` if the current working directory is in a git repository, `false` otherwise.
*/
async function isGitRepo() {
const shell = await execa('git', ['rev-parse', '--git-dir'], {reject: false});
debugShell('Check if the current working directory is a git repository', shell, debug);
return shell.code === 0;
}
module.exports = {gitTagHead, gitCommitTag, isCommitInHistory, unshallow, gitHead, repoUrl, isGitRepo};