Compare commits
76
Commits
@@ -1 +0,0 @@
|
||||
base: app0ZOxG0FnHmOiuU
|
||||
@@ -131,3 +131,6 @@ yarn.lock
|
||||
|
||||
# Gitbook
|
||||
_book
|
||||
|
||||
# Mockserver
|
||||
CertificateAuthorityCertificate.pem
|
||||
|
||||
+7
-28
@@ -1,32 +1,11 @@
|
||||
language: node_js
|
||||
version: ~> 1.0
|
||||
|
||||
services:
|
||||
- docker
|
||||
|
||||
node_js:
|
||||
- 10
|
||||
- 8
|
||||
|
||||
# Trigger a push build on master and greenkeeper branches + PRs build on every branches
|
||||
# Avoid double build on PRs (See https://github.com/travis-ci/travis-ci/issues/1147)
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
- /^greenkeeper.*$/
|
||||
|
||||
# Retry install on fail to avoid failing a build on network/disk/external errors
|
||||
install:
|
||||
- travis_retry npm install
|
||||
|
||||
script:
|
||||
- npm run test
|
||||
|
||||
after_success:
|
||||
- npm run codecov
|
||||
|
||||
jobs:
|
||||
include:
|
||||
- stage: release
|
||||
node_js: lts/*
|
||||
script:
|
||||
- npm run semantic-release
|
||||
import:
|
||||
- .travis/node.yml
|
||||
- .travis/node-versions.yml
|
||||
- .travis/semantic-release.yml
|
||||
- .travis/greenkeeper.yml
|
||||
- .travis/codecov.yml
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
after_success:
|
||||
- npm run codecov
|
||||
@@ -0,0 +1,3 @@
|
||||
branches:
|
||||
only:
|
||||
- /^greenkeeper.*$/
|
||||
@@ -0,0 +1,4 @@
|
||||
node_js:
|
||||
- 12
|
||||
- 10
|
||||
- 8.16
|
||||
@@ -0,0 +1,11 @@
|
||||
language: node_js
|
||||
|
||||
cache:
|
||||
npm: false
|
||||
|
||||
# Retry install on fail to avoid failing a build on network/disk/external errors
|
||||
install:
|
||||
- travis_retry npm install
|
||||
|
||||
script:
|
||||
- npm run test
|
||||
@@ -0,0 +1,15 @@
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
- next
|
||||
- beta
|
||||
- /^\d+\.(\d+|x)(\.x)?$/
|
||||
|
||||
jobs:
|
||||
include:
|
||||
- stage: release
|
||||
node_js: lts/*
|
||||
install:
|
||||
- travis_retry npm install
|
||||
script:
|
||||
- npm run semantic-release
|
||||
+1
-1
@@ -224,7 +224,7 @@ All the [semantic-release](https://github.com/semantic-release) repositories use
|
||||
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`.
|
||||
- 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 errors directly in your editor and automatically fix them on save.
|
||||
|
||||
### Tests
|
||||
|
||||
@@ -52,7 +52,7 @@ This removes the immediate connection between human emotions and version numbers
|
||||
|
||||
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/conventional-changelog/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.
|
||||
Tools such as [commitizen](https://github.com/commitizen/cz-cli) or [commitlint](https://github.com/conventional-changelog/commitlint) 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:
|
||||
|
||||
@@ -103,12 +103,13 @@ After running the tests, the command `semantic-release` will execute the followi
|
||||
- [Plugins](docs/extending/plugins-list.md)
|
||||
- [Shareable configuration](docs/extending/shareable-configurations-list.md)
|
||||
- Recipes
|
||||
- [CI configurations](docs/recipes/README.md)
|
||||
- [Package managers and languages](docs/recipes/README.md)
|
||||
- [CI configurations](docs/recipes/README.md#ci-configurations)
|
||||
- [Git hosted services](docs/recipes/README.md#git-hosted-services)
|
||||
- [Package managers and languages](docs/recipes/README.md#package-managers-and-languages)
|
||||
- Developer guide
|
||||
- [JavaScript API](docs/developer-guide/js-api.md)
|
||||
- [Plugins](docs/developer-guide/plugin.md)
|
||||
- [Shareable configuration](docs/developer-guide/shareable-configuration.md)
|
||||
- [Plugins development](docs/developer-guide/plugin.md)
|
||||
- [Shareable configuration development](docs/developer-guide/shareable-configuration.md)
|
||||
- Support
|
||||
- [Resources](docs/support/resources.md)
|
||||
- [Frequently Asked Questions](docs/support/FAQ.md)
|
||||
|
||||
+7
-7
@@ -1,6 +1,7 @@
|
||||
# Summary
|
||||
|
||||
## Usage
|
||||
- [Getting started](docs/usage/getting-started.md#getting-started)
|
||||
- [Installation](docs/usage/installation.md#installation)
|
||||
- [CI Configuration](docs/usage/ci-configuration.md#ci-configuration)
|
||||
- [Configuration](docs/usage/configuration.md#configuration)
|
||||
@@ -12,19 +13,18 @@
|
||||
- [Shareable configuration](docs/extending/shareable-configurations-list.md)
|
||||
|
||||
## Recipes
|
||||
- [CI configurations](docs/recipes/README.md)
|
||||
- [CircleCI 2.0 workflows](docs/recipes/circleci-workflows.md)
|
||||
- [CI configurations](docs/recipes/README.md#ci-configurations)
|
||||
- [CircleCI 2.0](docs/recipes/circleci-workflows.md)
|
||||
- [Travis CI](docs/recipes/travis.md)
|
||||
- [Travis CI with build stages](docs/recipes/travis-build-stages.md)
|
||||
- [GitLab CI](docs/recipes/gitlab-ci.md)
|
||||
- [Git hosted services](docs/recipes/README.md)
|
||||
- [Git hosted services](docs/recipes/README.md#git-hosted-services)
|
||||
- [Git authentication with SSH keys](docs/recipes/git-auth-ssh-keys.md)
|
||||
- [Package managers and languages](docs/recipes/README.md)
|
||||
- [Package managers and languages](docs/recipes/README.md#package-managers-and-languages)
|
||||
|
||||
## Developer guide
|
||||
- [JavaScript API](docs/developer-guide/js-api.md)
|
||||
- [Plugin](docs/developer-guide/plugin.md)
|
||||
- [Shareable configuration](docs/developer-guide/shareable-configuration.md)
|
||||
- [Plugin development](docs/developer-guide/plugin.md)
|
||||
- [Shareable configuration development](docs/developer-guide/shareable-configuration.md)
|
||||
|
||||
## Support
|
||||
- [Resources](docs/support/resources.md)
|
||||
|
||||
@@ -21,9 +21,8 @@ See https://github.com/semantic-release/semantic-release/blob/master/docs/suppor
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
execa
|
||||
.stdout('git', ['--version'])
|
||||
.then(stdout => {
|
||||
execa('git', ['--version'])
|
||||
.then(({stdout}) => {
|
||||
var gitVersion = findVersions(stdout)[0];
|
||||
if (semver.lt(gitVersion, MIN_GIT_VERSION)) {
|
||||
console.error(`[semantic-release]: Git version ${MIN_GIT_VERSION} is required. Found ${gitVersion}.`);
|
||||
|
||||
@@ -1 +1,106 @@
|
||||
# Plugin developer guide
|
||||
# Plugin Developer Guide
|
||||
|
||||
To create a plugin for `semantic-release`, you need to decide which parts of the release lifecycle are important to that plugin. For example, it is best to always have a `verify` step because you may be receiving inputs from a user and want to make sure they exist. A plugin can abide by any of the following lifecycles:
|
||||
|
||||
- `verify`
|
||||
- `prepare`
|
||||
- `publish`
|
||||
- `success`
|
||||
- `fail`
|
||||
|
||||
`semantic-release` will require the plugin via `node` and look through the required object for methods named like the lifecyles stated above. For example, if your plugin only had a `verify` and `success` step, the `main` file for your object would need to `export` an object with `verify` and `success` functions.
|
||||
|
||||
In addition to the lifecycle methods, each lifecyle is passed two objects:
|
||||
|
||||
1. `pluginConfig` - an object containing the options that a user may pass in via their `release.config.js` file (or similar)
|
||||
2. `context` - provided by `semantic-release` for access to things like `env` variables set on the running process.
|
||||
|
||||
For each lifecycle you create, you will want to ensure it can accept `pluginConfig` and `context` as parameters.
|
||||
|
||||
## Creating a Plugin Project
|
||||
|
||||
It is recommended that you generate a new project with `yarn init`. This will provide you with a basic node project to get started with. From there, create an `index.js` file, and make sure it is specified as the `main` in the `package.json`. We will use this file to orchestrate the lifecycle methods later on.
|
||||
|
||||
Next, create a `src` or `lib` folder in the root of the project. This is where we will store our logic and code for how our lifecycle methods work. Finally, create a `test` folder so you can write tests related to your logic.
|
||||
|
||||
We recommend you setup a linting system to ensure good javascript practices are enforced. ESLint is usually the system of choice, and the configuration can be whatever you or your team fancies.
|
||||
|
||||
## Exposing Lifecycle Methods
|
||||
|
||||
In your `index.js` file, you can start by writing the following code
|
||||
|
||||
```javascript
|
||||
const verifyConditions = require('./src/verify');
|
||||
|
||||
let verified;
|
||||
|
||||
/**
|
||||
* Called by semantic-release during the verification step
|
||||
* @param {*} pluginConfig The semantic-release plugin config
|
||||
* @param {*} context The context provided by semantic-release
|
||||
*/
|
||||
async function verify(pluginConfig, context) {
|
||||
await verifyConditions(pluginConfig, context);
|
||||
verified = true;
|
||||
}
|
||||
|
||||
module.exports = { verify };
|
||||
```
|
||||
|
||||
Then, in your `src` folder, create a file called `verify.js` and add the following
|
||||
|
||||
```javascript
|
||||
const AggregateError = require('aggregate-error');
|
||||
|
||||
/**
|
||||
* A method to verify that the user has given us a slack webhook url to post to
|
||||
*/
|
||||
module.exports = async (pluginConfig, context) => {
|
||||
const { logger } = context;
|
||||
const errors = [];
|
||||
|
||||
// Throw any errors we accumulated during the validation
|
||||
if (errors.length > 0) {
|
||||
throw new AggregateError(errors);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
As of right now, this code won't do anything. However, if you were to run this plugin via `semantic-release`, it would run when the `verify` step occurred.
|
||||
|
||||
Following this structure, you can create different steps and checks to run through out the release process.
|
||||
|
||||
## Supporting Options
|
||||
|
||||
Let's say we want to verify that an `option` is passed. An `option` is a configuration object that is specific to your plugin. For example, the user may set an `option` in their release config like:
|
||||
|
||||
```js
|
||||
{
|
||||
prepare: {
|
||||
path: "@semantic-release/my-special-plugin"
|
||||
message: "My cool release message"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This `message` option will be passed to the `pluginConfig` object mentioned earlier. We can use the validation method we created to verify this option exists so we can perform logic based on that knowledge. In our `verify` file, we can add the following:
|
||||
|
||||
```js
|
||||
const { message } = pluginConfig;
|
||||
|
||||
if (message.length) {
|
||||
//...
|
||||
}
|
||||
```
|
||||
|
||||
## Supporting Environment Variables
|
||||
|
||||
Similar to `options`, environment variables exist to allow users to pass tokens and set special URLs. These are set on the `context` object instead of the `pluginConfig` object. Let's say we wanted to check for `GITHUB_TOKEN` in the environment because we want to post to GitHub on the user's behalf. To do this, we can add the following to our `verify` command:
|
||||
|
||||
```js
|
||||
const { env } = context;
|
||||
|
||||
if (env.GITHUB_TOKEN) {
|
||||
//...
|
||||
}
|
||||
```
|
||||
@@ -91,3 +91,7 @@
|
||||
- [semantic-release-github-pages](https://github.com/qiwi/semantic-release-gh-pages-plugin)
|
||||
- `verifyConditions`: Verify the presence of the auth token set via environment variables.
|
||||
- `publish`: Pushes commit to the documentation branch.
|
||||
- [leiningen-semantic-release](https://github.com/NoxHarmonium/leiningen-semantic-release)
|
||||
- `verifyConditions`: Checks the project.clj is syntactically valid.
|
||||
- `prepare`: Update the project.clj version and package the output jar file.
|
||||
- `publish`: Publish the jar (and generated Maven metadata) to a maven repository (or clojars).
|
||||
|
||||
@@ -5,3 +5,17 @@
|
||||
- [@semantic-release/gitlab-config](https://github.com/semantic-release/gitlab-config) - semantic-release shareable configuration for GitLab
|
||||
|
||||
## Community configurations
|
||||
- [@jedmao/semantic-release-npm-github-config](https://github.com/jedmao/semantic-release-npm-github-config)
|
||||
- Provides an informative [git](https://github.com/semantic-release/git) commit message for the release commit that does not trigger continuous integration and conforms to the [conventional commits specification](https://www.conventionalcommits.org/) (e.g., "chore(release): 1.2.3 [skip ci]\n\nnotes").
|
||||
- Creates a tarball that gets uploaded with each [GitHub release](https://github.com/semantic-release/github).
|
||||
- Publishes the same tarball to [npm](https://github.com/semantic-release/npm).
|
||||
- Commits the version change in `package.json`.
|
||||
- Creates or updates a [changelog](https://github.com/semantic-release/changelog) file.
|
||||
- [semantic-release-npm-github-publish](https://github.com/oleg-koval/semantic-release-npm-github-publish)
|
||||
- Based on [angular preset](https://github.com/conventional-changelog/conventional-changelog/tree/master/packages/conventional-changelog-angular).
|
||||
- Adds more keywords for the `chore` **PATCH** release.
|
||||
- Generates or updates a [changelog](https://github.com/semantic-release/changelog) file including all **PATCH** keywords (not included in default angular package).
|
||||
- Updates GitHub release with release-notes.
|
||||
- Bumps a version in package.json.
|
||||
- Publishes the new version to [NPM](https://npmjs.org).
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
- [CircleCI 2.0 workflows](circleci-workflows.md)
|
||||
- [Travis CI](travis.md)
|
||||
- [GitLab CI](gitlab-ci.md)
|
||||
- [GitHub Actions](github-actions.md)
|
||||
|
||||
## Git hosted services
|
||||
- [Git authentication with SSH keys](git-auth-ssh-keys.md)
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
# Using semantic-release with [GitHub Actions](https://help.github.com/en/categories/automating-your-workflow-with-github-actions)
|
||||
|
||||
## Environment variables
|
||||
|
||||
The [Authentication](../usage/ci-configuration.md#authentication) environment variables can be configured with [Secret Variables](https://help.github.com/en/articles/virtual-environments-for-github-actions#creating-and-using-secrets-encrypted-variables).
|
||||
|
||||
In this example an [`NPM_TOKEN`](https://docs.npmjs.com/creating-and-viewing-authentication-tokens) is required to publish a package to the npm registry. GitHub Actions [automatically populate](https://help.github.com/en/articles/virtual-environments-for-github-actions#github_token-secret) a [`GITHUB_TOKEN`](https://help.github.com/en/articles/creating-a-personal-access-token-for-the-command-line) environment variable which can be used in Workflows.
|
||||
|
||||
## Node project configuration
|
||||
|
||||
[GitHub Actions](https://github.com/features/actions) support [Workflows](https://help.github.com/en/articles/configuring-workflows), allowing to run tests on multiple Node versions and publish a release only when all test pass.
|
||||
|
||||
**Note**: The publish pipeline must run on [Node version >= 8.16](../support/FAQ.md#why-does-semantic-release-require-node-version--816).
|
||||
|
||||
### `.github/workflows/release.yml` configuration for Node projects
|
||||
|
||||
The following is a minimal configuration for [`semantic-release`](https://github.com/semantic-release/semantic-release) with a build running on Node 12 when a new commit is pushed to a `master` branch. See [Configuring a Workflow](https://help.github.com/en/articles/configuring-a-workflow) for additional configuration options.
|
||||
|
||||
```yaml
|
||||
name: Release
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
jobs:
|
||||
release:
|
||||
name: Release
|
||||
runs-on: ubuntu-18.04
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v1
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 12
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
- name: Release
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
run: npx semantic-release
|
||||
```
|
||||
|
||||
## Pushing `package.json` changes to a `master` branch
|
||||
|
||||
To keep `package.json` updated in the `master` branch, [`@semantic-release/git`](https://github.com/semantic-release/git) plugin can be used.
|
||||
|
||||
**Note**: Automatically populated `GITHUB_TOKEN` cannot be used if branch protection is enabled for the target branch. It is **not** advised to mitigate this limitation by overriding an automatically populated `GITHUB_TOKEN` variable with a [Personal Access Tokens](https://help.github.com/en/github/authenticating-to-github/creating-a-personal-access-token-for-the-command-line), as it poses a security risk. Since Secret Variables are available for Workflows triggered by any branch, it becomes a potential vector of attack, where a Workflow triggered from a non-protected branch can expose and use a token with elevated permissions, yielding branch protection insignificant. One can use Personal Access Tokens in trusted environments, where all developers should have the ability to perform administrative actions in the given repository and branch protection is enabled solely for convenience purposes, to remind about required reviews or CI checks.
|
||||
|
||||
## Trigger semantic-release on demand
|
||||
|
||||
There is a way to trigger semantic-relase on demand. Use [`repository_dispatch`](https://help.github.com/en/articles/events-that-trigger-workflows#external-events-repository_dispatch) event to have control on when to generate a release by making an HTTP request, e.g.:
|
||||
|
||||
```yaml
|
||||
name: Release
|
||||
on:
|
||||
repository_dispatch:
|
||||
types: [semantic-release]
|
||||
jobs:
|
||||
# ...
|
||||
```
|
||||
|
||||
To trigger a release, call (with a [Personal Access Tokens](https://help.github.com/en/github/authenticating-to-github/creating-a-personal-access-token-for-the-command-line) stored in `GITHUB_TOKEN` environment variable):
|
||||
|
||||
```
|
||||
$ curl -v -H "Accept: application/vnd.github.everest-preview+json" -H "Authorization: token ${GITHUB_TOKEN}" https://api.github.com/repos/[org-name-or-username]/[repository]/dispatches -d '{ "event_type": "semantic-release" }'
|
||||
```
|
||||
@@ -2,13 +2,15 @@
|
||||
|
||||
## Environment variables
|
||||
|
||||
The [Authentication](../usage/ci-configuration.md#authentication) environment variables can be configured with [Secret variables](https://docs.gitlab.com/ce/ci/variables/README.html#secret-variables).
|
||||
The [Authentication](../usage/ci-configuration.md#authentication) environment variables can be configured with [Protected variables](https://docs.gitlab.com/ce/ci/variables/README.html#protected-environment-variables).
|
||||
|
||||
**Note**: Make sure to configure your release branch as [protected](https://docs.gitlab.com/ce/user/project/protected_branches.html) in order for the CI/CD build to access the protected variables.
|
||||
|
||||
## Node project configuration
|
||||
|
||||
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--83).
|
||||
**Note**: The publish pipeline must run a [Node >= 8.16 version](../support/FAQ.md#why-does-semantic-release-require-node-version--816).
|
||||
|
||||
### `.gitlab-ci.yml` configuration for Node projects
|
||||
|
||||
|
||||
+10
-10
@@ -4,7 +4,7 @@
|
||||
|
||||
**semantic-release** takes care of updating the `package.json`’s version before publishing to [npm](https://www.npmjs.com).
|
||||
|
||||
By default, only the published package will contains the version, which is the only place where it is *really* required, but the updated `package.json` will not be pushed to the Git repository
|
||||
By default, only the published package will contain the version, which is the only place where it is *really* required, but the updated `package.json` will not be pushed to the Git repository
|
||||
|
||||
However, the [`@semantic-release/git`](https://github.com/semantic-release/git) plugin can be used to push the updated `package.json` as well as other files to the Git repository.
|
||||
|
||||
@@ -38,7 +38,7 @@ Yes with the [dry-run options](../usage/configuration.md#dryrun) which prints to
|
||||
|
||||
## Can I use semantic-release with Yarn?
|
||||
|
||||
If you are using a [local](../usage/installation.md#local-installation) **semantic-release** installation and run multiple CI jobs with different versions, the `yarn install` command will fail on jobs running with Node < 8 as **semantic-release** requires [Node >= 8.3](#why-does-semantic-release-require-node-version--83) and specifies it in its `package.json`s [`engines`](https://docs.npmjs.com/files/package.json#engines) key.
|
||||
If you are using a [local](../usage/installation.md#local-installation) **semantic-release** installation and run multiple CI jobs with different versions, the `yarn install` command will fail on jobs running with Node < 8 as **semantic-release** requires [Node >= 8.16](#why-does-semantic-release-require-node-version--816) and specifies it in its `package.json`s [`engines`](https://docs.npmjs.com/files/package.json#engines) key.
|
||||
|
||||
The recommended solution is to use the [Yarn](https://yarnpkg.com) [--ignore-engines](https://yarnpkg.com/en/docs/cli/install#toc-yarn-install-ignore-engines) option to install the project dependencies on the CI environment, so Yarn will ignore the **semantic-release**'s `engines` key:
|
||||
|
||||
@@ -48,7 +48,7 @@ $ yarn install --ignore-engines
|
||||
|
||||
**Note**: Several CI services use Yarn by default if your repository contains a `yarn.lock` file. So you should override the install step to specify `yarn install --ignore-engines`.
|
||||
|
||||
Alternatively you can use a [global](../usage/installation.md#global-installation) **semantic-release** installation and make sure to install and run the `semantic-release` command only in a CI jobs running with Node >= 8.3.
|
||||
Alternatively you can use a [global](../usage/installation.md#global-installation) **semantic-release** installation and make sure to install and run the `semantic-release` command only in a CI jobs running with Node >= 8.16.
|
||||
|
||||
If your CI environment provides [nvm](https://github.com/creationix/nvm) you can switch to Node 8 before installing and running the `semantic-release` command:
|
||||
|
||||
@@ -73,7 +73,7 @@ Yes, **semantic-release** is a Node CLI application but it can be used to publis
|
||||
To publish a non-Node package (without a `package.json`) you would need to:
|
||||
- Use a [global](../usage/installation.md#global-installation) **semantic-release** installation
|
||||
- Set **semantic-release** [options](../usage/configuration.md#options) via [CLI arguments or rc file](../usage/configuration.md#configuration)
|
||||
- Make sure your CI job executing the `semantic-release` command has access to [Node >= 8](#why-does-semantic-release-require-node-version--83) to execute the `semantic-release` command
|
||||
- Make sure your CI job executing the `semantic-release` command has access to [Node >= 8.16](#why-does-semantic-release-require-node-version--816) to execute the `semantic-release` command
|
||||
|
||||
See the [CI configuration recipes](../recipes/README.md#ci-configurations) for more details on specific CI environments.
|
||||
|
||||
@@ -141,11 +141,11 @@ See the [`@semantic-release/npm`](https://github.com/semantic-release/npm#semant
|
||||
|
||||
If you have introduced a breaking bug in a release you have 2 options:
|
||||
- If you have a fix immediately ready, commit and push it (or merge it via a pull request) to the release branch
|
||||
- Otherwise [revert the commit](https://git-scm.com/docs/git-revert) that introduced the bug and push the revert commit (or merge it via a pull request) to the release branch
|
||||
- Otherwise, [revert the commit](https://git-scm.com/docs/git-revert) that introduced the bug and push the revert commit (or merge it via a pull request) to the release branch
|
||||
|
||||
In both cases **semantic-release** will publish a new release, so your package users' will get the fixed/reverted version.
|
||||
In both cases **semantic-release** will publish a new release, so your package users will get the fixed/reverted version.
|
||||
|
||||
Depending on the package manager you are using, you might be able to un-publish or deprecate a release, in order to prevent users from downloading it by accident. For example npm allows you to [un-publish](https://docs.npmjs.com/cli/unpublish) [within 72 hours](https://www.npmjs.com/policies/unpublish) after releasing. You may also [deprecate](https://docs.npmjs.com/cli/deprecate) a release if you would rather avoid un-publishing.
|
||||
Depending on the package manager you are using, you might be able to un-publish or deprecate a release, in order to prevent users from downloading it by accident. For example, npm allows you to [un-publish](https://docs.npmjs.com/cli/unpublish) [within 72 hours](https://www.npmjs.com/policies/unpublish) after release. You may also [deprecate](https://docs.npmjs.com/cli/deprecate) a release if you would rather avoid un-publishing.
|
||||
|
||||
In any case **do not remove the Git tag associated with the buggy version**, otherwise **semantic-release** will later try to republish that version. Publishing a version after un-publishing is not supported by most package managers.
|
||||
|
||||
@@ -232,11 +232,11 @@ See [“Introduction to SemVer” - Irina Gebauer](https://blog.greenkeeper.io/i
|
||||
|
||||
In addition the [verify conditions step](../../README.md#release-steps) verifies that all necessary conditions for proceeding with a release are met, and a new release will be performed [only if all your tests pass](../usage/ci-configuration.md#run-semantic-release-only-after-all-tests-succeeded).
|
||||
|
||||
## Why does semantic-release require Node version >= 8.3?
|
||||
## Why does semantic-release require Node version >= 8.16?
|
||||
|
||||
**semantic-release** is written using the latest [ECMAScript 2017](https://www.ecma-international.org/publications/standards/Ecma-262.htm) features, without transpilation which **requires Node version 8.3 or higher**.
|
||||
**semantic-release** is written using the latest [ECMAScript 2017](https://www.ecma-international.org/publications/standards/Ecma-262.htm) features, without transpilation which **requires Node version 8.16 or higher**.
|
||||
|
||||
See [Node version requirement](../support/node-version.md#node-version-requirement) for more details and solutions.
|
||||
See [Node version requirement](./node-version.md#node-version-requirement) for more details and solutions.
|
||||
|
||||
## What is npx?
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Node version requirement
|
||||
|
||||
**semantic-release** is written using the latest [ECMAScript 2017](https://www.ecma-international.org/publications/standards/Ecma-262.htm) features, without transpilation which requires **requires Node version 8.3 or higher**.
|
||||
**semantic-release** is written using the latest [ECMAScript 2017](https://www.ecma-international.org/publications/standards/Ecma-262.htm) features, without transpilation which requires **requires Node version 8.16 or higher**.
|
||||
|
||||
**semantic-release** is meant to be used in a CI environment as a development support tool, not as a production dependency. Therefore the only constraint is to run the `semantic-release` in a CI environment providing Node 8 or higher.
|
||||
|
||||
@@ -8,9 +8,9 @@ See our [Node Support Policy](node-support-policy.md) for our long-term promise
|
||||
|
||||
## Recommended solution
|
||||
|
||||
### Run at least one CI job with Node >= 8.3
|
||||
### Run at least one CI job with Node >= 8.16
|
||||
|
||||
The recommended approach is to run the `semantic-release` command from a CI job running on Node 8.3 or higher. This can either be a job used by your project to test on Node >= 8.3 or a dedicated job for the release steps.
|
||||
The recommended approach is to run the `semantic-release` command from a CI job running on Node 8.16 or higher. This can either be a job used by your project to test on Node >= 8.16 or a dedicated job for the release steps.
|
||||
|
||||
See [CI configuration](../usage/ci-configuration.md) and [CI configuration recipes](../recipes/README.md#ci-configurations) for more details.
|
||||
|
||||
|
||||
@@ -2,12 +2,23 @@
|
||||
|
||||
## Run `semantic-release` only after all tests succeeded
|
||||
|
||||
The `semantic-release` command must be executed only after all the tests in the CI build pass. If the build runs multiple jobs (for example to test on multiple Operating Systems or Node versions) the CI has to be configured to guarantee that the `semantic-release` command is executed only after all jobs are successful. This can be achieved with [Travis Build Stages](https://docs.travis-ci.com/user/build-stages), [CircleCI Workflows](https://circleci.com/docs/2.0/workflows), [Codeship Deployment Pipelines](https://documentation.codeship.com/basic/builds-and-configuration/deployment-pipelines), [GitLab Pipelines](https://docs.gitlab.com/ee/ci/pipelines.html#introduction-to-pipelines-and-jobs), [Codefresh Pipelines](https://codefresh.io/docs/docs/configure-ci-cd-pipeline/introduction-to-codefresh-pipelines), [Wercker Workflows](http://devcenter.wercker.com/docs/workflows) or [GoCD Pipelines](https://docs.gocd.org/current/introduction/concepts_in_go.html#pipeline).
|
||||
The `semantic-release` command must be executed only after all the tests in the CI build pass. If the build runs multiple jobs (for example to test on multiple Operating Systems or Node versions) the CI has to be configured to guarantee that the `semantic-release` command is executed only after all jobs are successful.
|
||||
Here is a few example of the CI services that can be used to achieve this:
|
||||
- [Travis Build Stages](https://docs.travis-ci.com/user/build-stages)
|
||||
- [CircleCI Workflows](https://circleci.com/docs/2.0/workflows)
|
||||
- [GitHub Actions](https://github.com/features/actions)
|
||||
- [Codeship Deployment Pipelines](https://documentation.codeship.com/basic/builds-and-configuration/deployment-pipelines)
|
||||
- [GitLab Pipelines](https://docs.gitlab.com/ee/ci/pipelines.html#introduction-to-pipelines-and-jobs)
|
||||
- [Codefresh Pipelines](https://codefresh.io/docs/docs/configure-ci-cd-pipeline/introduction-to-codefresh-pipelines)
|
||||
- [Wercker Workflows](http://devcenter.wercker.com/docs/workflows)
|
||||
- [GoCD Pipelines](https://docs.gocd.org/current/introduction/concepts_in_go.html#pipeline).
|
||||
|
||||
See [CI configuration recipes](../recipes/README.md#ci-configurations) for more details.
|
||||
|
||||
## Authentication
|
||||
|
||||
### Push access to the remote repository
|
||||
|
||||
**semantic-release** requires push access to the project Git repository in order to create [Git tags](https://git-scm.com/book/en/v2/Git-Basics-Tagging). The Git authentication can be set with one of the following environment variables:
|
||||
|
||||
| Variable | Description |
|
||||
@@ -19,6 +30,8 @@ See [CI configuration recipes](../recipes/README.md#ci-configurations) for more
|
||||
|
||||
Alternatively the Git authentication can be set up via [SSH keys](../recipes/git-auth-ssh-keys.md).
|
||||
|
||||
### Authentication for plugins
|
||||
|
||||
Most **semantic-release** [plugins](plugins.md) require setting up authentication in order to publish to a package manager registry. The default [@semantic-release/npm](https://github.com/semantic-release/npm#environment-variables) and [@semantic-release/github](https://github.com/semantic-release/github#environment-variables) plugins require the following environment variables:
|
||||
|
||||
| Variable | Description |
|
||||
@@ -31,3 +44,5 @@ See each plugin's documentation for the environment variables required.
|
||||
The authentication token/credentials have to be made available in the CI service via environment variables.
|
||||
|
||||
See [CI configuration recipes](../recipes/README.md#ci-configurations) for more details on how to configure environment variables in your CI service.
|
||||
|
||||
**Note**: The environment variables `GH_TOKEN`, `GITHUB_TOKEN`, `GL_TOKEN` and `GITLAB_TOKEN` can be used for both the Git authentication and the API authentication required by [@semantic-release/github](https://github.com/semantic-release/github) and [@semantic-release/gitlab](https://github.com/semantic-release/gitlab).
|
||||
|
||||
+10
-18
@@ -1,17 +1,17 @@
|
||||
# Configuration
|
||||
|
||||
**semantic-release** configuration consists of:
|
||||
- Git repository options ([URL](#repositoryurl), [release branch](#branch) and [tag format](#tagformat))
|
||||
- [plugins](#plugins) definition
|
||||
- run mode ([debug](#debug), [dry run](#dryrun) and [local (no CI)](#ci))
|
||||
- Git repository ([URL](#repositoryurl) and options [release branch](#branch) and [tag format](#tagformat))
|
||||
- Plugins [declaration](#plugins) and options
|
||||
- Run mode ([debug](#debug), [dry run](#dryrun) and [local (no CI)](#ci))
|
||||
|
||||
All of these options can be configured directly or by extending a [shareable configuration](shareable-configurations.md).
|
||||
All of these options can be configured through config file, CLI arguments or by extending a [shareable configuration](shareable-configurations.md).
|
||||
|
||||
Additionally, metadata of Git tags generated by **semantic-release** can be customized via standard [Git environment variables](#git-environment-variables).
|
||||
|
||||
## Configuration file
|
||||
|
||||
**semantic-release**’s [options](#options), mode and [plugins](plugins.md) can be set via:
|
||||
**semantic-release**’s [options](#options), mode and [plugins](plugins.md) can be set via either:
|
||||
- A `.releaserc` file, written in YAML or JSON, with optional extensions: .`yaml`/`.yml`/`.json`/`.js`
|
||||
- A `release.config.js` file that exports an object
|
||||
- A `release` key in the project's `package.json` file
|
||||
@@ -20,8 +20,7 @@ Alternatively, some options can be set via CLI arguments.
|
||||
|
||||
The following three examples are the same.
|
||||
|
||||
Via `release` key in the project's `package.json` file:
|
||||
|
||||
- Via `release` key in the project's `package.json` file:
|
||||
```json
|
||||
{
|
||||
"release": {
|
||||
@@ -29,23 +28,15 @@ Via `release` key in the project's `package.json` file:
|
||||
}
|
||||
}
|
||||
```
|
||||
```bash
|
||||
$ semantic-release
|
||||
```
|
||||
|
||||
Via `.releaserc` file:
|
||||
|
||||
- Via `.releaserc` file:
|
||||
```json
|
||||
{
|
||||
"branch": "next"
|
||||
}
|
||||
```
|
||||
```bash
|
||||
$ semantic-release
|
||||
```
|
||||
|
||||
Via CLI argument:
|
||||
|
||||
- Via CLI argument:
|
||||
```bash
|
||||
$ semantic-release --branch next
|
||||
```
|
||||
@@ -146,7 +137,8 @@ Output debugging information. This can also be enabled by setting the `DEBUG` en
|
||||
|
||||
## Existing version tags
|
||||
|
||||
**semantic-release** uses [Git tags](https://git-scm.com/book/en/v2/Git-Basics-Tagging) to determine the commits added since the last release. If a release has been published before setting up **semantic-release** you must make sure the most recent commit included in the last published release is in the [release branch](#branch) history and is tagged with the version released, formatted according to the [tag format](#tagformat) configured (defaults to `vx.y.z`).
|
||||
**semantic-release** uses [Git tags](https://git-scm.com/book/en/v2/Git-Basics-Tagging) to determine the commits added since the last release.
|
||||
If a release has been published before setting up **semantic-release** you must make sure the most recent commit included in the last published release is in the [release branch](#branch) history and is tagged with the version released, formatted according to the [tag format](#tagformat) configured (defaults to `vx.y.z`).
|
||||
|
||||
If the previous releases were published with [`npm publish`](https://docs.npmjs.com/cli/publish) this should already be the case.
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
# Getting started
|
||||
|
||||
In order to use **semantic-release** you must follow these steps:
|
||||
- [Install](./installation.md#installation) **semantic-release** in your project
|
||||
- Configure your Continuous Integration service to [run **semantic-release**](./ci-configuration.md#run-semantic-release-only-after-all-tests-succeeded)
|
||||
- Configure your Git repository and package manager repository [authentication](ci-configuration.md#authentication) in your Continuous Integration service
|
||||
- Configure **semantic-release** [options and plugins](./configuration.md#configuration)
|
||||
1. [Install](./installation.md#installation) **semantic-release** in your project
|
||||
2. Configure your Continuous Integration service to [run **semantic-release**](./ci-configuration.md#run-semantic-release-only-after-all-tests-succeeded)
|
||||
3. Configure your Git repository and package manager repository [authentication](ci-configuration.md#authentication) in your Continuous Integration service
|
||||
4. Configure **semantic-release** [options and plugins](./configuration.md#configuration)
|
||||
|
||||
Alternatively those steps can be easily done with the [**semantic-release** interactive CLI](https://github.com/semantic-release/cli):
|
||||
|
||||
|
||||
@@ -24,6 +24,6 @@ For other type of projects we recommend installing **semantic-release** directly
|
||||
$ 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@15`, or `npm install -g semantic-release@15`). 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@15`). 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.
|
||||
|
||||
+14
-11
@@ -19,22 +19,25 @@ A plugin is a npm module that can implement one or more of the following steps:
|
||||
|
||||
## Plugins installation
|
||||
|
||||
These five plugins are already part of **semantic-release** and don't have to be installed separately:
|
||||
### Default plugins
|
||||
|
||||
These four plugins are already part of **semantic-release** and don't have to be installed separately:
|
||||
```
|
||||
"@semantic-release/commit-analyzer"
|
||||
"@semantic-release/error"
|
||||
"@semantic-release/github"
|
||||
"@semantic-release/npm"
|
||||
"@semantic-release/commit-analyzer"
|
||||
"@semantic-release/github"
|
||||
"@semantic-release/npm"
|
||||
"@semantic-release/release-notes-generator"
|
||||
```
|
||||
|
||||
### Additional plugins
|
||||
|
||||
[Additional plugins](../extending/plugins-list.md) have to be installed via npm:
|
||||
|
||||
```bash
|
||||
$ npm install @semantic-release/git @semantic-release/changelog -D
|
||||
```
|
||||
|
||||
## Plugins configuration
|
||||
## Plugins declaration and execution order
|
||||
|
||||
Each plugin must be configured with the [`plugins` options](./configuration.md#plugins) by specifying the list of plugins by npm module name.
|
||||
|
||||
@@ -44,9 +47,9 @@ Each plugin must be configured with the [`plugins` options](./configuration.md#p
|
||||
}
|
||||
```
|
||||
|
||||
## Plugin ordering
|
||||
**Note:** If the `plugins` option is defined, it overrides the default plugin list, rather than merging with it.
|
||||
|
||||
For each [release step](../../README.md#release-steps) the plugins that implement that step will be executed in the order in which the are defined.
|
||||
For each [release step](../../README.md#release-steps) the plugins that implement that step will be executed in the order in which they are defined.
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -66,11 +69,11 @@ With this configuration **semantic-release** will:
|
||||
- execute the `generateNotes` implementation of `@semantic-release/release-notes-generator`
|
||||
- execute the `publish` implementation of `@semantic-release/npm`
|
||||
|
||||
## Plugin options
|
||||
## Plugin options configuration
|
||||
|
||||
A plugin options can specified by wrapping the name and an options object in an array. Options configured this way will be passed only to that specific plugin.
|
||||
A plugin configuration can be specified by wrapping the name and an options object in an array. Options configured this way will be passed only to that specific plugin.
|
||||
|
||||
Global plugin options can defined at the root of the **semantic-release** configuration object. Options configured this way will be passed to all plugins.
|
||||
Global plugin configuration can be defined at the root of the **semantic-release** configuration object. Options configured this way will be passed to all plugins.
|
||||
|
||||
```json
|
||||
{
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
/* eslint require-atomic-updates: off */
|
||||
|
||||
const {template, pick} = require('lodash');
|
||||
const marked = require('marked');
|
||||
const TerminalRenderer = require('marked-terminal');
|
||||
@@ -21,7 +23,7 @@ marked.setOptions({renderer: new TerminalRenderer()});
|
||||
|
||||
async function run(context, plugins) {
|
||||
const {cwd, env, options, logger} = context;
|
||||
const {isCi, branch: ciBranch, isPr} = envCi({env, cwd});
|
||||
const {isCi, branch: ciBranch, isPr} = context.envCi;
|
||||
|
||||
if (!isCi && !options.dryRun && !options.noCi) {
|
||||
logger.warn('This run was not triggered in a known CI environment, running in dry-run mode.');
|
||||
@@ -46,9 +48,7 @@ async function run(context, plugins) {
|
||||
|
||||
if (ciBranch !== options.branch) {
|
||||
logger.log(
|
||||
`This test run was triggered on the branch ${ciBranch}, while semantic-release is configured to only publish from ${
|
||||
options.branch
|
||||
}, therefore a new version won’t be published.`
|
||||
`This test run was triggered on the branch ${ciBranch}, while semantic-release is configured to only publish from ${options.branch}, therefore a new version won’t be published.`
|
||||
);
|
||||
return false;
|
||||
}
|
||||
@@ -65,7 +65,7 @@ async function run(context, plugins) {
|
||||
try {
|
||||
await verifyAuth(options.repositoryUrl, options.branch, {cwd, env});
|
||||
} catch (error) {
|
||||
if (!(await isBranchUpToDate(options.branch, {cwd, env}))) {
|
||||
if (!(await isBranchUpToDate(options.repositoryUrl, options.branch, {cwd, env}))) {
|
||||
logger.log(
|
||||
`The local branch ${options.branch} is behind the remote one, therefore a new version won't be published.`
|
||||
);
|
||||
@@ -75,7 +75,7 @@ async function run(context, plugins) {
|
||||
throw error;
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error(`The command "${error.cmd}" failed with the error message ${error.stderr}.`);
|
||||
logger.error(`The command "${error.command}" failed with the error message ${error.stderr}.`);
|
||||
throw getError('EGITNOPERMISSION', {options});
|
||||
}
|
||||
|
||||
@@ -160,7 +160,13 @@ module.exports = async (opts = {}, {cwd = process.cwd(), env = process.env, stdo
|
||||
{silent: false, streams: [process.stdout, process.stderr, stdout, stderr].filter(Boolean)},
|
||||
hideSensitive(env)
|
||||
);
|
||||
const context = {cwd, env, stdout: stdout || process.stdout, stderr: stderr || process.stderr};
|
||||
const context = {
|
||||
cwd,
|
||||
env,
|
||||
stdout: stdout || process.stdout,
|
||||
stderr: stderr || process.stderr,
|
||||
envCi: envCi({env, cwd}),
|
||||
};
|
||||
context.logger = getLogger(context);
|
||||
context.logger.log(`Running ${pkg.name} version ${pkg.version}`);
|
||||
try {
|
||||
|
||||
@@ -27,16 +27,17 @@ Please make sure to add the \`repositoryUrl\` to the [semantic-release configura
|
||||
)}).`,
|
||||
}),
|
||||
EGITNOPERMISSION: ({options}) => ({
|
||||
message: 'The push permission to the Git repository is required.',
|
||||
message: 'Cannot push to the Git repository.',
|
||||
details: `**semantic-release** cannot push the version tag to the branch \`${
|
||||
options.branch
|
||||
}\` on remote Git repository with URL \`${options.repositoryUrl}\`.
|
||||
}\` on the remote Git repository with URL \`${options.repositoryUrl}\`.
|
||||
|
||||
Please refer to the [authentication configuration documentation](${linkify(
|
||||
'docs/usage/ci-configuration.md#authentication'
|
||||
)}) to configure the Git credentials on your CI environment and make sure the [repositoryUrl](${linkify(
|
||||
'docs/usage/configuration.md#repositoryurl'
|
||||
)}) is configured with a [valid Git URL](https://git-scm.com/book/en/v2/Git-on-the-Server-The-Protocols).`,
|
||||
This can be caused by:
|
||||
- a misconfiguration of the [repositoryUrl](${linkify('docs/usage/configuration.md#repositoryurl')}) option
|
||||
- the repository being unavailable
|
||||
- or missing push permission for the user configured via the [Git credentials on your CI environment](${linkify(
|
||||
'docs/usage/ci-configuration.md#authentication'
|
||||
)})`,
|
||||
}),
|
||||
EINVALIDTAGFORMAT: ({tagFormat}) => ({
|
||||
message: 'Invalid `tagFormat` option.',
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
/* eslint require-atomic-updates: off */
|
||||
|
||||
const {isString, isPlainObject} = require('lodash');
|
||||
const {getGitHead} = require('../git');
|
||||
const hideSensitive = require('../hide-sensitive');
|
||||
|
||||
+5
-3
@@ -17,9 +17,11 @@ module.exports = async ({cwd, env, lastRelease: {gitHead}, logger}) => {
|
||||
}
|
||||
|
||||
Object.assign(gitLogParser.fields, {hash: 'H', message: 'B', gitTags: 'd', committerDate: {key: 'ci', type: Date}});
|
||||
const commits = (await getStream.array(
|
||||
gitLogParser.parse({_: `${gitHead ? gitHead + '..' : ''}HEAD`}, {cwd, env: {...process.env, ...env}})
|
||||
)).map(commit => {
|
||||
const commits = (
|
||||
await getStream.array(
|
||||
gitLogParser.parse({_: `${gitHead ? gitHead + '..' : ''}HEAD`}, {cwd, env: {...process.env, ...env}})
|
||||
)
|
||||
).map(commit => {
|
||||
commit.message = commit.message.trim();
|
||||
commit.gitTags = commit.gitTags.trim();
|
||||
return commit;
|
||||
|
||||
+3
-3
@@ -1,6 +1,6 @@
|
||||
const {castArray, pickBy, isNil, isString, isPlainObject} = require('lodash');
|
||||
const readPkgUp = require('read-pkg-up');
|
||||
const cosmiconfig = require('cosmiconfig');
|
||||
const {cosmiconfig} = require('cosmiconfig');
|
||||
const resolveFrom = require('resolve-from');
|
||||
const debug = require('debug')('semantic-release:config');
|
||||
const {repoUrl} = require('./git');
|
||||
@@ -85,6 +85,6 @@ module.exports = async (context, opts) => {
|
||||
};
|
||||
|
||||
async function pkgRepoUrl(opts) {
|
||||
const {package: pkg} = (await readPkgUp(opts)) || {};
|
||||
return pkg && (isPlainObject(pkg.repository) ? pkg.repository.url : pkg.repository);
|
||||
const {packageJson} = (await readPkgUp(opts)) || {};
|
||||
return packageJson && (isPlainObject(packageJson.repository) ? packageJson.repository.url : packageJson.repository);
|
||||
}
|
||||
|
||||
@@ -41,23 +41,21 @@ module.exports = async ({cwd, env, options: {repositoryUrl, branch}}) => {
|
||||
// Test if push is allowed without transforming the URL (e.g. is ssh keys are set up)
|
||||
try {
|
||||
await verifyAuth(repositoryUrl, branch, {cwd, env});
|
||||
} catch (error) {
|
||||
} catch (_) {
|
||||
const envVar = Object.keys(GIT_TOKENS).find(envVar => !isNil(env[envVar]));
|
||||
const gitCredentials = `${GIT_TOKENS[envVar] || ''}${env[envVar] || ''}`;
|
||||
|
||||
console.log(`-------- envVar -------- `);
|
||||
console.log(envVar);
|
||||
console.log(`-------- GIT_TOKENS[envVar] -------- `);
|
||||
console.log(GIT_TOKENS[envVar]);
|
||||
console.log(`-------- env[envVar].substr(5) -------- `);
|
||||
console.log((env[envVar] || '').substr(5));
|
||||
|
||||
if (gitCredentials) {
|
||||
// If credentials are set via environment variables, convert the URL to http/https and add basic auth, otherwise return `repositoryUrl` as is
|
||||
const [match, auth, host, path] = /^(?!.+:\/\/)(?:(.*)@)?(.*?):(.*)$/.exec(repositoryUrl) || [];
|
||||
const {port, hostname, ...parsed} = parse(
|
||||
match ? `ssh://${auth ? `${auth}@` : ''}${host}/${path}` : repositoryUrl
|
||||
);
|
||||
|
||||
return format({
|
||||
...parse(match ? `ssh://${auth ? `${auth}@` : ''}${host}/${path}` : repositoryUrl),
|
||||
...parsed,
|
||||
auth: gitCredentials,
|
||||
host: `${hostname}${protocol === 'ssh:' ? '' : port ? `:${port}` : ''}`,
|
||||
protocol: protocol && /http[^s]/.test(protocol) ? 'http' : 'https',
|
||||
});
|
||||
}
|
||||
|
||||
+12
-11
@@ -11,7 +11,7 @@ const debug = require('debug')('semantic-release:git');
|
||||
*/
|
||||
async function getTagHead(tagName, execaOpts) {
|
||||
try {
|
||||
return await execa.stdout('git', ['rev-list', '-1', tagName], execaOpts);
|
||||
return (await execa('git', ['rev-list', '-1', tagName], execaOpts)).stdout;
|
||||
} catch (error) {
|
||||
debug(error);
|
||||
}
|
||||
@@ -26,7 +26,7 @@ async function getTagHead(tagName, execaOpts) {
|
||||
* @throws {Error} If the `git` command fails.
|
||||
*/
|
||||
async function getTags(execaOpts) {
|
||||
return (await execa.stdout('git', ['tag'], execaOpts))
|
||||
return (await execa('git', ['tag'], execaOpts)).stdout
|
||||
.split('\n')
|
||||
.map(tag => tag.trim())
|
||||
.filter(Boolean);
|
||||
@@ -45,7 +45,7 @@ async function isRefInHistory(ref, execaOpts) {
|
||||
await execa('git', ['merge-base', '--is-ancestor', ref, 'HEAD'], execaOpts);
|
||||
return true;
|
||||
} catch (error) {
|
||||
if (error.code === 1) {
|
||||
if (error.exitCode === 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ async function isRefInHistory(ref, execaOpts) {
|
||||
async function fetch(repositoryUrl, execaOpts) {
|
||||
try {
|
||||
await execa('git', ['fetch', '--unshallow', '--tags', repositoryUrl], execaOpts);
|
||||
} catch (error) {
|
||||
} catch (_) {
|
||||
await execa('git', ['fetch', '--tags', repositoryUrl], execaOpts);
|
||||
}
|
||||
}
|
||||
@@ -75,8 +75,8 @@ async function fetch(repositoryUrl, execaOpts) {
|
||||
*
|
||||
* @return {String} the sha of the HEAD commit.
|
||||
*/
|
||||
function getGitHead(execaOpts) {
|
||||
return execa.stdout('git', ['rev-parse', 'HEAD'], execaOpts);
|
||||
async function getGitHead(execaOpts) {
|
||||
return (await execa('git', ['rev-parse', 'HEAD'], execaOpts)).stdout;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -88,7 +88,7 @@ function getGitHead(execaOpts) {
|
||||
*/
|
||||
async function repoUrl(execaOpts) {
|
||||
try {
|
||||
return await execa.stdout('git', ['config', '--get', 'remote.origin.url'], execaOpts);
|
||||
return (await execa('git', ['config', '--get', 'remote.origin.url'], execaOpts)).stdout;
|
||||
} catch (error) {
|
||||
debug(error);
|
||||
}
|
||||
@@ -103,7 +103,7 @@ async function repoUrl(execaOpts) {
|
||||
*/
|
||||
async function isGitRepo(execaOpts) {
|
||||
try {
|
||||
return (await execa('git', ['rev-parse', '--git-dir'], execaOpts)).code === 0;
|
||||
return (await execa('git', ['rev-parse', '--git-dir'], execaOpts)).exitCode === 0;
|
||||
} catch (error) {
|
||||
debug(error);
|
||||
}
|
||||
@@ -161,7 +161,7 @@ async function push(repositoryUrl, execaOpts) {
|
||||
*/
|
||||
async function verifyTagName(tagName, execaOpts) {
|
||||
try {
|
||||
return (await execa('git', ['check-ref-format', `refs/tags/${tagName}`], execaOpts)).code === 0;
|
||||
return (await execa('git', ['check-ref-format', `refs/tags/${tagName}`], execaOpts)).exitCode === 0;
|
||||
} catch (error) {
|
||||
debug(error);
|
||||
}
|
||||
@@ -170,13 +170,14 @@ async function verifyTagName(tagName, execaOpts) {
|
||||
/**
|
||||
* Verify the local branch is up to date with the remote one.
|
||||
*
|
||||
* @param {String} repositoryUrl The remote repository URL.
|
||||
* @param {String} branch The repository branch for which to verify status.
|
||||
* @param {Object} [execaOpts] Options to pass to `execa`.
|
||||
*
|
||||
* @return {Boolean} `true` is the HEAD of the current local branch is the same as the HEAD of the remote branch, falsy otherwise.
|
||||
*/
|
||||
async function isBranchUpToDate(branch, execaOpts) {
|
||||
const remoteHead = await execa.stdout('git', ['ls-remote', '--heads', 'origin', branch], execaOpts);
|
||||
async function isBranchUpToDate(repositoryUrl, branch, execaOpts) {
|
||||
const {stdout: remoteHead} = await execa('git', ['ls-remote', '--heads', repositoryUrl, branch], execaOpts);
|
||||
try {
|
||||
return await isRefInHistory(remoteHead.match(/^(\w+)?/)[1], execaOpts);
|
||||
} catch (error) {
|
||||
|
||||
+22
-22
@@ -3,17 +3,20 @@
|
||||
"description": "Automated semver compliant package publishing",
|
||||
"version": "0.0.0-development",
|
||||
"author": "Stephan Bönnemann <stephan@boennemann.me> (http://boennemann.me)",
|
||||
"ava": {
|
||||
"files": [
|
||||
"test/**/*.test.js"
|
||||
],
|
||||
"helpers": [
|
||||
"test/helpers/**/*"
|
||||
]
|
||||
},
|
||||
"bin": {
|
||||
"semantic-release": "bin/semantic-release.js"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/semantic-release/semantic-release/issues"
|
||||
},
|
||||
"config": {
|
||||
"commitizen": {
|
||||
"path": "cz-conventional-changelog"
|
||||
}
|
||||
},
|
||||
"contributors": [
|
||||
"Gregor Martynus (https://twitter.com/gr2m)",
|
||||
"Pierre Vanduynslager (https://twitter.com/@pvdlg_)"
|
||||
@@ -25,51 +28,49 @@
|
||||
"@semantic-release/npm": "^5.0.5",
|
||||
"@semantic-release/release-notes-generator": "^7.1.2",
|
||||
"aggregate-error": "^3.0.0",
|
||||
"cosmiconfig": "^5.0.1",
|
||||
"cosmiconfig": "^6.0.0",
|
||||
"debug": "^4.0.0",
|
||||
"env-ci": "^3.0.0",
|
||||
"execa": "^1.0.0",
|
||||
"env-ci": "^4.0.0",
|
||||
"execa": "^3.2.0",
|
||||
"figures": "^3.0.0",
|
||||
"find-versions": "^3.0.0",
|
||||
"get-stream": "^5.0.0",
|
||||
"git-log-parser": "^1.2.0",
|
||||
"hook-std": "^2.0.0",
|
||||
"hosted-git-info": "^2.7.1",
|
||||
"lodash": "^4.17.4",
|
||||
"marked": "^0.6.0",
|
||||
"hosted-git-info": "^3.0.0",
|
||||
"lodash": "^4.17.15",
|
||||
"marked": "^0.7.0",
|
||||
"marked-terminal": "^3.2.0",
|
||||
"p-locate": "^4.0.0",
|
||||
"p-reduce": "^2.0.0",
|
||||
"read-pkg-up": "^6.0.0",
|
||||
"read-pkg-up": "^7.0.0",
|
||||
"resolve-from": "^5.0.0",
|
||||
"semver": "^6.0.0",
|
||||
"signale": "^1.2.1",
|
||||
"yargs": "^13.1.0"
|
||||
"yargs": "^15.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"ava": "^1.3.1",
|
||||
"clear-module": "^3.0.0",
|
||||
"ava": "^2.0.0",
|
||||
"clear-module": "^4.0.0",
|
||||
"codecov": "^3.0.0",
|
||||
"commitizen": "^3.0.0",
|
||||
"cz-conventional-changelog": "^2.0.0",
|
||||
"delay": "^4.0.0",
|
||||
"dockerode": "^2.5.2",
|
||||
"dockerode": "^3.0.0",
|
||||
"file-url": "^3.0.0",
|
||||
"fs-extra": "^8.0.0",
|
||||
"got": "^9.0.0",
|
||||
"js-yaml": "^3.10.0",
|
||||
"mockserver-client": "^5.1.1",
|
||||
"nock": "^10.0.0",
|
||||
"nock": "^11.1.0",
|
||||
"nyc": "^14.0.0",
|
||||
"p-retry": "^4.0.0",
|
||||
"proxyquire": "^2.0.0",
|
||||
"sinon": "^7.2.7",
|
||||
"stream-buffers": "^3.0.2",
|
||||
"tempy": "^0.3.0",
|
||||
"xo": "^0.24.0"
|
||||
"xo": "^0.25.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8.3"
|
||||
"node": ">=8.16"
|
||||
},
|
||||
"files": [
|
||||
"bin",
|
||||
@@ -117,7 +118,6 @@
|
||||
"url": "git+https://github.com/semantic-release/semantic-release.git"
|
||||
},
|
||||
"scripts": {
|
||||
"cm": "git-cz",
|
||||
"codecov": "codecov -f coverage/coverage-final.json",
|
||||
"lint": "xo",
|
||||
"pretest": "npm run lint",
|
||||
|
||||
+3
-3
@@ -174,7 +174,7 @@ test.serial('Display help', async t => {
|
||||
t.is(exitCode, 0);
|
||||
});
|
||||
|
||||
test.serial('Return error code and prints help if called with a command', async t => {
|
||||
test.serial('Return error exitCode and prints help if called with a command', async t => {
|
||||
const run = stub().resolves(true);
|
||||
const argv = ['', '', 'pre'];
|
||||
const cli = requireNoCache('../cli', {'.': run, process: {...process, argv}});
|
||||
@@ -186,7 +186,7 @@ test.serial('Return error code and prints help if called with a command', async
|
||||
t.is(exitCode, 1);
|
||||
});
|
||||
|
||||
test.serial('Return error code if multiple plugin are set for single plugin', async t => {
|
||||
test.serial('Return error exitCode if multiple plugin are set for single plugin', async t => {
|
||||
const run = stub().resolves(true);
|
||||
const argv = ['', '', '--analyze-commits', 'analyze1', 'analyze2'];
|
||||
const cli = requireNoCache('../cli', {'.': run, process: {...process, argv}});
|
||||
@@ -198,7 +198,7 @@ test.serial('Return error code if multiple plugin are set for single plugin', as
|
||||
t.is(exitCode, 1);
|
||||
});
|
||||
|
||||
test.serial('Return error code if semantic-release throw error', async t => {
|
||||
test.serial('Return error exitCode if semantic-release throw error', async t => {
|
||||
const run = stub().rejects(new Error('semantic-release error'));
|
||||
const argv = ['', ''];
|
||||
const cli = requireNoCache('../cli', {'.': run, process: {...process, argv}});
|
||||
|
||||
@@ -482,6 +482,6 @@ test('Throw an Error if one of the shareable config cannot be found', async t =>
|
||||
|
||||
const error = await t.throwsAsync(t.context.getConfig({cwd}), Error);
|
||||
|
||||
t.is(error.message, "Cannot find module 'non-existing-path'");
|
||||
t.regex(error.message, /Cannot find module 'non-existing-path'/);
|
||||
t.is(error.code, 'MODULE_NOT_FOUND');
|
||||
});
|
||||
|
||||
@@ -70,8 +70,8 @@ test('Convert shorthand URL', async t => {
|
||||
const {cwd} = await gitRepo();
|
||||
|
||||
t.is(
|
||||
await getAuthUrl({cwd, env, options: {repositoryUrl: 'semanitc-release/semanitc-release'}}),
|
||||
'https://github.com/semanitc-release/semanitc-release.git'
|
||||
await getAuthUrl({cwd, env, options: {repositoryUrl: 'semantic-release/semantic-release'}}),
|
||||
'https://github.com/semantic-release/semantic-release.git'
|
||||
);
|
||||
});
|
||||
|
||||
@@ -82,9 +82,9 @@ test('Convert GitLab shorthand URL', async t => {
|
||||
await getAuthUrl({
|
||||
cwd,
|
||||
env,
|
||||
options: {branch: 'master', repositoryUrl: 'gitlab:semanitc-release/semanitc-release'},
|
||||
options: {branch: 'master', repositoryUrl: 'gitlab:semantic-release/semantic-release'},
|
||||
}),
|
||||
'https://gitlab.com/semanitc-release/semanitc-release.git'
|
||||
'https://gitlab.com/semantic-release/semantic-release.git'
|
||||
);
|
||||
});
|
||||
|
||||
@@ -140,6 +140,19 @@ test('Return the "http" formatted URL if "gitCredentials" is defined and reposit
|
||||
);
|
||||
});
|
||||
|
||||
test('Return the "http" formatted URL if "gitCredentials" is defined and repositoryUrl is a "http" URL with custom port', async t => {
|
||||
const {cwd} = await gitRepo();
|
||||
|
||||
t.is(
|
||||
await getAuthUrl({
|
||||
cwd,
|
||||
env: {...env, GIT_CREDENTIALS: 'user:pass'},
|
||||
options: {branch: 'master', repositoryUrl: 'http://host.null:8080/owner/repo.git'},
|
||||
}),
|
||||
'http://user:pass@host.null:8080/owner/repo.git'
|
||||
);
|
||||
});
|
||||
|
||||
test('Return the "https" formatted URL if "gitCredentials" is defined and repositoryUrl is a "git+https" URL', async t => {
|
||||
const {cwd} = await gitRepo();
|
||||
|
||||
@@ -166,6 +179,19 @@ test('Return the "http" formatted URL if "gitCredentials" is defined and reposit
|
||||
);
|
||||
});
|
||||
|
||||
test('Return the "http" formatted URL if "gitCredentials" is defined and repositoryUrl is a "ssh" URL', async t => {
|
||||
const {cwd} = await gitRepo();
|
||||
|
||||
t.is(
|
||||
await getAuthUrl({
|
||||
cwd,
|
||||
env: {...env, GIT_CREDENTIALS: 'user:pass'},
|
||||
options: {branch: 'master', repositoryUrl: 'ssh://git@host.null:2222/owner/repo.git'},
|
||||
}),
|
||||
'https://user:pass@host.null/owner/repo.git'
|
||||
);
|
||||
});
|
||||
|
||||
test('Return the "https" formatted URL if "gitCredentials" is defined with "GH_TOKEN"', async t => {
|
||||
const {cwd} = await gitRepo();
|
||||
|
||||
|
||||
+4
-4
@@ -214,7 +214,7 @@ test('Return "true" if repository is up to date', async t => {
|
||||
await gitCommits(['First'], {cwd});
|
||||
await gitPush(repositoryUrl, 'master', {cwd});
|
||||
|
||||
t.true(await isBranchUpToDate('master', {cwd}));
|
||||
t.true(await isBranchUpToDate(repositoryUrl, 'master', {cwd}));
|
||||
});
|
||||
|
||||
test('Return falsy if repository is not up to date', async t => {
|
||||
@@ -223,13 +223,13 @@ test('Return falsy if repository is not up to date', async t => {
|
||||
await gitCommits(['Second'], {cwd});
|
||||
await gitPush(repositoryUrl, 'master', {cwd});
|
||||
|
||||
t.true(await isBranchUpToDate('master', {cwd}));
|
||||
t.true(await isBranchUpToDate(repositoryUrl, 'master', {cwd}));
|
||||
|
||||
const tmpRepo = await gitShallowClone(repositoryUrl);
|
||||
await gitCommits(['Third'], {cwd: tmpRepo});
|
||||
await gitPush('origin', 'master', {cwd: tmpRepo});
|
||||
|
||||
t.falsy(await isBranchUpToDate('master', {cwd}));
|
||||
t.falsy(await isBranchUpToDate(repositoryUrl, 'master', {cwd}));
|
||||
});
|
||||
|
||||
test('Return "true" if local repository is ahead', async t => {
|
||||
@@ -238,5 +238,5 @@ test('Return "true" if local repository is ahead', async t => {
|
||||
await gitPush(repositoryUrl, 'master', {cwd});
|
||||
await gitCommits(['Second'], {cwd});
|
||||
|
||||
t.true(await isBranchUpToDate('master', {cwd}));
|
||||
t.true(await isBranchUpToDate(repositoryUrl, 'master', {cwd}));
|
||||
});
|
||||
|
||||
+19
-15
@@ -69,8 +69,10 @@ export async function initBareRepo(repositoryUrl, branch = 'master') {
|
||||
* @returns {Array<Commit>} The created commits, in reverse order (to match `git log` order).
|
||||
*/
|
||||
export async function gitCommits(messages, execaOpts) {
|
||||
await pReduce(messages, (_, message) =>
|
||||
execa.stdout('git', ['commit', '-m', message, '--allow-empty', '--no-gpg-sign'], execaOpts)
|
||||
await pReduce(
|
||||
messages,
|
||||
async (_, message) =>
|
||||
(await execa('git', ['commit', '-m', message, '--allow-empty', '--no-gpg-sign'], execaOpts)).stdout
|
||||
);
|
||||
return (await gitGetCommits(undefined, execaOpts)).slice(0, messages.length);
|
||||
}
|
||||
@@ -85,9 +87,11 @@ export async function gitCommits(messages, execaOpts) {
|
||||
*/
|
||||
export async function gitGetCommits(from, execaOpts) {
|
||||
Object.assign(gitLogParser.fields, {hash: 'H', message: 'B', gitTags: 'd', committerDate: {key: 'ci', type: Date}});
|
||||
return (await getStream.array(
|
||||
gitLogParser.parse({_: `${from ? from + '..' : ''}HEAD`}, {...execaOpts, env: {...process.env, ...execaOpts.env}})
|
||||
)).map(commit => {
|
||||
return (
|
||||
await getStream.array(
|
||||
gitLogParser.parse({_: `${from ? from + '..' : ''}HEAD`}, {...execaOpts, env: {...process.env, ...execaOpts.env}})
|
||||
)
|
||||
).map(commit => {
|
||||
commit.message = commit.message.trim();
|
||||
commit.gitTags = commit.gitTags.trim();
|
||||
return commit;
|
||||
@@ -98,10 +102,10 @@ export async function gitGetCommits(from, execaOpts) {
|
||||
* Checkout a branch on the current git repository.
|
||||
*
|
||||
* @param {String} branch Branch name.
|
||||
* @param {Boolean} create `true` to create the branch, `false` to checkout an existing branch.
|
||||
* @param {Boolean} create to create the branch, `false` to checkout an existing branch.
|
||||
* @param {Object} [execaOpts] Options to pass to `execa`.
|
||||
*/
|
||||
export async function gitCheckout(branch, create = true, execaOpts) {
|
||||
export async function gitCheckout(branch, create, execaOpts) {
|
||||
await execa('git', create ? ['checkout', '-b', branch] : ['checkout', branch], execaOpts);
|
||||
}
|
||||
|
||||
@@ -112,8 +116,8 @@ export async function gitCheckout(branch, create = true, execaOpts) {
|
||||
*
|
||||
* @return {String} The sha of the head commit in the current git repository.
|
||||
*/
|
||||
export function gitHead(execaOpts) {
|
||||
return execa.stdout('git', ['rev-parse', 'HEAD'], execaOpts);
|
||||
export async function gitHead(execaOpts) {
|
||||
return (await execa('git', ['rev-parse', 'HEAD'], execaOpts)).stdout;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -181,8 +185,8 @@ export async function gitAddConfig(name, value, execaOpts) {
|
||||
*
|
||||
* @return {String} The sha of the commit associated with `tagName` on the local repository.
|
||||
*/
|
||||
export function gitTagHead(tagName, execaOpts) {
|
||||
return execa.stdout('git', ['rev-list', '-1', tagName], execaOpts);
|
||||
export async function gitTagHead(tagName, execaOpts) {
|
||||
return (await execa('git', ['rev-list', '-1', tagName], execaOpts)).stdout;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -195,7 +199,7 @@ export function gitTagHead(tagName, execaOpts) {
|
||||
* @return {String} The sha of the commit associated with `tagName` on the remote repository.
|
||||
*/
|
||||
export async function gitRemoteTagHead(repositoryUrl, tagName, execaOpts) {
|
||||
return (await execa.stdout('git', ['ls-remote', '--tags', repositoryUrl, tagName], execaOpts))
|
||||
return (await execa('git', ['ls-remote', '--tags', repositoryUrl, tagName], execaOpts)).stdout
|
||||
.split('\n')
|
||||
.filter(tag => Boolean(tag))
|
||||
.map(tag => tag.match(/^(\S+)/)[1])[0];
|
||||
@@ -209,8 +213,8 @@ export async function gitRemoteTagHead(repositoryUrl, tagName, execaOpts) {
|
||||
*
|
||||
* @return {String} The tag associatedwith the sha in parameter or `null`.
|
||||
*/
|
||||
export function gitCommitTag(gitHead, execaOpts) {
|
||||
return execa.stdout('git', ['describe', '--tags', '--exact-match', gitHead], execaOpts);
|
||||
export async function gitCommitTag(gitHead, execaOpts) {
|
||||
return (await execa('git', ['describe', '--tags', '--exact-match', gitHead], execaOpts)).stdout;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -222,6 +226,6 @@ export function gitCommitTag(gitHead, execaOpts) {
|
||||
*
|
||||
* @throws {Error} if the push failed.
|
||||
*/
|
||||
export async function gitPush(repositoryUrl = 'origin', branch = 'master', execaOpts) {
|
||||
export async function gitPush(repositoryUrl, branch, execaOpts) {
|
||||
await execa('git', ['push', '--tags', repositoryUrl, `HEAD:${branch}`], execaOpts);
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ async function start() {
|
||||
minTimeout: 1000,
|
||||
factor: 2,
|
||||
});
|
||||
} catch (error) {
|
||||
} catch (_) {
|
||||
throw new Error(`Couldn't start mock-server after 2 min`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ async function start() {
|
||||
minTimeout: 1000,
|
||||
factor: 2,
|
||||
});
|
||||
} catch (error) {
|
||||
} catch (_) {
|
||||
throw new Error(`Couldn't start npm-registry-docker after 2 min`);
|
||||
}
|
||||
|
||||
|
||||
+11
-1
@@ -75,10 +75,11 @@ test('Plugins are called with expected values', async t => {
|
||||
publish: [publish1, pluginNoop],
|
||||
success,
|
||||
};
|
||||
const envCi = {branch: 'master', isCi: true, isPr: false};
|
||||
|
||||
const semanticRelease = requireNoCache('..', {
|
||||
'./lib/get-logger': () => t.context.logger,
|
||||
'env-ci': () => ({isCi: true, branch: 'master', isPr: false}),
|
||||
'env-ci': () => envCi,
|
||||
});
|
||||
const result = await semanticRelease(options, {
|
||||
cwd,
|
||||
@@ -97,6 +98,7 @@ test('Plugins are called with expected values', async t => {
|
||||
t.deepEqual(verifyConditions2.args[0][1].cwd, cwd);
|
||||
t.deepEqual(verifyConditions2.args[0][1].options, options);
|
||||
t.deepEqual(verifyConditions2.args[0][1].logger, t.context.logger);
|
||||
t.deepEqual(verifyConditions2.args[0][1].envCi, envCi);
|
||||
|
||||
t.is(analyzeCommits.callCount, 1);
|
||||
t.deepEqual(analyzeCommits.args[0][0], config);
|
||||
@@ -105,6 +107,7 @@ test('Plugins are called with expected values', async t => {
|
||||
t.deepEqual(analyzeCommits.args[0][1].lastRelease, lastRelease);
|
||||
t.deepEqual(analyzeCommits.args[0][1].commits[0].hash, commits[0].hash);
|
||||
t.deepEqual(analyzeCommits.args[0][1].commits[0].message, commits[0].message);
|
||||
t.deepEqual(analyzeCommits.args[0][1].envCi, envCi);
|
||||
|
||||
t.is(verifyRelease.callCount, 1);
|
||||
t.deepEqual(verifyRelease.args[0][0], config);
|
||||
@@ -114,6 +117,7 @@ test('Plugins are called with expected values', async t => {
|
||||
t.deepEqual(verifyRelease.args[0][1].commits[0].hash, commits[0].hash);
|
||||
t.deepEqual(verifyRelease.args[0][1].commits[0].message, commits[0].message);
|
||||
t.deepEqual(verifyRelease.args[0][1].nextRelease, nextRelease);
|
||||
t.deepEqual(verifyRelease.args[0][1].envCi, envCi);
|
||||
|
||||
t.is(generateNotes1.callCount, 1);
|
||||
t.deepEqual(generateNotes1.args[0][0], config);
|
||||
@@ -123,6 +127,7 @@ test('Plugins are called with expected values', async t => {
|
||||
t.deepEqual(generateNotes1.args[0][1].commits[0].hash, commits[0].hash);
|
||||
t.deepEqual(generateNotes1.args[0][1].commits[0].message, commits[0].message);
|
||||
t.deepEqual(generateNotes1.args[0][1].nextRelease, nextRelease);
|
||||
t.deepEqual(generateNotes1.args[0][1].envCi, envCi);
|
||||
|
||||
t.is(generateNotes2.callCount, 1);
|
||||
t.deepEqual(generateNotes2.args[0][0], config);
|
||||
@@ -132,6 +137,7 @@ test('Plugins are called with expected values', async t => {
|
||||
t.deepEqual(generateNotes2.args[0][1].commits[0].hash, commits[0].hash);
|
||||
t.deepEqual(generateNotes2.args[0][1].commits[0].message, commits[0].message);
|
||||
t.deepEqual(generateNotes2.args[0][1].nextRelease, {...nextRelease, notes: notes1});
|
||||
t.deepEqual(generateNotes2.args[0][1].envCi, envCi);
|
||||
|
||||
t.is(generateNotes3.callCount, 1);
|
||||
t.deepEqual(generateNotes3.args[0][0], config);
|
||||
@@ -141,6 +147,7 @@ test('Plugins are called with expected values', async t => {
|
||||
t.deepEqual(generateNotes3.args[0][1].commits[0].hash, commits[0].hash);
|
||||
t.deepEqual(generateNotes3.args[0][1].commits[0].message, commits[0].message);
|
||||
t.deepEqual(generateNotes3.args[0][1].nextRelease, {...nextRelease, notes: `${notes1}\n\n${notes2}`});
|
||||
t.deepEqual(generateNotes3.args[0][1].envCi, envCi);
|
||||
|
||||
t.is(prepare.callCount, 1);
|
||||
t.deepEqual(prepare.args[0][0], config);
|
||||
@@ -150,6 +157,7 @@ test('Plugins are called with expected values', async t => {
|
||||
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: `${notes1}\n\n${notes2}\n\n${notes3}`});
|
||||
t.deepEqual(prepare.args[0][1].envCi, envCi);
|
||||
|
||||
t.is(publish1.callCount, 1);
|
||||
t.deepEqual(publish1.args[0][0], config);
|
||||
@@ -159,6 +167,7 @@ test('Plugins are called with expected values', async t => {
|
||||
t.deepEqual(publish1.args[0][1].commits[0].hash, commits[0].hash);
|
||||
t.deepEqual(publish1.args[0][1].commits[0].message, commits[0].message);
|
||||
t.deepEqual(publish1.args[0][1].nextRelease, {...nextRelease, notes: `${notes1}\n\n${notes2}\n\n${notes3}`});
|
||||
t.deepEqual(publish1.args[0][1].envCi, envCi);
|
||||
|
||||
t.is(success.callCount, 1);
|
||||
t.deepEqual(success.args[0][0], config);
|
||||
@@ -172,6 +181,7 @@ test('Plugins are called with expected values', async t => {
|
||||
{...release1, ...nextRelease, notes: `${notes1}\n\n${notes2}\n\n${notes3}`, pluginName: '[Function: functionStub]'},
|
||||
{...nextRelease, notes: `${notes1}\n\n${notes2}\n\n${notes3}`, pluginName: pluginNoop},
|
||||
]);
|
||||
t.deepEqual(success.args[0][1].envCi, envCi);
|
||||
|
||||
t.deepEqual(result, {
|
||||
lastRelease,
|
||||
|
||||
+28
-28
@@ -72,9 +72,9 @@ test('Release patch, minor and major versions', async t => {
|
||||
t.log('Commit a chore');
|
||||
await gitCommits(['chore: Init repository'], {cwd});
|
||||
t.log('$ semantic-release');
|
||||
let {stdout, code} = await execa(cli, [], {env, cwd});
|
||||
let {stdout, exitCode} = await execa(cli, [], {env, cwd});
|
||||
t.regex(stdout, /There are no relevant changes, so no new version is released/);
|
||||
t.is(code, 0);
|
||||
t.is(exitCode, 0);
|
||||
|
||||
/* Initial release */
|
||||
let version = '1.0.0';
|
||||
@@ -95,10 +95,10 @@ test('Release patch, minor and major versions', async t => {
|
||||
t.log('Commit a feature');
|
||||
await gitCommits(['feat: Initial commit'], {cwd});
|
||||
t.log('$ semantic-release');
|
||||
({stdout, code} = await execa(cli, [], {env, cwd}));
|
||||
({stdout, exitCode} = await execa(cli, [], {env, cwd}));
|
||||
t.regex(stdout, new RegExp(`Published GitHub release: release-url/${version}`));
|
||||
t.regex(stdout, new RegExp(`Publishing version ${version} to npm registry`));
|
||||
t.is(code, 0);
|
||||
t.is(exitCode, 0);
|
||||
|
||||
// Verify package.json and npm-shrinkwrap.json have been updated
|
||||
t.is((await readJson(path.resolve(cwd, 'package.json'))).version, version);
|
||||
@@ -137,10 +137,10 @@ test('Release patch, minor and major versions', async t => {
|
||||
t.log('Commit a fix');
|
||||
await gitCommits(['fix: bar'], {cwd});
|
||||
t.log('$ semantic-release');
|
||||
({stdout, code} = await execa(cli, [], {env, cwd}));
|
||||
({stdout, exitCode} = await execa(cli, [], {env, cwd}));
|
||||
t.regex(stdout, new RegExp(`Published GitHub release: release-url/${version}`));
|
||||
t.regex(stdout, new RegExp(`Publishing version ${version} to npm registry`));
|
||||
t.is(code, 0);
|
||||
t.is(exitCode, 0);
|
||||
|
||||
// Verify package.json and npm-shrinkwrap.json have been updated
|
||||
t.is((await readJson(path.resolve(cwd, 'package.json'))).version, version);
|
||||
@@ -179,10 +179,10 @@ test('Release patch, minor and major versions', async t => {
|
||||
t.log('Commit a feature');
|
||||
await gitCommits(['feat: baz'], {cwd});
|
||||
t.log('$ semantic-release');
|
||||
({stdout, code} = await execa(cli, [], {env, cwd}));
|
||||
({stdout, exitCode} = await execa(cli, [], {env, cwd}));
|
||||
t.regex(stdout, new RegExp(`Published GitHub release: release-url/${version}`));
|
||||
t.regex(stdout, new RegExp(`Publishing version ${version} to npm registry`));
|
||||
t.is(code, 0);
|
||||
t.is(exitCode, 0);
|
||||
|
||||
// Verify package.json and npm-shrinkwrap.json have been updated
|
||||
t.is((await readJson(path.resolve(cwd, 'package.json'))).version, version);
|
||||
@@ -221,10 +221,10 @@ test('Release patch, minor and major versions', async t => {
|
||||
t.log('Commit a breaking change');
|
||||
await gitCommits(['feat: foo\n\n BREAKING CHANGE: bar'], {cwd});
|
||||
t.log('$ semantic-release');
|
||||
({stdout, code} = await execa(cli, [], {env, cwd}));
|
||||
({stdout, exitCode} = await execa(cli, [], {env, cwd}));
|
||||
t.regex(stdout, new RegExp(`Published GitHub release: release-url/${version}`));
|
||||
t.regex(stdout, new RegExp(`Publishing version ${version} to npm registry`));
|
||||
t.is(code, 0);
|
||||
t.is(exitCode, 0);
|
||||
|
||||
// Verify package.json and npm-shrinkwrap.json have been updated
|
||||
t.is((await readJson(path.resolve(cwd, 'package.json'))).version, version);
|
||||
@@ -258,8 +258,8 @@ test('Exit with 1 if a plugin is not found', async t => {
|
||||
release: {analyzeCommits: 'non-existing-path', success: false, fail: false},
|
||||
});
|
||||
|
||||
const {code, stderr} = await t.throwsAsync(execa(cli, [], {env, cwd}));
|
||||
t.is(code, 1);
|
||||
const {exitCode, stderr} = await t.throwsAsync(execa(cli, [], {env, cwd}));
|
||||
t.is(exitCode, 1);
|
||||
t.regex(stderr, /Cannot find module/);
|
||||
});
|
||||
|
||||
@@ -276,8 +276,8 @@ test('Exit with 1 if a shareable config is not found', async t => {
|
||||
release: {extends: 'non-existing-path', success: false, fail: false},
|
||||
});
|
||||
|
||||
const {code, stderr} = await t.throwsAsync(execa(cli, [], {env, cwd}));
|
||||
t.is(code, 1);
|
||||
const {exitCode, stderr} = await t.throwsAsync(execa(cli, [], {env, cwd}));
|
||||
t.is(exitCode, 1);
|
||||
t.regex(stderr, /Cannot find module/);
|
||||
});
|
||||
|
||||
@@ -297,8 +297,8 @@ test('Exit with 1 if a shareable config reference a not found plugin', async t =
|
||||
});
|
||||
await writeJson(path.resolve(cwd, 'shareable.json'), shareable);
|
||||
|
||||
const {code, stderr} = await t.throwsAsync(execa(cli, [], {env, cwd}));
|
||||
t.is(code, 1);
|
||||
const {exitCode, stderr} = await t.throwsAsync(execa(cli, [], {env, cwd}));
|
||||
t.is(exitCode, 1);
|
||||
t.regex(stderr, /Cannot find module/);
|
||||
});
|
||||
|
||||
@@ -327,11 +327,11 @@ test('Dry-run', async t => {
|
||||
t.log('Commit a feature');
|
||||
await gitCommits(['feat: Initial commit'], {cwd});
|
||||
t.log('$ semantic-release -d');
|
||||
const {stdout, code} = await execa(cli, ['-d'], {env, cwd});
|
||||
const {stdout, exitCode} = await execa(cli, ['-d'], {env, cwd});
|
||||
t.regex(stdout, new RegExp(`There is no previous release, the next release version is ${version}`));
|
||||
t.regex(stdout, new RegExp(`Release note for version ${version}`));
|
||||
t.regex(stdout, /Initial commit/);
|
||||
t.is(code, 0);
|
||||
t.is(exitCode, 0);
|
||||
|
||||
// Verify package.json and has not been modified
|
||||
t.is((await readJson(path.resolve(cwd, 'package.json'))).version, '0.0.0-dev');
|
||||
@@ -375,10 +375,10 @@ test('Allow local releases with "noCi" option', async t => {
|
||||
t.log('Commit a feature');
|
||||
await gitCommits(['feat: Initial commit'], {cwd});
|
||||
t.log('$ semantic-release --no-ci');
|
||||
const {stdout, code} = await execa(cli, ['--no-ci'], {env: envNoCi, cwd});
|
||||
const {stdout, exitCode} = await execa(cli, ['--no-ci'], {env: envNoCi, cwd});
|
||||
t.regex(stdout, new RegExp(`Published GitHub release: release-url/${version}`));
|
||||
t.regex(stdout, new RegExp(`Publishing version ${version} to npm registry`));
|
||||
t.is(code, 0);
|
||||
t.is(exitCode, 0);
|
||||
|
||||
// Verify package.json and has been updated
|
||||
t.is((await readJson(path.resolve(cwd, 'package.json'))).version, version);
|
||||
@@ -417,7 +417,7 @@ test('Pass options via CLI arguments', async t => {
|
||||
t.log('Commit a feature');
|
||||
await gitCommits(['feat: Initial commit'], {cwd});
|
||||
t.log('$ semantic-release');
|
||||
const {stdout, code} = await execa(
|
||||
const {stdout, exitCode} = await execa(
|
||||
cli,
|
||||
[
|
||||
'--verify-conditions',
|
||||
@@ -433,7 +433,7 @@ test('Pass options via CLI arguments', async t => {
|
||||
{env, cwd}
|
||||
);
|
||||
t.regex(stdout, new RegExp(`Publishing version ${version} to npm registry`));
|
||||
t.is(code, 0);
|
||||
t.is(exitCode, 0);
|
||||
|
||||
// Verify package.json and has been updated
|
||||
t.is((await readJson(path.resolve(cwd, 'package.json'))).version, version);
|
||||
@@ -528,14 +528,14 @@ test('Log unexpected errors from plugins and exit with 1', async t => {
|
||||
t.log('Commit a feature');
|
||||
await gitCommits(['feat: Initial commit'], {cwd});
|
||||
t.log('$ semantic-release');
|
||||
const {stderr, code} = await execa(cli, [], {env, cwd, reject: false});
|
||||
const {stderr, exitCode} = await execa(cli, [], {env, cwd, reject: false});
|
||||
// Verify the type and message are logged
|
||||
t.regex(stderr, /Error: a/);
|
||||
// Verify the the stacktrace is logged
|
||||
t.regex(stderr, new RegExp(pluginError));
|
||||
// Verify the Error properties are logged
|
||||
t.regex(stderr, /errorProperty: 'errorProperty'/);
|
||||
t.is(code, 1);
|
||||
t.is(exitCode, 1);
|
||||
});
|
||||
|
||||
test('Log errors inheriting SemanticReleaseError and exit with 1', async t => {
|
||||
@@ -555,10 +555,10 @@ test('Log errors inheriting SemanticReleaseError and exit with 1', async t => {
|
||||
t.log('Commit a feature');
|
||||
await gitCommits(['feat: Initial commit'], {cwd});
|
||||
t.log('$ semantic-release');
|
||||
const {stderr, code} = await execa(cli, [], {env, cwd, reject: false});
|
||||
const {stderr, exitCode} = await execa(cli, [], {env, cwd, reject: false});
|
||||
// Verify the type and message are logged
|
||||
t.regex(stderr, /EINHERITED Inherited error/);
|
||||
t.is(code, 1);
|
||||
t.is(exitCode, 1);
|
||||
});
|
||||
|
||||
test('Exit with 1 if missing permission to push to the remote repository', async t => {
|
||||
@@ -573,14 +573,14 @@ test('Exit with 1 if missing permission to push to the remote repository', async
|
||||
await gitCommits(['feat: Initial commit'], {cwd});
|
||||
await gitPush('origin', 'master', {cwd});
|
||||
t.log('$ semantic-release');
|
||||
const {stderr, code} = await execa(
|
||||
const {stderr, exitCode} = await execa(
|
||||
cli,
|
||||
['--repository-url', 'http://user:wrong_pass@localhost:2080/git/unauthorized.git'],
|
||||
{env: {...env, GH_TOKEN: 'user:wrong_pass'}, cwd, reject: false}
|
||||
);
|
||||
// Verify the type and message are logged
|
||||
t.regex(stderr, /EGITNOPERMISSION/);
|
||||
t.is(code, 1);
|
||||
t.is(exitCode, 1);
|
||||
});
|
||||
|
||||
test('Hide sensitive environment variable values from the logs', async t => {
|
||||
|
||||
@@ -255,6 +255,6 @@ test('Throws an error if the plugin is not found', t => {
|
||||
Error
|
||||
);
|
||||
|
||||
t.is(error.message, "Cannot find module 'non-existing-path'");
|
||||
t.regex(error.message, /Cannot find module 'non-existing-path'/);
|
||||
t.is(error.code, 'MODULE_NOT_FOUND');
|
||||
});
|
||||
|
||||
@@ -47,7 +47,12 @@ test('Execute each function in series passing the "lastResult" and "result" to "
|
||||
const result = await pipeline([step1, step2, step3, step4], {settleAll: false, getNextInput})(5);
|
||||
|
||||
t.deepEqual(result, [1, 2, 3, 4]);
|
||||
t.deepEqual(getNextInput.args, [[5, 1], [5, 2], [5, 3], [5, 4]]);
|
||||
t.deepEqual(getNextInput.args, [
|
||||
[5, 1],
|
||||
[5, 2],
|
||||
[5, 3],
|
||||
[5, 4],
|
||||
]);
|
||||
});
|
||||
|
||||
test('Execute each function in series calling "transform" to modify the results', async t => {
|
||||
@@ -61,7 +66,12 @@ test('Execute each function in series calling "transform" to modify the results'
|
||||
const result = await pipeline([step1, step2, step3, step4], {getNextInput, transform})(5);
|
||||
|
||||
t.deepEqual(result, [1 + 1, 2 + 1, 3 + 1, 4 + 1]);
|
||||
t.deepEqual(getNextInput.args, [[5, 1 + 1], [5, 2 + 1], [5, 3 + 1], [5, 4 + 1]]);
|
||||
t.deepEqual(getNextInput.args, [
|
||||
[5, 1 + 1],
|
||||
[5, 2 + 1],
|
||||
[5, 3 + 1],
|
||||
[5, 4 + 1],
|
||||
]);
|
||||
});
|
||||
|
||||
test('Execute each function in series calling "transform" to modify the results with "settleAll"', async t => {
|
||||
@@ -75,7 +85,12 @@ test('Execute each function in series calling "transform" to modify the results
|
||||
const result = await pipeline([step1, step2, step3, step4], {settleAll: true, getNextInput, transform})(5);
|
||||
|
||||
t.deepEqual(result, [1 + 1, 2 + 1, 3 + 1, 4 + 1]);
|
||||
t.deepEqual(getNextInput.args, [[5, 1 + 1], [5, 2 + 1], [5, 3 + 1], [5, 4 + 1]]);
|
||||
t.deepEqual(getNextInput.args, [
|
||||
[5, 1 + 1],
|
||||
[5, 2 + 1],
|
||||
[5, 3 + 1],
|
||||
[5, 4 + 1],
|
||||
]);
|
||||
});
|
||||
|
||||
test('Stop execution and throw error if a step rejects', async t => {
|
||||
|
||||
Reference in New Issue
Block a user