Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e5a73d8628 | ||
|
|
50f3c6e140 | ||
|
|
78f0937f24 | ||
|
|
a7c187f31b | ||
|
|
c2beb643fa | ||
|
|
20246c02b1 | ||
|
|
857d4180e9 | ||
|
|
4d0490122c | ||
|
|
aa724e80f7 | ||
|
|
ce15b75edf | ||
|
|
1966f0e3e2 | ||
|
|
305f4ee8eb | ||
|
|
9788fcad4e |
+9
-9
@@ -58,7 +58,7 @@ Good pull requests, whether patches, improvements, or new features, are a fantas
|
||||
|
||||
**Please ask first** before embarking on any significant pull requests (e.g. implementing features, refactoring code), otherwise you risk spending a lot of time working on something that the project's developers might not want to merge into the project.
|
||||
|
||||
If you never created a pull request before, welcome 🎉 😄. [Here is a great tutorial](https://opensource.guide/how-to-contribute/#opening-a-pull-request) on how to send one :)
|
||||
If you have never created a pull request before, welcome 🎉 😄. [Here is a great tutorial](https://opensource.guide/how-to-contribute/#opening-a-pull-request) on how to send one :)
|
||||
|
||||
Here is a summary of the steps to follow:
|
||||
|
||||
@@ -83,7 +83,7 @@ $ git push origin <topic-branch-name>
|
||||
|
||||
**Tips**:
|
||||
- For ambitious tasks, open a Pull Request as soon as possible with the `[WIP]` prefix in the title, in order to get feedback and help from the community.
|
||||
- [Allow semantic-release maintainers to make changes to you Pull Request branch](https://help.github.com/articles/allowing-changes-to-a-pull-request-branch-created-from-a-fork). This way, we can rebase it and make some minor changes if necessary. All changes we make will be done in new commit and we'll ask for your approval before merging them.
|
||||
- [Allow semantic-release maintainers to make changes to your Pull Request branch](https://help.github.com/articles/allowing-changes-to-a-pull-request-branch-created-from-a-fork). This way, we can rebase it and make some minor changes if necessary. All changes we make will be done in new commit and we'll ask for your approval before merging them.
|
||||
|
||||
## Coding rules
|
||||
|
||||
@@ -91,7 +91,7 @@ $ git push origin <topic-branch-name>
|
||||
|
||||
To ensure consistency and quality throughout the source code, all code modifications must have:
|
||||
- No [linting](#lint) errors
|
||||
- A [test](#tests) for every possible cases introduced by your code change
|
||||
- A [test](#tests) for every possible case introduced by your code change
|
||||
- **100%** test coverage
|
||||
- [Valid commit message(s)](#commit-message-guidelines)
|
||||
- Documentation for new features
|
||||
@@ -102,7 +102,7 @@ To ensure consistency and quality throughout the source code, all code modificat
|
||||
To ensure consistency and quality, all documentation modifications must:
|
||||
- Refer to brand in [bold](https://help.github.com/articles/basic-writing-and-formatting-syntax/#styling-text) with proper capitalization, i.e. **GitHub**, **semantic-release**, **npm**
|
||||
- Prefer [tables](https://help.github.com/articles/organizing-information-with-tables) over [lists](https://help.github.com/articles/basic-writing-and-formatting-syntax/#lists) when listing key values, i.e. List of options with their description
|
||||
- Use [links](https://help.github.com/articles/basic-writing-and-formatting-syntax/#links) when, the first you are referring to:
|
||||
- Use [links](https://help.github.com/articles/basic-writing-and-formatting-syntax/#links) when you are referring to:
|
||||
- a **semantic-release** concept described somewhere else in the documentation, i.e. How to [contribute](CONTRIBUTING.md)
|
||||
- a third-party product/brand/service, i.e. Integrate with [GitHub](https://github.com)
|
||||
- an external concept or feature, i.e. Create a [GitHub release](https://help.github.com/articles/creating-releases)
|
||||
@@ -125,7 +125,7 @@ If possible, make [atomic commits](https://en.wikipedia.org/wiki/Atomic_commit),
|
||||
- a functional change should be contained in exactly one commit
|
||||
- a commit should not create an inconsistent state (such as test errors, linting errors, partial fix, feature with documentation etc...)
|
||||
|
||||
A complex feature can be broken down into multiple commits as long as each one keep a consistent state and consist of a self-contained change.
|
||||
A complex feature can be broken down into multiple commits as long as each one maintains a consistent state and consists of a self-contained change.
|
||||
|
||||
#### Commit message format
|
||||
|
||||
@@ -219,13 +219,13 @@ $ npm install
|
||||
|
||||
### Lint
|
||||
|
||||
All the [semantic-release](https://github.com/semantic-release) repository use [XO](https://github.com/sindresorhus/xo) for linting and [Prettier](https://prettier.io) for formatting. Prettier formatting will be automatically verified and fixed by XO.
|
||||
All the [semantic-release](https://github.com/semantic-release) repositories use [XO](https://github.com/sindresorhus/xo) for linting and [Prettier](https://prettier.io) for formatting. Prettier formatting will be automatically verified and fixed by XO.
|
||||
|
||||
Before pushing your code changes make sure there is no linting errors with `npm run lint`.
|
||||
Before pushing your code changes make sure there are no linting errors with `npm run lint`.
|
||||
|
||||
**Tips**:
|
||||
- Most linting errors can be automatically fixed with `npm run lint --fix`.
|
||||
- Install the [XO plugin](https://github.com/sindresorhus/xo#editor-plugins) for your editor to see linting error directly in your editor and automatically fix them on save.
|
||||
- Install the [XO plugin](https://github.com/sindresorhus/xo#editor-plugins) for your editor to see linting errors directly in your editor and automatically fix them on save.
|
||||
|
||||
### Tests
|
||||
|
||||
@@ -246,6 +246,6 @@ $ npm run test
|
||||
|
||||
### Commits
|
||||
|
||||
All the [semantic-release](https://github.com/semantic-release) repository use [Commitizen](https://github.com/commitizen/cz-cli) to help you create [valid commit messages](#commit-message-guidelines).
|
||||
All the [semantic-release](https://github.com/semantic-release) repositories use [Commitizen](https://github.com/commitizen/cz-cli) to help you create [valid commit messages](#commit-message-guidelines).
|
||||
|
||||
After staging your changes with `git add`, run `npm run cm` to start the interactive commit message CLI.
|
||||
|
||||
@@ -48,11 +48,11 @@ This removes the immediate connection between human emotions and version numbers
|
||||
|
||||
### Commit message format
|
||||
|
||||
**semantic-release** uses the commit messages to determine the type of changes in the codebase. Following formalized conventions for commit messages, **semantic-release** automatically determines the next [semantic version](https://semver.org) number, generates a changelog and publish the release.
|
||||
**semantic-release** uses the commit messages to determine the type of changes in the codebase. Following formalized conventions for commit messages, **semantic-release** automatically determines the next [semantic version](https://semver.org) number, generates a changelog and publishes the release.
|
||||
|
||||
By default **semantic-release** uses [Angular Commit Message Conventions](https://github.com/angular/angular.js/blob/master/DEVELOPERS.md#-git-commit-guidelines). The commit message format can be changed with the [`preset` or `config` options](docs/usage/configuration.md#options) of the [@semantic-release/commit-analyzer](https://github.com/semantic-release/commit-analyzer#options) and [@semantic-release/release-notes-generator](https://github.com/semantic-release/release-notes-generator#options) plugins.
|
||||
|
||||
Tools such as [commitizen](https://github.com/commitizen/cz-cli), [commitlint](https://github.com/marionebl/commitlint) or [semantic-git-commit-cli](https://github.com/JPeer264/node-semantic-git-commit-cli) can be used to help contributor and enforce valid commits message.
|
||||
Tools such as [commitizen](https://github.com/commitizen/cz-cli), [commitlint](https://github.com/marionebl/commitlint) or [semantic-git-commit-cli](https://github.com/JPeer264/node-semantic-git-commit-cli) can be used to help contributors and enforce valid commit messages.
|
||||
|
||||
Here is an example of the release type that will be done based on a commit messages:
|
||||
|
||||
@@ -82,11 +82,12 @@ After running the tests the command `semantic-release` will execute the followin
|
||||
|-------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| Verify Conditions | Verify all the conditions to proceed with the release with the [verify conditions plugins](docs/usage/plugins.md#verifyconditions-plugin). |
|
||||
| Get last release | Obtain the commit corresponding to the last release by analyzing [Git tags](https://git-scm.com/book/en/v2/Git-Basics-Tagging). |
|
||||
| Analyze commits | Determine the type of release to do with the [analyze commits plugin](docs/usage/plugins.md#analyzecommits-plugin) based on the commits added since the last release. |
|
||||
| Verify release | Verify the release conformity with the [verify release plugins](docs/usage/plugins.md#verifyrelease-plugin). |
|
||||
| Analyze commits | Determine the type of release with the [analyze commits plugin](docs/usage/plugins.md#analyzecommits-plugin) based on the commits added since the last release. |
|
||||
| Verify release | Verify the release conformity with the [verify release plugins](docs/usage/plugins.md#verifyrelease-plugin). |
|
||||
| Generate notes | Generate release notes with the [generate notes plugin](docs/usage/plugins.md#generatenotes-plugin) for the commits added since the last release. |
|
||||
| Create Git tag | Create a Git tag corresponding the new release version |
|
||||
| Publish | Publish the release with the [publish plugins](docs/usage/plugins.md#publish-plugin). |
|
||||
| Create Git tag | Create a Git tag corresponding to the new release version |
|
||||
| Prepare | Prepare the release with the [prepare plugins](docs/usage/plugins.md#prepare-plugin). |
|
||||
| Publish | Publish the release with the [publish plugins](docs/usage/plugins.md#publish-plugin). |
|
||||
| Notify | Notify of new releases or errors with the [success](docs/usage/plugins.md#success-plugin) and [fail](docs/usage/plugins.md#fail-plugin) plugins. |
|
||||
|
||||
## Documentation
|
||||
|
||||
@@ -20,23 +20,26 @@ Usage:
|
||||
.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: true, type: 'boolean', 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('prepare', {...stringList, group: 'Plugins'})
|
||||
.option('publish', {...stringList, group: 'Plugins'})
|
||||
.option('success', {...stringList, group: 'Plugins'})
|
||||
.option('fail', {...stringList, group: 'Plugins'})
|
||||
.option('debug', {describe: 'Output debugging information', default: false, type: 'boolean', group: 'Options'})
|
||||
.option('d', {alias: 'dry-run', describe: 'Skip publishing', default: false, type: 'boolean', group: 'Options'})
|
||||
.option('h', {alias: 'help', group: 'Options'})
|
||||
.option('v', {alias: 'version', group: 'Options'})
|
||||
.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 {
|
||||
const {help, version, ...opts} = cli.argv;
|
||||
// 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;
|
||||
@@ -52,8 +55,7 @@ Usage:
|
||||
require('debug').enable('semantic-release:*');
|
||||
}
|
||||
|
||||
// Remove option with undefined values, as yargs sets non defined options as `undefined`
|
||||
await require('.')(pickBy(opts, value => !isUndefined(value)));
|
||||
await require('.')(opts);
|
||||
process.exitCode = 0;
|
||||
} catch (err) {
|
||||
if (err.name !== 'YError') {
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
- [fail](https://github.com/semantic-release/github#fail): Open a GitHub issue when a release fails
|
||||
- [@semantic-release/npm](https://github.com/semantic-release/npm)
|
||||
- [verifyConditions](https://github.com/semantic-release/npm#verifyconditions): Verify the presence and the validity of the npm authentication and release configuration
|
||||
- [prepare](https://github.com/semantic-release/npm#prepare): Update the package.json version and create the npm package tarball
|
||||
- [publish](https://github.com/semantic-release/npm#publish): Publish the package on the npm registry
|
||||
|
||||
## Official plugins
|
||||
@@ -18,15 +19,16 @@
|
||||
- [publish](https://github.com/semantic-release/gitlab#publish): Publish a [GitLab release](https://docs.gitlab.com/ce/workflow/releases.html)
|
||||
- [@semantic-release/git](https://github.com/semantic-release/git)
|
||||
- [verifyConditions](https://github.com/semantic-release/git#verifyconditions): Verify the presence and the validity of the Git authentication and release configuration
|
||||
- [publish](https://github.com/semantic-release/git#publish): Push a release commit and tag, including configurable files
|
||||
- [prepare](https://github.com/semantic-release/git#prepare): Push a release commit and tag, including configurable files
|
||||
- [@semantic-release/changelog](https://github.com/semantic-release/changelog)
|
||||
- [verifyConditions](https://github.com/semantic-release/changelog#verifyconditions): Verify the presence and the validity of the configuration
|
||||
- [publish](https://github.com/semantic-release/changelog#publish): Create or update the changelog file in the local project repository
|
||||
- [prepare](https://github.com/semantic-release/changelog#prepare): Create or update the changelog file in the local project repository
|
||||
- [@semantic-release/exec](https://github.com/semantic-release/exec)
|
||||
- [verifyConditions](https://github.com/semantic-release/exec#verifyconditions): Execute a shell command to verify if the release should happen
|
||||
- [analyzeCommits](https://github.com/semantic-release/exec#analyzecommits): Execute a shell command to determine the type of release
|
||||
- [verifyRelease](https://github.com/semantic-release/exec#verifyrelease): Execute a shell command to verifying a release that was determined before and is about to be published.
|
||||
- [generateNotes](https://github.com/semantic-release/exec#analyzecommits): Execute a shell command to generate the release note
|
||||
- [prepare](https://github.com/semantic-release/exec#prepare): Execute a shell command to prepare the release
|
||||
- [publish](https://github.com/semantic-release/exec#publish): Execute a shell command to publish the release
|
||||
- [success](https://github.com/semantic-release/exec#success): Execute a shell command to notify of a new release
|
||||
- [fail](https://github.com/semantic-release/exec#fail): Execute a shell command to notify of a failed release
|
||||
|
||||
@@ -155,6 +155,18 @@ Define the [generate notes plugin](plugins.md#generatenotes-plugin).
|
||||
|
||||
See [Plugins configuration](plugins.md#configuration) for more details.
|
||||
|
||||
### prepare
|
||||
|
||||
Type: `Array`, `String`, `Object`
|
||||
|
||||
Default: `['@semantic-release/npm']`
|
||||
|
||||
CLI argument: `--prepare`
|
||||
|
||||
Define the list of [prepare plugins](plugins.md#prepare-plugin). Plugins will run in series, in the order defined in the `Array`.
|
||||
|
||||
See [Plugins configuration](plugins.md#configuration) for more details.
|
||||
|
||||
### publish
|
||||
|
||||
Type: `Array`, `String`, `Object`
|
||||
@@ -171,7 +183,7 @@ See [Plugins configuration](plugins.md#configuration) for more details.
|
||||
|
||||
Type: `Array`, `String`, `Object`
|
||||
|
||||
Default: `[]`
|
||||
Default: `['@semantic-release/github']`
|
||||
|
||||
CLI argument: `--success`
|
||||
|
||||
@@ -183,7 +195,7 @@ See [Plugins configuration](plugins.md#configuration) for more details.
|
||||
|
||||
Type: `Array`, `String`, `Object`
|
||||
|
||||
Default: `[]`
|
||||
Default: `['@semantic-release/github']`
|
||||
|
||||
CLI argument: `--fail`
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
## Local installation
|
||||
|
||||
For [Node modules projects](https://docs.npmjs.com/getting-started/creating-node-modules) we recommend to install **semantic-release** locally and to run the `semantic-release` command with [npx](https://www.npmjs.com/package/npx):
|
||||
For [Node modules projects](https://docs.npmjs.com/getting-started/creating-node-modules) we recommend installing **semantic-release** locally and running the `semantic-release` command with [npx](https://www.npmjs.com/package/npx):
|
||||
|
||||
```bash
|
||||
$ npm install --save-dev semantic-release
|
||||
@@ -14,16 +14,16 @@ Then in the CI environment:
|
||||
$ npx semantic-release
|
||||
```
|
||||
|
||||
**Note:**: `npx` is a tool bundled with `npm@>=5.2.0`. It is used to conveniently find the semantic-release binary and to execute it. See [What is npx](../support/FAQ.md#what-is-npx) for more details.
|
||||
**Note:** `npx` is a tool bundled with `npm@>=5.2.0`. It is used to conveniently find the semantic-release binary and to execute it. See [What is npx](../support/FAQ.md#what-is-npx) for more details.
|
||||
|
||||
## Global installation
|
||||
|
||||
For other type of projects we recommend to install **semantic-release** directly in the CI environment, also with [npx](https://www.npmjs.com/package/npx):
|
||||
For other type of projects we recommend installing **semantic-release** directly in the CI environment, also with [npx](https://www.npmjs.com/package/npx):
|
||||
|
||||
```bash
|
||||
$ npx semantic-release
|
||||
```
|
||||
|
||||
**Note:**: For a global installation, it's recommended to specify the major **semantic-release** version to install (for example with with `npx semantic-release@12`, or `npm install -g semantic-release@12`). This way your build will not automatically use the next major **semantic-release** release that could possibly break your build. You will have to upgrade manually when a new major version is released.
|
||||
**Note:** For a global installation, it's recommended to specify the major **semantic-release** version to install (for example with with `npx semantic-release@12`, or `npm install -g semantic-release@12`). This way your build will not automatically use the next major **semantic-release** release that could possibly break your build. You will have to upgrade manually when a new major version is released.
|
||||
|
||||
**Note:**: `npx` is a tool bundled with `npm@>=5.2.0`. It is used to conveniently install the semantic-release binary and to execute it. See [What is npx](../support/FAQ.md#what-is-npx) for more details.
|
||||
**Note:** `npx` is a tool bundled with `npm@>=5.2.0`. It is used to conveniently install the semantic-release binary and to execute it. See [What is npx](../support/FAQ.md#what-is-npx) for more details.
|
||||
+17
-9
@@ -1,48 +1,56 @@
|
||||
# Plugins
|
||||
|
||||
Each [release steps](../../README.md#release-steps) is implemented within a plugin or a list of plugins that can be configured, allowing to support different [commit message format](../../README.md#commit-message-format), release not generator and publishing platforms.
|
||||
Each [release step](../../README.md#release-steps) is implemented within a plugin or a list of plugins that can be configured. This allows for support of different [commit message formats](../../README.md#commit-message-format), release note generators and publishing platforms.
|
||||
|
||||
## Plugin types
|
||||
|
||||
### verifyConditions plugin
|
||||
|
||||
Plugin responsible for verifying all the conditions to proceed with the release: configuration is correct, authentication token are valid, etc...
|
||||
Responsible for verifying conditions necessary to proceed with the release: configuration is correct, authentication token are valid, etc...
|
||||
|
||||
Default implementation: [npm](https://github.com/semantic-release/npm#verifyconditions) and [github](https://github.com/semantic-release/github#verifyconditions).
|
||||
|
||||
### analyzeCommits plugin
|
||||
|
||||
Plugin responsible for determining the type of the next release (`major`, `minor` or `patch`).
|
||||
Responsible for determining the type of the next release (`major`, `minor` or `patch`).
|
||||
|
||||
Default implementation: [@semantic-release/commit-analyzer](https://github.com/semantic-release/commit-analyzer).
|
||||
|
||||
### verifyRelease plugin
|
||||
|
||||
Plugin responsible for verifying the parameters (version, type, dist-tag etc...) of the release that is about to be published match certain expectations. For example the [cracks plugin](https://github.com/semantic-release/cracks) allows to verify that if a release contains breaking changes, its type must be `major`.
|
||||
Responsible for verifying the parameters (version, type, dist-tag etc...) of the release that is about to be published match certain expectations. For example the [cracks plugin](https://github.com/semantic-release/cracks) is able to verify that if a release contains breaking changes, its type must be `major`.
|
||||
|
||||
Default implementation: none.
|
||||
|
||||
### generateNotes plugin
|
||||
|
||||
Plugin responsible for generating release notes.
|
||||
Responsible for generating release notes.
|
||||
|
||||
Default implementation: [@semantic-release/release-notes-generator](https://github.com/semantic-release/release-notes-generator).
|
||||
|
||||
### prepare plugin
|
||||
|
||||
Responsible for preparing the release, including:
|
||||
- Creating or updating files such as `package.json`, `CHANGELOG.md`, documentation or compiled assets.
|
||||
- Create and push commits
|
||||
|
||||
Default implementation: [npm](https://github.com/semantic-release/npm#prepare).
|
||||
|
||||
### publish plugin
|
||||
|
||||
Plugin responsible for publishing the release.
|
||||
Responsible for publishing the release.
|
||||
|
||||
Default implementation: [npm](https://github.com/semantic-release/npm#publish) and [github](https://github.com/semantic-release/github#publish).
|
||||
|
||||
### success plugin
|
||||
|
||||
Plugin responsible for notifying of a new release.
|
||||
Responsible for notifying of a new release.
|
||||
|
||||
Default implementation: [github](https://github.com/semantic-release/github#success).
|
||||
|
||||
### fail plugin
|
||||
|
||||
Plugin responsible for notifying of a failed release.
|
||||
Responsible for notifying of a failed release.
|
||||
|
||||
Default implementation: [github](https://github.com/semantic-release/github#fail).
|
||||
|
||||
@@ -50,7 +58,7 @@ Default implementation: [github](https://github.com/semantic-release/github#fail
|
||||
|
||||
Plugin can be configured by specifying the plugin's module name or file path directly as a `String` or within the `path` key of an `Object`.
|
||||
|
||||
Plugins specific options can be set similarly to the other **semantic-release** [options](configuration.md#options) or within the plugin `Object`. Plugins options defined along the other **semantic-release** [options](configuration.md#options) will apply to all plugins, while the one defined within the plugin `Object` will apply only to this specific plugin.
|
||||
Plugins specific options can be set similarly to the other **semantic-release** [options](configuration.md#options) or within the plugin `Object`. Plugins options defined along with the other **semantic-release** [options](configuration.md#options) will apply to all plugins. Options defined within the plugin `Object` will apply to that specific plugin.
|
||||
|
||||
For example:
|
||||
```json
|
||||
|
||||
@@ -12,7 +12,7 @@ const getCommits = require('./lib/get-commits');
|
||||
const getLastRelease = require('./lib/get-last-release');
|
||||
const {extractErrors} = require('./lib/utils');
|
||||
const logger = require('./lib/logger');
|
||||
const {unshallow, gitHead: getGitHead, tag, push, deleteTag} = require('./lib/git');
|
||||
const {unshallow, gitHead: getGitHead, tag, push} = require('./lib/git');
|
||||
|
||||
marked.setOptions({renderer: new TerminalRenderer()});
|
||||
|
||||
@@ -29,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');
|
||||
@@ -72,26 +79,14 @@ async function run(options, plugins) {
|
||||
logger.log('Call plugin %s', 'generateNotes');
|
||||
nextRelease.notes = await plugins.generateNotes(generateNotesParam);
|
||||
|
||||
// Create the tag before calling the publish plugins as some require the tag to exists
|
||||
logger.log('Create tag %s', nextRelease.gitTag);
|
||||
await tag(nextRelease.gitTag);
|
||||
await push(options.repositoryUrl, branch);
|
||||
|
||||
logger.log('Call plugin %s', 'publish');
|
||||
const releases = await plugins.publish(
|
||||
logger.log('Call plugin %s', 'prepare');
|
||||
await plugins.prepare(
|
||||
{options, logger, lastRelease, commits, nextRelease},
|
||||
{
|
||||
getNextInput: async lastResult => {
|
||||
const newGitHead = await getGitHead();
|
||||
// If previous publish plugin has created a commit (gitHead changed)
|
||||
// If previous prepare plugin has created a commit (gitHead changed)
|
||||
if (lastResult.nextRelease.gitHead !== newGitHead) {
|
||||
// Delete the previously created tag
|
||||
await deleteTag(options.repositoryUrl, nextRelease.gitTag);
|
||||
// Recreate the tag, referencing the new gitHead
|
||||
logger.log('Create tag %s', nextRelease.gitTag);
|
||||
await tag(nextRelease.gitTag);
|
||||
await push(options.repositoryUrl, branch);
|
||||
|
||||
nextRelease.gitHead = newGitHead;
|
||||
// Regenerate the release notes
|
||||
logger.log('Call plugin %s', 'generateNotes');
|
||||
@@ -100,11 +95,21 @@ async function run(options, plugins) {
|
||||
// Call the next publish plugin with the updated `nextRelease`
|
||||
return {options, logger, lastRelease, commits, nextRelease};
|
||||
},
|
||||
// Add nextRelease and plugin properties to published release
|
||||
transform: (release, step) => ({...(isPlainObject(release) ? release : {}), ...nextRelease, ...step}),
|
||||
}
|
||||
);
|
||||
|
||||
// Create the tag before calling the publish plugins as some require the tag to exists
|
||||
logger.log('Create tag %s', nextRelease.gitTag);
|
||||
await tag(nextRelease.gitTag);
|
||||
await push(options.repositoryUrl, branch);
|
||||
|
||||
logger.log('Call plugin %s', 'publish');
|
||||
const releases = await plugins.publish(
|
||||
{options, logger, lastRelease, commits, nextRelease},
|
||||
// Add nextRelease and plugin properties to published release
|
||||
{transform: (release, step) => ({...(isPlainObject(release) ? release : {}), ...nextRelease, ...step})}
|
||||
);
|
||||
|
||||
await plugins.success(
|
||||
{options, logger, lastRelease, commits, nextRelease, releases: castArray(releases)},
|
||||
{settleAll: true}
|
||||
|
||||
@@ -36,6 +36,12 @@ module.exports = {
|
||||
error: 'ERELEASENOTESOUTPUT',
|
||||
},
|
||||
},
|
||||
prepare: {
|
||||
default: ['@semantic-release/npm'],
|
||||
config: {
|
||||
validator: conf => !conf || (isArray(conf) ? conf : [conf]).every(conf => validatePluginConfig(conf)),
|
||||
},
|
||||
},
|
||||
publish: {
|
||||
default: ['@semantic-release/npm', '@semantic-release/github'],
|
||||
config: {
|
||||
@@ -47,13 +53,13 @@ module.exports = {
|
||||
},
|
||||
},
|
||||
success: {
|
||||
default: false,
|
||||
default: ['@semantic-release/github'],
|
||||
config: {
|
||||
validator: conf => !conf || (isArray(conf) ? conf : [conf]).every(conf => validatePluginConfig(conf)),
|
||||
},
|
||||
},
|
||||
fail: {
|
||||
default: false,
|
||||
default: ['@semantic-release/github'],
|
||||
config: {
|
||||
validator: conf => !conf || (isArray(conf) ? conf : [conf]).every(conf => validatePluginConfig(conf)),
|
||||
},
|
||||
|
||||
+4
-2
@@ -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');
|
||||
@@ -51,7 +51,9 @@ module.exports = async (opts, logger) => {
|
||||
...pickBy(options, option => !isUndefined(option) && !isNull(option)),
|
||||
};
|
||||
|
||||
options.repositoryUrl = options.repositoryUrl ? getGitAuthUrl(options.repositoryUrl) : options.repositoryUrl;
|
||||
if (!await verifyAuth(options.repositoryUrl, options.branch)) {
|
||||
options.repositoryUrl = options.repositoryUrl ? getGitAuthUrl(options.repositoryUrl) : options.repositoryUrl;
|
||||
}
|
||||
|
||||
debug('options values: %O', options);
|
||||
|
||||
|
||||
-17
@@ -115,22 +115,6 @@ async function push(origin, branch) {
|
||||
await execa('git', ['push', '--tags', origin, `HEAD:${branch}`]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a tag locally and remotely.
|
||||
*
|
||||
* @param {String} origin The remote repository URL.
|
||||
* @param {String} tagName The tag name to delete.
|
||||
*/
|
||||
async function deleteTag(origin, tagName) {
|
||||
// Delete the local tag
|
||||
let shell = await execa('git', ['tag', '-d', tagName], {reject: false});
|
||||
debug('delete local tag', shell);
|
||||
|
||||
// Delete the tag remotely
|
||||
shell = await execa('git', ['push', '--delete', origin, tagName], {reject: false});
|
||||
debug('delete remote tag', shell);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify a tag name is a valid Git reference.
|
||||
*
|
||||
@@ -157,6 +141,5 @@ module.exports = {
|
||||
verifyAuth,
|
||||
tag,
|
||||
push,
|
||||
deleteTag,
|
||||
verifyTagName,
|
||||
};
|
||||
|
||||
@@ -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
-19
@@ -22,7 +22,7 @@
|
||||
"@semantic-release/commit-analyzer": "^5.0.0",
|
||||
"@semantic-release/error": "^2.2.0",
|
||||
"@semantic-release/github": "^4.1.0",
|
||||
"@semantic-release/npm": "^3.1.0",
|
||||
"@semantic-release/npm": "^3.2.0",
|
||||
"@semantic-release/release-notes-generator": "^6.0.0",
|
||||
"aggregate-error": "^1.0.0",
|
||||
"chalk": "^2.3.0",
|
||||
@@ -53,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",
|
||||
@@ -63,11 +61,10 @@
|
||||
"nock": "^9.0.2",
|
||||
"nyc": "^11.2.1",
|
||||
"p-retry": "^1.0.0",
|
||||
"prettier": "~1.10.0",
|
||||
"proxyquire": "^1.8.0",
|
||||
"proxyquire": "^2.0.0",
|
||||
"sinon": "^4.0.0",
|
||||
"tempy": "^0.2.1",
|
||||
"xo": "^0.18.2"
|
||||
"xo": "^0.20.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8.3"
|
||||
@@ -107,10 +104,7 @@
|
||||
"all": true
|
||||
},
|
||||
"prettier": {
|
||||
"printWidth": 120,
|
||||
"singleQuote": true,
|
||||
"bracketSpacing": false,
|
||||
"trailingComma": "es5"
|
||||
"printWidth": 120
|
||||
},
|
||||
"publishConfig": {
|
||||
"tag": "next"
|
||||
@@ -131,14 +125,7 @@
|
||||
"test": "nyc ava -v"
|
||||
},
|
||||
"xo": {
|
||||
"extends": [
|
||||
"prettier"
|
||||
],
|
||||
"plugins": [
|
||||
"prettier"
|
||||
],
|
||||
"rules": {
|
||||
"prettier/prettier": 2
|
||||
}
|
||||
"prettier": true,
|
||||
"space": true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,6 +53,9 @@ test.serial('Pass options to semantic-release API', async t => {
|
||||
'verify2',
|
||||
'--generate-notes',
|
||||
'notes',
|
||||
'--prepare',
|
||||
'prepare1',
|
||||
'prepare2',
|
||||
'--publish',
|
||||
'publish1',
|
||||
'publish2',
|
||||
@@ -76,6 +79,7 @@ test.serial('Pass options to semantic-release API', async t => {
|
||||
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].prepare, ['prepare1', 'prepare2']);
|
||||
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']);
|
||||
@@ -153,6 +157,22 @@ test.serial('Pass empty Array to semantic-release API for list option set to "fa
|
||||
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});
|
||||
|
||||
@@ -46,6 +46,17 @@ test('The "generateNotes" plugin, if defined, must be a single plugin definition
|
||||
t.true(plugins.generateNotes.config.validator(() => {}));
|
||||
});
|
||||
|
||||
test('The "prepare" plugin, if defined, must be a single or an array of plugins definition', t => {
|
||||
t.false(plugins.verifyRelease.config.validator({}));
|
||||
t.false(plugins.verifyRelease.config.validator({path: null}));
|
||||
|
||||
t.true(plugins.verifyRelease.config.validator({path: 'plugin-path.js'}));
|
||||
t.true(plugins.verifyRelease.config.validator());
|
||||
t.true(plugins.verifyRelease.config.validator('plugin-path.js'));
|
||||
t.true(plugins.verifyRelease.config.validator(() => {}));
|
||||
t.true(plugins.verifyRelease.config.validator([{path: 'plugin-path.js'}, 'plugin-path.js', () => {}]));
|
||||
});
|
||||
|
||||
test('The "publish" plugin is mandatory, and must be a single or an array of plugins definition', t => {
|
||||
t.false(plugins.publish.config.validator({}));
|
||||
t.false(plugins.publish.config.validator({path: null}));
|
||||
|
||||
@@ -80,6 +80,19 @@ test.serial('Default values, reading repositoryUrl (http url) from package.json
|
||||
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'},
|
||||
|
||||
@@ -10,7 +10,6 @@ import {
|
||||
push,
|
||||
gitTags,
|
||||
isGitRepo,
|
||||
deleteTag,
|
||||
verifyTagName,
|
||||
} from '../lib/git';
|
||||
import {
|
||||
@@ -139,18 +138,6 @@ test.serial('Add tag on head commit', async t => {
|
||||
await t.is(await gitCommitTag(commits[0].hash), 'tag_name');
|
||||
});
|
||||
|
||||
test.serial('Delete a tag', async t => {
|
||||
// Create a git repository with a remote, set the current working directory at the root of the repo
|
||||
const repo = await gitRepo(true);
|
||||
await gitCommits(['Test commit']);
|
||||
await tag('tag_name');
|
||||
await push(repo, 'master');
|
||||
|
||||
await deleteTag(repo, 'tag_name');
|
||||
t.falsy(await gitTagHead('tag_name'));
|
||||
t.falsy(await gitRemoteTagHead(repo, 'tag_name'));
|
||||
});
|
||||
|
||||
test.serial('Push tag and commit to remote repository', async t => {
|
||||
// Create a git repository with a remote, set the current working directory at the root of the repo
|
||||
const repo = await gitRepo(true);
|
||||
|
||||
@@ -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=');
|
||||
});
|
||||
|
||||
+42
-16
@@ -67,6 +67,7 @@ test.serial('Plugins are called with expected values', async t => {
|
||||
const verifyRelease = stub().resolves();
|
||||
const generateNotes = stub().resolves(notes);
|
||||
const release1 = {name: 'Release 1', url: 'https://release1.com'};
|
||||
const prepare = stub().resolves();
|
||||
const publish1 = stub().resolves(release1);
|
||||
const success = stub().resolves();
|
||||
|
||||
@@ -77,6 +78,7 @@ test.serial('Plugins are called with expected values', async t => {
|
||||
analyzeCommits,
|
||||
verifyRelease,
|
||||
generateNotes,
|
||||
prepare,
|
||||
publish: [publish1, pluginNoop],
|
||||
success,
|
||||
};
|
||||
@@ -119,6 +121,15 @@ test.serial('Plugins are called with expected values', async t => {
|
||||
t.deepEqual(generateNotes.args[0][1].commits[0].message, commits[0].message);
|
||||
t.deepEqual(generateNotes.args[0][1].nextRelease, nextRelease);
|
||||
|
||||
t.is(prepare.callCount, 1);
|
||||
t.deepEqual(prepare.args[0][0], config);
|
||||
t.deepEqual(prepare.args[0][1].options, options);
|
||||
t.deepEqual(prepare.args[0][1].logger, t.context.logger);
|
||||
t.deepEqual(prepare.args[0][1].lastRelease, lastRelease);
|
||||
t.deepEqual(prepare.args[0][1].commits[0].hash, commits[0].hash);
|
||||
t.deepEqual(prepare.args[0][1].commits[0].message, commits[0].message);
|
||||
t.deepEqual(prepare.args[0][1].nextRelease, {...nextRelease, ...{notes}});
|
||||
|
||||
t.is(publish1.callCount, 1);
|
||||
t.deepEqual(publish1.args[0][0], config);
|
||||
t.deepEqual(publish1.args[0][1].options, options);
|
||||
@@ -161,6 +172,7 @@ test.serial('Use custom tag format', async t => {
|
||||
analyzeCommits: stub().resolves(nextRelease.type),
|
||||
verifyRelease: stub().resolves(),
|
||||
generateNotes: stub().resolves(notes),
|
||||
prepare: stub().resolves(),
|
||||
publish: stub().resolves(),
|
||||
success: stub().resolves(),
|
||||
fail: stub().resolves(),
|
||||
@@ -177,7 +189,7 @@ test.serial('Use custom tag format', async t => {
|
||||
t.is(await gitRemoteTagHead(repositoryUrl, nextRelease.gitTag), nextRelease.gitHead);
|
||||
});
|
||||
|
||||
test.serial('Use new gitHead, and recreate release notes if a publish plugin create a commit', async t => {
|
||||
test.serial('Use new gitHead, and recreate release notes if a prepare plugin create a commit', async t => {
|
||||
// Create a git repository, set the current working directory at the root of the repo
|
||||
const repositoryUrl = await gitRepo(true);
|
||||
// Add commits to the master branch
|
||||
@@ -191,10 +203,11 @@ test.serial('Use new gitHead, and recreate release notes if a publish plugin cre
|
||||
const notes = 'Release notes';
|
||||
|
||||
const generateNotes = stub().resolves(notes);
|
||||
const publish1 = stub().callsFake(async () => {
|
||||
const prepare1 = stub().callsFake(async () => {
|
||||
commits = (await gitCommits(['Third'])).concat(commits);
|
||||
});
|
||||
const publish2 = stub().resolves();
|
||||
const prepare2 = stub().resolves();
|
||||
const publish = stub().resolves();
|
||||
|
||||
const options = {
|
||||
branch: 'master',
|
||||
@@ -203,7 +216,8 @@ test.serial('Use new gitHead, and recreate release notes if a publish plugin cre
|
||||
analyzeCommits: stub().resolves(nextRelease.type),
|
||||
verifyRelease: stub().resolves(),
|
||||
generateNotes,
|
||||
publish: [publish1, publish2],
|
||||
prepare: [prepare1, prepare2],
|
||||
publish,
|
||||
success: stub().resolves(),
|
||||
fail: stub().resolves(),
|
||||
};
|
||||
@@ -217,14 +231,17 @@ test.serial('Use new gitHead, and recreate release notes if a publish plugin cre
|
||||
|
||||
t.is(generateNotes.callCount, 2);
|
||||
t.deepEqual(generateNotes.args[0][1].nextRelease, nextRelease);
|
||||
t.is(publish1.callCount, 1);
|
||||
t.deepEqual(publish1.args[0][1].nextRelease, {...nextRelease, ...{notes}});
|
||||
t.is(prepare1.callCount, 1);
|
||||
t.deepEqual(prepare1.args[0][1].nextRelease, {...nextRelease, ...{notes}});
|
||||
|
||||
nextRelease.gitHead = await getGitHead();
|
||||
|
||||
t.deepEqual(generateNotes.secondCall.args[1].nextRelease, {...nextRelease, ...{notes}});
|
||||
t.is(publish2.callCount, 1);
|
||||
t.deepEqual(publish2.args[0][1].nextRelease, {...nextRelease, ...{notes}});
|
||||
t.deepEqual(generateNotes.args[1][1].nextRelease, {...nextRelease, ...{notes}});
|
||||
t.is(prepare2.callCount, 1);
|
||||
t.deepEqual(prepare2.args[0][1].nextRelease, {...nextRelease, ...{notes}});
|
||||
|
||||
t.is(publish.callCount, 1);
|
||||
t.deepEqual(publish.args[0][1].nextRelease, {...nextRelease, ...{notes}});
|
||||
|
||||
// Verify the tag has been created on the local and remote repo and reference the last gitHead
|
||||
t.is(await gitTagHead(nextRelease.gitTag), commits[0].hash);
|
||||
@@ -258,6 +275,7 @@ test.serial('Call all "success" plugins even if one errors out', async t => {
|
||||
verifyConditions: [verifyConditions1, verifyConditions2],
|
||||
analyzeCommits,
|
||||
generateNotes,
|
||||
prepare: stub().resolves(),
|
||||
publish,
|
||||
success: [success1, success2],
|
||||
};
|
||||
@@ -302,9 +320,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 +363,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);
|
||||
@@ -383,6 +401,7 @@ test.serial('Dry-run skips publish and success', async t => {
|
||||
analyzeCommits,
|
||||
verifyRelease,
|
||||
generateNotes,
|
||||
prepare: stub().resolves(),
|
||||
publish,
|
||||
success,
|
||||
};
|
||||
@@ -428,9 +447,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);
|
||||
@@ -464,6 +483,7 @@ test.serial('Force a dry-run if not on a CI and "noCi" is not explicitly set', a
|
||||
analyzeCommits,
|
||||
verifyRelease,
|
||||
generateNotes,
|
||||
prepare: stub().resolves(),
|
||||
publish,
|
||||
success,
|
||||
fail: stub().resolves(),
|
||||
@@ -512,6 +532,7 @@ test.serial('Allow local releases with "noCi" option', async t => {
|
||||
analyzeCommits,
|
||||
verifyRelease,
|
||||
generateNotes,
|
||||
prepare: stub().resolves(),
|
||||
publish,
|
||||
success,
|
||||
fail: stub().resolves(),
|
||||
@@ -560,6 +581,7 @@ test.serial('Accept "undefined" value returned by the "generateNotes" plugins',
|
||||
analyzeCommits,
|
||||
verifyRelease,
|
||||
generateNotes,
|
||||
prepare: stub().resolves(),
|
||||
publish,
|
||||
success: stub().resolves(),
|
||||
fail: stub().resolves(),
|
||||
@@ -611,6 +633,7 @@ test.serial('Returns falsy value if not running from the configured branch', asy
|
||||
analyzeCommits: stub().resolves(),
|
||||
verifyRelease: stub().resolves(),
|
||||
generateNotes: stub().resolves(),
|
||||
prepare: stub().resolves(),
|
||||
publish: stub().resolves(),
|
||||
success: stub().resolves(),
|
||||
fail: stub().resolves(),
|
||||
@@ -646,6 +669,7 @@ test.serial('Returns falsy value if there is no relevant changes', async t => {
|
||||
analyzeCommits,
|
||||
verifyRelease,
|
||||
generateNotes,
|
||||
prepare: stub().resolves(),
|
||||
publish,
|
||||
success: stub().resolves(),
|
||||
fail: stub().resolves(),
|
||||
@@ -686,6 +710,7 @@ test.serial('Exclude commits with [skip release] or [release skip] from analysis
|
||||
analyzeCommits,
|
||||
verifyRelease: stub().resolves(),
|
||||
generateNotes: stub().resolves(),
|
||||
prepare: stub().resolves(),
|
||||
publish: stub().resolves(),
|
||||
success: stub().resolves(),
|
||||
fail: stub().resolves(),
|
||||
@@ -785,7 +810,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');
|
||||
@@ -844,6 +869,7 @@ test.serial('Get all commits including the ones not in the shallow clone', async
|
||||
analyzeCommits,
|
||||
verifyRelease: stub().resolves(),
|
||||
generateNotes: stub().resolves(notes),
|
||||
prepare: stub().resolves(),
|
||||
publish: stub().resolves(),
|
||||
success: stub().resolves(),
|
||||
fail: stub().resolves(),
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -27,6 +27,7 @@ test('Export default plugins', t => {
|
||||
t.is(typeof plugins.analyzeCommits, 'function');
|
||||
t.is(typeof plugins.verifyRelease, 'function');
|
||||
t.is(typeof plugins.generateNotes, 'function');
|
||||
t.is(typeof plugins.prepare, 'function');
|
||||
t.is(typeof plugins.publish, 'function');
|
||||
t.is(typeof plugins.success, 'function');
|
||||
t.is(typeof plugins.fail, 'function');
|
||||
@@ -49,6 +50,7 @@ test('Export plugins based on config', t => {
|
||||
t.is(typeof plugins.analyzeCommits, 'function');
|
||||
t.is(typeof plugins.verifyRelease, 'function');
|
||||
t.is(typeof plugins.generateNotes, 'function');
|
||||
t.is(typeof plugins.prepare, 'function');
|
||||
t.is(typeof plugins.publish, 'function');
|
||||
t.is(typeof plugins.success, 'function');
|
||||
t.is(typeof plugins.fail, 'function');
|
||||
@@ -79,6 +81,7 @@ test.serial('Export plugins loaded from the dependency of a shareable config mod
|
||||
t.is(typeof plugins.analyzeCommits, 'function');
|
||||
t.is(typeof plugins.verifyRelease, 'function');
|
||||
t.is(typeof plugins.generateNotes, 'function');
|
||||
t.is(typeof plugins.prepare, 'function');
|
||||
t.is(typeof plugins.publish, 'function');
|
||||
t.is(typeof plugins.success, 'function');
|
||||
t.is(typeof plugins.fail, 'function');
|
||||
@@ -106,6 +109,7 @@ test.serial('Export plugins loaded from the dependency of a shareable config fil
|
||||
t.is(typeof plugins.analyzeCommits, 'function');
|
||||
t.is(typeof plugins.verifyRelease, 'function');
|
||||
t.is(typeof plugins.generateNotes, 'function');
|
||||
t.is(typeof plugins.prepare, 'function');
|
||||
t.is(typeof plugins.publish, 'function');
|
||||
t.is(typeof plugins.success, 'function');
|
||||
t.is(typeof plugins.fail, 'function');
|
||||
@@ -136,16 +140,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