feat: make semantic-release CI agnostic

- Remove `@semantic-release/condition-travis` from the default plugins
- Verify the current branch in the core
- Verify the build is not triggered by a PR in the core
- Run in dry-run mode if not triggered on CI
- Dry-run mode runs the `verifyConditions` plugins, allowing to detect configuration error locally
- Return without error when no version has to be released due to no changes
- Return without error if the build is triggered from a PR
- Return without error if the current branch is not the configured branch
- CLI return with exit code 1 if there is a `semanticReleaseError`, allowing to fail builds in case of config error, missing token etc...

BREAKING CHANGE: `semantic-release` doesn't make sure it runs only on one Travis job anymore.
The CI configuration has to be done such that `semantic-release`
- runs only once per build
- runs only after all tests are successful on every jobs of the build
- runs on Node >=8

This can easily be done with [travis-deploy-once](https://github.com/semantic-release/travis-deploy-once).

Migration Guide

Modify your `.travis.yml` to use `travis-deploy-once`.
Replace:
```yaml
after_success:
  - npm run semantic-release
```
by:
Replace
```yaml
after_success:
  - npm install -g travis-deploy-once@4
  - travis-deploy-once "npm run semantic-release"
```
This commit is contained in:
Pierre Vanduynslager
2017-12-30 23:15:25 -05:00
parent 996305d69c
commit 8d575654c2
8 changed files with 228 additions and 42 deletions
+29 -7
View File
@@ -1,6 +1,6 @@
const marked = require('marked');
const TerminalRenderer = require('marked-terminal');
const SemanticReleaseError = require('@semantic-release/error');
const envCi = require('env-ci');
const getConfig = require('./lib/get-config');
const getNextVersion = require('./lib/get-next-version');
const getCommits = require('./lib/get-commits');
@@ -8,19 +8,39 @@ const logger = require('./lib/logger');
const {gitHead: getGitHead, isGitRepo} = require('./lib/git');
module.exports = async opts => {
const {isCi, branch, isPr} = envCi();
if (!isCi && !opts.dryRun) {
logger.log('This run was not triggered in a known CI environment, running in dry-run mode.');
opts.dryRun = true;
}
if (isCi && isPr) {
logger.log('This run was triggered by a pull request and therefore a new version wont be published.');
return;
}
if (!await isGitRepo()) {
throw new SemanticReleaseError('Semantic-release must run from a git repository', 'ENOGITREPO');
logger.error('Semantic-release must run from a git repository.');
return;
}
const config = await getConfig(opts, logger);
const {plugins, options} = config;
if (branch !== options.branch) {
logger.log(
`This test run was triggered on the branch ${branch}, while semantic-release is configured to only publish from ${
options.branch
}, therefore a new version wont be published.`
);
return;
}
logger.log('Run automated release from branch %s', options.branch);
if (!options.dryRun) {
logger.log('Call plugin %s', 'verify-conditions');
await plugins.verifyConditions({options, logger});
}
logger.log('Call plugin %s', 'verify-conditions');
await plugins.verifyConditions({options, logger});
logger.log('Call plugin %s', 'get-last-release');
const {commits, lastRelease} = await getCommits(
@@ -32,7 +52,8 @@ module.exports = async opts => {
logger.log('Call plugin %s', 'analyze-commits');
const type = await plugins.analyzeCommits({options, logger, lastRelease, commits});
if (!type) {
throw new SemanticReleaseError('There are no relevant changes, so no new version is released.', 'ENOCHANGE');
logger.log('There are no relevant changes, so no new version is released.');
return;
}
const version = getNextVersion(type, lastRelease, logger);
const nextRelease = {type, version, gitHead: await getGitHead(), gitTag: `v${version}`};
@@ -67,4 +88,5 @@ module.exports = async opts => {
});
logger.log('Published release: %s', nextRelease.version);
}
return true;
};