Compare commits
@@ -1,64 +1,65 @@
|
||||
const program = require('commander');
|
||||
const {pickBy, isUndefined} = require('lodash');
|
||||
|
||||
function list(values) {
|
||||
return values
|
||||
.split(',')
|
||||
.map(value => value.trim())
|
||||
.filter(value => value && value !== 'false');
|
||||
}
|
||||
const stringList = {
|
||||
type: 'string',
|
||||
array: true,
|
||||
coerce: values =>
|
||||
values.length === 1 && values[0].trim() === 'false'
|
||||
? []
|
||||
: values.reduce((values, value) => values.concat(value.split(',').map(value => value.trim())), []),
|
||||
};
|
||||
|
||||
module.exports = async () => {
|
||||
program
|
||||
.name('semantic-release')
|
||||
.description('Run automated package publishing')
|
||||
.option('-b, --branch <branch>', 'Branch to release from')
|
||||
.option('-r, --repository-url <repositoryUrl>', 'Git repository URL')
|
||||
.option('-t, --tag-format <tagFormat>', `Git tag format`)
|
||||
.option('-e, --extends <paths>', 'Comma separated list of shareable config paths or packages name', list)
|
||||
.option(
|
||||
'--verify-conditions <paths>',
|
||||
'Comma separated list of paths or packages name for the verifyConditions plugin(s)',
|
||||
list
|
||||
)
|
||||
.option('--analyze-commits <path>', 'Path or package name for the analyzeCommits plugin')
|
||||
.option(
|
||||
'--verify-release <paths>',
|
||||
'Comma separated list of paths or packages name for the verifyRelease plugin(s)',
|
||||
list
|
||||
)
|
||||
.option('--generate-notes <path>', 'Path or package name for the generateNotes plugin')
|
||||
.option('--publish <paths>', 'Comma separated list of paths or packages name for the publish plugin(s)', list)
|
||||
.option('--success <paths>', 'Comma separated list of paths or packages name for the success plugin(s)', list)
|
||||
.option('--fail <paths>', 'Comma separated list of paths or packages name for the fail plugin(s)', list)
|
||||
.option(
|
||||
'--no-ci',
|
||||
'Skip Continuous Integration environment verifications, allowing to make releases from a local machine'
|
||||
)
|
||||
.option('--debug', 'Output debugging information')
|
||||
.option(
|
||||
'-d, --dry-run',
|
||||
'Dry-run mode, skipping verifyConditions, publishing and release, printing next version and release notes'
|
||||
)
|
||||
.parse(process.argv);
|
||||
|
||||
if (program.debug) {
|
||||
// Debug must be enabled before other requires in order to work
|
||||
require('debug').enable('semantic-release:*');
|
||||
}
|
||||
const cli = require('yargs')
|
||||
.command('$0', 'Run automated package publishing', yargs => {
|
||||
yargs.demandCommand(0, 0).usage(`Run automated package publishing
|
||||
Usage:
|
||||
semantic-release [options] [plugins]`);
|
||||
})
|
||||
.option('b', {alias: 'branch', describe: 'Git branch to release from', type: 'string', group: 'Options'})
|
||||
.option('r', {alias: 'repository-url', describe: 'Git repository URL', type: 'string', group: 'Options'})
|
||||
.option('t', {alias: 'tag-format', describe: 'Git tag format', type: 'string', group: 'Options'})
|
||||
.option('e', {alias: 'extends', describe: 'Shareable configurations', ...stringList, group: 'Options'})
|
||||
.option('ci', {describe: 'Toggle CI verifications', default: undefined, type: 'boolean', group: 'Options'})
|
||||
.option('verify-conditions', {...stringList, group: 'Plugins'})
|
||||
.option('analyze-commits', {type: 'string', group: 'Plugins'})
|
||||
.option('verify-release', {...stringList, group: 'Plugins'})
|
||||
.option('generate-notes', {type: 'string', group: 'Plugins'})
|
||||
.option('publish', {...stringList, group: 'Plugins'})
|
||||
.option('success', {...stringList, group: 'Plugins'})
|
||||
.option('fail', {...stringList, group: 'Plugins'})
|
||||
.option('debug', {describe: 'Output debugging information', default: undefined, type: 'boolean', group: 'Options'})
|
||||
.option('d', {alias: 'dry-run', describe: 'Skip publishing', default: undefined, type: 'boolean', group: 'Options'})
|
||||
.option('h', {alias: 'help', default: undefined, group: 'Options'})
|
||||
.option('v', {alias: 'version', default: undefined, group: 'Options'})
|
||||
.strict(false)
|
||||
.exitProcess(false);
|
||||
|
||||
try {
|
||||
if (program.args.length > 0) {
|
||||
program.outputHelp();
|
||||
process.exitCode = 1;
|
||||
} else {
|
||||
const opts = program.opts();
|
||||
// Set the `noCi` options as commander.js sets the `ci` options instead (because args starts with `--no`)
|
||||
opts.noCi = opts.ci === false ? true : undefined;
|
||||
// Remove option with undefined values, as commander.js sets non defined options as `undefined`
|
||||
await require('.')(pickBy(opts, value => !isUndefined(value)));
|
||||
// Remove option with undefined values, as yargs sets non defined options as `undefined`
|
||||
const {help, version, ...opts} = pickBy(cli.argv, value => !isUndefined(value));
|
||||
|
||||
if (Boolean(help) || Boolean(version)) {
|
||||
process.exitCode = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
// Set the `noCi` options as yargs sets the `ci` options instead (because arg starts with `--no`)
|
||||
if (opts.ci === false) {
|
||||
opts.noCi = true;
|
||||
}
|
||||
|
||||
if (opts.debug) {
|
||||
// Debug must be enabled before other requires in order to work
|
||||
require('debug').enable('semantic-release:*');
|
||||
}
|
||||
|
||||
await require('.')(opts);
|
||||
process.exitCode = 0;
|
||||
} catch (err) {
|
||||
if (err.name !== 'YError') {
|
||||
console.error(err);
|
||||
}
|
||||
process.exitCode = 1;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1 +1,65 @@
|
||||
# CircleCI 2.0 workflows
|
||||
# Using semantic-release with [CircleCI 2.0 workflows](https://circleci.com/docs/2.0/workflows)
|
||||
|
||||
## Environment variables
|
||||
|
||||
The [Authentication](../usage/ci-configuration.md#authentication) environment variables can be configured in [CircleCi Project Settings](https://circleci.com/docs/2.0/env-vars/#adding-environment-variables-in-the-app)..
|
||||
|
||||
Alternatively, the default `NPM_TOKEN` and `GH_TOKEN` can be easily [setup with semantic-release-cli](../usage/ci-configuration.md#automatic-setup-with-semantic-release-cli).
|
||||
|
||||
## Multiple Node jobs configuration
|
||||
|
||||
### `.circleci/config.yml` configuration for multiple Node jobs
|
||||
|
||||
This example is a minimal configuration for **semantic-release** with a build running Node 6 and 8. See [CircleCI documentation](https://circleci.com/docs/2.0) for additional configuration options.
|
||||
|
||||
This example create the workflows `test_node_4`, `test_node_6`, `test_node_8` and `release`. The release workflows will [run `semantic-release` only after the all the `test_node_*` are successful](../usage/ci-configuration.md#run-semantic-release-only-after-all-tests-succeeded).
|
||||
|
||||
```yaml
|
||||
version: 2
|
||||
jobs:
|
||||
test_node_6:
|
||||
docker:
|
||||
- image: circleci/node:6
|
||||
steps:
|
||||
# Configure your test steps here (checkout, npm install, cache management, tests etc...)
|
||||
|
||||
test_node_8:
|
||||
docker:
|
||||
- image: circleci/node:8
|
||||
steps:
|
||||
# Configure your test steps here (checkout, npm install, cache management, tests etc...)
|
||||
|
||||
release:
|
||||
docker:
|
||||
- image: circleci/node:8
|
||||
steps:
|
||||
- checkout
|
||||
- run: npm install
|
||||
# Run optional required steps before releasing
|
||||
# - run: npm run build-script
|
||||
- run: npx semantic-release
|
||||
|
||||
workflows:
|
||||
version: 2
|
||||
test_and_release:
|
||||
# Run the test jobs first, then the release only when all the test jobs are successful
|
||||
jobs:
|
||||
- test_node_6
|
||||
- test_node_8
|
||||
- release:
|
||||
requires:
|
||||
- test_node_6
|
||||
- test_node_8
|
||||
```
|
||||
|
||||
### `package.json` configuration for multiple Node jobs
|
||||
|
||||
A `package.json` is required only for [local](../usage/installation.md#local-installation) **semantic-release** installation.
|
||||
|
||||
```json
|
||||
{
|
||||
"devDependencies": {
|
||||
"semantic-release": "^12.0.0"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -8,11 +8,11 @@ The [Authentication](../usage/ci-configuration.md#authentication) environment va
|
||||
|
||||
GitLab CI supports [Pipelines](https://docs.gitlab.com/ee/ci/pipelines.html) allowing to test on multiple Node versions and publishing a release only when all test pass.
|
||||
|
||||
**Note**: The publish pipeline must run a [Node >= 8 version](../support/FAQ.md#why-does-semantic-release-require-node-version--8).
|
||||
**Note**: The publish pipeline must run a [Node >= 8 version](../support/FAQ.md#why-does-semantic-release-require-node-version--83).
|
||||
|
||||
### `.gitlab-ci.yml` configuration for Node projects
|
||||
|
||||
This example is a minimal configuration for **semantic-release** with a build running Node 4, 6 and 8 on Linux. See [GitLab CI - Configuration of your jobs with .gitlab-ci.yml](https://docs.gitlab.com/ee/ci/yaml/README.html) for additional configuration options.
|
||||
This example is a minimal configuration for **semantic-release** with a build running Node 6 and 8. See [GitLab CI - Configuration of your jobs with .gitlab-ci.yml](https://docs.gitlab.com/ee/ci/yaml/README.html) for additional configuration options.
|
||||
|
||||
**Note**: The`semantic-release` execution command varies depending if you are using a [local](../usage/installation.md#local-installation) or [global](../usage/installation.md#global-installation) **semantic-release** installation.
|
||||
|
||||
@@ -25,12 +25,6 @@ stages:
|
||||
before_script:
|
||||
- npm install
|
||||
|
||||
node:4:
|
||||
image: node:4
|
||||
stage: test
|
||||
script:
|
||||
- npm test
|
||||
|
||||
node:6:
|
||||
image: node:6
|
||||
stage: test
|
||||
|
||||
@@ -1 +1,95 @@
|
||||
# Using semantic-release with [Travis CI build stages](https://docs.travis-ci.com/user/build-stages)
|
||||
|
||||
## Environment variables
|
||||
|
||||
The [Authentication](../usage/ci-configuration.md#authentication) environment variables can be configured in [Travis Repository Settings](https://docs.travis-ci.com/user/environment-variables/#defining-variables-in-repository-Settings) or with the [travis env set CLI](https://github.com/travis-ci/travis.rb#env).
|
||||
|
||||
Alternatively, the default `NPM_TOKEN` and `GH_TOKEN` can be easily [setup with semantic-release-cli](../usage/ci-configuration.md#automatic-setup-with-semantic-release-cli).
|
||||
|
||||
## Multiple Node jobs configuration
|
||||
|
||||
### `.travis.yml` configuration for multiple Node jobs
|
||||
|
||||
This example is a minimal configuration for **semantic-release** with a build running Node 6 and 8. See [Travis - Customizing the Build](https://docs.travis-ci.com/user/customizing-the-build) for additional configuration options.
|
||||
|
||||
This example creates a `release` [build stage](https://docs.travis-ci.com/user/build-stages) that [runs `semantic-release` only after all test jobs are successful](../usage/ci-configuration.md#run-semantic-release-only-after-all-tests-succeeded).
|
||||
|
||||
It's recommended to run the `semantic-release` command in the [Travis `deploy` step](https://docs.travis-ci.com/user/customizing-the-build/#The-Build-Lifecycle) so if an error occurs the build will fail and Travis will send a notification.
|
||||
|
||||
**Note**: It's not recommended to run the `semantic-release` command in the Travis `script` step as each script in this step will be executed regardless of the outcome of the previous one. See [travis-ci/travis-ci#1066](https://github.com/travis-ci/travis-ci/issues/1066).
|
||||
|
||||
**Advanced configuration**: Running the tests in the `script` step of the `release` stage is not necessary as the previous stage(s) already ran them. To increase speed, the `script` step of the `release` stage can be overwritten to skip the tests. Note that other commands such as build or compilation might still be required.
|
||||
|
||||
```yaml
|
||||
language: node_js
|
||||
|
||||
node_js:
|
||||
- 8
|
||||
- 6
|
||||
|
||||
jobs:
|
||||
include:
|
||||
# Define the release stage that runs semantic-release
|
||||
- stage: release
|
||||
node_js: lts/*
|
||||
# Advanced: optionally overwrite your default `script` step to skip the tests
|
||||
# script: skip
|
||||
deploy:
|
||||
provider: script
|
||||
skip_cleanup: true
|
||||
script:
|
||||
- npx semantic-release
|
||||
```
|
||||
|
||||
### `package.json` configuration for multiple Node jobs
|
||||
|
||||
A `package.json` is required only for [local](../usage/installation.md#local-installation) **semantic-release** installation.
|
||||
|
||||
```json
|
||||
{
|
||||
"devDependencies": {
|
||||
"semantic-release": "^12.0.0"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Non-JavaScript projects configuration
|
||||
|
||||
For projects that require to be tested with one or multiple version of a Non-JavaScript [language](https://docs.travis-ci.com/user/languages), optionally on multiple [Operating Systems](https://docs.travis-ci.com/user/multi-os).
|
||||
|
||||
This recipe cover the Travis specifics only. See [Non JavaScript projects recipe](../support/FAQ.md#can-i-use-semantic-release-to-publish-non-javascript-packages) for more information on the **semantic-release** configuration.
|
||||
|
||||
### `.travis.yml` configuration for non-JavaScript projects
|
||||
|
||||
This example is a minimal configuration for **semantic-release** with a build running [Go 1.6 and 1.7](https://docs.travis-ci.com/user/languages/go). See [Travis - Customizing the Build](https://docs.travis-ci.com/user/customizing-the-build) for additional configuration options.
|
||||
|
||||
This example creates a `release` [build stage](https://docs.travis-ci.com/user/build-stages) that [runs `semantic-release` only after all test jobs are successful](../usage/ci-configuration.md#run-semantic-release-only-after-all-tests-succeeded).
|
||||
|
||||
It's recommended to run the `semantic-release` command in the [Travis `deploy` step](https://docs.travis-ci.com/user/customizing-the-build/#The-Build-Lifecycle) so if an error occurs the build will fail and Travis will send a notification.
|
||||
|
||||
**Note**: It's not recommended to run the `semantic-release` command in the Travis `script` step as each script in this step will be executed regardless of the outcome of the previous one. See [travis-ci/travis-ci#1066](https://github.com/travis-ci/travis-ci/issues/1066).
|
||||
|
||||
**Advanced configuration**: Running the tests in the `script` step of the `release` stage is not necessary as the previous stage(s) already ran them. To increase speed, the `script` step of the `release` stage can be overwritten to skip the tests. Note that other commands such as build or compilation might still be required.
|
||||
|
||||
```yaml
|
||||
language: go
|
||||
|
||||
go:
|
||||
- 1.6
|
||||
- 1.7
|
||||
|
||||
jobs:
|
||||
include:
|
||||
# Define the release stage that runs semantic-release
|
||||
- stage: release
|
||||
# Advanced: optionally overwrite your default `script` step to skip the tests
|
||||
# script:
|
||||
# - make
|
||||
deploy:
|
||||
provider: script
|
||||
skip_cleanup: true
|
||||
script:
|
||||
# Use nvm to install and use the Node LTS version (nvm is installed on all Travis images)
|
||||
- nvm install lts/*
|
||||
- npx semantic-release
|
||||
```
|
||||
|
||||
+5
-22
@@ -10,7 +10,7 @@ Alternatively, the default `NPM_TOKEN` and `GH_TOKEN` can be easily [setup with
|
||||
|
||||
For projects that require to be tested only with a single [Node version](https://docs.travis-ci.com/user/getting-started/#Selecting-a-different-programming-language) on [one Operating System](https://docs.travis-ci.com/user/getting-started/#Selecting-infrastructure-(optional)).
|
||||
|
||||
**Note**: [Node 8 is the minimal version required](../support/FAQ.md#why-does-semantic-release-require-node-version--8).
|
||||
**Note**: [Node 8 is the minimal version required](../support/FAQ.md#why-does-semantic-release-require-node-version--83).
|
||||
|
||||
### `.travis.yml` configuration for single Node job
|
||||
|
||||
@@ -25,10 +25,6 @@ language: node_js
|
||||
|
||||
node_js: 8
|
||||
|
||||
script:
|
||||
# Run tests
|
||||
- npm run test
|
||||
|
||||
deploy:
|
||||
provider: script
|
||||
skip_cleanup: true
|
||||
@@ -56,9 +52,9 @@ For projects that require to be tested with multiple [Node versions](https://doc
|
||||
|
||||
### `.travis.yml` configuration for multiple Node jobs
|
||||
|
||||
This example is a minimal configuration for **semantic-release** with a build running Node 4, 6 and 8 on Linux and OSX. See [Travis - Customizing the Build](https://docs.travis-ci.com/user/customizing-the-build) for additional configuration options.
|
||||
This example is a minimal configuration for **semantic-release** with a build running Node 6 and 8. See [Travis - Customizing the Build](https://docs.travis-ci.com/user/customizing-the-build) for additional configuration options.
|
||||
|
||||
This example uses [`travis-deploy-once`](https://github.com/semantic-release/travis-deploy-once) in order to command [Run `semantic-release` only after all tests succeeded](../usage/ci-configuration.md#run-semantic-release-only-after-all-tests-succeeded). Alternatively you can use [Travis CI Build Stages recipe](travis-build-stages.md).
|
||||
This example uses [`travis-deploy-once`](https://github.com/semantic-release/travis-deploy-once) in order to [Run `semantic-release` only after all tests succeeded](../usage/ci-configuration.md#run-semantic-release-only-after-all-tests-succeeded). Alternatively you can use [Travis CI Build Stages recipe](travis-build-stages.md).
|
||||
|
||||
It's recommended to run the `semantic-release` command in the [Travis `deploy` step](https://docs.travis-ci.com/user/customizing-the-build/#The-Build-Lifecycle) so if an error occurs the build will fail and Travis will send a notification.
|
||||
|
||||
@@ -70,15 +66,6 @@ language: node_js
|
||||
node_js:
|
||||
- 8
|
||||
- 6
|
||||
- 4
|
||||
|
||||
os:
|
||||
- linux
|
||||
- osx
|
||||
|
||||
script:
|
||||
# Run tests
|
||||
- npm run test
|
||||
|
||||
deploy:
|
||||
provider: script
|
||||
@@ -110,7 +97,7 @@ This recipe cover the Travis specifics only. See [Non JavaScript projects recipe
|
||||
|
||||
### `.travis.yml` configuration for non-JavaScript projects
|
||||
|
||||
This example is a minimal configuration for semantic-release with a build running [Go 1.6 and 1.7](https://docs.travis-ci.com/user/languages/go) on Linux and OSX. See [Travis - Customizing the Build](https://docs.travis-ci.com/user/customizing-the-build) for additional configuration options.
|
||||
This example is a minimal configuration for **semantic-release** with a build running [Go 1.6 and 1.7](https://docs.travis-ci.com/user/languages/go) on Linux and OSX. See [Travis - Customizing the Build](https://docs.travis-ci.com/user/customizing-the-build) for additional configuration options.
|
||||
|
||||
This example uses [`travis-deploy-once`](https://github.com/semantic-release/travis-deploy-once) in order to [run `semantic-release` only after all tests succeeded](../usage/ci-configuration.md#run-semantic-release-only-after-all-tests-succeeded). Alternatively you can use [Travis CI Build Stages recipe](travis-build-stages.md).
|
||||
|
||||
@@ -129,17 +116,13 @@ os:
|
||||
- linux
|
||||
- osx
|
||||
|
||||
script:
|
||||
# Run tests
|
||||
- go test -v ./...
|
||||
|
||||
deploy:
|
||||
provider: script
|
||||
skip_cleanup: true
|
||||
script:
|
||||
# Use nvm to install and use the Node LTS version (nvm is installed on all Travis images)
|
||||
- nvm install lts/*
|
||||
# Run semantic-release only on job, after all other are successful
|
||||
# Run semantic-release only on one job, after all other are successful
|
||||
- npx travis-deploy-once "npx semantic-release"
|
||||
```
|
||||
|
||||
|
||||
@@ -166,3 +166,27 @@ CLI argument: `--publish`
|
||||
Define the list of [publish plugins](plugins.md#publish-plugin). Plugins will run in series, in the order defined in the `Array`.
|
||||
|
||||
See [Plugins configuration](plugins.md#configuration) for more details.
|
||||
|
||||
### success
|
||||
|
||||
Type: `Array`, `String`, `Object`
|
||||
|
||||
Default: `['@semantic-release/github']`
|
||||
|
||||
CLI argument: `--success`
|
||||
|
||||
Define the list of [success plugins](plugins.md#success-plugin). Plugins will run in series, in the order defined in the `Array`.
|
||||
|
||||
See [Plugins configuration](plugins.md#configuration) for more details.
|
||||
|
||||
### fail
|
||||
|
||||
Type: `Array`, `String`, `Object`
|
||||
|
||||
Default: `['@semantic-release/github']`
|
||||
|
||||
CLI argument: `--fail`
|
||||
|
||||
Define the list of [fail plugins](plugins.md#fail-plugin). Plugins will run in series, in the order defined in the `Array`.
|
||||
|
||||
See [Plugins configuration](plugins.md#configuration) for more details.
|
||||
|
||||
@@ -3,6 +3,7 @@ const marked = require('marked');
|
||||
const TerminalRenderer = require('marked-terminal');
|
||||
const envCi = require('env-ci');
|
||||
const hookStd = require('hook-std');
|
||||
const pkg = require('./package.json');
|
||||
const hideSensitive = require('./lib/hide-sensitive');
|
||||
const getConfig = require('./lib/get-config');
|
||||
const verify = require('./lib/verify');
|
||||
@@ -28,10 +29,17 @@ async function run(options, plugins) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!await verify(options, branch, logger)) {
|
||||
return;
|
||||
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 won’t be published.`
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
await verify(options);
|
||||
|
||||
logger.log('Run automated release from branch %s', options.branch);
|
||||
|
||||
logger.log('Call plugin %s', 'verify-conditions');
|
||||
@@ -140,6 +148,7 @@ async function callFail(plugins, options, error) {
|
||||
}
|
||||
|
||||
module.exports = async opts => {
|
||||
logger.log(`Running %s version %s`, pkg.name, pkg.version);
|
||||
const unhook = hookStd({silent: false}, hideSensitive);
|
||||
try {
|
||||
const config = await getConfig(opts, logger);
|
||||
|
||||
@@ -53,13 +53,13 @@ Your configuration for the \`tagFormat\` option is \`${stringify(tagFormat)}\`.`
|
||||
|
||||
Your configuration for the \`tagFormat\` option is \`${stringify(tagFormat)}\`.`,
|
||||
}),
|
||||
EPLUGINCONF: ({pluginName, pluginConf}) => ({
|
||||
message: `The \`${pluginName}\` plugin configuration is invalid.`,
|
||||
details: `The [${pluginName} plugin configuration](${linkify(
|
||||
`docs/usage/plugins.md#${toLower(pluginName)}-plugin`
|
||||
EPLUGINCONF: ({pluginType, pluginConf}) => ({
|
||||
message: `The \`${pluginType}\` plugin configuration is invalid.`,
|
||||
details: `The [${pluginType} plugin configuration](${linkify(
|
||||
`docs/usage/plugins.md#${toLower(pluginType)}-plugin`
|
||||
)}) if defined, must be a single or an array of plugins definition. A plugin definition is either a string or an object with a \`path\` property.
|
||||
|
||||
Your configuration for the \`${pluginName}\` plugin is \`${stringify(pluginConf)}\`.`,
|
||||
Your configuration for the \`${pluginType}\` plugin is \`${stringify(pluginConf)}\`.`,
|
||||
}),
|
||||
EPLUGIN: ({pluginName, pluginType}) => ({
|
||||
message: `A plugin configured in the step ${pluginType} is not a valid semantic-release plugin.`,
|
||||
|
||||
+6
-4
@@ -3,7 +3,7 @@ const readPkgUp = require('read-pkg-up');
|
||||
const cosmiconfig = require('cosmiconfig');
|
||||
const resolveFrom = require('resolve-from');
|
||||
const debug = require('debug')('semantic-release:config');
|
||||
const {repoUrl} = require('./git');
|
||||
const {repoUrl, verifyAuth} = require('./git');
|
||||
const PLUGINS_DEFINITIONS = require('./definitions/plugins');
|
||||
const plugins = require('./plugins');
|
||||
const getGitAuthUrl = require('./get-git-auth-url');
|
||||
@@ -42,17 +42,19 @@ module.exports = async (opts, logger) => {
|
||||
};
|
||||
}
|
||||
|
||||
const repositoryUrl = (await pkgRepoUrl()) || (await repoUrl());
|
||||
|
||||
// Set default options values if not defined yet
|
||||
options = {
|
||||
branch: 'master',
|
||||
repositoryUrl: repositoryUrl ? getGitAuthUrl(repositoryUrl) : repositoryUrl,
|
||||
repositoryUrl: (await pkgRepoUrl()) || (await repoUrl()),
|
||||
tagFormat: `v\${version}`,
|
||||
// Remove `null` and `undefined` options so they can be replaced with default ones
|
||||
...pickBy(options, option => !isUndefined(option) && !isNull(option)),
|
||||
};
|
||||
|
||||
if (!await verifyAuth(options.repositoryUrl, options.branch)) {
|
||||
options.repositoryUrl = options.repositoryUrl ? getGitAuthUrl(options.repositoryUrl) : options.repositoryUrl;
|
||||
}
|
||||
|
||||
debug('options values: %O', options);
|
||||
|
||||
return {options, plugins: await plugins(options, pluginsPath, logger)};
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
const {escapeRegExp} = require('lodash');
|
||||
|
||||
const toReplace = Object.keys(process.env).filter(envVar => /token|password|credential|secret|private/i.test(envVar));
|
||||
const toReplace = Object.keys(process.env).filter(
|
||||
envVar => /token|password|credential|secret|private/i.test(envVar) && process.env[envVar].trim()
|
||||
);
|
||||
|
||||
const regexp = new RegExp(toReplace.map(envVar => escapeRegExp(process.env[envVar])).join('|'), 'g');
|
||||
|
||||
module.exports = output => {
|
||||
|
||||
+1
-12
@@ -3,7 +3,7 @@ const AggregateError = require('aggregate-error');
|
||||
const {isGitRepo, verifyAuth, verifyTagName} = require('./git');
|
||||
const getError = require('./get-error');
|
||||
|
||||
module.exports = async (options, branch, logger) => {
|
||||
module.exports = async options => {
|
||||
const errors = [];
|
||||
|
||||
if (!await isGitRepo()) {
|
||||
@@ -29,15 +29,4 @@ module.exports = async (options, branch, logger) => {
|
||||
if (errors.length > 0) {
|
||||
throw new AggregateError(errors);
|
||||
}
|
||||
|
||||
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 won’t be published.`
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
+6
-18
@@ -42,7 +42,8 @@
|
||||
"p-reduce": "^1.0.0",
|
||||
"read-pkg-up": "^3.0.0",
|
||||
"resolve-from": "^4.0.0",
|
||||
"semver": "^5.4.1"
|
||||
"semver": "^5.4.1",
|
||||
"yargs": "^11.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"ava": "^0.25.0",
|
||||
@@ -52,8 +53,6 @@
|
||||
"cz-conventional-changelog": "^2.0.0",
|
||||
"delay": "^2.0.0",
|
||||
"dockerode": "^2.5.2",
|
||||
"eslint-config-prettier": "^2.5.0",
|
||||
"eslint-plugin-prettier": "^2.3.0",
|
||||
"file-url": "^2.0.2",
|
||||
"fs-extra": "^5.0.0",
|
||||
"got": "^8.0.0",
|
||||
@@ -62,11 +61,10 @@
|
||||
"nock": "^9.0.2",
|
||||
"nyc": "^11.2.1",
|
||||
"p-retry": "^1.0.0",
|
||||
"prettier": "~1.10.0",
|
||||
"proxyquire": "^1.8.0",
|
||||
"sinon": "^4.0.0",
|
||||
"tempy": "^0.2.1",
|
||||
"xo": "^0.18.2"
|
||||
"xo": "^0.20.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8.3"
|
||||
@@ -106,10 +104,7 @@
|
||||
"all": true
|
||||
},
|
||||
"prettier": {
|
||||
"printWidth": 120,
|
||||
"singleQuote": true,
|
||||
"bracketSpacing": false,
|
||||
"trailingComma": "es5"
|
||||
"printWidth": 120
|
||||
},
|
||||
"publishConfig": {
|
||||
"tag": "next"
|
||||
@@ -130,14 +125,7 @@
|
||||
"test": "nyc ava -v"
|
||||
},
|
||||
"xo": {
|
||||
"extends": [
|
||||
"prettier"
|
||||
],
|
||||
"plugins": [
|
||||
"prettier"
|
||||
],
|
||||
"rules": {
|
||||
"prettier/prettier": 2
|
||||
}
|
||||
"prettier": true,
|
||||
"space": true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,233 @@
|
||||
import test from 'ava';
|
||||
import proxyquire from 'proxyquire';
|
||||
import clearModule from 'clear-module';
|
||||
import {stub} from 'sinon';
|
||||
|
||||
// Save the current process.env and process.argv
|
||||
const envBackup = Object.assign({}, process.env);
|
||||
const argvBackup = Object.assign({}, process.argv);
|
||||
|
||||
test.beforeEach(t => {
|
||||
clearModule('yargs');
|
||||
t.context.logs = '';
|
||||
t.context.errors = '';
|
||||
t.context.stdout = stub(process.stdout, 'write').callsFake(val => {
|
||||
t.context.logs += val.toString();
|
||||
});
|
||||
t.context.stderr = stub(process.stderr, 'write').callsFake(val => {
|
||||
t.context.errors += val.toString();
|
||||
});
|
||||
});
|
||||
|
||||
test.afterEach.always(t => {
|
||||
process.env = envBackup;
|
||||
process.argv = argvBackup;
|
||||
t.context.stdout.restore();
|
||||
t.context.stderr.restore();
|
||||
delete process.exitCode;
|
||||
});
|
||||
|
||||
test.serial('Pass options to semantic-release API', async t => {
|
||||
const run = stub().resolves(true);
|
||||
const cli = proxyquire('../cli', {'.': run});
|
||||
|
||||
process.argv = [
|
||||
'',
|
||||
'',
|
||||
'-b',
|
||||
'master',
|
||||
'-r',
|
||||
'https://github/com/owner/repo.git',
|
||||
'-t',
|
||||
`v\${version}`,
|
||||
'-e',
|
||||
'config1',
|
||||
'config2',
|
||||
'--verify-conditions',
|
||||
'condition1',
|
||||
'condition2',
|
||||
'--analyze-commits',
|
||||
'analyze',
|
||||
'--verify-release',
|
||||
'verify1',
|
||||
'verify2',
|
||||
'--generate-notes',
|
||||
'notes',
|
||||
'--publish',
|
||||
'publish1',
|
||||
'publish2',
|
||||
'--success',
|
||||
'success1',
|
||||
'success2',
|
||||
'--fail',
|
||||
'fail1',
|
||||
'fail2',
|
||||
'--debug',
|
||||
'-d',
|
||||
];
|
||||
|
||||
await cli();
|
||||
|
||||
t.is(run.args[0][0].branch, 'master');
|
||||
t.is(run.args[0][0].repositoryUrl, 'https://github/com/owner/repo.git');
|
||||
t.is(run.args[0][0].tagFormat, `v\${version}`);
|
||||
t.deepEqual(run.args[0][0].extends, ['config1', 'config2']);
|
||||
t.deepEqual(run.args[0][0].verifyConditions, ['condition1', 'condition2']);
|
||||
t.is(run.args[0][0].analyzeCommits, 'analyze');
|
||||
t.deepEqual(run.args[0][0].verifyRelease, ['verify1', 'verify2']);
|
||||
t.is(run.args[0][0].generateNotes, 'notes');
|
||||
t.deepEqual(run.args[0][0].publish, ['publish1', 'publish2']);
|
||||
t.deepEqual(run.args[0][0].success, ['success1', 'success2']);
|
||||
t.deepEqual(run.args[0][0].fail, ['fail1', 'fail2']);
|
||||
t.is(run.args[0][0].debug, true);
|
||||
t.is(run.args[0][0].dryRun, true);
|
||||
|
||||
t.is(process.exitCode, 0);
|
||||
});
|
||||
|
||||
test.serial('Pass options to semantic-release API with alias arguments', async t => {
|
||||
const run = stub().resolves(true);
|
||||
const cli = proxyquire('../cli', {'.': run});
|
||||
|
||||
process.argv = [
|
||||
'',
|
||||
'',
|
||||
'--branch',
|
||||
'master',
|
||||
'--repository-url',
|
||||
'https://github/com/owner/repo.git',
|
||||
'--tag-format',
|
||||
`v\${version}`,
|
||||
'--extends',
|
||||
'config1',
|
||||
'config2',
|
||||
'--dry-run',
|
||||
];
|
||||
|
||||
await cli();
|
||||
|
||||
t.is(run.args[0][0].branch, 'master');
|
||||
t.is(run.args[0][0].repositoryUrl, 'https://github/com/owner/repo.git');
|
||||
t.is(run.args[0][0].tagFormat, `v\${version}`);
|
||||
t.deepEqual(run.args[0][0].extends, ['config1', 'config2']);
|
||||
t.is(run.args[0][0].dryRun, true);
|
||||
|
||||
t.is(process.exitCode, 0);
|
||||
});
|
||||
|
||||
test.serial('Pass unknown options to semantic-release API', async t => {
|
||||
const run = stub().resolves(true);
|
||||
const cli = proxyquire('../cli', {'.': run});
|
||||
|
||||
process.argv = [
|
||||
'',
|
||||
'',
|
||||
'--bool',
|
||||
'--first-option',
|
||||
'value1',
|
||||
'--second-option',
|
||||
'value2',
|
||||
'--second-option',
|
||||
'value3',
|
||||
];
|
||||
|
||||
await cli();
|
||||
|
||||
t.is(run.args[0][0].bool, true);
|
||||
t.is(run.args[0][0].firstOption, 'value1');
|
||||
t.deepEqual(run.args[0][0].secondOption, ['value2', 'value3']);
|
||||
|
||||
t.is(process.exitCode, 0);
|
||||
});
|
||||
|
||||
test.serial('Pass empty Array to semantic-release API for list option set to "false"', async t => {
|
||||
const run = stub().resolves(true);
|
||||
const cli = proxyquire('../cli', {'.': run});
|
||||
|
||||
process.argv = ['', '', '--publish', 'false'];
|
||||
|
||||
await cli();
|
||||
|
||||
t.deepEqual(run.args[0][0].publish, []);
|
||||
|
||||
t.is(process.exitCode, 0);
|
||||
});
|
||||
|
||||
test.serial('Do not set properties in option for which arg is not in command line', async t => {
|
||||
const run = stub().resolves(true);
|
||||
const cli = proxyquire('../cli', {'.': run});
|
||||
|
||||
process.argv = ['', '', '-b', 'master'];
|
||||
|
||||
await cli();
|
||||
|
||||
t.false(Object.prototype.hasOwnProperty.call(run.args[0][0], 'ci'));
|
||||
t.false(Object.prototype.hasOwnProperty.call(run.args[0][0], 'd'));
|
||||
t.false(Object.prototype.hasOwnProperty.call(run.args[0][0], 'dry-run'));
|
||||
t.false(Object.prototype.hasOwnProperty.call(run.args[0][0], 'debug'));
|
||||
t.false(Object.prototype.hasOwnProperty.call(run.args[0][0], 'r'));
|
||||
t.false(Object.prototype.hasOwnProperty.call(run.args[0][0], 't'));
|
||||
});
|
||||
|
||||
test.serial('Set "noCi" options to "true" with "--no-ci"', async t => {
|
||||
const run = stub().resolves(true);
|
||||
const cli = proxyquire('../cli', {'.': run});
|
||||
|
||||
process.argv = ['', '', '--no-ci'];
|
||||
|
||||
await cli();
|
||||
|
||||
t.is(run.args[0][0].noCi, true);
|
||||
|
||||
t.is(process.exitCode, 0);
|
||||
});
|
||||
|
||||
test.serial('Display help', async t => {
|
||||
const run = stub().resolves(true);
|
||||
const cli = proxyquire('../cli', {'.': run});
|
||||
|
||||
process.argv = ['', '', '--help'];
|
||||
|
||||
await cli();
|
||||
|
||||
t.regex(t.context.logs, /Run automated package publishing/);
|
||||
t.is(process.exitCode, 0);
|
||||
});
|
||||
|
||||
test.serial('Returns error code and prints help if called with a command', async t => {
|
||||
const run = stub().resolves(true);
|
||||
const cli = proxyquire('../cli', {'.': run});
|
||||
|
||||
process.argv = ['', '', 'pre'];
|
||||
|
||||
await cli();
|
||||
|
||||
t.regex(t.context.errors, /Run automated package publishing/);
|
||||
t.regex(t.context.errors, /Too many non-option arguments/);
|
||||
t.is(process.exitCode, 1);
|
||||
});
|
||||
|
||||
test.serial('Return error code if multiple plugin are set for single plugin', async t => {
|
||||
const run = stub().resolves(true);
|
||||
const cli = proxyquire('../cli', {'.': run});
|
||||
|
||||
process.argv = ['', '', '--analyze-commits', 'analyze1', 'analyze2'];
|
||||
|
||||
await cli();
|
||||
|
||||
t.regex(t.context.errors, /Run automated package publishing/);
|
||||
t.regex(t.context.errors, /Too many non-option arguments/);
|
||||
t.is(process.exitCode, 1);
|
||||
});
|
||||
|
||||
test.serial('Return error code if semantic-release throw error', async t => {
|
||||
const run = stub().rejects(new Error('semantic-release error'));
|
||||
const cli = proxyquire('../cli', {'.': run});
|
||||
|
||||
process.argv = ['', ''];
|
||||
|
||||
await cli();
|
||||
|
||||
t.regex(t.context.errors, /semantic-release error/);
|
||||
t.is(process.exitCode, 1);
|
||||
});
|
||||
+21
-5
@@ -31,7 +31,8 @@ test.afterEach.always(() => {
|
||||
});
|
||||
|
||||
test.serial('Default values, reading repositoryUrl from package.json', async t => {
|
||||
const pkg = {repository: 'git@package.com:owner/module.git'};
|
||||
process.env.GIT_CREDENTIALS = 'user:pass';
|
||||
const pkg = {repository: 'https://package.com/owner/module.git'};
|
||||
// Create a git repository, set the current working directory at the root of the repo
|
||||
await gitRepo();
|
||||
await gitCommits(['First']);
|
||||
@@ -44,25 +45,27 @@ test.serial('Default values, reading repositoryUrl from package.json', async t =
|
||||
|
||||
// Verify the default options are set
|
||||
t.is(options.branch, 'master');
|
||||
t.is(options.repositoryUrl, 'git@package.com:owner/module.git');
|
||||
t.is(options.repositoryUrl, 'https://user:pass@package.com/owner/module.git');
|
||||
t.is(options.tagFormat, `v\${version}`);
|
||||
});
|
||||
|
||||
test.serial('Default values, reading repositoryUrl from repo if not set in package.json', async t => {
|
||||
process.env.GIT_CREDENTIALS = 'user:pass';
|
||||
// Create a git repository, set the current working directory at the root of the repo
|
||||
await gitRepo();
|
||||
// Add remote.origin.url config
|
||||
await gitAddConfig('remote.origin.url', 'git@repo.com:owner/module.git');
|
||||
await gitAddConfig('remote.origin.url', 'https://hostname.com/owner/module.git');
|
||||
|
||||
const {options} = await t.context.getConfig();
|
||||
|
||||
// Verify the default options are set
|
||||
t.is(options.branch, 'master');
|
||||
t.is(options.repositoryUrl, 'git@repo.com:owner/module.git');
|
||||
t.is(options.repositoryUrl, 'https://user:pass@hostname.com/owner/module.git');
|
||||
t.is(options.tagFormat, `v\${version}`);
|
||||
});
|
||||
|
||||
test.serial('Default values, reading repositoryUrl (http url) from package.json if not set in repo', async t => {
|
||||
process.env.GIT_CREDENTIALS = 'user:pass';
|
||||
const pkg = {repository: 'https://hostname.com/owner/module.git'};
|
||||
// Create a git repository, set the current working directory at the root of the repo
|
||||
await gitRepo();
|
||||
@@ -73,10 +76,23 @@ test.serial('Default values, reading repositoryUrl (http url) from package.json
|
||||
|
||||
// Verify the default options are set
|
||||
t.is(options.branch, 'master');
|
||||
t.is(options.repositoryUrl, pkg.repository);
|
||||
t.is(options.repositoryUrl, 'https://user:pass@hostname.com/owner/module.git');
|
||||
t.is(options.tagFormat, `v\${version}`);
|
||||
});
|
||||
|
||||
test.serial('Do not add git credential to repositoryUrl if push is allowed', async t => {
|
||||
process.env.GIT_CREDENTIALS = 'user:pass';
|
||||
// Create a git repository, set the current working directory at the root of the repo
|
||||
const repositoryUrl = await gitRepo(true);
|
||||
const pkg = {repository: repositoryUrl};
|
||||
// Create package.json in repository root
|
||||
await outputJson('./package.json', pkg);
|
||||
|
||||
const {options} = await t.context.getConfig();
|
||||
|
||||
t.is(options.repositoryUrl, repositoryUrl);
|
||||
});
|
||||
|
||||
test.serial('Read options from package.json', async t => {
|
||||
const release = {
|
||||
analyzeCommits: {path: 'analyzeCommits', param: 'analyzeCommits_param'},
|
||||
|
||||
@@ -40,3 +40,18 @@ test.serial('Accept "undefined" input', t => {
|
||||
test.serial('Return same string if no environment variable has to be replaced', t => {
|
||||
t.is(require('../lib/hide-sensitive')('test'), 'test');
|
||||
});
|
||||
|
||||
test.serial('Exclude empty environment variables from the regexp', t => {
|
||||
process.env.SOME_PASSWORD = 'password';
|
||||
process.env.SOME_TOKEN = '';
|
||||
t.is(
|
||||
require('../lib/hide-sensitive')(`https://user:${process.env.SOME_PASSWORD}@host.com?token=`),
|
||||
'https://user:[secure]@host.com?token='
|
||||
);
|
||||
});
|
||||
|
||||
test.serial('Exclude empty environment variables from the regexp if there is only empty ones', t => {
|
||||
process.env.SOME_PASSWORD = '';
|
||||
process.env.SOME_TOKEN = ' \n ';
|
||||
t.is(require('../lib/hide-sensitive')(`https://host.com?token=`), 'https://host.com?token=');
|
||||
});
|
||||
|
||||
+13
-13
@@ -302,9 +302,9 @@ test.serial('Log all "verifyConditions" errors', async t => {
|
||||
'./lib/logger': t.context.logger,
|
||||
'env-ci': () => ({isCi: true, branch: 'master', isPr: false}),
|
||||
});
|
||||
const errors = await t.throws(semanticRelease(options));
|
||||
const errors = [...(await t.throws(semanticRelease(options)))];
|
||||
|
||||
t.deepEqual(Array.from(errors), [error1, error2, error3]);
|
||||
t.deepEqual(errors, [error1, error2, error3]);
|
||||
t.deepEqual(t.context.log.args[t.context.log.args.length - 2], ['%s error 2', 'ERR2']);
|
||||
t.deepEqual(t.context.log.args[t.context.log.args.length - 1], ['%s error 3', 'ERR3']);
|
||||
t.deepEqual(t.context.error.args[t.context.error.args.length - 1], [
|
||||
@@ -345,9 +345,9 @@ test.serial('Log all "verifyRelease" errors', async t => {
|
||||
'./lib/logger': t.context.logger,
|
||||
'env-ci': () => ({isCi: true, branch: 'master', isPr: false}),
|
||||
});
|
||||
const errors = await t.throws(semanticRelease(options));
|
||||
const errors = [...(await t.throws(semanticRelease(options)))];
|
||||
|
||||
t.deepEqual(Array.from(errors), [error1, error2]);
|
||||
t.deepEqual(errors, [error1, error2]);
|
||||
t.deepEqual(t.context.log.args[t.context.log.args.length - 2], ['%s error 1', 'ERR1']);
|
||||
t.deepEqual(t.context.log.args[t.context.log.args.length - 1], ['%s error 2', 'ERR2']);
|
||||
t.is(fail.callCount, 1);
|
||||
@@ -428,9 +428,9 @@ test.serial('Dry-run skips fail', async t => {
|
||||
'./lib/logger': t.context.logger,
|
||||
'env-ci': () => ({isCi: true, branch: 'master', isPr: false}),
|
||||
});
|
||||
const errors = await t.throws(semanticRelease(options));
|
||||
const errors = [...(await t.throws(semanticRelease(options)))];
|
||||
|
||||
t.deepEqual(Array.from(errors), [error1, error2]);
|
||||
t.deepEqual(errors, [error1, error2]);
|
||||
t.deepEqual(t.context.log.args[t.context.log.args.length - 2], ['%s error 1', 'ERR1']);
|
||||
t.deepEqual(t.context.log.args[t.context.log.args.length - 1], ['%s error 2', 'ERR2']);
|
||||
t.is(fail.callCount, 0);
|
||||
@@ -475,7 +475,7 @@ test.serial('Force a dry-run if not on a CI and "noCi" is not explicitly set', a
|
||||
});
|
||||
t.truthy(await semanticRelease(options));
|
||||
|
||||
t.is(t.context.log.args[0][0], 'This run was not triggered in a known CI environment, running in dry-run mode.');
|
||||
t.is(t.context.log.args[1][0], 'This run was not triggered in a known CI environment, running in dry-run mode.');
|
||||
t.is(verifyConditions.callCount, 1);
|
||||
t.is(analyzeCommits.callCount, 1);
|
||||
t.is(verifyRelease.callCount, 1);
|
||||
@@ -596,7 +596,7 @@ test.serial('Returns falsy value if triggered by a PR', async t => {
|
||||
|
||||
t.falsy(await semanticRelease({repositoryUrl}));
|
||||
t.is(
|
||||
t.context.log.args[8][0],
|
||||
t.context.log.args[t.context.log.args.length - 1][0],
|
||||
"This run was triggered by a pull request and therefore a new version won't be published."
|
||||
);
|
||||
});
|
||||
@@ -623,7 +623,7 @@ test.serial('Returns falsy value if not running from the configured branch', asy
|
||||
|
||||
t.falsy(await semanticRelease(options));
|
||||
t.is(
|
||||
t.context.log.args[0][0],
|
||||
t.context.log.args[1][0],
|
||||
'This test run was triggered on the branch other-branch, while semantic-release is configured to only publish from master, therefore a new version won’t be published.'
|
||||
);
|
||||
});
|
||||
@@ -661,7 +661,7 @@ test.serial('Returns falsy value if there is no relevant changes', async t => {
|
||||
t.is(verifyRelease.callCount, 0);
|
||||
t.is(generateNotes.callCount, 0);
|
||||
t.is(publish.callCount, 0);
|
||||
t.is(t.context.log.args[6][0], 'There are no relevant changes, so no new version is released.');
|
||||
t.is(t.context.log.args[7][0], 'There are no relevant changes, so no new version is released.');
|
||||
});
|
||||
|
||||
test.serial('Exclude commits with [skip release] or [release skip] from analysis', async t => {
|
||||
@@ -723,8 +723,8 @@ test.serial('Hide sensitive environment variable values from the logs', async t
|
||||
|
||||
await t.throws(semanticRelease(options));
|
||||
|
||||
t.regex(t.context.stdout.args[8][0], /Console: The token \[secure\] is invalid/);
|
||||
t.regex(t.context.stdout.args[9][0], /Log: The token \[secure\] is invalid/);
|
||||
t.regex(t.context.stdout.args[t.context.stdout.args.length - 2][0], /Console: The token \[secure\] is invalid/);
|
||||
t.regex(t.context.stdout.args[t.context.stdout.args.length - 1][0], /Log: The token \[secure\] is invalid/);
|
||||
t.regex(t.context.stderr.args[0][0], /Error: The token \[secure\] is invalid/);
|
||||
t.regex(t.context.stderr.args[1][0], /Invalid token \[secure\]/);
|
||||
});
|
||||
@@ -785,7 +785,7 @@ test.serial('Throw SemanticReleaseError if repositoryUrl is not set and cannot b
|
||||
'./lib/logger': t.context.logger,
|
||||
'env-ci': () => ({isCi: true, branch: 'master', isPr: false}),
|
||||
});
|
||||
const errors = Array.from(await t.throws(semanticRelease()));
|
||||
const errors = [...(await t.throws(semanticRelease()))];
|
||||
|
||||
// Verify error code and type
|
||||
t.is(errors[0].code, 'ENOREPOURL');
|
||||
|
||||
@@ -603,11 +603,11 @@ test.serial('Exit with 1 if missing permission to push to the remote repository'
|
||||
|
||||
// Create a git repository, set the current working directory at the root of the repo
|
||||
t.log('Create git repository');
|
||||
const {repositoryUrl} = await gitbox.createRepo(packageName);
|
||||
await gitbox.createRepo(packageName);
|
||||
await writeJson('./package.json', {
|
||||
name: packageName,
|
||||
version: '0.0.0-dev',
|
||||
repository: {url: repositoryUrl},
|
||||
repository: {url: 'http://user:wrong_pass@localhost:2080/git/unauthorized.git'},
|
||||
});
|
||||
|
||||
/* Initial release */
|
||||
@@ -619,24 +619,3 @@ test.serial('Exit with 1 if missing permission to push to the remote repository'
|
||||
t.regex(stdout, /EGITNOPERMISSION/);
|
||||
t.is(code, 1);
|
||||
});
|
||||
|
||||
test.serial('CLI returns error code and prints help if called with a command', async t => {
|
||||
t.log('$ semantic-release pre');
|
||||
const {stdout, code} = await execa(cli, ['pre'], {env, reject: false});
|
||||
t.regex(stdout, /Usage: semantic-release/);
|
||||
t.is(code, 1);
|
||||
});
|
||||
|
||||
test.serial('CLI prints help if called with --help', async t => {
|
||||
t.log('$ semantic-release --help');
|
||||
const {stdout, code} = await execa(cli, ['--help'], {env});
|
||||
t.regex(stdout, /Usage: semantic-release/);
|
||||
t.is(code, 0);
|
||||
});
|
||||
|
||||
test.serial('CLI returns error code with invalid option', async t => {
|
||||
t.log('$ semantic-release --unknown-option');
|
||||
const {stderr, code} = await execa(cli, ['--unknown-option'], {env, reject: false});
|
||||
t.regex(stderr, /unknown option/);
|
||||
t.is(code, 1);
|
||||
});
|
||||
|
||||
@@ -118,7 +118,7 @@ test('Throw all errors from the first step throwing an AggregateError', async t
|
||||
|
||||
const errors = await t.throws(pipeline([step1, step2, step3])(0));
|
||||
|
||||
t.deepEqual(Array.from(errors), [error1, error2]);
|
||||
t.deepEqual([...errors], [error1, error2]);
|
||||
t.true(step1.calledWith(0));
|
||||
t.true(step2.calledWith(0));
|
||||
t.true(step3.notCalled);
|
||||
@@ -133,7 +133,7 @@ test('Execute all even if a Promise rejects', async t => {
|
||||
|
||||
const errors = await t.throws(pipeline([step1, step2, step3])(0, {settleAll: true}));
|
||||
|
||||
t.deepEqual(Array.from(errors), [error1, error2]);
|
||||
t.deepEqual([...errors], [error1, error2]);
|
||||
t.true(step1.calledWith(0));
|
||||
t.true(step2.calledWith(0));
|
||||
t.true(step3.calledWith(0));
|
||||
@@ -149,7 +149,7 @@ test('Throw all errors from all steps throwing an AggregateError', async t => {
|
||||
|
||||
const errors = await t.throws(pipeline([step1, step2])(0, {settleAll: true}));
|
||||
|
||||
t.deepEqual(Array.from(errors), [error1, error2, error3, error4]);
|
||||
t.deepEqual([...errors], [error1, error2, error3, error4]);
|
||||
t.true(step1.calledWith(0));
|
||||
t.true(step2.calledWith(0));
|
||||
});
|
||||
@@ -165,7 +165,7 @@ test('Execute each function in series passing a transformed input even if a step
|
||||
|
||||
const errors = await t.throws(pipeline([step1, step2, step3, step4])(0, {settleAll: true, getNextInput}));
|
||||
|
||||
t.deepEqual(Array.from(errors), [error2, error3]);
|
||||
t.deepEqual([...errors], [error2, error3]);
|
||||
t.true(step1.calledWith(0));
|
||||
t.true(step2.calledWith(0 + 1));
|
||||
t.true(step3.calledWith(0 + 1 + error2));
|
||||
|
||||
@@ -136,16 +136,16 @@ test('Merge global options with plugin options', async t => {
|
||||
});
|
||||
|
||||
test('Throw an error if plugins configuration are missing a path for plugin pipeline', t => {
|
||||
const errors = Array.from(t.throws(() => getPlugins({verifyConditions: {}}, {}, t.context.logger)));
|
||||
const errors = [...t.throws(() => getPlugins({verifyConditions: {}}, {}, t.context.logger))];
|
||||
|
||||
t.is(errors[0].name, 'SemanticReleaseError');
|
||||
t.is(errors[0].code, 'EPLUGINCONF');
|
||||
});
|
||||
|
||||
test('Throw an error if an array of plugin configuration is missing a path for plugin pipeline', t => {
|
||||
const errors = Array.from(
|
||||
t.throws(() => getPlugins({verifyConditions: [{path: '@semantic-release/npm'}, {}]}, {}, t.context.logger))
|
||||
);
|
||||
const errors = [
|
||||
...t.throws(() => getPlugins({verifyConditions: [{path: '@semantic-release/npm'}, {}]}, {}, t.context.logger)),
|
||||
];
|
||||
|
||||
t.is(errors[0].name, 'SemanticReleaseError');
|
||||
t.is(errors[0].code, 'EPLUGINCONF');
|
||||
|
||||
+7
-19
@@ -1,5 +1,4 @@
|
||||
import test from 'ava';
|
||||
import {stub} from 'sinon';
|
||||
import tempy from 'tempy';
|
||||
import verify from '../lib/verify';
|
||||
import {gitRepo} from './helpers/git-utils';
|
||||
@@ -9,17 +8,13 @@ const envBackup = Object.assign({}, process.env);
|
||||
// Save the current working diretory
|
||||
const cwd = process.cwd();
|
||||
|
||||
test.beforeEach(t => {
|
||||
test.beforeEach(() => {
|
||||
// Delete environment variables that could have been set on the machine running the tests
|
||||
delete process.env.GIT_CREDENTIALS;
|
||||
delete process.env.GH_TOKEN;
|
||||
delete process.env.GITHUB_TOKEN;
|
||||
delete process.env.GL_TOKEN;
|
||||
delete process.env.GITLAB_TOKEN;
|
||||
// Stub the logger functions
|
||||
t.context.log = stub();
|
||||
t.context.error = stub();
|
||||
t.context.logger = {log: t.context.log, error: t.context.error};
|
||||
});
|
||||
|
||||
test.afterEach.always(() => {
|
||||
@@ -32,7 +27,7 @@ test.afterEach.always(() => {
|
||||
test.serial('Throw a AggregateError', async t => {
|
||||
await gitRepo();
|
||||
|
||||
const errors = Array.from(await t.throws(verify({}, 'master', t.context.logger)));
|
||||
const errors = [...(await t.throws(verify({})))];
|
||||
|
||||
t.is(errors[0].name, 'SemanticReleaseError');
|
||||
t.is(errors[0].code, 'ENOREPOURL');
|
||||
@@ -46,7 +41,7 @@ test.serial('Throw a SemanticReleaseError if does not run on a git repository',
|
||||
const dir = tempy.directory();
|
||||
process.chdir(dir);
|
||||
|
||||
const errors = Array.from(await t.throws(verify({}, 'master', t.context.logger)));
|
||||
const errors = [...(await t.throws(verify({})))];
|
||||
|
||||
t.is(errors[0].name, 'SemanticReleaseError');
|
||||
t.is(errors[0].code, 'ENOGITREPO');
|
||||
@@ -56,7 +51,7 @@ test.serial('Throw a SemanticReleaseError if the "tagFormat" is not valid', asyn
|
||||
const repositoryUrl = await gitRepo(true);
|
||||
const options = {repositoryUrl, tagFormat: `?\${version}`};
|
||||
|
||||
const errors = Array.from(await t.throws(verify(options, 'master', t.context.logger)));
|
||||
const errors = [...(await t.throws(verify(options, 'master', t.context.logger)))];
|
||||
|
||||
t.is(errors[0].name, 'SemanticReleaseError');
|
||||
t.is(errors[0].code, 'EINVALIDTAGFORMAT');
|
||||
@@ -66,7 +61,7 @@ test.serial('Throw a SemanticReleaseError if the "tagFormat" does not contains t
|
||||
const repositoryUrl = await gitRepo(true);
|
||||
const options = {repositoryUrl, tagFormat: 'test'};
|
||||
|
||||
const errors = Array.from(await t.throws(verify(options, 'master', t.context.logger)));
|
||||
const errors = [...(await t.throws(verify(options, 'master', t.context.logger)))];
|
||||
|
||||
t.is(errors[0].name, 'SemanticReleaseError');
|
||||
t.is(errors[0].code, 'ETAGNOVERSION');
|
||||
@@ -76,22 +71,15 @@ test.serial('Throw a SemanticReleaseError if the "tagFormat" contains multiple "
|
||||
const repositoryUrl = await gitRepo(true);
|
||||
const options = {repositoryUrl, tagFormat: `\${version}v\${version}`};
|
||||
|
||||
const errors = Array.from(await t.throws(verify(options, 'master', t.context.logger)));
|
||||
const errors = [...(await t.throws(verify(options)))];
|
||||
|
||||
t.is(errors[0].name, 'SemanticReleaseError');
|
||||
t.is(errors[0].code, 'ETAGNOVERSION');
|
||||
});
|
||||
|
||||
test.serial('Return "false" if the current branch is not the once configured', async t => {
|
||||
const repositoryUrl = await gitRepo(true);
|
||||
const options = {repositoryUrl, tagFormat: `v\${version}`, branch: 'master'};
|
||||
|
||||
t.false(await verify(options, 'other', t.context.logger));
|
||||
});
|
||||
|
||||
test.serial('Return "true" if all verification pass', async t => {
|
||||
const repositoryUrl = await gitRepo(true);
|
||||
const options = {repositoryUrl, tagFormat: `v\${version}`, branch: 'master'};
|
||||
|
||||
t.true(await verify(options, 'master', t.context.logger));
|
||||
await t.notThrows(verify(options));
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user