Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
66340accda |
@@ -1 +0,0 @@
|
||||
* text=auto eol=lf
|
||||
@@ -0,0 +1,28 @@
|
||||
name: Happy Path
|
||||
on:
|
||||
release:
|
||||
types: [prereleased, published]
|
||||
|
||||
jobs:
|
||||
happy-path:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 12
|
||||
|
||||
# notify happy-path e2e test repository
|
||||
- uses: octokit/request-action@v2.x
|
||||
with:
|
||||
route: POST /repos/:owner/:repo/dispatches
|
||||
owner: semantic-release-test
|
||||
repo: dispatcher
|
||||
event_type: github_release
|
||||
client_payload: |
|
||||
{
|
||||
"tag": "${{ github.event.release.tag_name }}"
|
||||
"prerelease": ${{ github.event.release.prerelease }}
|
||||
}
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.SEMANTIC_RELEASE_BOT_PAT_FOR_DISPATCH_EVENTS }}
|
||||
@@ -1,23 +0,0 @@
|
||||
name: Release
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- next
|
||||
- beta
|
||||
- "*.x" # maintenance releases branches
|
||||
|
||||
jobs:
|
||||
release:
|
||||
name: release
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
cache: npm
|
||||
- run: npm ci
|
||||
- run: npx semantic-release
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
NPM_TOKEN: ${{ secrets.SEMANTIC_RELEASE_BOT_NPM_TOKEN }}
|
||||
@@ -1,50 +0,0 @@
|
||||
name: Test
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
# renovate/** branches are generated by https://github.com/apps/renovate
|
||||
- renovate/**
|
||||
|
||||
pull_request:
|
||||
types:
|
||||
- opened
|
||||
- synchronize
|
||||
|
||||
jobs:
|
||||
test_matrix:
|
||||
strategy:
|
||||
matrix:
|
||||
node-version:
|
||||
- 14.17
|
||||
- 16
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- run: git config --global user.name github-actions
|
||||
- run: git config --global user.email github-actions@github.com
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
cache: npm
|
||||
- name: Ensure dependencies are compatible with the version of node
|
||||
run: echo 'engine-strict=true' >> .npmrc
|
||||
- run: npm ci
|
||||
- run: npm run test:ci
|
||||
|
||||
# separate job to set as required in branch protection,
|
||||
# as the build names above change each time Node versions change
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
needs: test_matrix
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
cache: npm
|
||||
- run: npm ci
|
||||
- run: npm run lint
|
||||
@@ -125,6 +125,10 @@ $RECYCLE.BIN/
|
||||
# Windows shortcuts
|
||||
*.lnk
|
||||
|
||||
# Lockfiles
|
||||
package-lock.json
|
||||
yarn.lock
|
||||
|
||||
# Gitbook
|
||||
_book
|
||||
|
||||
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
version: ~> 1.0
|
||||
|
||||
services:
|
||||
- docker
|
||||
|
||||
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,3 @@
|
||||
node_js:
|
||||
- 12
|
||||
- 10.18
|
||||
@@ -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
|
||||
+5
-5
@@ -107,11 +107,11 @@ To ensure consistency and quality, all documentation modifications must:
|
||||
- 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)
|
||||
- a package or module, i.e. The [`@semantic-release/github`](https://github.com/semantic-release/github) module
|
||||
- Use the [single backtick `code` quoting](https://help.github.com/articles/basic-writing-and-formatting-syntax/#quoting-code) for:
|
||||
- Use the the [single backtick `code` quoting](https://help.github.com/articles/basic-writing-and-formatting-syntax/#quoting-code) for:
|
||||
- commands inside sentences, i.e. the `semantic-release` command
|
||||
- programming language keywords, i.e. `function`, `async`, `String`
|
||||
- packages or modules, i.e. The [`@semantic-release/github`](https://github.com/semantic-release/github) module
|
||||
- Use the [triple backtick `code` formatting](https://help.github.com/articles/creating-and-highlighting-code-blocks) for:
|
||||
- Use the the [triple backtick `code` formatting](https://help.github.com/articles/creating-and-highlighting-code-blocks) for:
|
||||
- code examples
|
||||
- configuration examples
|
||||
- sequence of command lines
|
||||
@@ -183,17 +183,17 @@ The footer should contain any information about **Breaking Changes** and is also
|
||||
#### Examples
|
||||
|
||||
```commit
|
||||
fix(pencil): stop graphite breaking when too much pressure applied
|
||||
`fix(pencil): stop graphite breaking when too much pressure applied`
|
||||
```
|
||||
|
||||
```commit
|
||||
feat(pencil): add 'graphiteWidth' option
|
||||
`feat(pencil): add 'graphiteWidth' option`
|
||||
|
||||
Fix #42
|
||||
```
|
||||
|
||||
```commit
|
||||
perf(pencil): remove graphiteWidth option
|
||||
perf(pencil): remove graphiteWidth option`
|
||||
|
||||
BREAKING CHANGE: The graphiteWidth option has been removed.
|
||||
|
||||
|
||||
@@ -1,11 +1,17 @@
|
||||
<h1 align="center" style="border-bottom: none;">📦🚀 semantic-release</h1>
|
||||
<h3 align="center">Fully automated version management and package publishing</h3>
|
||||
<p align="center">
|
||||
<a href="https://github.com/semantic-release/semantic-release/discussions">
|
||||
<img alt="Join the community on GitHub Discussions" src="https://img.shields.io/badge/Join%20the%20community-on%20GitHub%20Discussions-blue">
|
||||
<a href="https://spectrum.chat/semantic-release">
|
||||
<img alt="Join the community on Spectrum" src="https://withspectrum.github.io/badge/badge.svg">
|
||||
</a>
|
||||
<a href="https://github.com/semantic-release/semantic-release/actions?query=workflow%3ATest+branch%3Amaster">
|
||||
<img alt="Build states" src="https://github.com/semantic-release/semantic-release/workflows/Test/badge.svg">
|
||||
<a href="https://travis-ci.org/semantic-release/semantic-release">
|
||||
<img alt="Travis" src="https://img.shields.io/travis/semantic-release/semantic-release/master.svg">
|
||||
</a>
|
||||
<a href="https://codecov.io/gh/semantic-release/semantic-release">
|
||||
<img alt="Codecov" src="https://img.shields.io/codecov/c/github/semantic-release/semantic-release/master.svg">
|
||||
</a>
|
||||
<a href="https://greenkeeper.io">
|
||||
<img alt="Greenkeeper" src="https://badges.greenkeeper.io/semantic-release/semantic-release.svg">
|
||||
</a>
|
||||
<a href="#badge">
|
||||
<img alt="semantic-release" src="https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg">
|
||||
@@ -48,14 +54,14 @@ This removes the immediate connection between human emotions and version numbers
|
||||
|
||||
**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/blob/master/CONTRIBUTING.md#-commit-message-format). 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.
|
||||
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) or [commitlint](https://github.com/conventional-changelog/commitlint) can be used to help contributors and enforce valid commit messages.
|
||||
|
||||
The table below shows which commit message gets you which release type when `semantic-release` runs (using the default configuration):
|
||||
Here is an example of the release type that will be done based on a commit messages:
|
||||
|
||||
| Commit message | Release type |
|
||||
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------- |
|
||||
|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------|
|
||||
| `fix(pencil): stop graphite breaking when too much pressure applied` | Patch Release |
|
||||
| `feat(pencil): add 'graphiteWidth' option` | ~~Minor~~ Feature Release |
|
||||
| `perf(pencil): remove graphiteWidth option`<br><br>`BREAKING CHANGE: The graphiteWidth option has been removed.`<br>`The default graphite width of 10mm is always used for performance reasons.` | ~~Major~~ Breaking Release |
|
||||
@@ -69,7 +75,6 @@ The table below shows which commit message gets you which release type when `sem
|
||||
For each new commits added to one of the release branches (for example `master`, `next`, `beta`), with `git push` or by merging a pull request or merging from another branch, a CI build is triggered and runs the `semantic-release` command to make a release if there are codebase changes since the last release that affect the package functionalities.
|
||||
|
||||
**semantic-release** offers various ways to control the timing, the content and the audience of published releases. See example workflows in the following recipes:
|
||||
|
||||
- [Using distribution channels](docs/recipes/distribution-channels.md#publishing-on-distribution-channels)
|
||||
- [Maintenance releases](docs/recipes/maintenance-releases.md#publishing-maintenance-releases)
|
||||
- [Pre-releases](docs/recipes/pre-releases.md#publishing-pre-releases)
|
||||
@@ -79,7 +84,7 @@ For each new commits added to one of the release branches (for example `master`,
|
||||
After running the tests, the command `semantic-release` will execute the following steps:
|
||||
|
||||
| Step | Description |
|
||||
| ----------------- | ------------------------------------------------------------------------------------------------------------------------------- |
|
||||
|-------------------|---------------------------------------------------------------------------------------------------------------------------------|
|
||||
| Verify Conditions | Verify all the conditions to proceed with the release. |
|
||||
| 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 based on the commits added since the last release. |
|
||||
@@ -93,11 +98,10 @@ After running the tests, the command `semantic-release` will execute the followi
|
||||
## Requirements
|
||||
|
||||
In order to use **semantic-release** you need:
|
||||
|
||||
- To host your code in a [Git repository](https://git-scm.com)
|
||||
- Use a Continuous Integration service that allows you to [securely set up credentials](docs/usage/ci-configuration.md#authentication)
|
||||
- Git CLI version [2.7.1 or higher](docs/support/FAQ.md#why-does-semantic-release-require-git-version--271) installed in your Continuous Integration environment
|
||||
- [Node.js](https://nodejs.org) version [14.17 or higher](docs/support/FAQ.md#why-does-semantic-release-require-node-version--1417) installed in your Continuous Integration environment
|
||||
- [Node.js](https://nodejs.org) version [10.18 or higher](docs/support/FAQ.md#why-does-semantic-release-require-node-version--1018) installed in your Continuous Integration environment
|
||||
|
||||
## Documentation
|
||||
|
||||
@@ -131,7 +135,7 @@ In order to use **semantic-release** you need:
|
||||
## Get help
|
||||
|
||||
- [Stack Overflow](https://stackoverflow.com/questions/tagged/semantic-release)
|
||||
- [GitHub Discussions](https://github.com/semantic-release/semantic-release/discussions)
|
||||
- [Spectrum community](https://spectrum.chat/semantic-release)
|
||||
- [Twitter](https://twitter.com/SemanticRelease)
|
||||
|
||||
## Badge
|
||||
@@ -146,15 +150,9 @@ Let people know that your package is published using **semantic-release** by inc
|
||||
|
||||
## Team
|
||||
|
||||
| [](https://github.com/gr2m) | [](https://github.com/pvdlg) | [](https://github.com/travi) |
|
||||
| ----------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------- |
|
||||
| [Gregor Martynus](https://github.com/gr2m) | [Pierre Vanduynslager](https://github.com/pvdlg) | [Matt Travi](https://github.com/travi) |
|
||||
|
||||
## Alumni
|
||||
|
||||
| [](https://github.com/boennemann) | [](https://github.com/relekang) | [](https://github.com/jo) | [](https://github.com/finnp) | [](https://github.com/christophwitzko) |
|
||||
| ------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- |
|
||||
| [Stephan Bönnemann](https://github.com/boennemann) | [Rolf Erik Lekang](https://github.com/relekang) | [Johannes Jörg Schmidt](https://github.com/jo) | [Finn Pauls](https://github.com/finnp) | [Christoph Witzko](https://github.com/christophwitzko) |
|
||||
| [](https://github.com/boennemann) | [](https://github.com/relekang) | [](https://github.com/jo) | [](https://github.com/gr2m) | [](https://github.com/finnp) | [](https://github.com/pvdlg) | [](https://github.com/christophwitzko) |
|
||||
|---------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------|
|
||||
| [Stephan Bönnemann](https://github.com/boennemann) | [Rolf Erik Lekang](https://github.com/relekang) | [Johannes Jörg Schmidt](https://github.com/jo) | [Gregor Martynus](https://github.com/gr2m) | [Finn Pauls](https://github.com/finnp) | [Pierre Vanduynslager](https://github.com/pvdlg) | [Christoph Witzko](https://github.com/christophwitzko) |
|
||||
|
||||
<p align="center">
|
||||
<img alt="Kill all humans" src="media/bender.png">
|
||||
|
||||
+15
-11
@@ -1,11 +1,16 @@
|
||||
#!/usr/bin/env node// Bad news: We have to write plain ES5 in this file
|
||||
import semver from 'semver';
|
||||
#!/usr/bin/env node
|
||||
|
||||
import execa from 'execa';
|
||||
import findVersions from 'find-versions';
|
||||
import pkg from '../package.json';
|
||||
// Bad news: We have to write plain ES5 in this file
|
||||
// Good news: It's the only file of the entire project
|
||||
|
||||
const MIN_GIT_VERSION = '2.7.1';
|
||||
/* eslint-disable no-var */
|
||||
|
||||
var semver = require('semver');
|
||||
var execa = require('execa');
|
||||
var findVersions = require('find-versions');
|
||||
var pkg = require('../package.json');
|
||||
|
||||
var MIN_GIT_VERSION = '2.7.1';
|
||||
|
||||
if (!semver.satisfies(process.version, pkg.engines.node)) {
|
||||
console.error(
|
||||
@@ -18,13 +23,13 @@ See https://github.com/semantic-release/semantic-release/blob/master/docs/suppor
|
||||
|
||||
execa('git', ['--version'])
|
||||
.then(({stdout}) => {
|
||||
const gitVersion = findVersions(stdout)[0];
|
||||
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}.`);
|
||||
process.exit(1);
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
.catch(error => {
|
||||
console.error(`[semantic-release]: Git version ${MIN_GIT_VERSION} is required. No git binary found.`);
|
||||
console.error(error);
|
||||
process.exit(1);
|
||||
@@ -32,10 +37,9 @@ execa('git', ['--version'])
|
||||
|
||||
// Node 10+ from this point on
|
||||
require('../cli')()
|
||||
.then((exitCode) => {
|
||||
.then(exitCode => {
|
||||
process.exitCode = exitCode;
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
.catch(() => {
|
||||
process.exitCode = 1;
|
||||
});
|
||||
|
||||
@@ -1,22 +1,19 @@
|
||||
import util from 'util';
|
||||
import hideSensitive from './lib/hide-sensitive';
|
||||
import yargs from 'yargs';
|
||||
import debug from 'debug';
|
||||
|
||||
import run from '.';
|
||||
const {argv, env, stderr} = require('process'); // eslint-disable-line node/prefer-global/process
|
||||
const util = require('util');
|
||||
const hideSensitive = require('./lib/hide-sensitive');
|
||||
|
||||
const stringList = {
|
||||
type: 'string',
|
||||
array: true,
|
||||
coerce: (values) =>
|
||||
coerce: values =>
|
||||
values.length === 1 && values[0].trim() === 'false'
|
||||
? []
|
||||
: values.reduce((values, value) => values.concat(value.split(',').map((value) => value.trim())), []),
|
||||
: values.reduce((values, value) => values.concat(value.split(',').map(value => value.trim())), []),
|
||||
};
|
||||
|
||||
export default async () => {
|
||||
const cli = yargs
|
||||
.command('$0', 'Run automated package publishing', (yargs) => {
|
||||
module.exports = async () => {
|
||||
const cli = require('yargs')
|
||||
.command('$0', 'Run automated package publishing', yargs => {
|
||||
yargs.demandCommand(0, 0).usage(`Run automated package publishing
|
||||
|
||||
Usage:
|
||||
@@ -44,22 +41,22 @@ Usage:
|
||||
.exitProcess(false);
|
||||
|
||||
try {
|
||||
const {help, version, ...options} = cli.parse(process.argv.slice(2));
|
||||
const {help, version, ...opts} = cli.parse(argv.slice(2));
|
||||
|
||||
if (Boolean(help) || Boolean(version)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (options.debug) {
|
||||
if (opts.debug) {
|
||||
// Debug must be enabled before other requires in order to work
|
||||
debug.enable('semantic-release:*');
|
||||
require('debug').enable('semantic-release:*');
|
||||
}
|
||||
|
||||
await run(options);
|
||||
await require('.')(opts);
|
||||
return 0;
|
||||
} catch (error) {
|
||||
if (error.name !== 'YError') {
|
||||
process.stderr.write(hideSensitive(process.env)(util.inspect(error, {colors: true})));
|
||||
stderr.write(hideSensitive(env)(util.inspect(error, {colors: true})));
|
||||
}
|
||||
|
||||
return 1;
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
# semantic-release documentation
|
||||
|
||||
- [Usage](usage/README.md) - **semantic-release** installation and configuration
|
||||
- [Extending](extending/README.md) - Extending **semantic-release** with plugins and shareable configurations
|
||||
- [Extending](extending/README.md)- Extending **semantic-release** with plugins and shareable configurations
|
||||
- [Recipes](recipes/README.md) - Community written recipes for common **semantic-release** use-cases
|
||||
- [Developer Guide](developer-guide/README.md) - The essentials of writing a **semantic-release** plugin or shareable configurations
|
||||
- [Support](support/README.md) - FAQ and troubleshooting
|
||||
|
||||
@@ -1,18 +1,14 @@
|
||||
# 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 `verifyConditions` 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:
|
||||
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:
|
||||
|
||||
- `verifyConditions`
|
||||
- `analyzeCommits`
|
||||
- `verifyRelease`
|
||||
- `generateNotes`
|
||||
- `addChannel`
|
||||
- `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 `verifyConditions` and `success` step, the `main` file for your object would need to `export` an object with `verifyConditions` and `success` functions.
|
||||
`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:
|
||||
|
||||
@@ -97,136 +93,7 @@ if (message.length) {
|
||||
}
|
||||
```
|
||||
|
||||
## Context
|
||||
|
||||
### Common context keys
|
||||
|
||||
* `stdout`
|
||||
* `stderr`
|
||||
* `logger`
|
||||
|
||||
### Context object keys by lifecycle
|
||||
|
||||
#### verifyConditions
|
||||
|
||||
Initially the context object contains the following keys (`verifyConditions` lifecycle):
|
||||
* `cwd`
|
||||
* Current working directory
|
||||
* `env`
|
||||
* Environment variables
|
||||
* `envCi`
|
||||
* Information about CI environment
|
||||
* Contains (at least) the following keys:
|
||||
* `isCi`
|
||||
* Boolean, true if the environment is a CI environment
|
||||
* `commit`
|
||||
* Commit hash
|
||||
* `branch`
|
||||
* Current branch
|
||||
* `options`
|
||||
* Options passed to `semantic-release` via CLI, configuration files etc.
|
||||
* `branch`
|
||||
* Information on the current branch
|
||||
* Object keys:
|
||||
* `channel`
|
||||
* `tags`
|
||||
* `type`
|
||||
* `name`
|
||||
* `range`
|
||||
* `accept`
|
||||
* `main`
|
||||
* `branches`
|
||||
* Information on branches
|
||||
* List of branch objects (see above)
|
||||
|
||||
#### analyzeCommits
|
||||
|
||||
Compared to the verifyConditions, `analyzeCommits` lifecycle context has keys
|
||||
|
||||
* `commits` (List)
|
||||
* List of commits taken into account when determining the new version.
|
||||
* Keys:
|
||||
* `commit` (Object)
|
||||
* Keys:
|
||||
* `long` (String, Commit hash)
|
||||
* `short` (String, Commit hash)
|
||||
* `tree` (Object)
|
||||
* Keys:
|
||||
* `long` (String, Commit hash)
|
||||
* `short` (String, Commit hash)
|
||||
* `author` (Object)
|
||||
* Keys:
|
||||
* `name` (String)
|
||||
* `email` (String)
|
||||
* `date` (String, ISO 8601 timestamp)
|
||||
* `committer` (Object)
|
||||
* Keys:
|
||||
* `name` (String)
|
||||
* `email` (String)
|
||||
* `date` (String, ISO 8601 timestamp)
|
||||
* `subject` (String, Commit message subject)
|
||||
* `body` (String, Commit message body)
|
||||
* `hash` (String, Commit hash)
|
||||
* `committerDate` (String, ISO 8601 timestamp)
|
||||
* `message` (String)
|
||||
* `gitTags` (String, List of git tags)
|
||||
* `releases` (List)
|
||||
* `lastRelease` (Object)
|
||||
* Keys
|
||||
* `version` (String)
|
||||
* `gitTag` (String)
|
||||
* `channels` (List)
|
||||
* `gitHead` (String, Commit hash)
|
||||
* `name` (String)
|
||||
|
||||
#### verifyRelease
|
||||
|
||||
Additional keys:
|
||||
|
||||
* `nextRelease` (Object)
|
||||
* `type` (String)
|
||||
* `channel` (String)
|
||||
* `gitHead` (String, Git hash)
|
||||
* `version` (String, version without `v`)
|
||||
* `gitTag` (String, version with `v`)
|
||||
* `name` (String)
|
||||
|
||||
#### generateNotes
|
||||
|
||||
No new content in the context.
|
||||
|
||||
#### addChannel
|
||||
|
||||
*This is run only if there are releases that have been merged from a higher branch but not added on the channel of the current branch.*
|
||||
|
||||
Context content is similar to lifecycle `verifyRelease`.
|
||||
|
||||
#### prepare
|
||||
|
||||
Only change is that `generateNotes` has populated `nextRelease.notes`.
|
||||
|
||||
#### publish
|
||||
|
||||
No new content in the context.
|
||||
|
||||
#### success
|
||||
|
||||
Lifecycles `success` and `fail` are mutually exclusive, only one of them will be run.
|
||||
|
||||
Additional keys:
|
||||
|
||||
* `releases`
|
||||
* Populated by `publish` lifecycle
|
||||
|
||||
#### fail
|
||||
|
||||
Lifecycles `success` and `fail` are mutually exclusive, only one of them will be run.
|
||||
|
||||
Additional keys:
|
||||
|
||||
* `errors`
|
||||
|
||||
### Supporting Environment Variables
|
||||
## 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:
|
||||
|
||||
@@ -234,33 +101,6 @@ Similar to `options`, environment variables exist to allow users to pass tokens
|
||||
const { env } = context;
|
||||
|
||||
if (env.GITHUB_TOKEN) {
|
||||
//...
|
||||
//...
|
||||
}
|
||||
```
|
||||
|
||||
## Execution order
|
||||
|
||||
For the lifecycles, the list at the top of the readme contains the order. If there are multiple plugins for the same lifecycle, then the order of the plugins determines the order in which they are executed.
|
||||
|
||||
## Handling errors
|
||||
|
||||
In order to be able to detect and handle errors properly, the errors thrown from the must be of type [SemanticReleaseError](https://github.com/semantic-release/error) or extend it as described in the package readme. This way the errors are handled properly and plugins using the `fail` lifecycle receive the errors correctly. For any other types of errors the internal error handling does nothing, lets them through up until the final catch and does not call any `fail` plugins.
|
||||
|
||||
## Advanced
|
||||
|
||||
Knowledge that might be useful for plugin developers.
|
||||
|
||||
### Multiple analyzeCommits plugins
|
||||
|
||||
While it may be trivial that multiple analyzeCommits (or any lifecycle plugins) can be defined, it is not that self-evident that the plugins executed AFTER the first one (for example, the default one: `commit-analyzer`) can change the result. This way it is possible to create more advanced rules or situations, e.g. if none of the commits would result in new release, then a default can be defined.
|
||||
|
||||
The commit must be a known release type, for example the commit-analyzer has the following default types:
|
||||
* major
|
||||
* premajor
|
||||
* minor
|
||||
* preminor
|
||||
* patch
|
||||
* prepatch
|
||||
* prerelease
|
||||
|
||||
If the analyzeCommits-lifecycle plugin does not return anything, then the earlier result is used, but if it returns a supported string value, then that overrides the previous result.
|
||||
```
|
||||
@@ -20,7 +20,7 @@
|
||||
- `publish`: Publish the package on the npm registry
|
||||
- [@semantic-release/gitlab](https://github.com/semantic-release/gitlab)
|
||||
- `verifyConditions`: Verify the presence and the validity of the GitLab authentication and release configuration
|
||||
- `publish`: Publish a [GitLab release](https://docs.gitlab.com/ee/user/project/releases/)
|
||||
- `publish`: Publish a [GitLab release](https://docs.gitlab.com/ce/workflow/releases.html)
|
||||
- [@semantic-release/git](https://github.com/semantic-release/git)
|
||||
- `verifyConditions`: Verify the presence and the validity of the Git authentication and release configuration
|
||||
- `prepare`: Push a release commit and tag, including configurable files
|
||||
@@ -52,9 +52,6 @@
|
||||
- [semantic-release-docker](https://github.com/felixfbecker/semantic-release-docker)
|
||||
- `verifyConditions`: Verify that all needed configuration is present and login to the Docker registry.
|
||||
- `publish`: Tag the image specified by `name` with the new version, push it to Docker Hub and update the latest tag
|
||||
- [@semantic-release-plus/docker](https://github.com/semantic-release-plus/semantic-release-plus/tree/master/packages/plugins/docker)
|
||||
- `verifyConditions`: Verify that all needed configuration is present and login to the configured docker registry.
|
||||
- `publish`: Tag the image specified by `name` with the new version, push it to the configured docker registry and update the `latest`, `major`, `minor` tags based on the configuration settings.
|
||||
- [semantic-release-gcr](https://github.com/carlos-cubas/semantic-release-gcr)
|
||||
- `verifyConditions`: Verify that all needed configuration is present and login to the Docker registry
|
||||
- `publish`: Tag the image specified by `name` with the new version, push it to Docker Hub and update the latest tag
|
||||
@@ -94,9 +91,6 @@
|
||||
- [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.
|
||||
- [semantic-release-github-pullrequest](https://github.com/asbiin/semantic-release-github-pullrequest)
|
||||
- `verifyConditions`: Verify the presence and the validity of the GitHub authentication and other configuration.
|
||||
- `publish`: Create a branch to upload all assets and create the pull request on the base branch on GitHub.
|
||||
- [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.
|
||||
@@ -105,38 +99,3 @@
|
||||
- `verifyConditions`: Verify the presence and the validity of the authentication and the assets option configuration.
|
||||
- `publish`: Publish a Gitea release, optionally uploading file assets.
|
||||
- `addChannel`: Update a Gitea release's pre-release field.
|
||||
- [@google/semantic-release-replace-plugin](https://github.com/google/semantic-release-replace-plugin)
|
||||
- `prepare`: Replace version strings in files using regex and glob.
|
||||
- [semantic-release-rubygem](https://github.com/Gusto/semantic-release-rubygem)
|
||||
- `verifyConditions`: Locate and validate a `.gemspec` file, locate and validate a `lib/**/version.rb` file, verify the presence of the `GEM_HOST_API_KEY` environment variable, and create a credentials file with the API key.
|
||||
- `prepare`: Update the version in the `lib/**/version.rb` version file and [build](https://guides.rubygems.org/command-reference/#gem-build) the gem.
|
||||
- `publish`: [Push the Ruby gem](https://guides.rubygems.org/command-reference/#gem-push) to the gem server.
|
||||
- [semantic-release-npm-deprecate-old-versions](https://github.com/ghusse/semantic-release-npm-deprecate-old-versions)
|
||||
- `verifyConditions`: Validates configuration.
|
||||
- `publish`: Deprecates old versions, based on the declaration of supported versions in the config.
|
||||
- [amanda-mitchell/semantic-release-npm-multiple](https://github.com/amanda-mitchell/semantic-release-npm-multiple)
|
||||
- **Note**: this is a thin wrapper around the built-in npm plugin that can target multiple registries
|
||||
- `verifyConditions`: Verify the presence and the validity of the npm authentication and release configuration for multiple registries
|
||||
- `prepare`: Update the package.json version and create the npm package tarball
|
||||
- `publish`: Publish the package on the npm registry for multiple registries
|
||||
- [semantic-release-license](https://github.com/cbhq/semantic-release-license) Automatically update dates and more in your license file for new releases.
|
||||
- `verifyConditions`: Verify the presence of a license file
|
||||
- `prepare`: Update the license file based on its type
|
||||
- [semantic-release-pypi](https://github.com/abichinger/semantic-release-pypi)
|
||||
- `verifyConditions`: Verify the environment variable ```PYPI_TOKEN``` and installation of build tools
|
||||
- `prepare`: Update the version in ```setup.cfg``` and create the distribution packages
|
||||
- `publish`: Publish the python package to a repository (default: pypi)
|
||||
- [semantic-release-helm](https://github.com/m1pl/semantic-release-helm)
|
||||
- `verifyConditions`: Validate configuration and (if present) credentials
|
||||
- `prepare`: Update version and appVersion in ```Chart.yaml```
|
||||
- `publish`: Publish the chart to a registry (if configured)
|
||||
- [semantic-release-codeartifact](https://github.com/ryansonshine/semantic-release-codeartifact)
|
||||
- `verifyConditions`: Validate configuration, get AWS CodeArtifact authentication and repository, validate `publishConfig` or `.npmrc` (if they exist), then pass the configuration to the associated plugins.
|
||||
- [semantic-release-telegram](https://github.com/pustovitDmytro/semantic-release-telegram)
|
||||
- `verifyConditions`: Validate configuration and verify ```TELEGRAM_BOT_ID``` and ```TELEGRAM_BOT_TOKEN```
|
||||
- `success`: Publish a message about the successful release to a telegram chat
|
||||
- `fail`: publish a message about failure to a telegram chat
|
||||
- [semantic-release-heroku](https://github.com/pustovitDmytro/semantic-release-heroku)
|
||||
- `verifyConditions`: Validate configuration and verify ```HEROKU_API_KEY```
|
||||
- `prepare`: Update the package.json version and create release tarball
|
||||
- `publish`: Publish version to heroku
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
- [Travis CI](travis.md)
|
||||
- [GitLab CI](gitlab-ci.md)
|
||||
- [GitHub Actions](github-actions.md)
|
||||
- [Jenkins CI](jenkins-ci.md)
|
||||
|
||||
## Git hosted services
|
||||
- [Git authentication with SSH keys](git-auth-ssh-keys.md)
|
||||
|
||||
@@ -10,39 +10,46 @@ Alternatively, the default `NPM_TOKEN` and `GH_TOKEN` can be easily [setup with
|
||||
|
||||
### `.circleci/config.yml` configuration for multiple Node jobs
|
||||
|
||||
This example is a minimal configuration for **semantic-release** with tests running against Node 16 and 14. See [CircleCI documentation](https://circleci.com/docs/2.0) for additional configuration options.
|
||||
This example is a minimal configuration for **semantic-release** with a build running Node 6 and 8. See [CircleCI documentation](https://circleci.com/docs/2.0) for additional configuration options.
|
||||
|
||||
In this example, the [`circleci/node`](https://circleci.com/developer/orbs/orb/circleci/node) orb is imported (Which makes some node operations easier), then a `release` job is defined which will run `semantic-release`.
|
||||
|
||||
To run our `release` job, we have created a workflow named `test_and_release` which will run two jobs, `node/test`, which comes from the node orb and will test our application, and our release job. Here, we are actually making use of [matrix jobs](https://circleci.com/blog/circleci-matrix-jobs/) so that our single `node/test` job will actually be executed twice, once for Node version 16, and once for version 14. Finally, we call our release job with a `requires` parameter so that `release` will only run after `node/test` has successfully tested against v14 and v16.
|
||||
This example create the workflows `test_node_4`, `test_node_6`, `test_node_8` and `release`. The release workflows will [run `semantic-release` only after the all the `test_node_*` are successful](../usage/ci-configuration.md#run-semantic-release-only-after-all-tests-succeeded).
|
||||
|
||||
```yaml
|
||||
version: 2.1
|
||||
orbs:
|
||||
node: circleci/node@4.5
|
||||
version: 2
|
||||
jobs:
|
||||
test_node_6:
|
||||
docker:
|
||||
- image: circleci/node:6
|
||||
steps:
|
||||
# Configure your test steps here (checkout, npm install, cache management, tests etc...)
|
||||
|
||||
test_node_8:
|
||||
docker:
|
||||
- image: circleci/node:8
|
||||
steps:
|
||||
# Configure your test steps here (checkout, npm install, cache management, tests etc...)
|
||||
|
||||
release:
|
||||
executor: node/default
|
||||
docker:
|
||||
- image: circleci/node:8
|
||||
steps:
|
||||
- checkout
|
||||
- node/install-packages # Install and automatically cache packages
|
||||
- run: npm install
|
||||
# Run optional required steps before releasing
|
||||
# - run: npm run build-script
|
||||
- run: npx semantic-release
|
||||
|
||||
workflows:
|
||||
version: 2
|
||||
test_and_release:
|
||||
# Run the test jobs first, then the release only when all the test jobs are successful
|
||||
jobs:
|
||||
- node/test:
|
||||
matrix:
|
||||
parameters:
|
||||
version:
|
||||
- 16.1.0
|
||||
- 14.17.0
|
||||
- test_node_6
|
||||
- test_node_8
|
||||
- release:
|
||||
requires:
|
||||
- node/test
|
||||
- test_node_6
|
||||
- test_node_8
|
||||
```
|
||||
|
||||
### `package.json` configuration for multiple Node jobs
|
||||
@@ -52,7 +59,7 @@ A `package.json` is required only for [local](../usage/installation.md#local-ins
|
||||
```json
|
||||
{
|
||||
"devDependencies": {
|
||||
"semantic-release": "^18.0.0"
|
||||
"semantic-release": "^15.0.0"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -97,7 +97,7 @@ The Git history of the repository is now:
|
||||
|
||||
After a period of feedback from our users using the `@next` dist-tag we feel confident to make our big feature available to all users. To do so we merge the `next` branch into `master`. There should be no conflict as `next` is strictly ahead of `master`.
|
||||
|
||||
Once the merge commit is pushed to `master`, **semantic-release** will add the version `2.1.0` to the dist-tag `@latest` so all users will receive it when installing out module with `npm install example-module`.
|
||||
Once the merge commit is pushed to `next`, **semantic-release** will add the version `2.1.0` to the dist-tag `@latest` so all users will receive it when installing out module with `npm install example-module`.
|
||||
|
||||
The Git history of the repository is now:
|
||||
|
||||
|
||||
@@ -4,18 +4,17 @@
|
||||
|
||||
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 a publish type [`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.
|
||||
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 >= 14.17](../support/FAQ.md#why-does-semantic-release-require-node-version--1417).
|
||||
**Note**: The publish pipeline must run on [Node version >= 10.18](../support/FAQ.md#why-does-semantic-release-require-node-version--1018).
|
||||
|
||||
### `.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 14.17 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.
|
||||
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
|
||||
@@ -26,16 +25,14 @@ on:
|
||||
jobs:
|
||||
release:
|
||||
name: Release
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-18.04
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
uses: actions/checkout@v1
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: '14.17'
|
||||
node-version: 12
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
- name: Release
|
||||
@@ -51,23 +48,9 @@ To keep `package.json` updated in the `master` branch, [`@semantic-release/git`]
|
||||
|
||||
**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.
|
||||
|
||||
If the risk is acceptable, some extra configuration is needed. The [actions/checkout `persist-credentials`](https://github.com/marketplace/actions/checkout#usage) option needs to be `false`, otherwise the generated `GITHUB_TOKEN` will interfere with the custom one. Example:
|
||||
|
||||
```yaml
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
persist-credentials: false # <--- this
|
||||
```
|
||||
|
||||
## Trigger semantic-release on demand
|
||||
|
||||
### Using GUI:
|
||||
You can use [Manual Triggers](https://github.blog/changelog/2020-07-06-github-actions-manual-triggers-with-workflow_dispatch/) for GitHub Actions.
|
||||
|
||||
### Using HTTP:
|
||||
Use [`repository_dispatch`](https://docs.github.com/en/actions/reference/events-that-trigger-workflows#repository_dispatch) event to have control on when to generate a release by making an HTTP request, e.g.:
|
||||
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
|
||||
@@ -83,9 +66,3 @@ To trigger a release, call (with a [Personal Access Tokens](https://help.github.
|
||||
```
|
||||
$ 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" }'
|
||||
```
|
||||
|
||||
### Using 3rd party apps:
|
||||
If you'd like to use a GitHub app to manage this instead of creating a personal access token, you could consider using a project like:
|
||||
|
||||
* [Actions Panel](https://www.actionspanel.app/) - A declaratively configured way for triggering GitHub Actions
|
||||
* [Action Button](https://github-action-button.web.app/#details) - A simple badge based mechanism for triggering GitHub Actions
|
||||
|
||||
@@ -10,11 +10,11 @@ The [Authentication](../usage/ci-configuration.md#authentication) environment va
|
||||
|
||||
GitLab CI supports [Pipelines](https://docs.gitlab.com/ee/ci/pipelines.html) allowing to test on multiple Node versions and publishing a release only when all test pass.
|
||||
|
||||
**Note**: The publish pipeline must run a [Node >= 14.17 version](../support/FAQ.md#why-does-semantic-release-require-node-version--1417).
|
||||
**Note**: The publish pipeline must run a [Node >= 10.18 version](../support/FAQ.md#why-does-semantic-release-require-node-version--1018).
|
||||
|
||||
### `.gitlab-ci.yml` configuration for Node projects
|
||||
|
||||
This example is a minimal configuration for **semantic-release** with a build running Node 10 and 12. See [GitLab CI - Configuration of your jobs with .gitlab-ci.yml](https://docs.gitlab.com/ee/ci/yaml/README.html) for additional configuration options.
|
||||
This example is a minimal configuration for **semantic-release** with a build running Node 6 and 8. See [GitLab CI - Configuration of your jobs with .gitlab-ci.yml](https://docs.gitlab.com/ee/ci/yaml/README.html) for additional configuration options.
|
||||
|
||||
**Note**: The`semantic-release` execution command varies depending if you are using a [local](../usage/installation.md#local-installation) or [global](../usage/installation.md#global-installation) **semantic-release** installation.
|
||||
|
||||
@@ -27,60 +27,25 @@ stages:
|
||||
before_script:
|
||||
- npm install
|
||||
|
||||
node:10:
|
||||
image: node:10
|
||||
node:6:
|
||||
image: node:6
|
||||
stage: test
|
||||
script:
|
||||
- npm test
|
||||
|
||||
node:12:
|
||||
image: node:12
|
||||
node:8:
|
||||
image: node:8
|
||||
stage: test
|
||||
script:
|
||||
- npm test
|
||||
|
||||
publish:
|
||||
image: node:12
|
||||
image: node:8
|
||||
stage: release
|
||||
script:
|
||||
- npx semantic-release
|
||||
```
|
||||
|
||||
### `.gitlab-ci.yml` configuration for all projects
|
||||
|
||||
This example is a minimal configuration for **semantic-release** with a build running Node 10 and 12. See [GitLab CI - Configuration of your jobs with .gitlab-ci.yml](https://docs.gitlab.com/ee/ci/yaml/README.html) for additional configuration options.
|
||||
|
||||
**Note**: The`semantic-release` execution command varies depending if you are using a [local](../usage/installation.md#local-installation) or [global](../usage/installation.md#global-installation) **semantic-release** installation.
|
||||
|
||||
|
||||
```yaml
|
||||
# The release pipeline will run only on the master branch a commit is triggered
|
||||
stages:
|
||||
- release
|
||||
|
||||
release:
|
||||
image: node:10-buster-slim
|
||||
stage: release
|
||||
before_script:
|
||||
- apt-get update && apt-get install -y --no-install-recommends git-core ca-certificates
|
||||
- npm install -g semantic-release @semantic-release/gitlab
|
||||
script:
|
||||
- semantic-release
|
||||
only:
|
||||
- master
|
||||
|
||||
release:
|
||||
image: node:12-buster-slim
|
||||
stage: release
|
||||
before_script:
|
||||
- apt-get update && apt-get install -y --no-install-recommends git-core ca-certificates
|
||||
- npm install -g semantic-release @semantic-release/gitlab
|
||||
script:
|
||||
- semantic-release
|
||||
only:
|
||||
- master
|
||||
```
|
||||
|
||||
### `package.json` configuration
|
||||
|
||||
A `package.json` is required only for [local](../usage/installation.md#local-installation) **semantic-release** installation.
|
||||
|
||||
@@ -1,61 +0,0 @@
|
||||
# Using semantic-release with [Jenkins CI](https://www.jenkins.io/doc/book/pipeline/)
|
||||
|
||||
## Environment variables
|
||||
|
||||
The [Authentication](../usage/ci-configuration.md#authentication) environment variables can be configured in [Jenkins Project Settings](https://www.jenkins.io/doc/pipeline/tour/environment/)..
|
||||
|
||||
Alternatively, the default `NPM_TOKEN` and `GH_TOKEN` can be easily [setup with semantic-release-cli](../usage/getting-started.md#getting-started).
|
||||
|
||||
## Node.js project configuration
|
||||
|
||||
### `Jenkinsfile (Declarative Pipeline)` configuration for a Node.js job
|
||||
|
||||
**Note**: The publish pipeline must run a [Node >= 14.17 version](../support/FAQ.md#why-does-semantic-release-require-node-version--1417).
|
||||
|
||||
This example is a minimal configuration for **semantic-release** with a build running Node 14.17. See [Jenkins documentation](https://www.jenkins.io/doc/) for additional configuration options.
|
||||
|
||||
The`semantic-release` execution command varies depending if you are using a [local](../usage/installation.md#local-installation) or [global](../usage/installation.md#global-installation) **semantic-release** installation.
|
||||
|
||||
```yaml
|
||||
// The release stage in the pipeline will run only if the test stage in the pipeline is successful
|
||||
pipeline {
|
||||
agent any
|
||||
environment {
|
||||
GH_TOKEN = credentials('some-id')
|
||||
}
|
||||
stages {
|
||||
stage('Test') {
|
||||
steps {
|
||||
sh '''
|
||||
# Configure your test steps here (checkout, npm install, tests etc)
|
||||
npm install
|
||||
npm test
|
||||
'''
|
||||
}
|
||||
}
|
||||
stage('Release') {
|
||||
tools {
|
||||
nodejs "node 14.17"
|
||||
}
|
||||
steps {
|
||||
sh '''
|
||||
# Run optional required steps before releasing
|
||||
npx semantic-release
|
||||
'''
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### `package.json` configuration for a Node job
|
||||
|
||||
A `package.json` is required only for [local](../usage/installation.md#local-installation) **semantic-release** installation.
|
||||
|
||||
```json
|
||||
{
|
||||
"devDependencies": {
|
||||
"semantic-release": "^18.0.0"
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -33,7 +33,7 @@ The Git history of the repository is now:
|
||||
|
||||
One of our users request a new feature, however they cannot migrate to Node.js 8 or higher due to corporate policies.
|
||||
|
||||
If we were to push that feature on `master` and release it, the new version would require Node.js 8 or higher as the release would also contain the commit `feat: drop Node.js 6 support \n\n BREAKING CHANGE: Node.js >= 8 required`.
|
||||
If we were to push that feature on `master` and release it, the new version would require Node.js 8 or higher as the release would also contains the commit `feat: drop Node.js 6 support \n\n BREAKING CHANGE: Node.js >= 8 required`.
|
||||
|
||||
Instead, we create the branch `1.x` from the tag `v1.0.0` with the command `git checkout -b 1.x v1.0.0` and we commit that feature with the message `feat: a feature` to the branch `1.x`. When pushing that commit, **semantic-release** will release the version `1.1.0` on the dist-tag `@release-1.x` so users who can't migrate to Node.js 8 or higher can benefit from it.
|
||||
|
||||
@@ -48,7 +48,7 @@ The Git history of the repository is now:
|
||||
|
||||
## Releasing a bug fix for version 1.0.x users
|
||||
|
||||
Another user currently using version `1.0.0` reports a bug. They cannot migrate to Node.js 8 or higher and they also cannot migrate to `1.1.0` as they do not use the feature developed in `feat: a feature` and their corporate policies require to go through a costly quality assurance process for each `minor` upgrades.
|
||||
Another user currently using version `1.0.0` reports a bug. They cannot migrate to Node.js 8 or higher and they also cannot migrate to `1.1.0` as they do not use the feature developed in `feat: a feature` and their corporate policies require to go through a costly quality insurance process for each `minor` upgrades.
|
||||
|
||||
In order to deliver the bug fix in a `patch` release, we create the branch `1.0.x` from the tag `v1.0.0` with the command `git checkout -b 1.0.x v1.0.0` and we commit that fix with the message `fix: a fix` to the branch `1.0.x`. When pushing that commit, **semantic-release** will release the version `1.0.1` on the dist-tag `@release-1.0.x` so users who can't migrate to `1.1.x` or `2.x` can benefit from it.
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ Alternatively, the default `NPM_TOKEN` and `GH_TOKEN` can be easily [setup with
|
||||
|
||||
### `.travis.yml` configuration for multiple Node.js jobs
|
||||
|
||||
This example is a minimal configuration for **semantic-release** with a build running Node 14 and 16. See [Travis - Customizing the Build](https://docs.travis-ci.com/user/customizing-the-build) for additional configuration options.
|
||||
This example is a minimal configuration for **semantic-release** with a build running Node 6 and 8. See [Travis - Customizing the Build](https://docs.travis-ci.com/user/customizing-the-build) for additional configuration options.
|
||||
|
||||
This example creates a `release` [build stage](https://docs.travis-ci.com/user/build-stages) that [runs `semantic-release` only after all test jobs are successful](../usage/ci-configuration.md#run-semantic-release-only-after-all-tests-succeeded).
|
||||
|
||||
@@ -24,8 +24,8 @@ It's recommended to run the `semantic-release` command in the [Travis `deploy` s
|
||||
language: node_js
|
||||
|
||||
node_js:
|
||||
- 14
|
||||
- 16
|
||||
- 8
|
||||
- 6
|
||||
|
||||
jobs:
|
||||
include:
|
||||
@@ -48,7 +48,7 @@ A `package.json` is required only for [local](../usage/installation.md#local-ins
|
||||
```json
|
||||
{
|
||||
"devDependencies": {
|
||||
"semantic-release": "^18.0.0"
|
||||
"semantic-release": "^15.0.0"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
+35
-7
@@ -2,14 +2,12 @@
|
||||
|
||||
## Why is the `package.json`’s version not updated in my repository?
|
||||
|
||||
[`@semantic-release/npm`](https://github.com/semantic-release/npm) takes care of updating the `package.json`’s version before publishing to [npm](https://www.npmjs.com).
|
||||
**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 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.
|
||||
|
||||
If you wish to only update the `package.json` and push via Git you can set the project to `"private": true,` within your `package.json` to prevent publishing to [the npm registry](https://www.npmjs.com).
|
||||
|
||||
## How can I use a npm build script that requires the `package.json`’s version ?
|
||||
|
||||
The `package.json`’s version will be updated by the `semantic-release` command just before publishing to [npm](https://www.npmjs.com), therefore it won't be available for scripts ran before the `semantic-release` command.
|
||||
@@ -38,14 +36,44 @@ If using npm hook scripts is not possible, and alternative solution is to [`@sem
|
||||
|
||||
Yes with the [dry-run options](../usage/configuration.md#dryrun) which prints to the console the next version to be published and the release notes.
|
||||
|
||||
## 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 >= 10.18](#why-does-semantic-release-require-node-version--1018) 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:
|
||||
|
||||
```bash
|
||||
$ 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 >= 10.18.
|
||||
|
||||
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:
|
||||
|
||||
```bash
|
||||
$ nvm install 8 && yarn global add semantic-release && semantic-release
|
||||
```
|
||||
|
||||
See the [CI configuration recipes](../recipes/README.md#ci-configurations) for more details on specific CI environments.
|
||||
|
||||
As `semantic-release` is recommended to be executed with [`npx`](https://www.npmjs.com/package/npx) an alternative is required for usage with Yarn. Even though it is possible to install npx with Yarn, it's not recommended. Yarn and npx would be using different cache locations.
|
||||
|
||||
For [local installation](../usage/installation.md#local-installation) replace
|
||||
`npx semantic-release` with `yarn run semantic-release`.
|
||||
|
||||
For [global installation](../usage/installation.md#global-installation) replace
|
||||
`npx semantic-release` with `yarn global add semantic-release && semantic-release`.
|
||||
|
||||
## Can I use semantic-release to publish non-JavaScript packages?
|
||||
|
||||
Yes, **semantic-release** is a Node CLI application, but it can be used to publish any type of packages.
|
||||
Yes, **semantic-release** is a Node CLI application but it can be used to publish any type of packages.
|
||||
|
||||
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 >= 14.17](#why-does-semantic-release-require-node-version--1417) to execute the `semantic-release` command
|
||||
- Make sure your CI job executing the `semantic-release` command has access to [Node >= 10.18](#why-does-semantic-release-require-node-version--1018) to execute the `semantic-release` command
|
||||
|
||||
See the [CI configuration recipes](../recipes/README.md#ci-configurations) for more details on specific CI environments.
|
||||
|
||||
@@ -204,9 +232,9 @@ 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 >= 14.17?
|
||||
## Why does semantic-release require Node version >= 10.18?
|
||||
|
||||
**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 14.17 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 10.18 or higher**.
|
||||
|
||||
See [Node version requirement](./node-version.md#node-version-requirement) for more details and solutions.
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Node Support Policy
|
||||
|
||||
We only support [Long-Term Support](https://github.com/nodejs/Release) versions of Node starting with [Node 14.17.0 (LTS)](https://nodejs.org/en/blog/release/v14.17.0).
|
||||
We only support [Long-Term Support](https://github.com/nodejs/Release) versions of Node starting with [Node 8.9.0 (LTS)](https://nodejs.org/en/blog/release/v8.9.0).
|
||||
|
||||
We specifically limit our support to LTS versions of Node, not because this package won't work on other versions, but because we have a limited amount of time, and supporting LTS offers the greatest return on that investment.
|
||||
|
||||
|
||||
@@ -1,17 +1,16 @@
|
||||
# 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 14.17 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 10 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 14.17 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.
|
||||
|
||||
See our [Node Support Policy](node-support-policy.md) for our long-term promise regarding Node version support.
|
||||
|
||||
## Recommended solution
|
||||
|
||||
### Run at least one CI job with Node >= 14.17
|
||||
### Run at least one CI job with Node >= 10.18
|
||||
|
||||
The recommended approach is to run the `semantic-release` command from a CI job running on Node 14.17 or higher.
|
||||
This can either be a job used by your project to test on Node >= 14.17 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 10.18 or higher. This can either be a job used by your project to test on Node >= 10.18 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.
|
||||
|
||||
@@ -19,19 +18,18 @@ See [CI configuration](../usage/ci-configuration.md) and [CI configuration recip
|
||||
|
||||
### Use `npx`
|
||||
|
||||
`npx` is included with npm >= 5.2 and can be used to download the latest [Node 14 package published on npm](https://www.npmjs.com/package/node).
|
||||
Use it to execute the `semantic-release` command.
|
||||
`npx` is included with npm >= 5.2 and can be used to download the latest [Node 8 package published on npm](https://www.npmjs.com/package/node). Use it to execute the `semantic-release` command.
|
||||
|
||||
```bash
|
||||
$ npx -p node@14 -c "npx semantic-release"
|
||||
$ npx -p node@8 -c "npx semantic-release"
|
||||
```
|
||||
|
||||
**Note:**: See [What is npx](./FAQ.md#what-is-npx) for more details.
|
||||
|
||||
### Use `nvm`
|
||||
|
||||
If your CI environment provides [nvm](https://github.com/creationix/nvm) you can use it to switch to Node 14.17 before running the `semantic-release` command.
|
||||
If your CI environment provides [nvm](https://github.com/creationix/nvm) you can use it to switch to Node 8 before running the `semantic-release` command.
|
||||
|
||||
```bash
|
||||
$ nvm install 14.17 && npx semantic-release
|
||||
$ nvm install 8 && npx semantic-release
|
||||
```
|
||||
|
||||
@@ -14,8 +14,6 @@
|
||||
|
||||
- ["Introduction to SemVer" - Irina Gebauer](https://blog.greenkeeper.io/introduction-to-semver-d272990c44f2)
|
||||
- ["Introduction to Semantic Release" - liv](https://blog.greenkeeper.io/introduction-to-semantic-release-33f73b117c8)
|
||||
- ["Series - Semantic Release Automation" - Abdelrahman Wahdan](https://dev.to/abdelrahmanahmed/semantic-release-and-how-to-automate-it-part-1-4pa2)
|
||||
- ["Explain semantic release and how to use it on GitLab pipeline"](https://regoo707.medium.com/auto-bump-apps-versions-and-releases-using-gitlab-pipeline-e32f1d7fa3ee)
|
||||
|
||||
## Tutorials
|
||||
|
||||
|
||||
@@ -65,5 +65,5 @@ To recover from that situation, do the following:
|
||||
|
||||
1. Delete the tag(s) for the release(s) that have been lost from the git history. You can delete each tag from remote using `git push origin :[TAG NAME]`, e.g. `git push origin :v2.0.0-beta.1`. You can delete tags locally using `git tag -d [TAG NAME]`, e.g. `git tag -d v2.0.0-beta.1`.
|
||||
2. Re-create the tags locally: `git tag [TAG NAME] [COMMIT HASH]`, where `[COMMIT HASH]` is the new commit that created the release for the lost tag. E.g. `git tag v2.0.0-beta.1 abcdef0`
|
||||
3. Re-create the git notes for each release tag, e.g. `git notes --ref semantic-release add -f -m '{"channels":["beta"]}' v2.0.0-beta.1`. If the release was also published in the default channel (usually `master`), then set the first channel to `null`, e.g. `git notes --ref semantic-release add -f -m '{"channels":[null, "beta"]}'
|
||||
3. Re-create the git notes for each release tag, e.g. `git notes --ref semantic-release add -f -m '{"channels":["beta"]}' v3.0.0-beta.1`. If the release was also published in the default channel (usually `master`), then set the first channel to `null`, e.g. `git notes --ref semantic-release add -f -m '{"channels":[null, "beta"]}'
|
||||
4. Push the local notes: `git push --force origin refs/notes/semantic-release`. The `--force` is needed after the rebase. Be careful.
|
||||
|
||||
@@ -8,7 +8,7 @@ Here is a few example of the CI services that can be used to achieve this:
|
||||
- [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/ce/ci/introduction/)
|
||||
- [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).
|
||||
@@ -21,13 +21,12 @@ See [CI configuration recipes](../recipes/README.md#ci-configurations) for more
|
||||
|
||||
**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 |
|
||||
|-------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `GH_TOKEN` or `GITHUB_TOKEN` | A GitHub [personal access token](https://help.github.com/articles/creating-a-personal-access-token-for-the-command-line). |
|
||||
| `GL_TOKEN` or `GITLAB_TOKEN` | A GitLab [personal access token](https://docs.gitlab.com/ce/user/profile/personal_access_tokens.html). |
|
||||
| `BB_TOKEN` or `BITBUCKET_TOKEN` | A Bitbucket [personal access token](https://confluence.atlassian.com/bitbucketserver/personal-access-tokens-939515499.html). |
|
||||
| `BB_TOKEN_BASIC_AUTH` or `BITBUCKET_TOKEN_BASIC_AUTH` | A Bitbucket [personal access token](https://confluence.atlassian.com/bitbucketserver/personal-access-tokens-939515499.html) with basic auth support. For clearification `user:token` has to be the value of this env. |
|
||||
| `GIT_CREDENTIALS` | [URL encoded](https://en.wikipedia.org/wiki/Percent-encoding) Git username and password in the format `<username>:<password>`. The username and password must each be individually URL encoded, not the `:` separating them. |
|
||||
| Variable | Description |
|
||||
|---------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `GH_TOKEN` or `GITHUB_TOKEN` | A GitHub [personal access token](https://help.github.com/articles/creating-a-personal-access-token-for-the-command-line). |
|
||||
| `GL_TOKEN` or `GITLAB_TOKEN` | A GitLab [personal access token](https://docs.gitlab.com/ce/user/profile/personal_access_tokens.html). |
|
||||
| `BB_TOKEN` or `BITBUCKET_TOKEN` | A Bitbucket [personal access token](https://confluence.atlassian.com/bitbucketserver/personal-access-tokens-939515499.html). |
|
||||
| `GIT_CREDENTIALS` | [URL encoded](https://en.wikipedia.org/wiki/Percent-encoding) Git username and password in the format `<username>:<password>`. The username and password must each be individually URL encoded, not the `:` separating them. |
|
||||
|
||||
Alternatively the Git authentication can be set up via [SSH keys](../recipes/git-auth-ssh-keys.md).
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ The following three examples are the same.
|
||||
|
||||
- Via CLI argument:
|
||||
```bash
|
||||
$ semantic-release --branches next
|
||||
$ semantic-release --branch next
|
||||
```
|
||||
|
||||
**Note**: CLI arguments take precedence over options configured in the configuration file.
|
||||
@@ -74,7 +74,7 @@ The branches on which releases should happen. By default **semantic-release** wi
|
||||
|
||||
**Note**: If your repository does not have a release branch, then **semantic-release** will fail with an `ERELEASEBRANCHES` error message. If you are using the default configuration, you can fix this error by pushing a `master` branch.
|
||||
|
||||
**Note**: Once **semantic-release** is configured, any user with the permission to push commits on one of those branches will be able to publish a release. It is recommended to protect those branches, for example with [GitHub protected branches](https://docs.github.com/github/administering-a-repository/about-protected-branches).
|
||||
**Note**: Once **semantic-release** is configured, any user with the permission to push commits on one of those branches will be able to publish a release. It is recommended to protect those branches, for example with [GitHub protected branches](https://help.github.com/articles/about-protected-branches).
|
||||
|
||||
See [Workflow configuration](workflow-configuration.md#workflow-configuration) for more details.
|
||||
|
||||
@@ -116,9 +116,7 @@ Type: `Boolean`<br>
|
||||
Default: `false` if running in a CI environment, `true` otherwise<br>
|
||||
CLI arguments: `-d`, `--dry-run`
|
||||
|
||||
The objective of the dry-run mode is to get a preview of the pending release. Dry-run mode skips the following steps: prepare, publish, success and fail. In addition to this it prints the next version and release notes to the console.
|
||||
|
||||
**Note**: The Dry-run mode verifies the repository push permission, even though nothing will be pushed. The verification is done to help user to figure out potential configuration issues.
|
||||
Dry-run mode, skip publishing, print next version and release notes.
|
||||
|
||||
### ci
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
# Getting started
|
||||
|
||||
In order to use **semantic-release** you must follow these steps:
|
||||
|
||||
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
|
||||
@@ -10,8 +9,10 @@ In order to use **semantic-release** you must follow these steps:
|
||||
Alternatively those steps can be easily done with the [**semantic-release** interactive CLI](https://github.com/semantic-release/cli):
|
||||
|
||||
```bash
|
||||
npm install -g semantic-release-cli
|
||||
|
||||
cd your-module
|
||||
npx semantic-release-cli setup
|
||||
semantic-release-cli setup
|
||||
```
|
||||
|
||||

|
||||
|
||||
@@ -24,7 +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@18`).
|
||||
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.
|
||||
|
||||
@@ -12,24 +12,21 @@ A plugin is a npm module that can implement one or more of the following steps:
|
||||
| `generateNotes` | No | Responsible for generating the content of the release note. If multiple plugins with a `generateNotes` step are defined, the release notes will be the result of the concatenation of each plugin output. |
|
||||
| `prepare` | No | Responsible for preparing the release, for example creating or updating files such as `package.json`, `CHANGELOG.md`, documentation or compiled assets and pushing a commit. |
|
||||
| `publish` | No | Responsible for publishing the release. |
|
||||
| `addChannel` | No | Responsible for adding a release channel (e.g. adding an npm dist-tag to a release). |
|
||||
| `success` | No | Responsible for notifying of a new release. |
|
||||
| `fail` | No | Responsible for notifying of a failed release. |
|
||||
|
||||
Release steps will run in that order. At each step, **semantic-release** will run every plugin in the [`plugins` array](#plugins-declaration-and-execution-order), as long as the plugin implements the step.
|
||||
|
||||
**Note:** If no plugin with a `analyzeCommits` step is defined `@semantic-release/commit-analyzer` will be used.
|
||||
|
||||
## Plugins installation
|
||||
|
||||
### Default plugins
|
||||
|
||||
These four plugins are already part of **semantic-release** and are listed in order of execution. They do not have to be installed separately:
|
||||
These four plugins are already part of **semantic-release** and don't have to be installed separately:
|
||||
```
|
||||
"@semantic-release/commit-analyzer"
|
||||
"@semantic-release/release-notes-generator"
|
||||
"@semantic-release/npm"
|
||||
"@semantic-release/github"
|
||||
"@semantic-release/npm"
|
||||
"@semantic-release/release-notes-generator"
|
||||
```
|
||||
|
||||
### Additional plugins
|
||||
@@ -72,8 +69,6 @@ With this configuration **semantic-release** will:
|
||||
- execute the `prepare` implementation of `@semantic-release/npm` then `@semantic-release/git`
|
||||
- execute the `publish` implementation of `@semantic-release/npm`
|
||||
|
||||
Order is first determined by release steps (such as `verifyConditions` → `anayzeCommits`). At each release step, plugins are executed in the order in which they are defined.
|
||||
|
||||
## Plugin options configuration
|
||||
|
||||
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.
|
||||
|
||||
@@ -13,7 +13,7 @@ Each branch can be defined either as a string, a [glob](https://github.com/micro
|
||||
|
||||
A branch can defined as one of three types:
|
||||
- [release](#release-branches): to make releases on top of the last version released
|
||||
- [maintenance](#maintenance-branches): to make releases on top of an old release
|
||||
- [maintenance](#maintenance-branches): to make release on top of an old release
|
||||
- [pre-release](#pre-release-branches): to make pre-releases
|
||||
|
||||
The type of the branch is automatically determined based on naming convention and/or [properties](#branches-properties).
|
||||
@@ -25,7 +25,7 @@ The type of the branch is automatically determined based on naming convention an
|
||||
| `name` | All | **Required.** The Git branch holding the commits to analyze and the code to release. See [name](#name). | - The value itself if defined as a `String` or the matching branches name if defined as a glob. |
|
||||
| `channel` | All | The distribution channel on which to publish releases from this branch. Set to `false` to force the default distribution channel instead of using the default. See [channel](#channel). | `undefined` for the first release branch, the value of `name` for subsequent ones. |
|
||||
| `range` | [maintenance](#maintenance-branches) only | **Required unless `name` is formatted like `N.N.x` or `N.x` (`N` is a number).** The range of [semantic versions](https://semver.org) to support on this branch. See [range](#range). | The value of `name`. |
|
||||
| `prerelease` | [pre-release](#pre-release-branches) only | **Required.** The pre-release denotation to append to [semantic versions](https://semver.org) released from this branch. See [prerelease](#prerelease). | - |
|
||||
| `prerelease` | [pre-release](#pre-release-branches) only | **Required.** The pre-release detonation to append to [semantic versions](https://semver.org) released from this branch. See [prerelease](#prerelease). | - |
|
||||
|
||||
### name
|
||||
|
||||
@@ -140,7 +140,7 @@ A maintenance branch is characterized by a range which defines the versions that
|
||||
|
||||
Maintenance branches are always considered lower than [release branches](#release-branches) and similarly to them, when a commit that would create a version conflict is pushed, **semantic-release** will not perform the release and will throw an `EINVALIDNEXTVERSION` error, listing the problematic commits and the valid branches on which to move them.
|
||||
|
||||
**semantic-release** will automatically add releases to the corresponding distribution channel when code is [merged from a release or maintenance branch to another maintenance branch](#merging-into-a-maintenance-branch), however only versions within the branch `range` can be merged. If a merged version is outside the maintenance branch `range`, **semantic-release** will not add to the corresponding channel and will throw an `EINVALIDMAINTENANCEMERGE` error.
|
||||
**semantic-release** will automatically add releases to the corresponding distribution channel when code is [merged from a release or maintenance branch to another maintenance branch](#merging-into-a-maintenance-branch), however only version version within the branch `range` can be merged. Ia merged version is outside the maintenance branch `range` **semantic-release** will not add to the corresponding channel and will throw an `EINVALIDMAINTENANCEMERGE` error.
|
||||
|
||||
See [publishing maintenance releases recipe](../recipes/maintenance-releases.md) for a detailed example.
|
||||
|
||||
@@ -162,7 +162,7 @@ With the configuration `"branches": ["1.0.x", "1.x", "master"]`, if the last rel
|
||||
### Pre-release branches
|
||||
|
||||
A pre-release branch is a type of branch used by **semantic-release** that allows to publish releases with a [pre-release version](https://semver.org/#spec-item-9).
|
||||
Using a pre-release version allow to publish multiple releases with the same version. Those release will be differentiated via their identifiers (in `1.0.0-alpha.1` the identifier is `alpha.1`).
|
||||
Using a pre-release version allow to publish multiple releases with the same version. Those release will be differentiated via there identifiers (in `1.0.0-alpha.1` the identifier is `alpha.1`).
|
||||
This is useful when you need to work on a future major release that will include many breaking changes but you do not want to increment the version number for each breaking change commit.
|
||||
|
||||
A pre-release branch is characterized by the `prerelease` property that defines the static part of the version released (in `1.0.0-alpha.1` the static part fo the identifier is `alpha`). The [`prerelease`](#prerelease) value of each pre-release branch must be unique across the project.
|
||||
|
||||
@@ -1,36 +1,32 @@
|
||||
import lodash from 'lodash'
|
||||
const {pick} = lodash
|
||||
import marked from 'marked';
|
||||
import TerminalRenderer from 'marked-terminal';
|
||||
import envCi from 'env-ci';
|
||||
import hookStd from 'hook-std';
|
||||
import semver from 'semver';
|
||||
import AggregateError from 'aggregate-error';
|
||||
import pkg from './package.json';
|
||||
import hideSensitive from './lib/hide-sensitive';
|
||||
import getConfig from './lib/get-config';
|
||||
import verify from './lib/verify';
|
||||
import getNextVersion from './lib/get-next-version';
|
||||
import getCommits from './lib/get-commits';
|
||||
import getLastRelease from './lib/get-last-release';
|
||||
import getReleaseToAdd from './lib/get-release-to-add';
|
||||
import {extractErrors, makeTag} from './lib/utils';
|
||||
import getGitAuthUrl from './lib/get-git-auth-url';
|
||||
import getBranches from './lib/branches';
|
||||
import getLogger from './lib/get-logger';
|
||||
|
||||
import {verifyAuth, isBranchUpToDate, getGitHead, tag, push, pushNotes, getTagHead, addNote} from './lib/git';
|
||||
|
||||
import getError from './lib/get-error';
|
||||
import {COMMIT_NAME, COMMIT_EMAIL} from './lib/definitions/constants';
|
||||
const {pick} = require('lodash');
|
||||
const marked = require('marked');
|
||||
const TerminalRenderer = require('marked-terminal');
|
||||
const envCi = require('env-ci');
|
||||
const hookStd = require('hook-std');
|
||||
const semver = require('semver');
|
||||
const AggregateError = require('aggregate-error');
|
||||
const pkg = require('./package.json');
|
||||
const hideSensitive = require('./lib/hide-sensitive');
|
||||
const getConfig = require('./lib/get-config');
|
||||
const verify = require('./lib/verify');
|
||||
const getNextVersion = require('./lib/get-next-version');
|
||||
const getCommits = require('./lib/get-commits');
|
||||
const getLastRelease = require('./lib/get-last-release');
|
||||
const getReleaseToAdd = require('./lib/get-release-to-add');
|
||||
const {extractErrors, makeTag} = require('./lib/utils');
|
||||
const getGitAuthUrl = require('./lib/get-git-auth-url');
|
||||
const getBranches = require('./lib/branches');
|
||||
const getLogger = require('./lib/get-logger');
|
||||
const {verifyAuth, isBranchUpToDate, getGitHead, tag, push, pushNotes, getTagHead, addNote} = require('./lib/git');
|
||||
const getError = require('./lib/get-error');
|
||||
const {COMMIT_NAME, COMMIT_EMAIL} = require('./lib/definitions/constants');
|
||||
|
||||
marked.setOptions({renderer: new TerminalRenderer()});
|
||||
|
||||
/* eslint complexity: off */
|
||||
async function run(context, plugins) {
|
||||
const {cwd, env, options, logger} = context;
|
||||
const {isCi, branch, prBranch, isPr} = context.envCi;
|
||||
const ciBranch = isPr ? prBranch : branch;
|
||||
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.');
|
||||
@@ -56,7 +52,7 @@ async function run(context, plugins) {
|
||||
// Verify config
|
||||
await verify(context);
|
||||
|
||||
options.repositoryUrl = await getGitAuthUrl({...context, branch: {name: ciBranch}});
|
||||
options.repositoryUrl = await getGitAuthUrl(context);
|
||||
context.branches = await getBranches(options.repositoryUrl, ciBranch, context);
|
||||
context.branch = context.branches.find(({name}) => name === ciBranch);
|
||||
|
||||
@@ -70,9 +66,7 @@ async function run(context, plugins) {
|
||||
}
|
||||
|
||||
logger[options.dryRun ? 'warn' : 'success'](
|
||||
`Run automated release from branch ${ciBranch} on repository ${options.repositoryUrl}${
|
||||
options.dryRun ? ' in dry-run mode' : ''
|
||||
}`
|
||||
`Run automated release from branch ${ciBranch}${options.dryRun ? ' in dry-run mode' : ''}`
|
||||
);
|
||||
|
||||
try {
|
||||
@@ -218,7 +212,7 @@ async function run(context, plugins) {
|
||||
}
|
||||
|
||||
function logErrors({logger, stderr}, err) {
|
||||
const errors = extractErrors(err).sort((error) => (error.semanticRelease ? -1 : 0));
|
||||
const errors = extractErrors(err).sort(error => (error.semanticRelease ? -1 : 0));
|
||||
for (const error of errors) {
|
||||
if (error.semanticRelease) {
|
||||
logger.error(`${error.code} ${error.message}`);
|
||||
@@ -232,7 +226,7 @@ function logErrors({logger, stderr}, err) {
|
||||
}
|
||||
|
||||
async function callFail(context, plugins, err) {
|
||||
const errors = extractErrors(err).filter((err) => err.semanticRelease);
|
||||
const errors = extractErrors(err).filter(err => err.semanticRelease);
|
||||
if (errors.length > 0) {
|
||||
try {
|
||||
await plugins.fail({...context, errors});
|
||||
@@ -242,7 +236,7 @@ async function callFail(context, plugins, err) {
|
||||
}
|
||||
}
|
||||
|
||||
export default async (cliOptions = {}, {cwd = process.cwd(), env = process.env, stdout, stderr} = {}) => {
|
||||
module.exports = async (opts = {}, {cwd = process.cwd(), env = process.env, stdout, stderr} = {}) => {
|
||||
const {unhook} = hookStd(
|
||||
{silent: false, streams: [process.stdout, process.stderr, stdout, stderr].filter(Boolean)},
|
||||
hideSensitive(env)
|
||||
@@ -257,7 +251,7 @@ export default async (cliOptions = {}, {cwd = process.cwd(), env = process.env,
|
||||
context.logger = getLogger(context);
|
||||
context.logger.log(`Running ${pkg.name} version ${pkg.version}`);
|
||||
try {
|
||||
const {plugins, options} = await getConfig(context, cliOptions);
|
||||
const {plugins, options} = await getConfig(context, opts);
|
||||
context.options = options;
|
||||
try {
|
||||
const result = await run(context, plugins);
|
||||
|
||||
@@ -1,17 +1,16 @@
|
||||
import lodash from 'lodash'
|
||||
const {isString, remove, omit, mapValues, template} = lodash
|
||||
import micromatch from 'micromatch';
|
||||
import {getBranches} from '../git.js';
|
||||
const {isString, remove, omit, mapValues, template} = require('lodash');
|
||||
const micromatch = require('micromatch');
|
||||
const {getBranches} = require('../git');
|
||||
|
||||
export default async (repositoryUrl, {cwd}, branches) => {
|
||||
module.exports = async (repositoryUrl, {cwd}, branches) => {
|
||||
const gitBranches = await getBranches(repositoryUrl, {cwd});
|
||||
|
||||
return branches.reduce(
|
||||
(branches, branch) => [
|
||||
...branches,
|
||||
...remove(gitBranches, (name) => micromatch(gitBranches, branch.name).includes(name)).map((name) => ({
|
||||
...remove(gitBranches, name => micromatch(gitBranches, branch.name).includes(name)).map(name => ({
|
||||
name,
|
||||
...mapValues(omit(branch, 'name'), (value) => (isString(value) ? template(value)({name}) : value)),
|
||||
...mapValues(omit(branch, 'name'), value => (isString(value) ? template(value)({name}) : value)),
|
||||
})),
|
||||
],
|
||||
[]
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
import lodash from 'lodash'
|
||||
const {template, escapeRegExp} = lodash
|
||||
import semver from 'semver';
|
||||
import pReduce from 'p-reduce';
|
||||
import debugFactory from 'debug';
|
||||
const debug = debugFactory('semantic-release:get-tags');
|
||||
import {getTags, getNote} from '../../lib/git.js';
|
||||
const {template, escapeRegExp} = require('lodash');
|
||||
const semver = require('semver');
|
||||
const pReduce = require('p-reduce');
|
||||
const debug = require('debug')('semantic-release:get-tags');
|
||||
const {getTags, getNote} = require('../../lib/git');
|
||||
|
||||
export default async ({cwd, env, options: {tagFormat}}, branches) => {
|
||||
module.exports = async ({cwd, env, options: {tagFormat}}, branches) => {
|
||||
// Generate a regex to parse tags formatted with `tagFormat`
|
||||
// by replacing the `version` variable in the template by `(.+)`.
|
||||
// The `tagFormat` is compiled with space as the `version` as it's an invalid tag character,
|
||||
|
||||
+15
-16
@@ -1,21 +1,20 @@
|
||||
import lodash from 'lodash'
|
||||
const {isString, isRegExp} = lodash
|
||||
import AggregateError from 'aggregate-error';
|
||||
import pEachSeries from 'p-each-series';
|
||||
import DEFINITIONS from '../definitions/branches.js';
|
||||
import getError from '../get-error.js';
|
||||
import {fetch, fetchNotes, verifyBranchName} from '../git.js';
|
||||
import expand from './expand.js';
|
||||
import getTags from './get-tags.js';
|
||||
import normalize from './normalize.js';
|
||||
const {isString, isRegExp} = require('lodash');
|
||||
const AggregateError = require('aggregate-error');
|
||||
const pEachSeries = require('p-each-series');
|
||||
const DEFINITIONS = require('../definitions/branches');
|
||||
const getError = require('../get-error');
|
||||
const {fetch, fetchNotes, verifyBranchName} = require('../git');
|
||||
const expand = require('./expand');
|
||||
const getTags = require('./get-tags');
|
||||
const normalize = require('./normalize');
|
||||
|
||||
export default async (repositoryUrl, ciBranch, context) => {
|
||||
module.exports = async (repositoryUrl, ciBranch, context) => {
|
||||
const {cwd, env} = context;
|
||||
|
||||
const remoteBranches = await expand(
|
||||
repositoryUrl,
|
||||
context,
|
||||
context.options.branches.map((branch) => (isString(branch) || isRegExp(branch) ? {name: branch} : branch))
|
||||
context.options.branches.map(branch => (isString(branch) || isRegExp(branch) ? {name: branch} : branch))
|
||||
);
|
||||
|
||||
await pEachSeries(remoteBranches, async ({name}) => {
|
||||
@@ -33,7 +32,7 @@ export default async (repositoryUrl, ciBranch, context) => {
|
||||
);
|
||||
|
||||
const result = Object.entries(DEFINITIONS).reduce((result, [type, {branchesValidator, branchValidator}]) => {
|
||||
branchesByType[type].forEach((branch) => {
|
||||
branchesByType[type].forEach(branch => {
|
||||
if (branchValidator && !branchValidator(branch)) {
|
||||
errors.push(getError(`E${type.toUpperCase()}BRANCH`, {branch}));
|
||||
}
|
||||
@@ -49,15 +48,15 @@ export default async (repositoryUrl, ciBranch, context) => {
|
||||
}, {});
|
||||
|
||||
const duplicates = [...branches]
|
||||
.map((branch) => branch.name)
|
||||
.map(branch => branch.name)
|
||||
.sort()
|
||||
.filter((_, idx, array) => array[idx] === array[idx + 1] && array[idx] !== array[idx - 1]);
|
||||
.filter((_, idx, arr) => arr[idx] === arr[idx + 1] && arr[idx] !== arr[idx - 1]);
|
||||
|
||||
if (duplicates.length > 0) {
|
||||
errors.push(getError('EDUPLICATEBRANCHES', {duplicates}));
|
||||
}
|
||||
|
||||
await pEachSeries(branches, async (branch) => {
|
||||
await pEachSeries(branches, async branch => {
|
||||
if (!(await verifyBranchName(branch.name))) {
|
||||
errors.push(getError('EINVALIDBRANCHNAME', branch));
|
||||
}
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
import lodash from 'lodash'
|
||||
const {sortBy, isNil} = lodash
|
||||
import semverDiff from 'semver-diff';
|
||||
import {FIRST_RELEASE, RELEASE_TYPE} from '../definitions/constants.js';
|
||||
|
||||
import {
|
||||
const {sortBy, isNil} = require('lodash');
|
||||
const semverDiff = require('semver-diff');
|
||||
const {FIRST_RELEASE, RELEASE_TYPE} = require('../definitions/constants');
|
||||
const {
|
||||
tagsToVersions,
|
||||
isMajorRange,
|
||||
getUpperBound,
|
||||
@@ -13,7 +11,7 @@ import {
|
||||
getLatestVersion,
|
||||
getFirstVersion,
|
||||
getRange,
|
||||
} from '../utils.js';
|
||||
} = require('../utils');
|
||||
|
||||
function maintenance({maintenance, release}) {
|
||||
return sortBy(
|
||||
@@ -105,11 +103,4 @@ function prerelease({prerelease}) {
|
||||
});
|
||||
}
|
||||
|
||||
const exported = {
|
||||
maintenance,
|
||||
release,
|
||||
prerelease,
|
||||
};
|
||||
|
||||
export default exported;
|
||||
export {maintenance, release, prerelease};
|
||||
module.exports = {maintenance, release, prerelease};
|
||||
|
||||
@@ -1,31 +1,23 @@
|
||||
import lodash from 'lodash'
|
||||
const {isNil, uniqBy} = lodash
|
||||
import semver from 'semver';
|
||||
import {isMaintenanceRange} from '../utils.js';
|
||||
const {isNil, uniqBy} = require('lodash');
|
||||
const semver = require('semver');
|
||||
const {isMaintenanceRange} = require('../utils');
|
||||
|
||||
const maintenance = {
|
||||
filter: ({name, range}) => (!isNil(range) && range !== false) || isMaintenanceRange(name),
|
||||
branchValidator: ({range}) => (isNil(range) ? true : isMaintenanceRange(range)),
|
||||
branchesValidator: (branches) => uniqBy(branches, ({range}) => semver.validRange(range)).length === branches.length,
|
||||
branchesValidator: branches => uniqBy(branches, ({range}) => semver.validRange(range)).length === branches.length,
|
||||
};
|
||||
|
||||
const prerelease = {
|
||||
filter: ({prerelease}) => !isNil(prerelease) && prerelease !== false,
|
||||
branchValidator: ({name, prerelease}) =>
|
||||
Boolean(prerelease) && Boolean(semver.valid(`1.0.0-${prerelease === true ? name : prerelease}.1`)),
|
||||
branchesValidator: (branches) => uniqBy(branches, 'prerelease').length === branches.length,
|
||||
branchesValidator: branches => uniqBy(branches, 'prerelease').length === branches.length,
|
||||
};
|
||||
|
||||
const release = {
|
||||
filter: (branch) => !maintenance.filter(branch) && !prerelease.filter(branch),
|
||||
branchesValidator: (branches) => branches.length <= 3 && branches.length > 0,
|
||||
filter: branch => !maintenance.filter(branch) && !prerelease.filter(branch),
|
||||
branchesValidator: branches => branches.length <= 3 && branches.length > 0,
|
||||
};
|
||||
|
||||
const exported = {
|
||||
maintenance,
|
||||
prerelease,
|
||||
release,
|
||||
};
|
||||
|
||||
export default exported;
|
||||
export {maintenance, prerelease, release};
|
||||
module.exports = {maintenance, prerelease, release};
|
||||
|
||||
@@ -16,20 +16,7 @@ const SECRET_MIN_SIZE = 5;
|
||||
|
||||
const GIT_NOTE_REF = 'semantic-release';
|
||||
|
||||
const exported = {
|
||||
RELEASE_TYPE,
|
||||
FIRST_RELEASE,
|
||||
FIRSTPRERELEASE,
|
||||
COMMIT_NAME,
|
||||
COMMIT_EMAIL,
|
||||
RELEASE_NOTES_SEPARATOR,
|
||||
SECRET_REPLACEMENT,
|
||||
SECRET_MIN_SIZE,
|
||||
GIT_NOTE_REF,
|
||||
};
|
||||
|
||||
export default exported;
|
||||
export {
|
||||
module.exports = {
|
||||
RELEASE_TYPE,
|
||||
FIRST_RELEASE,
|
||||
FIRSTPRERELEASE,
|
||||
|
||||
+11
-61
@@ -1,17 +1,15 @@
|
||||
import {inspect} from 'util';
|
||||
import lodash from 'lodash'
|
||||
const {toLower, isString, trim} = lodash
|
||||
import pkg from '../../package.json';
|
||||
import {RELEASE_TYPE} from './constants.js';
|
||||
const {inspect} = require('util');
|
||||
const {toLower, isString, trim} = require('lodash');
|
||||
const pkg = require('../../package.json');
|
||||
const {RELEASE_TYPE} = require('./constants');
|
||||
|
||||
const [homepage] = pkg.homepage.split('#');
|
||||
const stringify = (object) =>
|
||||
isString(object) ? object : inspect(object, {breakLength: Infinity, depth: 2, maxArrayLength: 5});
|
||||
const linkify = (file) => `${homepage}/blob/master/${file}`;
|
||||
const wordsList = (words) =>
|
||||
const stringify = obj => (isString(obj) ? obj : inspect(obj, {breakLength: Infinity, depth: 2, maxArrayLength: 5}));
|
||||
const linkify = file => `${homepage}/blob/master/${file}`;
|
||||
const wordsList = words =>
|
||||
`${words.slice(0, -1).join(', ')}${words.length > 1 ? ` or ${words[words.length - 1]}` : trim(words[0])}`;
|
||||
|
||||
const exported = {
|
||||
module.exports = {
|
||||
ENOGITREPO: ({cwd}) => ({
|
||||
message: 'Not running from a git repository.',
|
||||
details: `The \`semantic-release\` command must be executed from a Git repository.
|
||||
@@ -20,7 +18,6 @@ The current working directory is \`${cwd}\`.
|
||||
|
||||
Please verify your CI configuration to make sure the \`semantic-release\` command is executed from the root of the cloned repository.`,
|
||||
}),
|
||||
|
||||
ENOREPOURL: () => ({
|
||||
message: 'The `repositoryUrl` option is required.',
|
||||
details: `The [repositoryUrl option](${linkify(
|
||||
@@ -31,7 +28,6 @@ Please make sure to add the \`repositoryUrl\` to the [semantic-release configura
|
||||
'docs/usage/configuration.md'
|
||||
)}).`,
|
||||
}),
|
||||
|
||||
EGITNOPERMISSION: ({options: {repositoryUrl}, branch: {name}}) => ({
|
||||
message: 'Cannot push to the Git repository.',
|
||||
details: `**semantic-release** cannot push the version tag to the branch \`${name}\` on the remote Git repository with URL \`${repositoryUrl}\`.
|
||||
@@ -43,7 +39,6 @@ This can be caused by:
|
||||
'docs/usage/ci-configuration.md#authentication'
|
||||
)})`,
|
||||
}),
|
||||
|
||||
EINVALIDTAGFORMAT: ({options: {tagFormat}}) => ({
|
||||
message: 'Invalid `tagFormat` option.',
|
||||
details: `The [tagFormat](${linkify(
|
||||
@@ -52,7 +47,6 @@ This can be caused by:
|
||||
|
||||
Your configuration for the \`tagFormat\` option is \`${stringify(tagFormat)}\`.`,
|
||||
}),
|
||||
|
||||
ETAGNOVERSION: ({options: {tagFormat}}) => ({
|
||||
message: 'Invalid `tagFormat` option.',
|
||||
details: `The [tagFormat](${linkify(
|
||||
@@ -61,25 +55,22 @@ Your configuration for the \`tagFormat\` option is \`${stringify(tagFormat)}\`.`
|
||||
|
||||
Your configuration for the \`tagFormat\` option is \`${stringify(tagFormat)}\`.`,
|
||||
}),
|
||||
|
||||
EPLUGINCONF: ({type, required, pluginConf}) => ({
|
||||
message: `The \`${type}\` plugin configuration is invalid.`,
|
||||
details: `The [${type} plugin configuration](${linkify(`docs/usage/plugins.md#${toLower(type)}-plugin`)}) ${
|
||||
required ? 'is required and ' : ''
|
||||
} must be a single or an array of plugins definition. A plugin definition is an npm module name, optionally wrapped in an array with an object.
|
||||
} must be a single or an array of plugins definition. A plugin definition is an npm module name, optionnaly wrapped in an array with an object.
|
||||
|
||||
Your configuration for the \`${type}\` plugin is \`${stringify(pluginConf)}\`.`,
|
||||
}),
|
||||
|
||||
EPLUGINSCONF: ({plugin}) => ({
|
||||
message: 'The `plugins` configuration is invalid.',
|
||||
details: `The [plugins](${linkify(
|
||||
'docs/usage/configuration.md#plugins'
|
||||
)}) option must be an array of plugin definions. A plugin definition is an npm module name, optionally wrapped in an array with an object.
|
||||
)}) option must be an array of plugin definions. A plugin definition is an npm module name, optionnaly wrapped in an array with an object.
|
||||
|
||||
The invalid configuration is \`${stringify(plugin)}\`.`,
|
||||
}),
|
||||
|
||||
EPLUGIN: ({pluginName, type}) => ({
|
||||
message: `A plugin configured in the step ${type} is not a valid semantic-release plugin.`,
|
||||
details: `A valid \`${type}\` **semantic-release** plugin must be a function or an object with a function in the property \`${type}\`.
|
||||
@@ -90,11 +81,10 @@ Please refer to the \`${pluginName}\` and [semantic-release plugins configuratio
|
||||
'docs/usage/plugins.md'
|
||||
)}) documentation for more details.`,
|
||||
}),
|
||||
|
||||
EANALYZECOMMITSOUTPUT: ({result, pluginName}) => ({
|
||||
message: 'The `analyzeCommits` plugin returned an invalid value. It must return a valid semver release type.',
|
||||
details: `The \`analyzeCommits\` plugin must return a valid [semver](https://semver.org) release type. The valid values are: ${RELEASE_TYPE.map(
|
||||
(type) => `\`${type}\``
|
||||
type => `\`${type}\``
|
||||
).join(', ')}.
|
||||
|
||||
The \`analyzeCommits\` function of the \`${pluginName}\` returned \`${stringify(result)}\` instead.
|
||||
@@ -107,7 +97,6 @@ We recommend to report the issue to the \`${pluginName}\` authors, providing the
|
||||
'docs/developer-guide/plugin.md'
|
||||
)})`,
|
||||
}),
|
||||
|
||||
EGENERATENOTESOUTPUT: ({result, pluginName}) => ({
|
||||
message: 'The `generateNotes` plugin returned an invalid value. It must return a `String`.',
|
||||
details: `The \`generateNotes\` plugin must return a \`String\`.
|
||||
@@ -122,7 +111,6 @@ We recommend to report the issue to the \`${pluginName}\` authors, providing the
|
||||
'docs/developer-guide/plugin.md'
|
||||
)})`,
|
||||
}),
|
||||
|
||||
EPUBLISHOUTPUT: ({result, pluginName}) => ({
|
||||
message: 'A `publish` plugin returned an invalid value. It must return an `Object`.',
|
||||
details: `The \`publish\` plugins must return an \`Object\`.
|
||||
@@ -137,7 +125,6 @@ We recommend to report the issue to the \`${pluginName}\` authors, providing the
|
||||
'docs/developer-guide/plugin.md'
|
||||
)})`,
|
||||
}),
|
||||
|
||||
EADDCHANNELOUTPUT: ({result, pluginName}) => ({
|
||||
message: 'A `addChannel` plugin returned an invalid value. It must return an `Object`.',
|
||||
details: `The \`addChannel\` plugins must return an \`Object\`.
|
||||
@@ -152,7 +139,6 @@ We recommend to report the issue to the \`${pluginName}\` authors, providing the
|
||||
'docs/developer-guide/plugin.md'
|
||||
)})`,
|
||||
}),
|
||||
|
||||
EINVALIDBRANCH: ({branch}) => ({
|
||||
message: 'A branch is invalid in the `branches` configuration.',
|
||||
details: `Each branch in the [branches configuration](${linkify(
|
||||
@@ -161,7 +147,6 @@ We recommend to report the issue to the \`${pluginName}\` authors, providing the
|
||||
|
||||
Your configuration for the problematic branch is \`${stringify(branch)}\`.`,
|
||||
}),
|
||||
|
||||
EINVALIDBRANCHNAME: ({branch}) => ({
|
||||
message: 'A branch name is invalid in the `branches` configuration.',
|
||||
details: `Each branch in the [branches configuration](${linkify(
|
||||
@@ -170,7 +155,6 @@ Your configuration for the problematic branch is \`${stringify(branch)}\`.`,
|
||||
|
||||
Your configuration for the problematic branch is \`${stringify(branch)}\`.`,
|
||||
}),
|
||||
|
||||
EDUPLICATEBRANCHES: ({duplicates}) => ({
|
||||
message: 'The `branches` configuration has duplicate branches.',
|
||||
details: `Each branch in the [branches configuration](${linkify(
|
||||
@@ -179,7 +163,6 @@ Your configuration for the problematic branch is \`${stringify(branch)}\`.`,
|
||||
|
||||
Your configuration contains duplicates for the following branch names: \`${stringify(duplicates)}\`.`,
|
||||
}),
|
||||
|
||||
EMAINTENANCEBRANCH: ({branch}) => ({
|
||||
message: 'A maintenance branch is invalid in the `branches` configuration.',
|
||||
details: `Each maintenance branch in the [branches configuration](${linkify(
|
||||
@@ -188,7 +171,6 @@ Your configuration contains duplicates for the following branch names: \`${strin
|
||||
|
||||
Your configuration for the problematic branch is \`${stringify(branch)}\`.`,
|
||||
}),
|
||||
|
||||
EMAINTENANCEBRANCHES: ({branches}) => ({
|
||||
message: 'The maintenance branches are invalid in the `branches` configuration.',
|
||||
details: `Each maintenance branch in the [branches configuration](${linkify(
|
||||
@@ -197,7 +179,6 @@ Your configuration for the problematic branch is \`${stringify(branch)}\`.`,
|
||||
|
||||
Your configuration for the problematic branches is \`${stringify(branches)}\`.`,
|
||||
}),
|
||||
|
||||
ERELEASEBRANCHES: ({branches}) => ({
|
||||
message: 'The release branches are invalid in the `branches` configuration.',
|
||||
details: `A minimum of 1 and a maximum of 3 release branches are required in the [branches configuration](${linkify(
|
||||
@@ -208,7 +189,6 @@ This may occur if your repository does not have a release branch, such as \`mast
|
||||
|
||||
Your configuration for the problematic branches is \`${stringify(branches)}\`.`,
|
||||
}),
|
||||
|
||||
EPRERELEASEBRANCH: ({branch}) => ({
|
||||
message: 'A pre-release branch configuration is invalid in the `branches` configuration.',
|
||||
details: `Each pre-release branch in the [branches configuration](${linkify(
|
||||
@@ -217,7 +197,6 @@ Your configuration for the problematic branches is \`${stringify(branches)}\`.`,
|
||||
|
||||
Your configuration for the problematic branch is \`${stringify(branch)}\`.`,
|
||||
}),
|
||||
|
||||
EPRERELEASEBRANCHES: ({branches}) => ({
|
||||
message: 'The pre-release branches are invalid in the `branches` configuration.',
|
||||
details: `Each pre-release branch in the [branches configuration](${linkify(
|
||||
@@ -226,7 +205,6 @@ Your configuration for the problematic branch is \`${stringify(branch)}\`.`,
|
||||
|
||||
Your configuration for the problematic branches is \`${stringify(branches)}\`.`,
|
||||
}),
|
||||
|
||||
EINVALIDNEXTVERSION: ({nextRelease: {version}, branch: {name, range}, commits, validBranches}) => ({
|
||||
message: `The release \`${version}\` on branch \`${name}\` cannot be published as it is out of range.`,
|
||||
details: `Based on the releases published on other branches, only versions within the range \`${range}\` can be published from branch \`${name}\`.
|
||||
@@ -242,7 +220,6 @@ A valid branch could be ${wordsList(validBranches.map(({name}) => `\`${name}\``)
|
||||
|
||||
See the [workflow configuration documentation](${linkify('docs/usage/workflow-configuration.md')}) for more details.`,
|
||||
}),
|
||||
|
||||
EINVALIDMAINTENANCEMERGE: ({nextRelease: {channel, gitTag, version}, branch: {mergeRange, name}}) => ({
|
||||
message: `The release \`${version}\` on branch \`${name}\` cannot be published as it is out of range.`,
|
||||
details: `Only releases within the range \`${mergeRange}\` can be merged into the maintenance branch \`${name}\` and published to the \`${channel}\` distribution channel.
|
||||
@@ -252,30 +229,3 @@ The branch \`${name}\` head should be [reset](https://git-scm.com/docs/git-reset
|
||||
See the [workflow configuration documentation](${linkify('docs/usage/workflow-configuration.md')}) for more details.`,
|
||||
}),
|
||||
};
|
||||
|
||||
export default exported;
|
||||
|
||||
export const {
|
||||
ENOGITREPO,
|
||||
ENOREPOURL,
|
||||
EGITNOPERMISSION,
|
||||
EINVALIDTAGFORMAT,
|
||||
ETAGNOVERSION,
|
||||
EPLUGINCONF,
|
||||
EPLUGINSCONF,
|
||||
EPLUGIN,
|
||||
EANALYZECOMMITSOUTPUT,
|
||||
EGENERATENOTESOUTPUT,
|
||||
EPUBLISHOUTPUT,
|
||||
EADDCHANNELOUTPUT,
|
||||
EINVALIDBRANCH,
|
||||
EINVALIDBRANCHNAME,
|
||||
EDUPLICATEBRANCHES,
|
||||
EMAINTENANCEBRANCH,
|
||||
EMAINTENANCEBRANCHES,
|
||||
ERELEASEBRANCHES,
|
||||
EPRERELEASEBRANCH,
|
||||
EPRERELEASEBRANCHES,
|
||||
EINVALIDNEXTVERSION,
|
||||
EINVALIDMAINTENANCEMERGE,
|
||||
} = exported;
|
||||
|
||||
+14
-36
@@ -1,28 +1,27 @@
|
||||
import lodash from 'lodash'
|
||||
const {isString, isPlainObject} = lodash
|
||||
/* eslint require-atomic-updates: off */
|
||||
|
||||
import {getGitHead} from '../git.js';
|
||||
import hideSensitive from '../hide-sensitive.js';
|
||||
import {hideSensitiveValues} from '../utils.js';
|
||||
import {RELEASE_TYPE, RELEASE_NOTES_SEPARATOR} from './constants.js';
|
||||
const {isString, isPlainObject} = require('lodash');
|
||||
const {getGitHead} = require('../git');
|
||||
const hideSensitive = require('../hide-sensitive');
|
||||
const {hideSensitiveValues} = require('../utils');
|
||||
const {RELEASE_TYPE, RELEASE_NOTES_SEPARATOR} = require('./constants');
|
||||
|
||||
const exported = {
|
||||
module.exports = {
|
||||
verifyConditions: {
|
||||
required: false,
|
||||
dryRun: true,
|
||||
pipelineConfig: () => ({settleAll: true}),
|
||||
},
|
||||
|
||||
analyzeCommits: {
|
||||
default: ['@semantic-release/commit-analyzer'],
|
||||
required: true,
|
||||
dryRun: true,
|
||||
outputValidator: (output) => !output || RELEASE_TYPE.includes(output),
|
||||
outputValidator: output => !output || RELEASE_TYPE.includes(output),
|
||||
preprocess: ({commits, ...inputs}) => ({
|
||||
...inputs,
|
||||
commits: commits.filter((commit) => !/\[skip\s+release]|\[release\s+skip]/i.test(commit.message)),
|
||||
commits: commits.filter(commit => !/\[skip\s+release\]|\[release\s+skip\]/i.test(commit.message)),
|
||||
}),
|
||||
postprocess: (results) =>
|
||||
postprocess: results =>
|
||||
RELEASE_TYPE[
|
||||
results.reduce((highest, result) => {
|
||||
const typeIndex = RELEASE_TYPE.indexOf(result);
|
||||
@@ -30,17 +29,15 @@ const exported = {
|
||||
}, -1)
|
||||
],
|
||||
},
|
||||
|
||||
verifyRelease: {
|
||||
required: false,
|
||||
dryRun: true,
|
||||
pipelineConfig: () => ({settleAll: true}),
|
||||
},
|
||||
|
||||
generateNotes: {
|
||||
required: false,
|
||||
dryRun: true,
|
||||
outputValidator: (output) => !output || isString(output),
|
||||
outputValidator: output => !output || isString(output),
|
||||
pipelineConfig: () => ({
|
||||
getNextInput: ({nextRelease, ...context}, notes) => ({
|
||||
...context,
|
||||
@@ -52,12 +49,11 @@ const exported = {
|
||||
}),
|
||||
postprocess: (results, {env}) => hideSensitive(env)(results.filter(Boolean).join(RELEASE_NOTES_SEPARATOR)),
|
||||
},
|
||||
|
||||
prepare: {
|
||||
required: false,
|
||||
dryRun: false,
|
||||
pipelineConfig: ({generateNotes}) => ({
|
||||
getNextInput: async (context) => {
|
||||
getNextInput: async context => {
|
||||
const newGitHead = await getGitHead({cwd: context.cwd});
|
||||
// If previous prepare plugin has created a commit (gitHead changed)
|
||||
if (context.nextRelease.gitHead !== newGitHead) {
|
||||
@@ -71,11 +67,10 @@ const exported = {
|
||||
},
|
||||
}),
|
||||
},
|
||||
|
||||
publish: {
|
||||
required: false,
|
||||
dryRun: false,
|
||||
outputValidator: (output) => !output || isPlainObject(output),
|
||||
outputValidator: output => !output || isPlainObject(output),
|
||||
pipelineConfig: () => ({
|
||||
// Add `nextRelease` and plugin properties to published release
|
||||
transform: (release, step, {nextRelease}) => ({
|
||||
@@ -85,11 +80,10 @@ const exported = {
|
||||
}),
|
||||
}),
|
||||
},
|
||||
|
||||
addChannel: {
|
||||
required: false,
|
||||
dryRun: false,
|
||||
outputValidator: (output) => !output || isPlainObject(output),
|
||||
outputValidator: output => !output || isPlainObject(output),
|
||||
pipelineConfig: () => ({
|
||||
// Add `nextRelease` and plugin properties to published release
|
||||
transform: (release, step, {nextRelease}) => ({
|
||||
@@ -99,14 +93,12 @@ const exported = {
|
||||
}),
|
||||
}),
|
||||
},
|
||||
|
||||
success: {
|
||||
required: false,
|
||||
dryRun: false,
|
||||
pipelineConfig: () => ({settleAll: true}),
|
||||
preprocess: ({releases, env, ...inputs}) => ({...inputs, env, releases: hideSensitiveValues(env, releases)}),
|
||||
},
|
||||
|
||||
fail: {
|
||||
required: false,
|
||||
dryRun: false,
|
||||
@@ -114,17 +106,3 @@ const exported = {
|
||||
preprocess: ({errors, env, ...inputs}) => ({...inputs, env, errors: hideSensitiveValues(env, errors)}),
|
||||
},
|
||||
};
|
||||
|
||||
export default exported;
|
||||
|
||||
export const {
|
||||
verifyConditions,
|
||||
analyzeCommits,
|
||||
verifyRelease,
|
||||
generateNotes,
|
||||
prepare,
|
||||
publish,
|
||||
addChannel,
|
||||
success,
|
||||
fail,
|
||||
} = exported;
|
||||
|
||||
+3
-4
@@ -1,6 +1,5 @@
|
||||
import debugFactory from 'debug';
|
||||
const debug = debugFactory('semantic-release:get-commits');
|
||||
import {getCommits} from './git.js';
|
||||
const debug = require('debug')('semantic-release:get-commits');
|
||||
const {getCommits} = require('./git');
|
||||
|
||||
/**
|
||||
* Retrieve the list of commits on the current branch since the commit sha associated with the last release, or all the commits of the current branch if there is no last released version.
|
||||
@@ -9,7 +8,7 @@ import {getCommits} from './git.js';
|
||||
*
|
||||
* @return {Promise<Array<Object>>} The list of commits on the branch `branch` since the last release.
|
||||
*/
|
||||
export default async ({cwd, env, lastRelease: {gitHead: from}, nextRelease: {gitHead: to = 'HEAD'} = {}, logger}) => {
|
||||
module.exports = async ({cwd, env, lastRelease: {gitHead: from}, nextRelease: {gitHead: to = 'HEAD'} = {}, logger}) => {
|
||||
if (from) {
|
||||
debug('Use from: %s', from);
|
||||
} else {
|
||||
|
||||
+31
-25
@@ -1,25 +1,35 @@
|
||||
import lodash from 'lodash'
|
||||
const {castArray, pickBy, isNil, isString, isPlainObject} = lodash
|
||||
import readPkgUp from 'read-pkg-up';
|
||||
import {cosmiconfig} from 'cosmiconfig';
|
||||
import resolveFrom from 'resolve-from';
|
||||
import debugFactory from 'debug';
|
||||
const debug = debugFactory('semantic-release:config');
|
||||
import {repoUrl} from './git.js';
|
||||
import PLUGINS_DEFINITIONS from './definitions/plugins.js';
|
||||
import plugins from './plugins';
|
||||
import {validatePlugin, parseConfig} from './plugins/utils.js';
|
||||
const {castArray, pickBy, isNil, isString, isPlainObject} = require('lodash');
|
||||
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 PLUGINS_DEFINITIONS = require('./definitions/plugins');
|
||||
const plugins = require('./plugins');
|
||||
const {validatePlugin, parseConfig} = require('./plugins/utils');
|
||||
|
||||
const CONFIG_NAME = 'release';
|
||||
const CONFIG_FILES = [
|
||||
'package.json',
|
||||
`.${CONFIG_NAME}rc`,
|
||||
`.${CONFIG_NAME}rc.json`,
|
||||
`.${CONFIG_NAME}rc.yaml`,
|
||||
`.${CONFIG_NAME}rc.yml`,
|
||||
`.${CONFIG_NAME}rc.js`,
|
||||
`${CONFIG_NAME}.config.js`,
|
||||
];
|
||||
|
||||
export default async (context, cliOptions) => {
|
||||
module.exports = async (context, opts) => {
|
||||
const {cwd, env} = context;
|
||||
const {config, filepath} = (await cosmiconfig(CONFIG_NAME).search(cwd)) || {};
|
||||
const {config, filepath} = (await cosmiconfig(CONFIG_NAME, {searchPlaces: CONFIG_FILES}).search(cwd)) || {};
|
||||
|
||||
debug('load config from: %s', filepath);
|
||||
|
||||
// Merge config file options and CLI/API options
|
||||
let options = {...config, ...cliOptions};
|
||||
let options = {...config, ...opts};
|
||||
if (options.ci === false) {
|
||||
options.noCi = true;
|
||||
}
|
||||
|
||||
const pluginsPath = {};
|
||||
let extendPaths;
|
||||
@@ -28,14 +38,14 @@ export default async (context, cliOptions) => {
|
||||
// If `extends` is defined, load and merge each shareable config with `options`
|
||||
options = {
|
||||
...castArray(extendPaths).reduce((result, extendPath) => {
|
||||
const extendsOptions = require(resolveFrom.silent(__dirname, extendPath) || resolveFrom(cwd, extendPath));
|
||||
const extendsOpts = require(resolveFrom.silent(__dirname, extendPath) || resolveFrom(cwd, extendPath));
|
||||
|
||||
// For each plugin defined in a shareable config, save in `pluginsPath` the extendable config path,
|
||||
// so those plugin will be loaded relatively to the config file
|
||||
Object.entries(extendsOptions)
|
||||
Object.entries(extendsOpts)
|
||||
.filter(([, value]) => Boolean(value))
|
||||
.reduce((pluginsPath, [option, value]) => {
|
||||
castArray(value).forEach((plugin) => {
|
||||
castArray(value).forEach(plugin => {
|
||||
if (option === 'plugins' && validatePlugin(plugin)) {
|
||||
pluginsPath[parseConfig(plugin)[0]] = extendPath;
|
||||
} else if (
|
||||
@@ -48,7 +58,7 @@ export default async (context, cliOptions) => {
|
||||
return pluginsPath;
|
||||
}, pluginsPath);
|
||||
|
||||
return {...result, ...extendsOptions};
|
||||
return {...result, ...extendsOpts};
|
||||
}, {}),
|
||||
...options,
|
||||
};
|
||||
@@ -73,20 +83,16 @@ export default async (context, cliOptions) => {
|
||||
'@semantic-release/github',
|
||||
],
|
||||
// Remove `null` and `undefined` options so they can be replaced with default ones
|
||||
...pickBy(options, (option) => !isNil(option)),
|
||||
...pickBy(options, option => !isNil(option)),
|
||||
...(options.branches ? {branches: castArray(options.branches)} : {}),
|
||||
};
|
||||
|
||||
if (options.ci === false) {
|
||||
options.noCi = true;
|
||||
}
|
||||
|
||||
debug('options values: %O', options);
|
||||
|
||||
return {options, plugins: await plugins({...context, options}, pluginsPath)};
|
||||
};
|
||||
|
||||
async function pkgRepoUrl(options) {
|
||||
const {packageJson} = (await readPkgUp(options)) || {};
|
||||
async function pkgRepoUrl(opts) {
|
||||
const {packageJson} = (await readPkgUp(opts)) || {};
|
||||
return packageJson && (isPlainObject(packageJson.repository) ? packageJson.repository.url : packageJson.repository);
|
||||
}
|
||||
|
||||
+3
-3
@@ -1,7 +1,7 @@
|
||||
import SemanticReleaseError from '@semantic-release/error';
|
||||
import ERROR_DEFINITIONS from './definitions/errors.js';
|
||||
const SemanticReleaseError = require('@semantic-release/error');
|
||||
const ERROR_DEFINITIONS = require('./definitions/errors');
|
||||
|
||||
export default (code, ctx = {}) => {
|
||||
module.exports = (code, ctx = {}) => {
|
||||
const {message, details} = ERROR_DEFINITIONS[code](ctx);
|
||||
return new SemanticReleaseError(message, code, details);
|
||||
};
|
||||
|
||||
+21
-79
@@ -1,52 +1,7 @@
|
||||
import {format} from 'url';
|
||||
import lodash from 'lodash'
|
||||
const {isNil} = lodash
|
||||
import hostedGitInfo from 'hosted-git-info';
|
||||
import {verifyAuth} from './git.js';
|
||||
import debugFactory from 'debug';
|
||||
const debug = debugFactory('semantic-release:get-git-auth-url');
|
||||
|
||||
/**
|
||||
* Machinery to format a repository URL with the given credentials
|
||||
*
|
||||
* @param {String} protocol URL protocol (which should not be present in repositoryUrl)
|
||||
* @param {String} repositoryUrl User-given repository URL
|
||||
* @param {String} gitCredentials The basic auth part of the URL
|
||||
*
|
||||
* @return {String} The formatted Git repository URL.
|
||||
*/
|
||||
function formatAuthUrl(protocol, repositoryUrl, gitCredentials) {
|
||||
const [match, auth, host, basePort, path] =
|
||||
/^(?!.+:\/\/)(?:(?<auth>.*)@)?(?<host>.*?):(?<port>\d+)?:?\/?(?<path>.*)$/.exec(repositoryUrl) || [];
|
||||
const {port, hostname, ...parsed} = new URL(
|
||||
match ? `ssh://${auth ? `${auth}@` : ''}${host}${basePort ? `:${basePort}` : ''}/${path}` : repositoryUrl
|
||||
);
|
||||
|
||||
return format({
|
||||
...parsed,
|
||||
auth: gitCredentials,
|
||||
host: `${hostname}${protocol === 'ssh:' ? '' : port ? `:${port}` : ''}`,
|
||||
protocol: protocol && /http[^s]/.test(protocol) ? 'http' : 'https',
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify authUrl by calling git.verifyAuth, but don't throw on failure
|
||||
*
|
||||
* @param {Object} context semantic-release context.
|
||||
* @param {String} authUrl Repository URL to verify
|
||||
*
|
||||
* @return {String} The authUrl as is if the connection was successfull, null otherwise
|
||||
*/
|
||||
async function ensureValidAuthUrl({cwd, env, branch}, authUrl) {
|
||||
try {
|
||||
await verifyAuth(authUrl, branch.name, {cwd, env});
|
||||
return authUrl;
|
||||
} catch (error) {
|
||||
debug(error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
const {parse, format} = require('url'); // eslint-disable-line node/no-deprecated-api
|
||||
const {isNil} = require('lodash');
|
||||
const hostedGitInfo = require('hosted-git-info');
|
||||
const {verifyAuth} = require('./git');
|
||||
|
||||
/**
|
||||
* Determine the the git repository URL to use to push, either:
|
||||
@@ -59,8 +14,7 @@ async function ensureValidAuthUrl({cwd, env, branch}, authUrl) {
|
||||
*
|
||||
* @return {String} The formatted Git repository URL.
|
||||
*/
|
||||
export default async (context) => {
|
||||
const {cwd, env, branch} = context;
|
||||
module.exports = async ({cwd, env, branch, options: {repositoryUrl}}) => {
|
||||
const GIT_TOKENS = {
|
||||
GIT_CREDENTIALS: undefined,
|
||||
GH_TOKEN: undefined,
|
||||
@@ -71,13 +25,10 @@ export default async (context) => {
|
||||
GITLAB_TOKEN: 'gitlab-ci-token:',
|
||||
BB_TOKEN: 'x-token-auth:',
|
||||
BITBUCKET_TOKEN: 'x-token-auth:',
|
||||
BB_TOKEN_BASIC_AUTH: '',
|
||||
BITBUCKET_TOKEN_BASIC_AUTH: '',
|
||||
};
|
||||
|
||||
let {repositoryUrl} = context.options;
|
||||
const info = hostedGitInfo.fromUrl(repositoryUrl, {noGitPlus: true});
|
||||
const {protocol, ...parsed} = new URL(repositoryUrl);
|
||||
const {protocol, ...parsed} = parse(repositoryUrl);
|
||||
|
||||
if (info && info.getDefaultRepresentation() === 'shortcut') {
|
||||
// Expand shorthand URLs (such as `owner/repo` or `gitlab:owner/repo`)
|
||||
@@ -89,34 +40,25 @@ export default async (context) => {
|
||||
|
||||
// Test if push is allowed without transforming the URL (e.g. is ssh keys are set up)
|
||||
try {
|
||||
debug('Verifying ssh auth by attempting to push to %s', repositoryUrl);
|
||||
await verifyAuth(repositoryUrl, branch.name, {cwd, env});
|
||||
} catch (_) {
|
||||
debug('SSH key auth failed, falling back to https.');
|
||||
const envVars = Object.keys(GIT_TOKENS).filter((envVar) => !isNil(env[envVar]));
|
||||
const envVar = Object.keys(GIT_TOKENS).find(envVar => !isNil(env[envVar]));
|
||||
const gitCredentials = `${GIT_TOKENS[envVar] || ''}${env[envVar] || ''}`;
|
||||
|
||||
// Skip verification if there is no ambiguity on which env var to use for authentication
|
||||
if (envVars.length === 1) {
|
||||
const gitCredentials = `${GIT_TOKENS[envVars[0]] || ''}${env[envVars[0]]}`;
|
||||
return formatAuthUrl(protocol, repositoryUrl, gitCredentials);
|
||||
}
|
||||
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] =
|
||||
/^(?!.+:\/\/)(?:(?<auth>.*)@)?(?<host>.*?):(?<path>.*)$/.exec(repositoryUrl) || [];
|
||||
const {port, hostname, ...parsed} = parse(
|
||||
match ? `ssh://${auth ? `${auth}@` : ''}${host}/${path}` : repositoryUrl
|
||||
);
|
||||
|
||||
if (envVars.length > 1) {
|
||||
debug(`Found ${envVars.length} credentials in environment, trying all of them`);
|
||||
|
||||
const candidateRepositoryUrls = [];
|
||||
for (const envVar of envVars) {
|
||||
const gitCredentials = `${GIT_TOKENS[envVar] || ''}${env[envVar]}`;
|
||||
const authUrl = formatAuthUrl(protocol, repositoryUrl, gitCredentials);
|
||||
candidateRepositoryUrls.push(ensureValidAuthUrl(context, authUrl));
|
||||
}
|
||||
|
||||
const validRepositoryUrls = await Promise.all(candidateRepositoryUrls);
|
||||
const chosenAuthUrlIndex = validRepositoryUrls.findIndex((url) => url !== null);
|
||||
if (chosenAuthUrlIndex > -1) {
|
||||
debug(`Using "${envVars[chosenAuthUrlIndex]}" to authenticate`);
|
||||
return validRepositoryUrls[chosenAuthUrlIndex];
|
||||
}
|
||||
return format({
|
||||
...parsed,
|
||||
auth: gitCredentials,
|
||||
host: `${hostname}${protocol === 'ssh:' ? '' : port ? `:${port}` : ''}`,
|
||||
protocol: protocol && /http[^s]/.test(protocol) ? 'http' : 'https',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import lodash from 'lodash'
|
||||
const {isUndefined} = lodash
|
||||
import semver from 'semver';
|
||||
import {makeTag, isSameChannel} from './utils.js';
|
||||
const {isUndefined} = require('lodash');
|
||||
const semver = require('semver');
|
||||
const {makeTag, isSameChannel} = require('./utils');
|
||||
|
||||
/**
|
||||
* Last release.
|
||||
@@ -27,11 +26,11 @@ import {makeTag, isSameChannel} from './utils.js';
|
||||
*
|
||||
* @return {LastRelease} The last tagged release or empty object if none is found.
|
||||
*/
|
||||
export default ({branch, options: {tagFormat}}, {before} = {}) => {
|
||||
module.exports = ({branch, options: {tagFormat}}, {before} = {}) => {
|
||||
const [{version, gitTag, channels} = {}] = branch.tags
|
||||
.filter(
|
||||
(tag) =>
|
||||
((branch.type === 'prerelease' && tag.channels.some((channel) => isSameChannel(branch.channel, channel))) ||
|
||||
tag =>
|
||||
((branch.type === 'prerelease' && tag.channels.some(channel => isSameChannel(branch.channel, channel))) ||
|
||||
!semver.prerelease(tag.version)) &&
|
||||
(isUndefined(before) || semver.lt(tag.version, before))
|
||||
)
|
||||
|
||||
+3
-3
@@ -1,7 +1,7 @@
|
||||
import {Signale} from 'signale';
|
||||
import figures from 'figures';
|
||||
const {Signale} = require('signale');
|
||||
const figures = require('figures');
|
||||
|
||||
export default ({stdout, stderr}) =>
|
||||
module.exports = ({stdout, stderr}) =>
|
||||
new Signale({
|
||||
config: {displayTimestamp: true, underlineMessage: false, displayLabel: false},
|
||||
disabled: false,
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import semver from 'semver';
|
||||
import {FIRST_RELEASE, FIRSTPRERELEASE} from './definitions/constants.js';
|
||||
import {isSameChannel, getLatestVersion, tagsToVersions, highest} from './utils.js';
|
||||
const semver = require('semver');
|
||||
const {FIRST_RELEASE, FIRSTPRERELEASE} = require('./definitions/constants');
|
||||
const {isSameChannel, getLatestVersion, tagsToVersions, highest} = require('./utils');
|
||||
|
||||
export default ({branch, nextRelease: {type, channel}, lastRelease, logger}) => {
|
||||
module.exports = ({branch, nextRelease: {type, channel}, lastRelease, logger}) => {
|
||||
let version;
|
||||
if (lastRelease.version) {
|
||||
const {major, minor, patch} = semver.parse(lastRelease.version);
|
||||
@@ -10,7 +10,7 @@ export default ({branch, nextRelease: {type, channel}, lastRelease, logger}) =>
|
||||
if (branch.type === 'prerelease') {
|
||||
if (
|
||||
semver.prerelease(lastRelease.version) &&
|
||||
lastRelease.channels.some((lastReleaseChannel) => isSameChannel(lastReleaseChannel, channel))
|
||||
lastRelease.channels.some(lastReleaseChannel => isSameChannel(lastReleaseChannel, channel))
|
||||
) {
|
||||
version = highest(
|
||||
semver.inc(lastRelease.version, 'prerelease'),
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import lodash from 'lodash'
|
||||
const {uniqBy, intersection} = lodash
|
||||
import semver from 'semver';
|
||||
import semverDiff from 'semver-diff';
|
||||
import getLastRelease from './get-last-release.js';
|
||||
import {makeTag, getLowerBound} from './utils.js';
|
||||
const {uniqBy, intersection} = require('lodash');
|
||||
const semver = require('semver');
|
||||
const semverDiff = require('semver-diff');
|
||||
const getLastRelease = require('./get-last-release');
|
||||
const {makeTag, getLowerBound} = require('./utils');
|
||||
|
||||
/**
|
||||
* Find releases that have been merged from from a higher branch but not added on the channel of the current branch.
|
||||
@@ -12,7 +11,7 @@ import {makeTag, getLowerBound} from './utils.js';
|
||||
*
|
||||
* @return {Array<Object>} Last release and next release to be added on the channel of the current branch.
|
||||
*/
|
||||
export default (context) => {
|
||||
module.exports = context => {
|
||||
const {
|
||||
branch,
|
||||
branches,
|
||||
|
||||
+51
-75
@@ -1,9 +1,8 @@
|
||||
import gitLogParser from 'git-log-parser';
|
||||
import getStream from 'get-stream';
|
||||
import execa from 'execa';
|
||||
import debugFactory from 'debug';
|
||||
const debug = debugFactory('semantic-release:git');
|
||||
import {GIT_NOTE_REF} from './definitions/constants.js';
|
||||
const gitLogParser = require('git-log-parser');
|
||||
const getStream = require('get-stream');
|
||||
const execa = require('execa');
|
||||
const debug = require('debug')('semantic-release:git');
|
||||
const {GIT_NOTE_REF} = require('./definitions/constants');
|
||||
|
||||
Object.assign(gitLogParser.fields, {hash: 'H', message: 'B', gitTags: 'd', committerDate: {key: 'ci', type: Date}});
|
||||
|
||||
@@ -15,8 +14,8 @@ Object.assign(gitLogParser.fields, {hash: 'H', message: 'B', gitTags: 'd', commi
|
||||
*
|
||||
* @return {String} The commit sha of the tag in parameter or `null`.
|
||||
*/
|
||||
async function getTagHead(tagName, execaOptions) {
|
||||
return (await execa('git', ['rev-list', '-1', tagName], execaOptions)).stdout;
|
||||
async function getTagHead(tagName, execaOpts) {
|
||||
return (await execa('git', ['rev-list', '-1', tagName], execaOpts)).stdout;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -28,10 +27,10 @@ async function getTagHead(tagName, execaOptions) {
|
||||
* @return {Array<String>} List of git tags.
|
||||
* @throws {Error} If the `git` command fails.
|
||||
*/
|
||||
async function getTags(branch, execaOptions) {
|
||||
return (await execa('git', ['tag', '--merged', branch], execaOptions)).stdout
|
||||
async function getTags(branch, execaOpts) {
|
||||
return (await execa('git', ['tag', '--merged', branch], execaOpts)).stdout
|
||||
.split('\n')
|
||||
.map((tag) => tag.trim())
|
||||
.map(tag => tag.trim())
|
||||
.filter(Boolean);
|
||||
}
|
||||
|
||||
@@ -43,12 +42,12 @@ async function getTags(branch, execaOptions) {
|
||||
* @param {Object} [execaOpts] Options to pass to `execa`.
|
||||
* @return {Promise<Array<Object>>} The list of commits between `from` and `to`.
|
||||
*/
|
||||
async function getCommits(from, to, execaOptions) {
|
||||
async function getCommits(from, to, execaOpts) {
|
||||
return (
|
||||
await getStream.array(
|
||||
gitLogParser.parse(
|
||||
{_: `${from ? from + '..' : ''}${to}`},
|
||||
{cwd: execaOptions.cwd, env: {...process.env, ...execaOptions.env}}
|
||||
{cwd: execaOpts.cwd, env: {...process.env, ...execaOpts.env}}
|
||||
)
|
||||
)
|
||||
).map(({message, gitTags, ...commit}) => ({...commit, message: message.trim(), gitTags: gitTags.trim()}));
|
||||
@@ -63,11 +62,11 @@ async function getCommits(from, to, execaOptions) {
|
||||
* @return {Array<String>} List of git branches.
|
||||
* @throws {Error} If the `git` command fails.
|
||||
*/
|
||||
async function getBranches(repositoryUrl, execaOptions) {
|
||||
return (await execa('git', ['ls-remote', '--heads', repositoryUrl], execaOptions)).stdout
|
||||
async function getBranches(repositoryUrl, execaOpts) {
|
||||
return (await execa('git', ['ls-remote', '--heads', repositoryUrl], execaOpts)).stdout
|
||||
.split('\n')
|
||||
.filter(Boolean)
|
||||
.map((branch) => branch.match(/^.+refs\/heads\/(?<branch>.+)$/)[1]);
|
||||
.map(branch => branch.match(/^.+refs\/heads\/(?<branch>.+)$/)[1]);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -78,9 +77,9 @@ async function getBranches(repositoryUrl, execaOptions) {
|
||||
*
|
||||
* @return {Boolean} `true` if the reference exists, falsy otherwise.
|
||||
*/
|
||||
async function isRefExists(ref, execaOptions) {
|
||||
async function isRefExists(ref, execaOpts) {
|
||||
try {
|
||||
return (await execa('git', ['rev-parse', '--verify', ref], execaOptions)).exitCode === 0;
|
||||
return (await execa('git', ['rev-parse', '--verify', ref], execaOpts)).exitCode === 0;
|
||||
} catch (error) {
|
||||
debug(error);
|
||||
}
|
||||
@@ -100,9 +99,9 @@ async function isRefExists(ref, execaOptions) {
|
||||
* @param {String} branch The repository branch to fetch.
|
||||
* @param {Object} [execaOpts] Options to pass to `execa`.
|
||||
*/
|
||||
async function fetch(repositoryUrl, branch, ciBranch, execaOptions) {
|
||||
async function fetch(repositoryUrl, branch, ciBranch, execaOpts) {
|
||||
const isDetachedHead =
|
||||
(await execa('git', ['rev-parse', '--abbrev-ref', 'HEAD'], {...execaOptions, reject: false})).stdout === 'HEAD';
|
||||
(await execa('git', ['rev-parse', '--abbrev-ref', 'HEAD'], {...execaOpts, reject: false})).stdout === 'HEAD';
|
||||
|
||||
try {
|
||||
await execa(
|
||||
@@ -115,7 +114,7 @@ async function fetch(repositoryUrl, branch, ciBranch, execaOptions) {
|
||||
? [repositoryUrl]
|
||||
: ['--update-head-ok', repositoryUrl, `+refs/heads/${branch}:refs/heads/${branch}`]),
|
||||
],
|
||||
execaOptions
|
||||
execaOpts
|
||||
);
|
||||
} catch (_) {
|
||||
await execa(
|
||||
@@ -127,7 +126,7 @@ async function fetch(repositoryUrl, branch, ciBranch, execaOptions) {
|
||||
? [repositoryUrl]
|
||||
: ['--update-head-ok', repositoryUrl, `+refs/heads/${branch}:refs/heads/${branch}`]),
|
||||
],
|
||||
execaOptions
|
||||
execaOpts
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -138,16 +137,16 @@ async function fetch(repositoryUrl, branch, ciBranch, execaOptions) {
|
||||
* @param {String} repositoryUrl The remote repository URL.
|
||||
* @param {Object} [execaOpts] Options to pass to `execa`.
|
||||
*/
|
||||
async function fetchNotes(repositoryUrl, execaOptions) {
|
||||
async function fetchNotes(repositoryUrl, execaOpts) {
|
||||
try {
|
||||
await execa(
|
||||
'git',
|
||||
['fetch', '--unshallow', repositoryUrl, `+refs/notes/${GIT_NOTE_REF}:refs/notes/${GIT_NOTE_REF}`],
|
||||
execaOptions
|
||||
execaOpts
|
||||
);
|
||||
} catch (_) {
|
||||
await execa('git', ['fetch', repositoryUrl, `+refs/notes/${GIT_NOTE_REF}:refs/notes/${GIT_NOTE_REF}`], {
|
||||
...execaOptions,
|
||||
...execaOpts,
|
||||
reject: false,
|
||||
});
|
||||
}
|
||||
@@ -160,8 +159,8 @@ async function fetchNotes(repositoryUrl, execaOptions) {
|
||||
*
|
||||
* @return {String} the sha of the HEAD commit.
|
||||
*/
|
||||
async function getGitHead(execaOptions) {
|
||||
return (await execa('git', ['rev-parse', 'HEAD'], execaOptions)).stdout;
|
||||
async function getGitHead(execaOpts) {
|
||||
return (await execa('git', ['rev-parse', 'HEAD'], execaOpts)).stdout;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -171,9 +170,9 @@ async function getGitHead(execaOptions) {
|
||||
*
|
||||
* @return {string} The value of the remote git URL.
|
||||
*/
|
||||
async function repoUrl(execaOptions) {
|
||||
async function repoUrl(execaOpts) {
|
||||
try {
|
||||
return (await execa('git', ['config', '--get', 'remote.origin.url'], execaOptions)).stdout;
|
||||
return (await execa('git', ['config', '--get', 'remote.origin.url'], execaOpts)).stdout;
|
||||
} catch (error) {
|
||||
debug(error);
|
||||
}
|
||||
@@ -186,9 +185,9 @@ async function repoUrl(execaOptions) {
|
||||
*
|
||||
* @return {Boolean} `true` if the current working directory is in a git repository, falsy otherwise.
|
||||
*/
|
||||
async function isGitRepo(execaOptions) {
|
||||
async function isGitRepo(execaOpts) {
|
||||
try {
|
||||
return (await execa('git', ['rev-parse', '--git-dir'], execaOptions)).exitCode === 0;
|
||||
return (await execa('git', ['rev-parse', '--git-dir'], execaOpts)).exitCode === 0;
|
||||
} catch (error) {
|
||||
debug(error);
|
||||
}
|
||||
@@ -203,9 +202,9 @@ async function isGitRepo(execaOptions) {
|
||||
*
|
||||
* @throws {Error} if not authorized to push.
|
||||
*/
|
||||
async function verifyAuth(repositoryUrl, branch, execaOptions) {
|
||||
async function verifyAuth(repositoryUrl, branch, execaOpts) {
|
||||
try {
|
||||
await execa('git', ['push', '--dry-run', '--no-verify', repositoryUrl, `HEAD:${branch}`], execaOptions);
|
||||
await execa('git', ['push', '--dry-run', '--no-verify', repositoryUrl, `HEAD:${branch}`], execaOpts);
|
||||
} catch (error) {
|
||||
debug(error);
|
||||
throw error;
|
||||
@@ -221,8 +220,8 @@ async function verifyAuth(repositoryUrl, branch, execaOptions) {
|
||||
*
|
||||
* @throws {Error} if the tag creation failed.
|
||||
*/
|
||||
async function tag(tagName, ref, execaOptions) {
|
||||
await execa('git', ['tag', tagName, ref], execaOptions);
|
||||
async function tag(tagName, ref, execaOpts) {
|
||||
await execa('git', ['tag', tagName, ref], execaOpts);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -233,8 +232,8 @@ async function tag(tagName, ref, execaOptions) {
|
||||
*
|
||||
* @throws {Error} if the push failed.
|
||||
*/
|
||||
async function push(repositoryUrl, execaOptions) {
|
||||
await execa('git', ['push', '--tags', repositoryUrl], execaOptions);
|
||||
async function push(repositoryUrl, execaOpts) {
|
||||
await execa('git', ['push', '--tags', repositoryUrl], execaOpts);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -245,8 +244,8 @@ async function push(repositoryUrl, execaOptions) {
|
||||
*
|
||||
* @throws {Error} if the push failed.
|
||||
*/
|
||||
async function pushNotes(repositoryUrl, execaOptions) {
|
||||
await execa('git', ['push', repositoryUrl, `refs/notes/${GIT_NOTE_REF}`], execaOptions);
|
||||
async function pushNotes(repositoryUrl, execaOpts) {
|
||||
await execa('git', ['push', repositoryUrl, `refs/notes/${GIT_NOTE_REF}`], execaOpts);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -257,9 +256,9 @@ async function pushNotes(repositoryUrl, execaOptions) {
|
||||
*
|
||||
* @return {Boolean} `true` if valid, falsy otherwise.
|
||||
*/
|
||||
async function verifyTagName(tagName, execaOptions) {
|
||||
async function verifyTagName(tagName, execaOpts) {
|
||||
try {
|
||||
return (await execa('git', ['check-ref-format', `refs/tags/${tagName}`], execaOptions)).exitCode === 0;
|
||||
return (await execa('git', ['check-ref-format', `refs/tags/${tagName}`], execaOpts)).exitCode === 0;
|
||||
} catch (error) {
|
||||
debug(error);
|
||||
}
|
||||
@@ -273,9 +272,9 @@ async function verifyTagName(tagName, execaOptions) {
|
||||
*
|
||||
* @return {Boolean} `true` if valid, falsy otherwise.
|
||||
*/
|
||||
async function verifyBranchName(branch, execaOptions) {
|
||||
async function verifyBranchName(branch, execaOpts) {
|
||||
try {
|
||||
return (await execa('git', ['check-ref-format', `refs/heads/${branch}`], execaOptions)).exitCode === 0;
|
||||
return (await execa('git', ['check-ref-format', `refs/heads/${branch}`], execaOpts)).exitCode === 0;
|
||||
} catch (error) {
|
||||
debug(error);
|
||||
}
|
||||
@@ -290,10 +289,10 @@ async function verifyBranchName(branch, execaOptions) {
|
||||
*
|
||||
* @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(repositoryUrl, branch, execaOptions) {
|
||||
async function isBranchUpToDate(repositoryUrl, branch, execaOpts) {
|
||||
return (
|
||||
(await getGitHead(execaOptions)) ===
|
||||
(await execa('git', ['ls-remote', '--heads', repositoryUrl, branch], execaOptions)).stdout.match(/^(?<ref>\w+)?/)[1]
|
||||
(await getGitHead(execaOpts)) ===
|
||||
(await execa('git', ['ls-remote', '--heads', repositoryUrl, branch], execaOpts)).stdout.match(/^(?<ref>\w+)?/)[1]
|
||||
);
|
||||
}
|
||||
|
||||
@@ -305,9 +304,9 @@ async function isBranchUpToDate(repositoryUrl, branch, execaOptions) {
|
||||
*
|
||||
* @return {Object} the parsed JSON note if there is one, an empty object otherwise.
|
||||
*/
|
||||
async function getNote(ref, execaOptions) {
|
||||
async function getNote(ref, execaOpts) {
|
||||
try {
|
||||
return JSON.parse((await execa('git', ['notes', '--ref', GIT_NOTE_REF, 'show', ref], execaOptions)).stdout);
|
||||
return JSON.parse((await execa('git', ['notes', '--ref', GIT_NOTE_REF, 'show', ref], execaOpts)).stdout);
|
||||
} catch (error) {
|
||||
if (error.exitCode === 1) {
|
||||
return {};
|
||||
@@ -319,40 +318,17 @@ async function getNote(ref, execaOptions) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Add JSON note to a given reference.
|
||||
* Get and parse the JSON note of a given reference.
|
||||
*
|
||||
* @param {Object} note The object to save in the reference note.
|
||||
* @param {String} ref The Git reference to add the note to.
|
||||
* @param {Object} [execaOpts] Options to pass to `execa`.
|
||||
*/
|
||||
async function addNote(note, ref, execaOptions) {
|
||||
await execa('git', ['notes', '--ref', GIT_NOTE_REF, 'add', '-f', '-m', JSON.stringify(note), ref], execaOptions);
|
||||
async function addNote(note, ref, execaOpts) {
|
||||
await execa('git', ['notes', '--ref', GIT_NOTE_REF, 'add', '-f', '-m', JSON.stringify(note), ref], execaOpts);
|
||||
}
|
||||
|
||||
const exported = {
|
||||
getTagHead,
|
||||
getTags,
|
||||
getCommits,
|
||||
getBranches,
|
||||
isRefExists,
|
||||
fetch,
|
||||
fetchNotes,
|
||||
getGitHead,
|
||||
repoUrl,
|
||||
isGitRepo,
|
||||
verifyAuth,
|
||||
tag,
|
||||
push,
|
||||
pushNotes,
|
||||
verifyTagName,
|
||||
isBranchUpToDate,
|
||||
verifyBranchName,
|
||||
getNote,
|
||||
addNote,
|
||||
};
|
||||
|
||||
export default exported;
|
||||
export {
|
||||
module.exports = {
|
||||
getTagHead,
|
||||
getTags,
|
||||
getCommits,
|
||||
|
||||
+8
-17
@@ -1,21 +1,12 @@
|
||||
import lodash from 'lodash'
|
||||
const {escapeRegExp, size, isString} = lodash
|
||||
import {SECRET_REPLACEMENT, SECRET_MIN_SIZE} from './definitions/constants.js';
|
||||
const {escapeRegExp, size, isString} = require('lodash');
|
||||
const {SECRET_REPLACEMENT, SECRET_MIN_SIZE} = require('./definitions/constants');
|
||||
|
||||
export default (env) => {
|
||||
const toReplace = Object.keys(env).filter((envVar) => {
|
||||
// https://github.com/semantic-release/semantic-release/issues/1558
|
||||
if (envVar === 'GOPRIVATE') {
|
||||
return false;
|
||||
}
|
||||
|
||||
return /token|password|credential|secret|private/i.test(envVar) && size(env[envVar].trim()) >= SECRET_MIN_SIZE;
|
||||
});
|
||||
|
||||
const regexp = new RegExp(
|
||||
toReplace.map((envVar) => `${escapeRegExp(env[envVar])}|${escapeRegExp(encodeURI(env[envVar]))}`).join('|'),
|
||||
'g'
|
||||
module.exports = env => {
|
||||
const toReplace = Object.keys(env).filter(
|
||||
envVar => /token|password|credential|secret|private/i.test(envVar) && size(env[envVar].trim()) >= SECRET_MIN_SIZE
|
||||
);
|
||||
return (output) =>
|
||||
|
||||
const regexp = new RegExp(toReplace.map(envVar => escapeRegExp(env[envVar])).join('|'), 'g');
|
||||
return output =>
|
||||
output && isString(output) && toReplace.length > 0 ? output.toString().replace(regexp, SECRET_REPLACEMENT) : output;
|
||||
};
|
||||
|
||||
+14
-15
@@ -1,13 +1,12 @@
|
||||
import lodash from 'lodash'
|
||||
const {identity, isPlainObject, omit, castArray, isNil, isString} = lodash
|
||||
import AggregateError from 'aggregate-error';
|
||||
import getError from '../get-error.js';
|
||||
import PLUGINS_DEFINITIONS from '../definitions/plugins.js';
|
||||
import {validatePlugin, validateStep, loadPlugin, parseConfig} from './utils.js';
|
||||
import pipeline from './pipeline.js';
|
||||
import normalize from './normalize.js';
|
||||
const {identity, isPlainObject, omit, castArray, isNil, isString} = require('lodash');
|
||||
const AggregateError = require('aggregate-error');
|
||||
const getError = require('../get-error');
|
||||
const PLUGINS_DEFINITIONS = require('../definitions/plugins');
|
||||
const {validatePlugin, validateStep, loadPlugin, parseConfig} = require('./utils');
|
||||
const pipeline = require('./pipeline');
|
||||
const normalize = require('./normalize');
|
||||
|
||||
export default (context, pluginsPath) => {
|
||||
module.exports = (context, pluginsPath) => {
|
||||
let {options, logger} = context;
|
||||
const errors = [];
|
||||
|
||||
@@ -47,14 +46,14 @@ export default (context, pluginsPath) => {
|
||||
|
||||
const pluginsConf = Object.entries(PLUGINS_DEFINITIONS).reduce(
|
||||
(pluginsConf, [type, {required, default: def, pipelineConfig, postprocess = identity, preprocess = identity}]) => {
|
||||
let pluginOptions;
|
||||
let pluginOpts;
|
||||
|
||||
if (isNil(options[type]) && def) {
|
||||
pluginOptions = def;
|
||||
pluginOpts = def;
|
||||
} else {
|
||||
// If an object is passed and the path is missing, merge it with step options
|
||||
if (isPlainObject(options[type]) && !options[type].path) {
|
||||
options[type] = castArray(plugins[type]).map((plugin) =>
|
||||
options[type] = castArray(plugins[type]).map(plugin =>
|
||||
plugin ? [plugin[0], Object.assign(plugin[1], options[type])] : plugin
|
||||
);
|
||||
}
|
||||
@@ -64,10 +63,10 @@ export default (context, pluginsPath) => {
|
||||
return pluginsConf;
|
||||
}
|
||||
|
||||
pluginOptions = options[type];
|
||||
pluginOpts = options[type];
|
||||
}
|
||||
|
||||
const steps = castArray(pluginOptions).map((pluginOpt) =>
|
||||
const steps = castArray(pluginOpts).map(pluginOpt =>
|
||||
normalize(
|
||||
{...context, options: omit(options, Object.keys(PLUGINS_DEFINITIONS), 'plugins')},
|
||||
type,
|
||||
@@ -76,7 +75,7 @@ export default (context, pluginsPath) => {
|
||||
)
|
||||
);
|
||||
|
||||
pluginsConf[type] = async (input) =>
|
||||
pluginsConf[type] = async input =>
|
||||
postprocess(
|
||||
await pipeline(steps, pipelineConfig && pipelineConfig(pluginsConf, logger))(await preprocess(input)),
|
||||
input
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
import lodash from 'lodash'
|
||||
const {isPlainObject, isFunction, noop, cloneDeep, omit} = lodash
|
||||
import debugFactory from 'debug';
|
||||
const debug = debugFactory('semantic-release:plugins');
|
||||
import getError from '../get-error.js';
|
||||
import {extractErrors} from '../utils.js';
|
||||
import PLUGINS_DEFINITIONS from '../definitions/plugins.js';
|
||||
import {loadPlugin, parseConfig} from './utils.js';
|
||||
const {isPlainObject, isFunction, noop, cloneDeep, omit} = require('lodash');
|
||||
const debug = require('debug')('semantic-release:plugins');
|
||||
const getError = require('../get-error');
|
||||
const {extractErrors} = require('../utils');
|
||||
const PLUGINS_DEFINITIONS = require('../definitions/plugins');
|
||||
const {loadPlugin, parseConfig} = require('./utils');
|
||||
|
||||
export default (context, type, pluginOpt, pluginsPath) => {
|
||||
module.exports = (context, type, pluginOpt, pluginsPath) => {
|
||||
const {stdout, stderr, options, logger} = context;
|
||||
if (!pluginOpt) {
|
||||
return noop;
|
||||
@@ -28,7 +26,7 @@ export default (context, type, pluginOpt, pluginsPath) => {
|
||||
throw getError('EPLUGIN', {type, pluginName});
|
||||
}
|
||||
|
||||
const validator = async (input) => {
|
||||
const validator = async input => {
|
||||
const {dryRun, outputValidator} = PLUGINS_DEFINITIONS[type] || {};
|
||||
try {
|
||||
if (!input.options.dryRun || dryRun) {
|
||||
@@ -50,7 +48,7 @@ export default (context, type, pluginOpt, pluginsPath) => {
|
||||
logger.warn(`Skip step "${type}" of plugin "${pluginName}" in dry-run mode`);
|
||||
} catch (error) {
|
||||
logger.error(`Failed step "${type}" of plugin "${pluginName}"`);
|
||||
extractErrors(error).forEach((err) => Object.assign(err, {pluginName}));
|
||||
extractErrors(error).forEach(err => Object.assign(err, {pluginName}));
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import lodash from 'lodash'
|
||||
const {identity} = lodash
|
||||
import pReduce from 'p-reduce';
|
||||
import AggregateError from 'aggregate-error';
|
||||
import {extractErrors} from '../utils.js';
|
||||
const {identity} = require('lodash');
|
||||
const pReduce = require('p-reduce');
|
||||
const AggregateError = require('aggregate-error');
|
||||
const {extractErrors} = require('../utils');
|
||||
|
||||
/**
|
||||
* A Function that execute a list of function sequencially. If at least one Function ins the pipeline throws an Error or rejects, the pipeline function rejects as well.
|
||||
@@ -26,7 +25,7 @@ import {extractErrors} from '../utils.js';
|
||||
*
|
||||
* @return {Pipeline} A Function that execute the `steps` sequencially
|
||||
*/
|
||||
export default (steps, {settleAll = false, getNextInput = identity, transform = identity} = {}) => async (input) => {
|
||||
module.exports = (steps, {settleAll = false, getNextInput = identity, transform = identity} = {}) => async input => {
|
||||
const results = [];
|
||||
const errors = [];
|
||||
await pReduce(
|
||||
|
||||
+6
-15
@@ -1,10 +1,9 @@
|
||||
import {dirname} from 'path';
|
||||
import lodash from 'lodash'
|
||||
const {isString, isFunction, castArray, isArray, isPlainObject, isNil} = lodash
|
||||
import resolveFrom from 'resolve-from';
|
||||
const {dirname} = require('path');
|
||||
const {isString, isFunction, castArray, isArray, isPlainObject, isNil} = require('lodash');
|
||||
const resolveFrom = require('resolve-from');
|
||||
|
||||
const validateSteps = (conf) => {
|
||||
return conf.every((conf) => {
|
||||
const validateSteps = conf => {
|
||||
return conf.every(conf => {
|
||||
if (
|
||||
isArray(conf) &&
|
||||
(conf.length === 1 || conf.length === 2) &&
|
||||
@@ -66,12 +65,4 @@ function parseConfig(plugin) {
|
||||
return [path, config || {}];
|
||||
}
|
||||
|
||||
const exported = {
|
||||
validatePlugin,
|
||||
validateStep,
|
||||
loadPlugin,
|
||||
parseConfig,
|
||||
};
|
||||
|
||||
export default exported;
|
||||
export {validatePlugin, validateStep, loadPlugin, parseConfig};
|
||||
module.exports = {validatePlugin, validateStep, loadPlugin, parseConfig};
|
||||
|
||||
+14
-39
@@ -1,7 +1,6 @@
|
||||
import lodash from 'lodash'
|
||||
const {isFunction, union, template} = lodash
|
||||
import semver from 'semver';
|
||||
import hideSensitive from './hide-sensitive.js';
|
||||
const {isFunction, union, template} = require('lodash');
|
||||
const semver = require('semver');
|
||||
const hideSensitive = require('./hide-sensitive');
|
||||
|
||||
function extractErrors(err) {
|
||||
return err && isFunction(err[Symbol.iterator]) ? [...err] : [err];
|
||||
@@ -9,13 +8,13 @@ function extractErrors(err) {
|
||||
|
||||
function hideSensitiveValues(env, objs) {
|
||||
const hideFunction = hideSensitive(env);
|
||||
return objs.map((object) => {
|
||||
Object.getOwnPropertyNames(object).forEach((prop) => {
|
||||
if (object[prop]) {
|
||||
object[prop] = hideFunction(object[prop]);
|
||||
return objs.map(obj => {
|
||||
Object.getOwnPropertyNames(obj).forEach(prop => {
|
||||
if (obj[prop]) {
|
||||
obj[prop] = hideFunction(obj[prop]);
|
||||
}
|
||||
});
|
||||
return object;
|
||||
return obj;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -32,14 +31,9 @@ function isMaintenanceRange(range) {
|
||||
}
|
||||
|
||||
function getUpperBound(range) {
|
||||
const result = semver.valid(range)
|
||||
return semver.valid(range)
|
||||
? range
|
||||
: ((semver.validRange(range) || '').match(/<(?<upperBound>\d+\.\d+\.\d+(-\d+)?)$/) || [])[1];
|
||||
|
||||
return result
|
||||
? // https://github.com/npm/node-semver/issues/322
|
||||
result.replace(/-\d+$/, '')
|
||||
: result;
|
||||
: ((semver.validRange(range) || '').match(/<(?<upperBound>\d+\.\d+\.\d+)$/) || [])[1];
|
||||
}
|
||||
|
||||
function getLowerBound(range) {
|
||||
@@ -55,17 +49,17 @@ function lowest(version1, version2) {
|
||||
}
|
||||
|
||||
function getLatestVersion(versions, {withPrerelease} = {}) {
|
||||
return versions.filter((version) => withPrerelease || !semver.prerelease(version)).sort(semver.rcompare)[0];
|
||||
return versions.filter(version => withPrerelease || !semver.prerelease(version)).sort(semver.rcompare)[0];
|
||||
}
|
||||
|
||||
function getEarliestVersion(versions, {withPrerelease} = {}) {
|
||||
return versions.filter((version) => withPrerelease || !semver.prerelease(version)).sort(semver.compare)[0];
|
||||
return versions.filter(version => withPrerelease || !semver.prerelease(version)).sort(semver.compare)[0];
|
||||
}
|
||||
|
||||
function getFirstVersion(versions, lowerBranches) {
|
||||
const lowerVersion = union(...lowerBranches.map(({tags}) => tagsToVersions(tags))).sort(semver.rcompare);
|
||||
if (lowerVersion[0]) {
|
||||
return versions.sort(semver.compare).find((version) => semver.gt(version, lowerVersion[0]));
|
||||
return versions.sort(semver.compare).find(version => semver.gt(version, lowerVersion[0]));
|
||||
}
|
||||
|
||||
return getEarliestVersion(versions);
|
||||
@@ -83,26 +77,7 @@ function isSameChannel(channel, otherChannel) {
|
||||
return channel === otherChannel || (!channel && !otherChannel);
|
||||
}
|
||||
|
||||
const exported = {
|
||||
extractErrors,
|
||||
hideSensitiveValues,
|
||||
tagsToVersions,
|
||||
isMajorRange,
|
||||
isMaintenanceRange,
|
||||
getUpperBound,
|
||||
getLowerBound,
|
||||
highest,
|
||||
lowest,
|
||||
getLatestVersion,
|
||||
getEarliestVersion,
|
||||
getFirstVersion,
|
||||
getRange,
|
||||
makeTag,
|
||||
isSameChannel,
|
||||
};
|
||||
|
||||
export default exported;
|
||||
export {
|
||||
module.exports = {
|
||||
extractErrors,
|
||||
hideSensitiveValues,
|
||||
tagsToVersions,
|
||||
|
||||
+6
-7
@@ -1,10 +1,9 @@
|
||||
import lodash from 'lodash'
|
||||
const {template, isString, isPlainObject} = lodash
|
||||
import AggregateError from 'aggregate-error';
|
||||
import {isGitRepo, verifyTagName} from './git.js';
|
||||
import getError from './get-error.js';
|
||||
const {template, isString, isPlainObject} = require('lodash');
|
||||
const AggregateError = require('aggregate-error');
|
||||
const {isGitRepo, verifyTagName} = require('./git');
|
||||
const getError = require('./get-error');
|
||||
|
||||
export default async (context) => {
|
||||
module.exports = async context => {
|
||||
const {
|
||||
cwd,
|
||||
env,
|
||||
@@ -30,7 +29,7 @@ export default async (context) => {
|
||||
errors.push(getError('ETAGNOVERSION', context));
|
||||
}
|
||||
|
||||
branches.forEach((branch) => {
|
||||
branches.forEach(branch => {
|
||||
if (
|
||||
!((isString(branch) && branch.trim()) || (isPlainObject(branch) && isString(branch.name) && branch.name.trim()))
|
||||
) {
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="15 0 500 500" width="500px" height="500px">
|
||||
<path fill="#494949" fill-rule="nonzero" d="M265 300a50 50 0 110-100 50 50 0 010 100zm0-15a35 35 0 100-70 35 35 0 000 70zM189 65v36c2 11 21 55 35 81-18-13-58-43-69-58a92 92 0 01-17-29l-71 40v86c9 5 23 15 31 18 11 4 59 9 88 10-21 9-66 29-85 31-16 3-27 1-34 0v85l69 39c9-4 24-12 31-17 9-7 37-46 52-71-2 22-8 71-15 89-6 15-13 23-17 29l79 45 73-42c1-9 2-28 0-38-1-11-21-55-34-81 18 14 57 44 69 59 11 13 15 24 17 30l74-42v-84c-8-5-24-15-34-19s-58-9-87-10c21-10 66-29 85-31 18-3 29-1 36 1v-87l-70-40-31 19c-9 7-37 46-53 70 3-22 9-71 16-89 6-15 13-24 18-29l-79-45-77 44zm77-65l217 125v250L266 500 49 375V125L266 0z"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 715 B |
Generated
-17214
File diff suppressed because it is too large
Load Diff
+32
-44
@@ -1,6 +1,5 @@
|
||||
{
|
||||
"name": "semantic-release",
|
||||
"type": "module",
|
||||
"description": "Automated semver compliant package publishing",
|
||||
"version": "0.0.0-development",
|
||||
"author": "Stephan Bönnemann <stephan@boennemann.me> (http://boennemann.me)",
|
||||
@@ -27,52 +26,51 @@
|
||||
"@semantic-release/npm": "^7.0.0",
|
||||
"@semantic-release/release-notes-generator": "^9.0.0",
|
||||
"aggregate-error": "^3.0.0",
|
||||
"cosmiconfig": "^7.0.0",
|
||||
"cosmiconfig": "^6.0.0",
|
||||
"debug": "^4.0.0",
|
||||
"env-ci": "^5.0.0",
|
||||
"execa": "^5.0.0",
|
||||
"execa": "^4.0.0",
|
||||
"figures": "^3.0.0",
|
||||
"find-versions": "^4.0.0",
|
||||
"get-stream": "^6.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": "^4.0.0",
|
||||
"lodash": "^4.17.21",
|
||||
"marked": "^2.0.0",
|
||||
"marked-terminal": "^4.1.1",
|
||||
"hosted-git-info": "^3.0.0",
|
||||
"lodash": "^4.17.15",
|
||||
"marked": "^0.8.0",
|
||||
"marked-terminal": "^4.0.0",
|
||||
"micromatch": "^4.0.2",
|
||||
"p-each-series": "^2.1.0",
|
||||
"p-reduce": "^2.0.0",
|
||||
"read-pkg-up": "^7.0.0",
|
||||
"resolve-from": "^5.0.0",
|
||||
"semver": "^7.3.2",
|
||||
"semver": "^7.1.1",
|
||||
"semver-diff": "^3.1.1",
|
||||
"signale": "^1.2.1",
|
||||
"yargs": "^16.2.0"
|
||||
"yargs": "^15.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"ava": "3.15.0",
|
||||
"cjs2esm": "^2.0.0",
|
||||
"clear-module": "4.1.1",
|
||||
"codecov": "3.8.2",
|
||||
"delay": "5.0.0",
|
||||
"dockerode": "3.3.0",
|
||||
"file-url": "3.0.0",
|
||||
"fs-extra": "10.0.0",
|
||||
"got": "11.8.2",
|
||||
"js-yaml": "4.1.0",
|
||||
"mockserver-client": "5.11.2",
|
||||
"nock": "13.1.1",
|
||||
"nyc": "15.1.0",
|
||||
"p-retry": "4.6.1",
|
||||
"proxyquire": "2.1.3",
|
||||
"sinon": "11.1.1",
|
||||
"stream-buffers": "3.0.2",
|
||||
"tempy": "1.0.1",
|
||||
"xo": "0.29.1"
|
||||
"ava": "^3.1.0",
|
||||
"clear-module": "^4.0.0",
|
||||
"codecov": "^3.0.0",
|
||||
"delay": "^4.0.0",
|
||||
"dockerode": "^3.0.0",
|
||||
"file-url": "^3.0.0",
|
||||
"fs-extra": "^8.0.0",
|
||||
"got": "^10.5.2",
|
||||
"js-yaml": "^3.10.0",
|
||||
"mockserver-client": "^5.1.1",
|
||||
"nock": "^11.1.0",
|
||||
"nyc": "^15.0.0",
|
||||
"p-retry": "^4.0.0",
|
||||
"proxyquire": "^2.0.0",
|
||||
"sinon": "^8.0.4",
|
||||
"stream-buffers": "^3.0.2",
|
||||
"tempy": "^0.3.0",
|
||||
"xo": "^0.25.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.17"
|
||||
"node": ">=10.18"
|
||||
},
|
||||
"files": [
|
||||
"bin",
|
||||
@@ -94,7 +92,7 @@
|
||||
"version"
|
||||
],
|
||||
"license": "MIT",
|
||||
"exports": "./index.js",
|
||||
"main": "index.js",
|
||||
"nyc": {
|
||||
"include": [
|
||||
"lib/**/*.js",
|
||||
@@ -124,20 +122,10 @@
|
||||
"lint": "xo",
|
||||
"pretest": "npm run lint",
|
||||
"semantic-release": "./bin/semantic-release.js",
|
||||
"test": "nyc ava -v",
|
||||
"test:ci": "nyc ava -v"
|
||||
"test": "nyc ava -v"
|
||||
},
|
||||
"xo": {
|
||||
"prettier": true,
|
||||
"space": true,
|
||||
"rules": {
|
||||
"unicorn/string-content": "off",
|
||||
"import/extensions": "off"
|
||||
}
|
||||
},
|
||||
"renovate": {
|
||||
"extends": [
|
||||
"github>semantic-release/.github"
|
||||
]
|
||||
"space": true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import test from 'ava';
|
||||
import lodash from 'lodash'
|
||||
const {union} = lodash
|
||||
import semver from 'semver';
|
||||
import proxyquire from 'proxyquire';
|
||||
const test = require('ava');
|
||||
const {union} = require('lodash');
|
||||
const semver = require('semver');
|
||||
const proxyquire = require('proxyquire');
|
||||
|
||||
const getBranch = (branches, branch) => branches.find(({name}) => name === branch);
|
||||
const release = (branches, name, version) => getBranch(branches, name).tags.push({version});
|
||||
@@ -13,7 +12,7 @@ const merge = (branches, source, target, tag) => {
|
||||
);
|
||||
};
|
||||
|
||||
test('Enforce ranges with branching release workflow', async (t) => {
|
||||
test('Enforce ranges with branching release workflow', async t => {
|
||||
const branches = [
|
||||
{name: '1.x', tags: []},
|
||||
{name: '1.0.x', tags: []},
|
||||
@@ -190,7 +189,7 @@ test('Enforce ranges with branching release workflow', async (t) => {
|
||||
t.is(getBranch(result, '1.x').range, '>=1.2.0 <2.0.0', 'Can release on 1.x only within range');
|
||||
});
|
||||
|
||||
test('Throw SemanticReleaseError for invalid configurations', async (t) => {
|
||||
test('Throw SemanticReleaseError for invalid configurations', async t => {
|
||||
const branches = [
|
||||
{name: '123', range: '123', tags: []},
|
||||
{name: '1.x', tags: []},
|
||||
@@ -225,7 +224,7 @@ test('Throw SemanticReleaseError for invalid configurations', async (t) => {
|
||||
t.truthy(errors[4].details);
|
||||
});
|
||||
|
||||
test('Throw a SemanticReleaseError if there is duplicate branches', async (t) => {
|
||||
test('Throw a SemanticReleaseError if there is duplicate branches', async t => {
|
||||
const branches = [
|
||||
{name: 'master', tags: []},
|
||||
{name: 'master', tags: []},
|
||||
@@ -240,7 +239,7 @@ test('Throw a SemanticReleaseError if there is duplicate branches', async (t) =>
|
||||
t.truthy(errors[0].details);
|
||||
});
|
||||
|
||||
test('Throw a SemanticReleaseError for each invalid branch name', async (t) => {
|
||||
test('Throw a SemanticReleaseError for each invalid branch name', async t => {
|
||||
const branches = [
|
||||
{name: '~master', tags: []},
|
||||
{name: '^master', tags: []},
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import test from 'ava';
|
||||
import expand from '../../lib/branches/expand.js';
|
||||
import {gitRepo, gitCommits, gitCheckout, gitPush} from '../helpers/git-utils.js';
|
||||
const test = require('ava');
|
||||
const expand = require('../../lib/branches/expand');
|
||||
const {gitRepo, gitCommits, gitCheckout, gitPush} = require('../helpers/git-utils');
|
||||
|
||||
test('Expand branches defined with globs', async (t) => {
|
||||
test('Expand branches defined with globs', async t => {
|
||||
const {cwd, repositoryUrl} = await gitRepo(true);
|
||||
await gitCommits(['First'], {cwd});
|
||||
await gitPush(repositoryUrl, 'master', {cwd});
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import test from 'ava';
|
||||
import getTags from '../../lib/branches/get-tags.js';
|
||||
import {gitRepo, gitCommits, gitTagVersion, gitCheckout, gitAddNote} from '../helpers/git-utils.js';
|
||||
const test = require('ava');
|
||||
const getTags = require('../../lib/branches/get-tags');
|
||||
const {gitRepo, gitCommits, gitTagVersion, gitCheckout, gitAddNote} = require('../helpers/git-utils');
|
||||
|
||||
test('Get the valid tags', async (t) => {
|
||||
test('Get the valid tags', async t => {
|
||||
const {cwd} = await gitRepo();
|
||||
const commits = await gitCommits(['First'], {cwd});
|
||||
await gitTagVersion('foo', undefined, {cwd});
|
||||
@@ -28,7 +28,7 @@ test('Get the valid tags', async (t) => {
|
||||
]);
|
||||
});
|
||||
|
||||
test('Get the valid tags from multiple branches', async (t) => {
|
||||
test('Get the valid tags from multiple branches', async t => {
|
||||
const {cwd} = await gitRepo();
|
||||
await gitCommits(['First'], {cwd});
|
||||
await gitTagVersion('v1.0.0', undefined, {cwd});
|
||||
@@ -71,7 +71,7 @@ test('Get the valid tags from multiple branches', async (t) => {
|
||||
]);
|
||||
});
|
||||
|
||||
test('Return branches with and empty tags array if no valid tag is found', async (t) => {
|
||||
test('Return branches with and empty tags array if no valid tag is found', async t => {
|
||||
const {cwd} = await gitRepo();
|
||||
await gitCommits(['First'], {cwd});
|
||||
await gitTagVersion('foo', undefined, {cwd});
|
||||
@@ -85,7 +85,7 @@ test('Return branches with and empty tags array if no valid tag is found', async
|
||||
t.deepEqual(result, [{name: 'master', tags: []}]);
|
||||
});
|
||||
|
||||
test('Return branches with and empty tags array if no valid tag is found in history of configured branches', async (t) => {
|
||||
test('Return branches with and empty tags array if no valid tag is found in history of configured branches', async t => {
|
||||
const {cwd} = await gitRepo();
|
||||
await gitCommits(['First'], {cwd});
|
||||
await gitCheckout('next', true, {cwd});
|
||||
@@ -108,7 +108,7 @@ test('Return branches with and empty tags array if no valid tag is found in hist
|
||||
]);
|
||||
});
|
||||
|
||||
test('Get the highest valid tag corresponding to the "tagFormat"', async (t) => {
|
||||
test('Get the highest valid tag corresponding to the "tagFormat"', async t => {
|
||||
const {cwd} = await gitRepo();
|
||||
await gitCommits(['First'], {cwd});
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import test from 'ava';
|
||||
import normalize from '../../lib/branches/normalize.js';
|
||||
const test = require('ava');
|
||||
const normalize = require('../../lib/branches/normalize');
|
||||
|
||||
const toTags = (versions) => versions.map((version) => ({version}));
|
||||
const toTags = versions => versions.map(version => ({version}));
|
||||
|
||||
test('Maintenance branches - initial state', (t) => {
|
||||
test('Maintenance branches - initial state', t => {
|
||||
const maintenance = [
|
||||
{name: '1.x', channel: '1.x', tags: []},
|
||||
{name: '1.1.x', tags: []},
|
||||
@@ -48,7 +48,7 @@ test('Maintenance branches - initial state', (t) => {
|
||||
);
|
||||
});
|
||||
|
||||
test('Maintenance branches - cap range to first release present on default branch and not in any Maintenance one', (t) => {
|
||||
test('Maintenance branches - cap range to first release present on default branch and not in any Maintenance one', t => {
|
||||
const maintenance = [
|
||||
{name: '1.x', tags: toTags(['1.0.0', '1.1.0', '1.1.1', '1.2.0', '1.2.1', '1.3.0', '1.4.0', '1.5.0'])},
|
||||
{name: 'name', range: '1.1.x', tags: toTags(['1.0.0', '1.0.1', '1.1.0', '1.1.1'])},
|
||||
@@ -110,7 +110,7 @@ test('Maintenance branches - cap range to first release present on default branc
|
||||
);
|
||||
});
|
||||
|
||||
test('Maintenance branches - cap range to default branch last release if all release are also present on maintenance branch', (t) => {
|
||||
test('Maintenance branches - cap range to default branch last release if all release are also present on maintenance branch', t => {
|
||||
const maintenance = [
|
||||
{name: '1.x', tags: toTags(['1.0.0', '1.2.0', '1.3.0'])},
|
||||
{name: '2.x.x', tags: toTags(['1.0.0', '1.2.0', '1.3.0', '2.0.0'])},
|
||||
@@ -147,7 +147,7 @@ test('Maintenance branches - cap range to default branch last release if all rel
|
||||
);
|
||||
});
|
||||
|
||||
test('Release branches - initial state', (t) => {
|
||||
test('Release branches - initial state', t => {
|
||||
const release = [
|
||||
{name: 'master', tags: []},
|
||||
{name: 'next', channel: 'next', tags: []},
|
||||
@@ -187,7 +187,7 @@ test('Release branches - initial state', (t) => {
|
||||
);
|
||||
});
|
||||
|
||||
test('Release branches - 3 release branches', (t) => {
|
||||
test('Release branches - 3 release branches', t => {
|
||||
const release = [
|
||||
{name: 'master', tags: toTags(['1.0.0', '1.0.1', '1.0.2'])},
|
||||
{name: 'next', tags: toTags(['1.0.0', '1.0.1', '1.0.2', '1.1.0', '1.2.0'])},
|
||||
@@ -220,7 +220,7 @@ test('Release branches - 3 release branches', (t) => {
|
||||
);
|
||||
});
|
||||
|
||||
test('Release branches - 2 release branches', (t) => {
|
||||
test('Release branches - 2 release branches', t => {
|
||||
const release = [
|
||||
{name: 'master', tags: toTags(['1.0.0', '1.0.1', '1.1.0', '1.1.1', '1.2.0'])},
|
||||
{name: 'next', tags: toTags(['1.0.0', '1.0.1', '1.1.0', '1.1.1', '1.2.0', '2.0.0', '2.0.1', '2.1.0'])},
|
||||
@@ -251,7 +251,7 @@ test('Release branches - 2 release branches', (t) => {
|
||||
);
|
||||
});
|
||||
|
||||
test('Release branches - 1 release branches', (t) => {
|
||||
test('Release branches - 1 release branches', t => {
|
||||
const release = [{name: 'master', tags: toTags(['1.0.0', '1.1.0', '1.1.1', '1.2.0'])}];
|
||||
|
||||
t.deepEqual(
|
||||
@@ -260,7 +260,7 @@ test('Release branches - 1 release branches', (t) => {
|
||||
);
|
||||
});
|
||||
|
||||
test('Release branches - cap ranges to first release only present on following branch', (t) => {
|
||||
test('Release branches - cap ranges to first release only present on following branch', t => {
|
||||
const release = [
|
||||
{name: 'master', tags: toTags(['1.0.0', '1.1.0', '1.2.0', '2.0.0'])},
|
||||
{name: 'next', tags: toTags(['1.0.0', '1.1.0', '1.2.0', '2.0.0', '2.1.0'])},
|
||||
@@ -286,7 +286,7 @@ test('Release branches - cap ranges to first release only present on following b
|
||||
);
|
||||
});
|
||||
|
||||
test('Release branches - Handle missing previous tags in branch history', (t) => {
|
||||
test('Release branches - Handle missing previous tags in branch history', t => {
|
||||
const release = [
|
||||
{name: 'master', tags: toTags(['1.0.0', '2.0.0'])},
|
||||
{name: 'next', tags: toTags(['1.0.0', '1.1.0', '1.1.1', '1.2.0', '2.0.0'])},
|
||||
@@ -317,7 +317,7 @@ test('Release branches - Handle missing previous tags in branch history', (t) =>
|
||||
);
|
||||
});
|
||||
|
||||
test('Release branches - limit releases on 2nd and 3rd branch based on 1st branch last release', (t) => {
|
||||
test('Release branches - limit releases on 2nd and 3rd branch based on 1st branch last release', t => {
|
||||
const release = [
|
||||
{name: 'master', tags: toTags(['1.0.0', '1.1.0', '2.0.0', '3.0.0'])},
|
||||
{name: 'next', tags: toTags(['1.0.0', '1.1.0'])},
|
||||
@@ -357,7 +357,7 @@ test('Release branches - limit releases on 2nd and 3rd branch based on 1st branc
|
||||
);
|
||||
});
|
||||
|
||||
test('Prerelease branches', (t) => {
|
||||
test('Prerelease branches', t => {
|
||||
const prerelease = [
|
||||
{name: 'beta', channel: 'beta', prerelease: true, tags: []},
|
||||
{name: 'alpha', prerelease: 'preview', tags: []},
|
||||
@@ -372,7 +372,7 @@ test('Prerelease branches', (t) => {
|
||||
);
|
||||
});
|
||||
|
||||
test('Allow to set channel to "false" to prevent default', (t) => {
|
||||
test('Allow to set channel to "false" to prevent default', t => {
|
||||
const maintenance = [{name: '1.x', channel: false, tags: []}];
|
||||
const release = [
|
||||
{name: 'master', channel: false, tags: []},
|
||||
|
||||
+30
-31
@@ -1,28 +1,27 @@
|
||||
import test from 'ava';
|
||||
import lodash from 'lodash'
|
||||
const {escapeRegExp} = lodash
|
||||
const test = require('ava');
|
||||
const {escapeRegExp} = require('lodash');
|
||||
const proxyquire = require('proxyquire').noPreserveCache();
|
||||
import sinon from 'sinon';
|
||||
import {SECRET_REPLACEMENT} from '../lib/definitions/constants.js';
|
||||
const {stub} = require('sinon');
|
||||
const {SECRET_REPLACEMENT} = require('../lib/definitions/constants');
|
||||
|
||||
test.beforeEach((t) => {
|
||||
test.beforeEach(t => {
|
||||
t.context.logs = '';
|
||||
t.context.errors = '';
|
||||
t.context.stdout = sinon.stubprocess.stdout, 'write').callsFake((value) => {
|
||||
t.context.logs += value.toString();
|
||||
t.context.stdout = stub(process.stdout, 'write').callsFake(val => {
|
||||
t.context.logs += val.toString();
|
||||
});
|
||||
t.context.stderr = sinon.stubprocess.stderr, 'write').callsFake((value) => {
|
||||
t.context.errors += value.toString();
|
||||
t.context.stderr = stub(process.stderr, 'write').callsFake(val => {
|
||||
t.context.errors += val.toString();
|
||||
});
|
||||
});
|
||||
|
||||
test.afterEach.always((t) => {
|
||||
test.afterEach.always(t => {
|
||||
t.context.stdout.restore();
|
||||
t.context.stderr.restore();
|
||||
});
|
||||
|
||||
test.serial('Pass options to semantic-release API', async (t) => {
|
||||
const run = sinon.stub).resolves(true);
|
||||
test.serial('Pass options to semantic-release API', async t => {
|
||||
const run = stub().resolves(true);
|
||||
const argv = [
|
||||
'',
|
||||
'',
|
||||
@@ -87,8 +86,8 @@ test.serial('Pass options to semantic-release API', async (t) => {
|
||||
t.is(exitCode, 0);
|
||||
});
|
||||
|
||||
test.serial('Pass options to semantic-release API with alias arguments', async (t) => {
|
||||
const run = sinon.stub).resolves(true);
|
||||
test.serial('Pass options to semantic-release API with alias arguments', async t => {
|
||||
const run = stub().resolves(true);
|
||||
const argv = [
|
||||
'',
|
||||
'',
|
||||
@@ -120,8 +119,8 @@ test.serial('Pass options to semantic-release API with alias arguments', async (
|
||||
t.is(exitCode, 0);
|
||||
});
|
||||
|
||||
test.serial('Pass unknown options to semantic-release API', async (t) => {
|
||||
const run = sinon.stub).resolves(true);
|
||||
test.serial('Pass unknown options to semantic-release API', async t => {
|
||||
const run = stub().resolves(true);
|
||||
const argv = ['', '', '--bool', '--first-option', 'value1', '--second-option', 'value2', '--second-option', 'value3'];
|
||||
const cli = proxyquire('../cli', {'.': run, process: {...process, argv}});
|
||||
|
||||
@@ -134,8 +133,8 @@ test.serial('Pass unknown options to semantic-release API', async (t) => {
|
||||
t.is(exitCode, 0);
|
||||
});
|
||||
|
||||
test.serial('Pass empty Array to semantic-release API for list option set to "false"', async (t) => {
|
||||
const run = sinon.stub).resolves(true);
|
||||
test.serial('Pass empty Array to semantic-release API for list option set to "false"', async t => {
|
||||
const run = stub().resolves(true);
|
||||
const argv = ['', '', '--publish', 'false'];
|
||||
const cli = proxyquire('../cli', {'.': run, process: {...process, argv}});
|
||||
|
||||
@@ -146,8 +145,8 @@ test.serial('Pass empty Array to semantic-release API for list option set to "fa
|
||||
t.is(exitCode, 0);
|
||||
});
|
||||
|
||||
test.serial('Do not set properties in option for which arg is not in command line', async (t) => {
|
||||
const run = sinon.stub).resolves(true);
|
||||
test.serial('Do not set properties in option for which arg is not in command line', async t => {
|
||||
const run = stub().resolves(true);
|
||||
const argv = ['', '', '-b', 'master'];
|
||||
const cli = proxyquire('../cli', {'.': run, process: {...process, argv}});
|
||||
|
||||
@@ -163,8 +162,8 @@ test.serial('Do not set properties in option for which arg is not in command lin
|
||||
t.false('e' in run.args[0][0]);
|
||||
});
|
||||
|
||||
test.serial('Display help', async (t) => {
|
||||
const run = sinon.stub).resolves(true);
|
||||
test.serial('Display help', async t => {
|
||||
const run = stub().resolves(true);
|
||||
const argv = ['', '', '--help'];
|
||||
const cli = proxyquire('../cli', {'.': run, process: {...process, argv}});
|
||||
|
||||
@@ -174,8 +173,8 @@ test.serial('Display help', async (t) => {
|
||||
t.is(exitCode, 0);
|
||||
});
|
||||
|
||||
test.serial('Return error exitCode and prints help if called with a command', async (t) => {
|
||||
const run = sinon.stub).resolves(true);
|
||||
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 = proxyquire('../cli', {'.': run, process: {...process, argv}});
|
||||
|
||||
@@ -186,8 +185,8 @@ test.serial('Return error exitCode and prints help if called with a command', as
|
||||
t.is(exitCode, 1);
|
||||
});
|
||||
|
||||
test.serial('Return error exitCode if multiple plugin are set for single plugin', async (t) => {
|
||||
const run = sinon.stub).resolves(true);
|
||||
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 = proxyquire('../cli', {'.': run, process: {...process, argv}});
|
||||
|
||||
@@ -198,8 +197,8 @@ test.serial('Return error exitCode if multiple plugin are set for single plugin'
|
||||
t.is(exitCode, 1);
|
||||
});
|
||||
|
||||
test.serial('Return error exitCode if semantic-release throw error', async (t) => {
|
||||
const run = sinon.stub).rejects(new Error('semantic-release error'));
|
||||
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 = proxyquire('../cli', {'.': run, process: {...process, argv}});
|
||||
|
||||
@@ -209,9 +208,9 @@ test.serial('Return error exitCode if semantic-release throw error', async (t) =
|
||||
t.is(exitCode, 1);
|
||||
});
|
||||
|
||||
test.serial('Hide sensitive environment variable values from the logs', async (t) => {
|
||||
test.serial('Hide sensitive environment variable values from the logs', async t => {
|
||||
const env = {MY_TOKEN: 'secret token'};
|
||||
const run = sinon.stub).rejects(new Error(`Throw error: Exposing token ${env.MY_TOKEN}`));
|
||||
const run = stub().rejects(new Error(`Throw error: Exposing token ${env.MY_TOKEN}`));
|
||||
const argv = ['', ''];
|
||||
const cli = proxyquire('../cli', {'.': run, process: {...process, argv, env: {...process.env, ...env}}});
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import test from 'ava';
|
||||
import {maintenance, prerelease, release} from '../../lib/definitions/branches.js';
|
||||
const test = require('ava');
|
||||
const {maintenance, prerelease, release} = require('../../lib/definitions/branches');
|
||||
|
||||
test('A "maintenance" branch is identified by having a "range" property or a "name" formatted like "N.x", "N.x.x" or "N.N.x"', (t) => {
|
||||
test('A "maintenance" branch is identified by having a "range" property or a "name" formatted like "N.x", "N.x.x" or "N.N.x"', t => {
|
||||
t.true(maintenance.filter({name: '1.x.x'}));
|
||||
t.true(maintenance.filter({name: '1.0.x'}));
|
||||
t.true(maintenance.filter({name: '1.x'}));
|
||||
@@ -17,7 +17,7 @@ test('A "maintenance" branch is identified by having a "range" property or a "na
|
||||
t.false(maintenance.filter({name: 'x.x.x'}));
|
||||
});
|
||||
|
||||
test('A "maintenance" branches must have a "range" property formatted like "N.x", "N.x.x" or "N.N.x"', (t) => {
|
||||
test('A "maintenance" branches must have a "range" property formatted like "N.x", "N.x.x" or "N.N.x"', t => {
|
||||
t.true(maintenance.branchValidator({name: 'some-name', range: '1.x.x'}));
|
||||
t.true(maintenance.branchValidator({name: 'some-name', range: '1.1.x'}));
|
||||
|
||||
@@ -29,14 +29,14 @@ test('A "maintenance" branches must have a "range" property formatted like "N.x"
|
||||
t.false(maintenance.branchValidator({name: 'some-name', range: ''}));
|
||||
});
|
||||
|
||||
test('The "maintenance" branches must have unique ranges', (t) => {
|
||||
test('The "maintenance" branches must have unique ranges', t => {
|
||||
t.true(maintenance.branchesValidator([{range: '1.x.x'}, {range: '1.0.x'}]));
|
||||
|
||||
t.false(maintenance.branchesValidator([{range: '1.x.x'}, {range: '1.x.x'}]));
|
||||
t.false(maintenance.branchesValidator([{range: '1.x.x'}, {range: '1.x'}]));
|
||||
});
|
||||
|
||||
test('A "prerelease" branch is identified by having a thruthy "prerelease" property', (t) => {
|
||||
test('A "prerelease" branch is identified by having a thruthy "prerelease" property', t => {
|
||||
t.true(prerelease.filter({name: 'some-name', prerelease: true}));
|
||||
t.true(prerelease.filter({name: 'some-name', prerelease: 'beta'}));
|
||||
t.true(prerelease.filter({name: 'some-name', prerelease: ''}));
|
||||
@@ -46,7 +46,7 @@ test('A "prerelease" branch is identified by having a thruthy "prerelease" prope
|
||||
t.false(prerelease.filter({name: 'some-name'}));
|
||||
});
|
||||
|
||||
test('A "prerelease" branch must have a valid prerelease detonation in "prerelease" property or in "name" if "prerelease" is "true"', (t) => {
|
||||
test('A "prerelease" branch must have a valid prerelease detonation in "prerelease" property or in "name" if "prerelease" is "true"', t => {
|
||||
t.true(prerelease.branchValidator({name: 'beta', prerelease: true}));
|
||||
t.true(prerelease.branchValidator({name: 'some-name', prerelease: 'beta'}));
|
||||
|
||||
@@ -59,13 +59,13 @@ test('A "prerelease" branch must have a valid prerelease detonation in "prerelea
|
||||
t.false(prerelease.branchValidator({name: '#beta', prerelease: true}));
|
||||
});
|
||||
|
||||
test('The "prerelease" branches must have unique "prerelease" property', (t) => {
|
||||
test('The "prerelease" branches must have unique "prerelease" property', t => {
|
||||
t.true(prerelease.branchesValidator([{prerelease: 'beta'}, {prerelease: 'alpha'}]));
|
||||
|
||||
t.false(prerelease.branchesValidator([{range: 'beta'}, {range: 'beta'}, {range: 'alpha'}]));
|
||||
});
|
||||
|
||||
test('A "release" branch is identified by not havin a "range" or "prerelease" property or a "name" formatted like "N.x", "N.x.x" or "N.N.x"', (t) => {
|
||||
test('A "release" branch is identified by not havin a "range" or "prerelease" property or a "name" formatted like "N.x", "N.x.x" or "N.N.x"', t => {
|
||||
t.true(release.filter({name: 'some-name'}));
|
||||
|
||||
t.false(release.filter({name: '1.x.x'}));
|
||||
@@ -76,7 +76,7 @@ test('A "release" branch is identified by not havin a "range" or "prerelease" pr
|
||||
t.false(release.filter({name: 'some-name', prerelease: 'beta'}));
|
||||
});
|
||||
|
||||
test('There must be between 1 and 3 release branches', (t) => {
|
||||
test('There must be between 1 and 3 release branches', t => {
|
||||
t.true(release.branchesValidator([{name: 'branch1'}]));
|
||||
t.true(release.branchesValidator([{name: 'branch1'}, {name: 'branch2'}]));
|
||||
t.true(release.branchesValidator([{name: 'branch1'}, {name: 'branch2'}, {name: 'branch3'}]));
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import test from 'ava';
|
||||
import plugins from '../../lib/definitions/plugins.js';
|
||||
import {RELEASE_NOTES_SEPARATOR, SECRET_REPLACEMENT} from '../../lib/definitions/constants.js';
|
||||
const test = require('ava');
|
||||
const plugins = require('../../lib/definitions/plugins');
|
||||
const {RELEASE_NOTES_SEPARATOR, SECRET_REPLACEMENT} = require('../../lib/definitions/constants');
|
||||
|
||||
test('The "analyzeCommits" plugin output must be either undefined or a valid semver release type', (t) => {
|
||||
test('The "analyzeCommits" plugin output must be either undefined or a valid semver release type', t => {
|
||||
t.false(plugins.analyzeCommits.outputValidator('invalid'));
|
||||
t.false(plugins.analyzeCommits.outputValidator(1));
|
||||
t.false(plugins.analyzeCommits.outputValidator({}));
|
||||
@@ -12,7 +12,7 @@ test('The "analyzeCommits" plugin output must be either undefined or a valid sem
|
||||
t.true(plugins.analyzeCommits.outputValidator('major'));
|
||||
});
|
||||
|
||||
test('The "generateNotes" plugin output, if defined, must be a string', (t) => {
|
||||
test('The "generateNotes" plugin output, if defined, must be a string', t => {
|
||||
t.false(plugins.generateNotes.outputValidator(1));
|
||||
t.false(plugins.generateNotes.outputValidator({}));
|
||||
|
||||
@@ -22,7 +22,7 @@ test('The "generateNotes" plugin output, if defined, must be a string', (t) => {
|
||||
t.true(plugins.generateNotes.outputValidator('string'));
|
||||
});
|
||||
|
||||
test('The "publish" plugin output, if defined, must be an object or "false"', (t) => {
|
||||
test('The "publish" plugin output, if defined, must be an object or "false"', t => {
|
||||
t.false(plugins.publish.outputValidator(1));
|
||||
t.false(plugins.publish.outputValidator('string'));
|
||||
|
||||
@@ -33,7 +33,7 @@ test('The "publish" plugin output, if defined, must be an object or "false"', (t
|
||||
t.true(plugins.publish.outputValidator(false));
|
||||
});
|
||||
|
||||
test('The "addChannel" plugin output, if defined, must be an object', (t) => {
|
||||
test('The "addChannel" plugin output, if defined, must be an object', t => {
|
||||
t.false(plugins.addChannel.outputValidator(1));
|
||||
t.false(plugins.addChannel.outputValidator('string'));
|
||||
|
||||
@@ -43,7 +43,7 @@ test('The "addChannel" plugin output, if defined, must be an object', (t) => {
|
||||
t.true(plugins.addChannel.outputValidator(''));
|
||||
});
|
||||
|
||||
test('The "generateNotes" plugins output are concatenated with separator and sensitive data is hidden', (t) => {
|
||||
test('The "generateNotes" plugins output are concatenated with separator and sensitive data is hidden', t => {
|
||||
const env = {MY_TOKEN: 'secret token'};
|
||||
t.is(plugins.generateNotes.postprocess(['note 1', 'note 2'], {env}), `note 1${RELEASE_NOTES_SEPARATOR}note 2`);
|
||||
t.is(plugins.generateNotes.postprocess(['', 'note'], {env}), 'note');
|
||||
@@ -63,7 +63,7 @@ test('The "generateNotes" plugins output are concatenated with separator and sen
|
||||
);
|
||||
});
|
||||
|
||||
test('The "analyzeCommits" plugins output are reduced to the highest release type', (t) => {
|
||||
test('The "analyzeCommits" plugins output are reduced to the highest release type', t => {
|
||||
t.is(plugins.analyzeCommits.postprocess(['major', 'minor']), 'major');
|
||||
t.is(plugins.analyzeCommits.postprocess(['', 'minor']), 'minor');
|
||||
t.is(plugins.analyzeCommits.postprocess([undefined, 'patch']), 'patch');
|
||||
|
||||
Vendored
+1
-1
@@ -1 +1 @@
|
||||
export default () => {};
|
||||
module.exports = () => {};
|
||||
|
||||
Vendored
+1
-5
@@ -1,4 +1,4 @@
|
||||
const exported = {
|
||||
module.exports = {
|
||||
verifyConditions: () => {},
|
||||
getLastRelease: () => {},
|
||||
analyzeCommits: () => {},
|
||||
@@ -6,7 +6,3 @@ const exported = {
|
||||
generateNotes: () => {},
|
||||
publish: () => {},
|
||||
};
|
||||
|
||||
export default exported;
|
||||
|
||||
export const {verifyConditions, getLastRelease, analyzeCommits, verifyRelease, generateNotes, publish} = exported;
|
||||
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
import SemanticReleaseError from '@semantic-release/error';
|
||||
const SemanticReleaseError = require('@semantic-release/error');
|
||||
|
||||
class InheritedError extends SemanticReleaseError {
|
||||
constructor(message, code) {
|
||||
@@ -9,6 +9,6 @@ class InheritedError extends SemanticReleaseError {
|
||||
}
|
||||
}
|
||||
|
||||
export default () => {
|
||||
module.exports = () => {
|
||||
throw new InheritedError('Inherited error', 'EINHERITED');
|
||||
};
|
||||
|
||||
Vendored
+1
-1
@@ -1,4 +1,4 @@
|
||||
export default () => {
|
||||
module.exports = () => {
|
||||
const error = new Error('a');
|
||||
error.errorProperty = 'errorProperty';
|
||||
throw error;
|
||||
|
||||
Vendored
+2
-2
@@ -1,5 +1,5 @@
|
||||
import AggregateError from 'aggregate-error';
|
||||
const AggregateError = require('aggregate-error');
|
||||
|
||||
export default () => {
|
||||
module.exports = () => {
|
||||
throw new AggregateError([new Error('a'), new Error('b')]);
|
||||
};
|
||||
|
||||
Vendored
+1
-1
@@ -1 +1 @@
|
||||
export default (pluginConfig, context) => context;
|
||||
module.exports = (pluginConfig, context) => context;
|
||||
|
||||
Vendored
+1
-1
@@ -1,4 +1,4 @@
|
||||
export default (pluginConfig, {env, logger}) => {
|
||||
module.exports = (pluginConfig, {env, logger}) => {
|
||||
console.log(`Console: Exposing token ${env.MY_TOKEN}`);
|
||||
logger.log(`Log: Exposing token ${env.MY_TOKEN}`);
|
||||
logger.error(`Error: Console token ${env.MY_TOKEN}`);
|
||||
|
||||
Vendored
+1
-1
@@ -1 +1 @@
|
||||
export default () => {};
|
||||
module.exports = () => {};
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
export default (pluginConfig, context) => ({pluginConfig, context});
|
||||
module.exports = (pluginConfig, context) => ({pluginConfig, context});
|
||||
|
||||
+14
-14
@@ -1,16 +1,16 @@
|
||||
import test from 'ava';
|
||||
import sinon from 'sinon';
|
||||
import getCommits from '../lib/get-commits.js';
|
||||
import {gitRepo, gitCommits, gitDetachedHead} from './helpers/git-utils.js';
|
||||
const test = require('ava');
|
||||
const {stub} = require('sinon');
|
||||
const getCommits = require('../lib/get-commits');
|
||||
const {gitRepo, gitCommits, gitDetachedHead} = require('./helpers/git-utils');
|
||||
|
||||
test.beforeEach((t) => {
|
||||
test.beforeEach(t => {
|
||||
// Stub the logger functions
|
||||
t.context.log = sinon.stub();
|
||||
t.context.error = sinon.stub();
|
||||
t.context.log = stub();
|
||||
t.context.error = stub();
|
||||
t.context.logger = {log: t.context.log, error: t.context.error};
|
||||
});
|
||||
|
||||
test('Get all commits when there is no last release', async (t) => {
|
||||
test('Get all commits when there is no last release', async t => {
|
||||
// Create a git repository, set the current working directory at the root of the repo
|
||||
const {cwd} = await gitRepo();
|
||||
// Add commits to the master branch
|
||||
@@ -24,7 +24,7 @@ test('Get all commits when there is no last release', async (t) => {
|
||||
t.deepEqual(result, commits);
|
||||
});
|
||||
|
||||
test('Get all commits since gitHead (from lastRelease)', async (t) => {
|
||||
test('Get all commits since gitHead (from lastRelease)', async t => {
|
||||
// Create a git repository, set the current working directory at the root of the repo
|
||||
const {cwd} = await gitRepo();
|
||||
// Add commits to the master branch
|
||||
@@ -42,7 +42,7 @@ test('Get all commits since gitHead (from lastRelease)', async (t) => {
|
||||
t.deepEqual(result, commits.slice(0, 2));
|
||||
});
|
||||
|
||||
test('Get all commits since gitHead (from lastRelease) on a detached head repo', async (t) => {
|
||||
test('Get all commits since gitHead (from lastRelease) on a detached head repo', async t => {
|
||||
// Create a git repository, set the current working directory at the root of the repo
|
||||
let {cwd, repositoryUrl} = await gitRepo();
|
||||
// Add commits to the master branch
|
||||
@@ -66,7 +66,7 @@ test('Get all commits since gitHead (from lastRelease) on a detached head repo',
|
||||
t.truthy(result[0].committer.name);
|
||||
});
|
||||
|
||||
test('Get all commits between lastRelease.gitHead and a shas', async (t) => {
|
||||
test('Get all commits between lastRelease.gitHead and a shas', async t => {
|
||||
// Create a git repository, set the current working directory at the root of the repo
|
||||
const {cwd} = await gitRepo();
|
||||
// Add commits to the master branch
|
||||
@@ -82,10 +82,10 @@ test('Get all commits between lastRelease.gitHead and a shas', async (t) => {
|
||||
|
||||
// Verify the commits created and retrieved by the module are identical
|
||||
t.is(result.length, 1);
|
||||
t.deepEqual(result, commits.slice(1, -1));
|
||||
t.deepEqual(result, commits.slice(1, commits.length - 1));
|
||||
});
|
||||
|
||||
test('Return empty array if lastRelease.gitHead is the last commit', async (t) => {
|
||||
test('Return empty array if lastRelease.gitHead is the last commit', async t => {
|
||||
// Create a git repository, set the current working directory at the root of the repo
|
||||
const {cwd} = await gitRepo();
|
||||
// Add commits to the master branch
|
||||
@@ -102,7 +102,7 @@ test('Return empty array if lastRelease.gitHead is the last commit', async (t) =
|
||||
t.deepEqual(result, []);
|
||||
});
|
||||
|
||||
test('Return empty array if there is no commits', async (t) => {
|
||||
test('Return empty array if there is no commits', async t => {
|
||||
// Create a git repository, set the current working directory at the root of the repo
|
||||
const {cwd} = await gitRepo();
|
||||
|
||||
|
||||
+32
-94
@@ -1,13 +1,12 @@
|
||||
import path from 'path';
|
||||
import {format} from 'util';
|
||||
import test from 'ava';
|
||||
import {writeFile, outputJson} from 'fs-extra';
|
||||
import lodash from 'lodash'
|
||||
const {omit} = lodash
|
||||
import proxyquire from 'proxyquire';
|
||||
import sinon from 'sinon';
|
||||
import yaml from 'js-yaml';
|
||||
import {gitRepo, gitTagVersion, gitCommits, gitShallowClone, gitAddConfig} from './helpers/git-utils.js';
|
||||
const path = require('path');
|
||||
const {format} = require('util');
|
||||
const test = require('ava');
|
||||
const {writeFile, outputJson} = require('fs-extra');
|
||||
const {omit} = require('lodash');
|
||||
const proxyquire = require('proxyquire');
|
||||
const {stub} = require('sinon');
|
||||
const yaml = require('js-yaml');
|
||||
const {gitRepo, gitTagVersion, gitCommits, gitShallowClone, gitAddConfig} = require('./helpers/git-utils');
|
||||
|
||||
const DEFAULT_PLUGINS = [
|
||||
'@semantic-release/commit-analyzer',
|
||||
@@ -16,12 +15,12 @@ const DEFAULT_PLUGINS = [
|
||||
'@semantic-release/github',
|
||||
];
|
||||
|
||||
test.beforeEach((t) => {
|
||||
t.context.plugins = sinon.stub).returns({});
|
||||
test.beforeEach(t => {
|
||||
t.context.plugins = stub().returns({});
|
||||
t.context.getConfig = proxyquire('../lib/get-config', {'./plugins': t.context.plugins});
|
||||
});
|
||||
|
||||
test('Default values, reading repositoryUrl from package.json', async (t) => {
|
||||
test('Default values, reading repositoryUrl from package.json', async t => {
|
||||
const pkg = {repository: 'https://host.null/owner/package.git'};
|
||||
// Create a git repository, set the current working directory at the root of the repo
|
||||
const {cwd} = await gitRepo(true);
|
||||
@@ -48,7 +47,7 @@ test('Default values, reading repositoryUrl from package.json', async (t) => {
|
||||
t.is(result.tagFormat, `v\${version}`);
|
||||
});
|
||||
|
||||
test('Default values, reading repositoryUrl from repo if not set in package.json', async (t) => {
|
||||
test('Default values, reading repositoryUrl from repo if not set in package.json', async t => {
|
||||
// Create a git repository, set the current working directory at the root of the repo
|
||||
const {cwd} = await gitRepo(true);
|
||||
// Add remote.origin.url config
|
||||
@@ -69,7 +68,7 @@ test('Default values, reading repositoryUrl from repo if not set in package.json
|
||||
t.is(result.tagFormat, `v\${version}`);
|
||||
});
|
||||
|
||||
test('Default values, reading repositoryUrl (http url) from package.json if not set in repo', async (t) => {
|
||||
test('Default values, reading repositoryUrl (http url) from package.json if not set in repo', async t => {
|
||||
const pkg = {repository: 'https://host.null/owner/module.git'};
|
||||
// Create a git repository, set the current working directory at the root of the repo
|
||||
const {cwd} = await gitRepo();
|
||||
@@ -91,7 +90,7 @@ test('Default values, reading repositoryUrl (http url) from package.json if not
|
||||
t.is(result.tagFormat, `v\${version}`);
|
||||
});
|
||||
|
||||
test('Convert "ci" option to "noCi"', async (t) => {
|
||||
test('Convert "ci" option to "noCi"', async t => {
|
||||
const pkg = {repository: 'https://host.null/owner/module.git', release: {ci: false}};
|
||||
// Create a git repository, set the current working directory at the root of the repo
|
||||
const {cwd} = await gitRepo();
|
||||
@@ -103,7 +102,7 @@ test('Convert "ci" option to "noCi"', async (t) => {
|
||||
t.is(result.noCi, true);
|
||||
});
|
||||
|
||||
test('Read options from package.json', async (t) => {
|
||||
test('Read options from package.json', async t => {
|
||||
// Create a git repository, set the current working directory at the root of the repo
|
||||
const {cwd} = await gitRepo();
|
||||
const options = {
|
||||
@@ -126,7 +125,7 @@ test('Read options from package.json', async (t) => {
|
||||
t.deepEqual(t.context.plugins.args[0][0], {options: expected, cwd});
|
||||
});
|
||||
|
||||
test('Read options from .releaserc.yml', async (t) => {
|
||||
test('Read options from .releaserc.yml', async t => {
|
||||
// Create a git repository, set the current working directory at the root of the repo
|
||||
const {cwd} = await gitRepo();
|
||||
const options = {
|
||||
@@ -137,7 +136,7 @@ test('Read options from .releaserc.yml', async (t) => {
|
||||
plugins: false,
|
||||
};
|
||||
// Create package.json in repository root
|
||||
await writeFile(path.resolve(cwd, '.releaserc.yml'), yaml.dump(options));
|
||||
await writeFile(path.resolve(cwd, '.releaserc.yml'), yaml.safeDump(options));
|
||||
|
||||
const {options: result} = await t.context.getConfig({cwd});
|
||||
|
||||
@@ -148,7 +147,7 @@ test('Read options from .releaserc.yml', async (t) => {
|
||||
t.deepEqual(t.context.plugins.args[0][0], {options: expected, cwd});
|
||||
});
|
||||
|
||||
test('Read options from .releaserc.json', async (t) => {
|
||||
test('Read options from .releaserc.json', async t => {
|
||||
// Create a git repository, set the current working directory at the root of the repo
|
||||
const {cwd} = await gitRepo();
|
||||
const options = {
|
||||
@@ -170,7 +169,7 @@ test('Read options from .releaserc.json', async (t) => {
|
||||
t.deepEqual(t.context.plugins.args[0][0], {options: expected, cwd});
|
||||
});
|
||||
|
||||
test('Read options from .releaserc.js', async (t) => {
|
||||
test('Read options from .releaserc.js', async t => {
|
||||
// Create a git repository, set the current working directory at the root of the repo
|
||||
const {cwd} = await gitRepo();
|
||||
const options = {
|
||||
@@ -192,29 +191,7 @@ test('Read options from .releaserc.js', async (t) => {
|
||||
t.deepEqual(t.context.plugins.args[0][0], {options: expected, cwd});
|
||||
});
|
||||
|
||||
test('Read options from .releaserc.cjs', async (t) => {
|
||||
// Create a git repository, set the current working directory at the root of the repo
|
||||
const {cwd} = await gitRepo();
|
||||
const options = {
|
||||
analyzeCommits: {path: 'analyzeCommits', param: 'analyzeCommits_param'},
|
||||
branches: ['test_branch'],
|
||||
repositoryUrl: 'https://host.null/owner/module.git',
|
||||
tagFormat: `v\${version}`,
|
||||
plugins: false,
|
||||
};
|
||||
// Create .releaserc.cjs in repository root
|
||||
await writeFile(path.resolve(cwd, '.releaserc.cjs'), `module.exports = ${JSON.stringify(options)}`);
|
||||
|
||||
const {options: result} = await t.context.getConfig({cwd});
|
||||
|
||||
const expected = {...options, branches: ['test_branch']};
|
||||
// Verify the options contains the plugin config from .releaserc.cjs
|
||||
t.deepEqual(result, expected);
|
||||
// Verify the plugins module is called with the plugin options from .releaserc.cjs
|
||||
t.deepEqual(t.context.plugins.args[0][0], {options: expected, cwd});
|
||||
});
|
||||
|
||||
test('Read options from release.config.js', async (t) => {
|
||||
test('Read options from release.config.js', async t => {
|
||||
// Create a git repository, set the current working directory at the root of the repo
|
||||
const {cwd} = await gitRepo();
|
||||
const options = {
|
||||
@@ -236,29 +213,7 @@ test('Read options from release.config.js', async (t) => {
|
||||
t.deepEqual(t.context.plugins.args[0][0], {options: expected, cwd});
|
||||
});
|
||||
|
||||
test('Read options from release.config.cjs', async (t) => {
|
||||
// Create a git repository, set the current working directory at the root of the repo
|
||||
const {cwd} = await gitRepo();
|
||||
const options = {
|
||||
analyzeCommits: {path: 'analyzeCommits', param: 'analyzeCommits_param'},
|
||||
branches: ['test_branch'],
|
||||
repositoryUrl: 'https://host.null/owner/module.git',
|
||||
tagFormat: `v\${version}`,
|
||||
plugins: false,
|
||||
};
|
||||
// Create release.config.cjs in repository root
|
||||
await writeFile(path.resolve(cwd, 'release.config.cjs'), `module.exports = ${JSON.stringify(options)}`);
|
||||
|
||||
const {options: result} = await t.context.getConfig({cwd});
|
||||
|
||||
const expected = {...options, branches: ['test_branch']};
|
||||
// Verify the options contains the plugin config from release.config.cjs
|
||||
t.deepEqual(result, expected);
|
||||
// Verify the plugins module is called with the plugin options from release.config.cjs
|
||||
t.deepEqual(t.context.plugins.args[0][0], {options: expected, cwd});
|
||||
});
|
||||
|
||||
test('Prioritise CLI/API parameters over file configuration and git repo', async (t) => {
|
||||
test('Prioritise CLI/API parameters over file configuration and git repo', async t => {
|
||||
// Create a git repository, set the current working directory at the root of the repo
|
||||
let {cwd, repositoryUrl} = await gitRepo();
|
||||
await gitCommits(['First'], {cwd});
|
||||
@@ -288,7 +243,7 @@ test('Prioritise CLI/API parameters over file configuration and git repo', async
|
||||
t.deepEqual(t.context.plugins.args[0][0], {options: expected, cwd});
|
||||
});
|
||||
|
||||
test('Read configuration from file path in "extends"', async (t) => {
|
||||
test('Read configuration from file path in "extends"', async t => {
|
||||
// Create a git repository, set the current working directory at the root of the repo
|
||||
const {cwd} = await gitRepo();
|
||||
const pkgOptions = {extends: './shareable.json'};
|
||||
@@ -319,7 +274,7 @@ test('Read configuration from file path in "extends"', async (t) => {
|
||||
});
|
||||
});
|
||||
|
||||
test('Read configuration from module path in "extends"', async (t) => {
|
||||
test('Read configuration from module path in "extends"', async t => {
|
||||
// Create a git repository, set the current working directory at the root of the repo
|
||||
const {cwd} = await gitRepo();
|
||||
const pkgOptions = {extends: 'shareable'};
|
||||
@@ -348,7 +303,7 @@ test('Read configuration from module path in "extends"', async (t) => {
|
||||
});
|
||||
});
|
||||
|
||||
test('Read configuration from an array of paths in "extends"', async (t) => {
|
||||
test('Read configuration from an array of paths in "extends"', async t => {
|
||||
// Create a git repository, set the current working directory at the root of the repo
|
||||
const {cwd} = await gitRepo();
|
||||
const pkgOptions = {extends: ['./shareable1.json', './shareable2.json']};
|
||||
@@ -387,7 +342,7 @@ test('Read configuration from an array of paths in "extends"', async (t) => {
|
||||
});
|
||||
});
|
||||
|
||||
test('Prioritize configuration from config file over "extends"', async (t) => {
|
||||
test('Prioritize configuration from config file over "extends"', async t => {
|
||||
// Create a git repository, set the current working directory at the root of the repo
|
||||
const {cwd} = await gitRepo();
|
||||
const pkgOptions = {
|
||||
@@ -423,7 +378,7 @@ test('Prioritize configuration from config file over "extends"', async (t) => {
|
||||
});
|
||||
});
|
||||
|
||||
test('Prioritize configuration from cli/API options over "extends"', async (t) => {
|
||||
test('Prioritize configuration from cli/API options over "extends"', async t => {
|
||||
// Create a git repository, set the current working directory at the root of the repo
|
||||
const {cwd} = await gitRepo();
|
||||
const cliOptions = {
|
||||
@@ -466,7 +421,7 @@ test('Prioritize configuration from cli/API options over "extends"', async (t) =
|
||||
t.deepEqual(t.context.plugins.args[0][0], {options: expected, cwd});
|
||||
});
|
||||
|
||||
test('Allow to unset properties defined in shareable config with "null"', async (t) => {
|
||||
test('Allow to unset properties defined in shareable config with "null"', async t => {
|
||||
// Create a git repository, set the current working directory at the root of the repo
|
||||
const {cwd} = await gitRepo();
|
||||
const pkgOptions = {
|
||||
@@ -511,7 +466,7 @@ test('Allow to unset properties defined in shareable config with "null"', async
|
||||
});
|
||||
});
|
||||
|
||||
test('Allow to unset properties defined in shareable config with "undefined"', async (t) => {
|
||||
test('Allow to unset properties defined in shareable config with "undefined"', async t => {
|
||||
// Create a git repository, set the current working directory at the root of the repo
|
||||
const {cwd} = await gitRepo();
|
||||
const pkgOptions = {
|
||||
@@ -547,13 +502,13 @@ test('Allow to unset properties defined in shareable config with "undefined"', a
|
||||
});
|
||||
});
|
||||
|
||||
test('Throw an Error if one of the shareable config cannot be found', async (t) => {
|
||||
test('Throw an Error if one of the shareable config cannot be found', async t => {
|
||||
// Create a git repository, set the current working directory at the root of the repo
|
||||
const {cwd} = await gitRepo();
|
||||
const pkgOptions = {extends: ['./shareable1.json', 'non-existing-path']};
|
||||
const pkhOptions = {extends: ['./shareable1.json', 'non-existing-path']};
|
||||
const options1 = {analyzeCommits: 'analyzeCommits'};
|
||||
// Create package.json and shareable.json in repository root
|
||||
await outputJson(path.resolve(cwd, 'package.json'), {release: pkgOptions});
|
||||
await outputJson(path.resolve(cwd, 'package.json'), {release: pkhOptions});
|
||||
await outputJson(path.resolve(cwd, 'shareable1.json'), options1);
|
||||
|
||||
await t.throwsAsync(t.context.getConfig({cwd}), {
|
||||
@@ -561,20 +516,3 @@ test('Throw an Error if one of the shareable config cannot be found', async (t)
|
||||
code: 'MODULE_NOT_FOUND',
|
||||
});
|
||||
});
|
||||
|
||||
test('Convert "ci" option to "noCi" when set from extended config', async (t) => {
|
||||
// Create a git repository, set the current working directory at the root of the repo
|
||||
const {cwd} = await gitRepo();
|
||||
const pkgOptions = {extends: './no-ci.json'};
|
||||
const options = {
|
||||
ci: false,
|
||||
};
|
||||
// Create package.json and shareable.json in repository root
|
||||
await outputJson(path.resolve(cwd, 'package.json'), {release: pkgOptions});
|
||||
await outputJson(path.resolve(cwd, 'no-ci.json'), options);
|
||||
|
||||
const {options: result} = await t.context.getConfig({cwd});
|
||||
|
||||
t.is(result.ci, false);
|
||||
t.is(result.noCi, true);
|
||||
});
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import test from 'ava';
|
||||
import getAuthUrl from '../lib/get-git-auth-url.js';
|
||||
import {gitRepo} from './helpers/git-utils.js';
|
||||
const test = require('ava');
|
||||
const getAuthUrl = require('../lib/get-git-auth-url');
|
||||
const {gitRepo} = require('./helpers/git-utils');
|
||||
|
||||
const env = {GIT_ASKPASS: 'echo', GIT_TERMINAL_PROMPT: 0};
|
||||
|
||||
test('Return the same "git" formatted URL if "gitCredentials" is not defined', async (t) => {
|
||||
test('Return the same "git" formatted URL if "gitCredentials" is not defined', async t => {
|
||||
const {cwd} = await gitRepo();
|
||||
|
||||
t.is(
|
||||
@@ -13,7 +13,7 @@ test('Return the same "git" formatted URL if "gitCredentials" is not defined', a
|
||||
);
|
||||
});
|
||||
|
||||
test('Return the same "https" formatted URL if "gitCredentials" is not defined', async (t) => {
|
||||
test('Return the same "https" formatted URL if "gitCredentials" is not defined', async t => {
|
||||
const {cwd} = await gitRepo();
|
||||
|
||||
t.is(
|
||||
@@ -27,7 +27,7 @@ test('Return the same "https" formatted URL if "gitCredentials" is not defined',
|
||||
);
|
||||
});
|
||||
|
||||
test('Return the "https" formatted URL if "gitCredentials" is not defined and repositoryUrl is a "git+https" URL', async (t) => {
|
||||
test('Return the "https" formatted URL if "gitCredentials" is not defined and repositoryUrl is a "git+https" URL', async t => {
|
||||
const {cwd} = await gitRepo();
|
||||
|
||||
t.is(
|
||||
@@ -41,7 +41,7 @@ test('Return the "https" formatted URL if "gitCredentials" is not defined and re
|
||||
);
|
||||
});
|
||||
|
||||
test('Do not add trailing ".git" if not present in the origian URL', async (t) => {
|
||||
test('Do not add trailing ".git" if not present in the origian URL', async t => {
|
||||
const {cwd} = await gitRepo();
|
||||
|
||||
t.is(
|
||||
@@ -50,7 +50,7 @@ test('Do not add trailing ".git" if not present in the origian URL', async (t) =
|
||||
);
|
||||
});
|
||||
|
||||
test('Handle "https" URL with group and subgroup', async (t) => {
|
||||
test('Handle "https" URL with group and subgroup', async t => {
|
||||
const {cwd} = await gitRepo();
|
||||
|
||||
t.is(
|
||||
@@ -64,7 +64,7 @@ test('Handle "https" URL with group and subgroup', async (t) => {
|
||||
);
|
||||
});
|
||||
|
||||
test('Handle "git" URL with group and subgroup', async (t) => {
|
||||
test('Handle "git" URL with group and subgroup', async t => {
|
||||
const {cwd} = await gitRepo();
|
||||
|
||||
t.is(
|
||||
@@ -78,7 +78,7 @@ test('Handle "git" URL with group and subgroup', async (t) => {
|
||||
);
|
||||
});
|
||||
|
||||
test('Convert shorthand URL', async (t) => {
|
||||
test('Convert shorthand URL', async t => {
|
||||
const {cwd} = await gitRepo();
|
||||
|
||||
t.is(
|
||||
@@ -92,7 +92,7 @@ test('Convert shorthand URL', async (t) => {
|
||||
);
|
||||
});
|
||||
|
||||
test('Convert GitLab shorthand URL', async (t) => {
|
||||
test('Convert GitLab shorthand URL', async t => {
|
||||
const {cwd} = await gitRepo();
|
||||
|
||||
t.is(
|
||||
@@ -106,7 +106,7 @@ test('Convert GitLab shorthand URL', async (t) => {
|
||||
);
|
||||
});
|
||||
|
||||
test('Return the "https" formatted URL if "gitCredentials" is defined and repositoryUrl is a "git" URL', async (t) => {
|
||||
test('Return the "https" formatted URL if "gitCredentials" is defined and repositoryUrl is a "git" URL', async t => {
|
||||
const {cwd} = await gitRepo();
|
||||
|
||||
t.is(
|
||||
@@ -120,7 +120,7 @@ test('Return the "https" formatted URL if "gitCredentials" is defined and reposi
|
||||
);
|
||||
});
|
||||
|
||||
test('Return the "https" formatted URL if "gitCredentials" is defined and repositoryUrl is a "git" URL without user', async (t) => {
|
||||
test('Return the "https" formatted URL if "gitCredentials" is defined and repositoryUrl is a "git" URL without user', async t => {
|
||||
const {cwd} = await gitRepo();
|
||||
|
||||
t.is(
|
||||
@@ -133,33 +133,7 @@ test('Return the "https" formatted URL if "gitCredentials" is defined and reposi
|
||||
);
|
||||
});
|
||||
|
||||
test('Return the "https" formatted URL if "gitCredentials" is defined and repositoryUrl is a "git" URL without user and with a custom port', async (t) => {
|
||||
const {cwd} = await gitRepo();
|
||||
|
||||
t.is(
|
||||
await getAuthUrl({
|
||||
cwd,
|
||||
env: {...env, GIT_CREDENTIALS: 'user:pass'},
|
||||
options: {branch: 'master', repositoryUrl: 'host.null:6666:owner/repo.git'},
|
||||
}),
|
||||
'https://user:pass@host.null:6666/owner/repo.git'
|
||||
);
|
||||
});
|
||||
|
||||
test('Return the "https" formatted URL if "gitCredentials" is defined and repositoryUrl is a "git" URL without user and with a custom port followed by a slash', async (t) => {
|
||||
const {cwd} = await gitRepo();
|
||||
|
||||
t.is(
|
||||
await getAuthUrl({
|
||||
cwd,
|
||||
env: {...env, GIT_CREDENTIALS: 'user:pass'},
|
||||
options: {branch: 'master', repositoryUrl: 'host.null:6666:/owner/repo.git'},
|
||||
}),
|
||||
'https://user:pass@host.null:6666/owner/repo.git'
|
||||
);
|
||||
});
|
||||
|
||||
test('Return the "https" formatted URL if "gitCredentials" is defined and repositoryUrl is a "https" URL', async (t) => {
|
||||
test('Return the "https" formatted URL if "gitCredentials" is defined and repositoryUrl is a "https" URL', async t => {
|
||||
const {cwd} = await gitRepo();
|
||||
|
||||
t.is(
|
||||
@@ -173,7 +147,7 @@ test('Return the "https" formatted URL if "gitCredentials" is defined and reposi
|
||||
);
|
||||
});
|
||||
|
||||
test('Return the "http" formatted URL if "gitCredentials" is defined and repositoryUrl is a "http" URL', async (t) => {
|
||||
test('Return the "http" formatted URL if "gitCredentials" is defined and repositoryUrl is a "http" URL', async t => {
|
||||
const {cwd} = await gitRepo();
|
||||
|
||||
t.is(
|
||||
@@ -187,7 +161,7 @@ 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) => {
|
||||
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(
|
||||
@@ -200,7 +174,7 @@ test('Return the "http" formatted URL if "gitCredentials" is defined and reposit
|
||||
);
|
||||
});
|
||||
|
||||
test('Return the "https" formatted URL if "gitCredentials" is defined and repositoryUrl is a "git+https" URL', async (t) => {
|
||||
test('Return the "https" formatted URL if "gitCredentials" is defined and repositoryUrl is a "git+https" URL', async t => {
|
||||
const {cwd} = await gitRepo();
|
||||
|
||||
t.is(
|
||||
@@ -214,7 +188,7 @@ test('Return the "https" formatted URL if "gitCredentials" is defined and reposi
|
||||
);
|
||||
});
|
||||
|
||||
test('Return the "http" formatted URL if "gitCredentials" is defined and repositoryUrl is a "git+http" URL', async (t) => {
|
||||
test('Return the "http" formatted URL if "gitCredentials" is defined and repositoryUrl is a "git+http" URL', async t => {
|
||||
const {cwd} = await gitRepo();
|
||||
|
||||
t.is(
|
||||
@@ -228,7 +202,7 @@ 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) => {
|
||||
test('Return the "http" formatted URL if "gitCredentials" is defined and repositoryUrl is a "ssh" URL', async t => {
|
||||
const {cwd} = await gitRepo();
|
||||
|
||||
t.is(
|
||||
@@ -241,7 +215,7 @@ test('Return the "http" formatted URL if "gitCredentials" is defined and reposit
|
||||
);
|
||||
});
|
||||
|
||||
test('Return the "https" formatted URL if "gitCredentials" is defined with "GH_TOKEN"', async (t) => {
|
||||
test('Return the "https" formatted URL if "gitCredentials" is defined with "GH_TOKEN"', async t => {
|
||||
const {cwd} = await gitRepo();
|
||||
|
||||
t.is(
|
||||
@@ -255,7 +229,7 @@ test('Return the "https" formatted URL if "gitCredentials" is defined with "GH_T
|
||||
);
|
||||
});
|
||||
|
||||
test('Return the "https" formatted URL if "gitCredentials" is defined with "GITHUB_TOKEN"', async (t) => {
|
||||
test('Return the "https" formatted URL if "gitCredentials" is defined with "GITHUB_TOKEN"', async t => {
|
||||
const {cwd} = await gitRepo();
|
||||
|
||||
t.is(
|
||||
@@ -269,7 +243,7 @@ test('Return the "https" formatted URL if "gitCredentials" is defined with "GITH
|
||||
);
|
||||
});
|
||||
|
||||
test('Return the "https" formatted URL if "gitCredentials" is defined with "GL_TOKEN"', async (t) => {
|
||||
test('Return the "https" formatted URL if "gitCredentials" is defined with "GL_TOKEN"', async t => {
|
||||
const {cwd} = await gitRepo();
|
||||
|
||||
t.is(
|
||||
@@ -283,7 +257,7 @@ test('Return the "https" formatted URL if "gitCredentials" is defined with "GL_T
|
||||
);
|
||||
});
|
||||
|
||||
test('Return the "https" formatted URL if "gitCredentials" is defined with "GITLAB_TOKEN"', async (t) => {
|
||||
test('Return the "https" formatted URL if "gitCredentials" is defined with "GITLAB_TOKEN"', async t => {
|
||||
const {cwd} = await gitRepo();
|
||||
|
||||
t.is(
|
||||
@@ -297,7 +271,7 @@ test('Return the "https" formatted URL if "gitCredentials" is defined with "GITL
|
||||
);
|
||||
});
|
||||
|
||||
test('Return the "https" formatted URL if "gitCredentials" is defined with "BB_TOKEN"', async (t) => {
|
||||
test('Return the "https" formatted URL if "gitCredentials" is defined with "BB_TOKEN"', async t => {
|
||||
const {cwd} = await gitRepo();
|
||||
|
||||
t.is(
|
||||
@@ -311,7 +285,7 @@ test('Return the "https" formatted URL if "gitCredentials" is defined with "BB_T
|
||||
);
|
||||
});
|
||||
|
||||
test('Return the "https" formatted URL if "gitCredentials" is defined with "BITBUCKET_TOKEN"', async (t) => {
|
||||
test('Return the "https" formatted URL if "gitCredentials" is defined with "BITBUCKET_TOKEN"', async t => {
|
||||
const {cwd} = await gitRepo();
|
||||
|
||||
t.is(
|
||||
@@ -325,35 +299,7 @@ test('Return the "https" formatted URL if "gitCredentials" is defined with "BITB
|
||||
);
|
||||
});
|
||||
|
||||
test('Return the "https" formatted URL if "gitCredentials" is defined with "BB_TOKEN_BASIC_AUTH"', async (t) => {
|
||||
const {cwd} = await gitRepo();
|
||||
|
||||
t.is(
|
||||
await getAuthUrl({
|
||||
cwd,
|
||||
env: {...env, BB_TOKEN_BASIC_AUTH: 'username:token'},
|
||||
branch: {name: 'master'},
|
||||
options: {repositoryUrl: 'git@host.null:owner/repo.git'},
|
||||
}),
|
||||
'https://username:token@host.null/owner/repo.git'
|
||||
);
|
||||
});
|
||||
|
||||
test('Return the "https" formatted URL if "gitCredentials" is defined with "BITBUCKET_TOKEN_BASIC_AUTH"', async (t) => {
|
||||
const {cwd} = await gitRepo();
|
||||
|
||||
t.is(
|
||||
await getAuthUrl({
|
||||
cwd,
|
||||
env: {...env, BITBUCKET_TOKEN_BASIC_AUTH: 'username:token'},
|
||||
branch: {name: 'master'},
|
||||
options: {repositoryUrl: 'git@host.null:owner/repo.git'},
|
||||
}),
|
||||
'https://username:token@host.null/owner/repo.git'
|
||||
);
|
||||
});
|
||||
|
||||
test('Return the "https" formatted URL if "GITHUB_ACTION" is set', async (t) => {
|
||||
test('Return the "https" formatted URL if "GITHUB_ACTION" is set', async t => {
|
||||
const {cwd} = await gitRepo();
|
||||
|
||||
t.is(
|
||||
@@ -366,7 +312,7 @@ test('Return the "https" formatted URL if "GITHUB_ACTION" is set', async (t) =>
|
||||
);
|
||||
});
|
||||
|
||||
test('Handle "https" URL with group and subgroup, with "GIT_CREDENTIALS"', async (t) => {
|
||||
test('Handle "https" URL with group and subgroup, with "GIT_CREDENTIALS"', async t => {
|
||||
const {cwd} = await gitRepo();
|
||||
|
||||
t.is(
|
||||
@@ -380,7 +326,7 @@ test('Handle "https" URL with group and subgroup, with "GIT_CREDENTIALS"', async
|
||||
);
|
||||
});
|
||||
|
||||
test('Handle "git" URL with group and subgroup, with "GIT_CREDENTIALS', async (t) => {
|
||||
test('Handle "git" URL with group and subgroup, with "GIT_CREDENTIALS', async t => {
|
||||
const {cwd} = await gitRepo();
|
||||
|
||||
t.is(
|
||||
@@ -394,7 +340,7 @@ test('Handle "git" URL with group and subgroup, with "GIT_CREDENTIALS', async (t
|
||||
);
|
||||
});
|
||||
|
||||
test('Do not add git credential to repositoryUrl if push is allowed', async (t) => {
|
||||
test('Do not add git credential to repositoryUrl if push is allowed', async t => {
|
||||
const {cwd, repositoryUrl} = await gitRepo(true);
|
||||
|
||||
t.is(
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import test from 'ava';
|
||||
import getLastRelease from '../lib/get-last-release.js';
|
||||
const test = require('ava');
|
||||
const getLastRelease = require('../lib/get-last-release');
|
||||
|
||||
test('Get the highest non-prerelease valid tag', (t) => {
|
||||
test('Get the highest non-prerelease valid tag', t => {
|
||||
const result = getLastRelease({
|
||||
branch: {
|
||||
name: 'master',
|
||||
@@ -18,7 +18,7 @@ test('Get the highest non-prerelease valid tag', (t) => {
|
||||
t.deepEqual(result, {version: '2.0.0', gitTag: 'v2.0.0', name: 'v2.0.0', gitHead: 'v2.0.0', channels: undefined});
|
||||
});
|
||||
|
||||
test('Get the highest prerelease valid tag, ignoring other tags from other prerelease channels', (t) => {
|
||||
test('Get the highest prerelease valid tag, ignoring other tags from other prerelease channels', t => {
|
||||
const result = getLastRelease({
|
||||
branch: {
|
||||
name: 'beta',
|
||||
@@ -43,7 +43,7 @@ test('Get the highest prerelease valid tag, ignoring other tags from other prere
|
||||
});
|
||||
});
|
||||
|
||||
test('Return empty object if no valid tag is found', (t) => {
|
||||
test('Return empty object if no valid tag is found', t => {
|
||||
const result = getLastRelease({
|
||||
branch: {
|
||||
name: 'master',
|
||||
@@ -56,7 +56,7 @@ test('Return empty object if no valid tag is found', (t) => {
|
||||
t.deepEqual(result, {});
|
||||
});
|
||||
|
||||
test('Get the highest non-prerelease valid tag before a certain version', (t) => {
|
||||
test('Get the highest non-prerelease valid tag before a certain version', t => {
|
||||
const result = getLastRelease(
|
||||
{
|
||||
branch: {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import test from 'ava';
|
||||
import {spy} from 'sinon';
|
||||
import getLogger from '../lib/get-logger.js';
|
||||
const test = require('ava');
|
||||
const {spy} = require('sinon');
|
||||
const getLogger = require('../lib/get-logger');
|
||||
|
||||
test('Expose "error", "success" and "log" functions', (t) => {
|
||||
test('Expose "error", "success" and "log" functions', t => {
|
||||
const stdout = spy();
|
||||
const stderr = spy();
|
||||
const logger = getLogger({stdout: {write: stdout}, stderr: {write: stderr}});
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import test from 'ava';
|
||||
import sinon from 'sinon';
|
||||
import getNextVersion from '../lib/get-next-version.js';
|
||||
const test = require('ava');
|
||||
const {stub} = require('sinon');
|
||||
const getNextVersion = require('../lib/get-next-version');
|
||||
|
||||
test.beforeEach((t) => {
|
||||
test.beforeEach(t => {
|
||||
// Stub the logger functions
|
||||
t.context.log = sinon.stub);
|
||||
t.context.log = stub();
|
||||
t.context.logger = {log: t.context.log};
|
||||
});
|
||||
|
||||
test('Increase version for patch release', (t) => {
|
||||
test('Increase version for patch release', t => {
|
||||
t.is(
|
||||
getNextVersion({
|
||||
branch: {name: 'master', type: 'release', tags: [{gitTag: 'v1.0.0', version: '1.0.0', channels: [null]}]},
|
||||
@@ -20,7 +20,7 @@ test('Increase version for patch release', (t) => {
|
||||
);
|
||||
});
|
||||
|
||||
test('Increase version for minor release', (t) => {
|
||||
test('Increase version for minor release', t => {
|
||||
t.is(
|
||||
getNextVersion({
|
||||
branch: {name: 'master', type: 'release', tags: [{gitTag: 'v1.0.0', version: '1.0.0', channels: [null]}]},
|
||||
@@ -32,7 +32,7 @@ test('Increase version for minor release', (t) => {
|
||||
);
|
||||
});
|
||||
|
||||
test('Increase version for major release', (t) => {
|
||||
test('Increase version for major release', t => {
|
||||
t.is(
|
||||
getNextVersion({
|
||||
branch: {name: 'master', type: 'release', tags: [{gitTag: 'v1.0.0', version: '1.0.0', channels: [null]}]},
|
||||
@@ -44,7 +44,7 @@ test('Increase version for major release', (t) => {
|
||||
);
|
||||
});
|
||||
|
||||
test('Return 1.0.0 if there is no previous release', (t) => {
|
||||
test('Return 1.0.0 if there is no previous release', t => {
|
||||
t.is(
|
||||
getNextVersion({
|
||||
branch: {name: 'master', type: 'release', tags: []},
|
||||
@@ -56,7 +56,7 @@ test('Return 1.0.0 if there is no previous release', (t) => {
|
||||
);
|
||||
});
|
||||
|
||||
test('Increase version for patch release on prerelease branch', (t) => {
|
||||
test('Increase version for patch release on prerelease branch', t => {
|
||||
t.is(
|
||||
getNextVersion({
|
||||
branch: {
|
||||
@@ -106,7 +106,7 @@ test('Increase version for patch release on prerelease branch', (t) => {
|
||||
);
|
||||
});
|
||||
|
||||
test('Increase version for minor release on prerelease branch', (t) => {
|
||||
test('Increase version for minor release on prerelease branch', t => {
|
||||
t.is(
|
||||
getNextVersion({
|
||||
branch: {
|
||||
@@ -156,7 +156,7 @@ test('Increase version for minor release on prerelease branch', (t) => {
|
||||
);
|
||||
});
|
||||
|
||||
test('Increase version for major release on prerelease branch', (t) => {
|
||||
test('Increase version for major release on prerelease branch', t => {
|
||||
t.is(
|
||||
getNextVersion({
|
||||
branch: {
|
||||
@@ -206,7 +206,7 @@ test('Increase version for major release on prerelease branch', (t) => {
|
||||
);
|
||||
});
|
||||
|
||||
test('Return 1.0.0 if there is no previous release on prerelease branch', (t) => {
|
||||
test('Return 1.0.0 if there is no previous release on prerelease branch', t => {
|
||||
t.is(
|
||||
getNextVersion({
|
||||
branch: {name: 'beta', type: 'prerelease', prerelease: 'beta', tags: []},
|
||||
@@ -218,7 +218,7 @@ test('Return 1.0.0 if there is no previous release on prerelease branch', (t) =>
|
||||
);
|
||||
});
|
||||
|
||||
test('Increase version for release on prerelease branch after previous commits were merged to release branch', (t) => {
|
||||
test('Increase version for release on prerelease branch after previous commits were merged to release branch', t => {
|
||||
t.is(
|
||||
getNextVersion({
|
||||
branch: {
|
||||
@@ -239,7 +239,7 @@ test('Increase version for release on prerelease branch after previous commits w
|
||||
);
|
||||
});
|
||||
|
||||
test('Increase version for release on prerelease branch based on highest commit type since last regular release', (t) => {
|
||||
test('Increase version for release on prerelease branch based on highest commit type since last regular release', t => {
|
||||
t.is(
|
||||
getNextVersion({
|
||||
branch: {
|
||||
@@ -259,7 +259,7 @@ test('Increase version for release on prerelease branch based on highest commit
|
||||
);
|
||||
});
|
||||
|
||||
test('Increase version for release on prerelease branch when there is no regular releases on other branches', (t) => {
|
||||
test('Increase version for release on prerelease branch when there is no regular releases on other branches', t => {
|
||||
t.is(
|
||||
getNextVersion({
|
||||
branch: {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import test from 'ava';
|
||||
import getReleaseToAdd from '../lib/get-release-to-add.js';
|
||||
const test = require('ava');
|
||||
const getReleaseToAdd = require('../lib/get-release-to-add');
|
||||
|
||||
test('Return versions merged from release to maintenance branch, excluding lower than branch start range', (t) => {
|
||||
test('Return versions merged from release to maintenance branch, excluding lower than branch start range', t => {
|
||||
const result = getReleaseToAdd({
|
||||
branch: {
|
||||
name: '2.x',
|
||||
@@ -42,7 +42,7 @@ test('Return versions merged from release to maintenance branch, excluding lower
|
||||
});
|
||||
});
|
||||
|
||||
test('Return versions merged between release branches', (t) => {
|
||||
test('Return versions merged between release branches', t => {
|
||||
const result = getReleaseToAdd({
|
||||
branch: {
|
||||
name: 'master',
|
||||
@@ -83,7 +83,7 @@ test('Return versions merged between release branches', (t) => {
|
||||
});
|
||||
});
|
||||
|
||||
test('Return releases sorted by ascending order', (t) => {
|
||||
test('Return releases sorted by ascending order', t => {
|
||||
const result = getReleaseToAdd({
|
||||
branch: {
|
||||
name: 'master',
|
||||
@@ -118,7 +118,7 @@ test('Return releases sorted by ascending order', (t) => {
|
||||
});
|
||||
});
|
||||
|
||||
test('No lastRelease', (t) => {
|
||||
test('No lastRelease', t => {
|
||||
const result = getReleaseToAdd({
|
||||
branch: {
|
||||
name: 'master',
|
||||
@@ -149,7 +149,7 @@ test('No lastRelease', (t) => {
|
||||
});
|
||||
});
|
||||
|
||||
test('Ignore pre-release versions', (t) => {
|
||||
test('Ignore pre-release versions', t => {
|
||||
const result = getReleaseToAdd({
|
||||
branch: {
|
||||
name: 'master',
|
||||
@@ -188,7 +188,7 @@ test('Ignore pre-release versions', (t) => {
|
||||
});
|
||||
});
|
||||
|
||||
test('Exclude versions merged from release to maintenance branch if they have the same "channel"', (t) => {
|
||||
test('Exclude versions merged from release to maintenance branch if they have the same "channel"', t => {
|
||||
const result = getReleaseToAdd({
|
||||
branch: {
|
||||
name: '2.x',
|
||||
@@ -214,7 +214,7 @@ test('Exclude versions merged from release to maintenance branch if they have th
|
||||
t.is(result, undefined);
|
||||
});
|
||||
|
||||
test('Exclude versions merged between release branches if they have the same "channel"', (t) => {
|
||||
test('Exclude versions merged between release branches if they have the same "channel"', t => {
|
||||
const result = getReleaseToAdd({
|
||||
branch: {
|
||||
name: 'master',
|
||||
@@ -236,7 +236,7 @@ test('Exclude versions merged between release branches if they have the same "ch
|
||||
t.is(result, undefined);
|
||||
});
|
||||
|
||||
test('Exclude versions merged between release branches if they all have "channel" set to "false"', (t) => {
|
||||
test('Exclude versions merged between release branches if they all have "channel" set to "false"', t => {
|
||||
const result = getReleaseToAdd({
|
||||
branch: {
|
||||
name: 'master',
|
||||
@@ -258,7 +258,7 @@ test('Exclude versions merged between release branches if they all have "channel
|
||||
t.is(result, undefined);
|
||||
});
|
||||
|
||||
test('Exclude versions number less than the latest version already released on that branch', (t) => {
|
||||
test('Exclude versions number less than the latest version already released on that branch', t => {
|
||||
const result = getReleaseToAdd({
|
||||
branch: {
|
||||
name: '2.x',
|
||||
|
||||
+44
-46
@@ -1,7 +1,6 @@
|
||||
import test from 'ava';
|
||||
import tempy from 'tempy';
|
||||
|
||||
import {
|
||||
const test = require('ava');
|
||||
const tempy = require('tempy');
|
||||
const {
|
||||
getTagHead,
|
||||
isRefExists,
|
||||
fetch,
|
||||
@@ -17,9 +16,8 @@ import {
|
||||
getNote,
|
||||
addNote,
|
||||
fetchNotes,
|
||||
} from '../lib/git.js';
|
||||
|
||||
import {
|
||||
} = require('../lib/git');
|
||||
const {
|
||||
gitRepo,
|
||||
gitCommits,
|
||||
gitCheckout,
|
||||
@@ -36,9 +34,9 @@ import {
|
||||
gitGetNote,
|
||||
gitFetch,
|
||||
initGit,
|
||||
} from './helpers/git-utils.js';
|
||||
} = require('./helpers/git-utils');
|
||||
|
||||
test('Get the last commit sha', async (t) => {
|
||||
test('Get the last commit sha', async t => {
|
||||
// Create a git repository, set the current working directory at the root of the repo
|
||||
const {cwd} = await gitRepo();
|
||||
// Add commits to the master branch
|
||||
@@ -49,14 +47,14 @@ test('Get the last commit sha', async (t) => {
|
||||
t.is(result, commits[0].hash);
|
||||
});
|
||||
|
||||
test('Throw error if the last commit sha cannot be found', async (t) => {
|
||||
test('Throw error if the last commit sha cannot be found', async t => {
|
||||
// Create a git repository, set the current working directory at the root of the repo
|
||||
const {cwd} = await gitRepo();
|
||||
|
||||
await t.throwsAsync(getGitHead({cwd}));
|
||||
});
|
||||
|
||||
test('Unshallow and fetch repository', async (t) => {
|
||||
test('Unshallow and fetch repository', async t => {
|
||||
// Create a git repository, set the current working directory at the root of the repo
|
||||
let {cwd, repositoryUrl} = await gitRepo();
|
||||
// Add commits to the master branch
|
||||
@@ -73,7 +71,7 @@ test('Unshallow and fetch repository', async (t) => {
|
||||
t.is((await gitGetCommits(undefined, {cwd})).length, 2);
|
||||
});
|
||||
|
||||
test('Do not throw error when unshallow a complete repository', async (t) => {
|
||||
test('Do not throw error when unshallow a complete repository', async t => {
|
||||
// Create a git repository, set the current working directory at the root of the repo
|
||||
const {cwd, repositoryUrl} = await gitRepo(true);
|
||||
await gitCommits(['First'], {cwd});
|
||||
@@ -86,7 +84,7 @@ test('Do not throw error when unshallow a complete repository', async (t) => {
|
||||
await t.notThrowsAsync(fetch(repositoryUrl, 'second-branch', 'master', {cwd}));
|
||||
});
|
||||
|
||||
test('Fetch all tags on a detached head repository', async (t) => {
|
||||
test('Fetch all tags on a detached head repository', async t => {
|
||||
let {cwd, repositoryUrl} = await gitRepo();
|
||||
|
||||
await gitCommits(['First'], {cwd});
|
||||
@@ -103,7 +101,7 @@ test('Fetch all tags on a detached head repository', async (t) => {
|
||||
t.deepEqual((await getTags('master', {cwd})).sort(), ['v1.0.0', 'v1.0.1', 'v1.1.0'].sort());
|
||||
});
|
||||
|
||||
test('Fetch all tags on a repository with a detached head from branch (CircleCI)', async (t) => {
|
||||
test('Fetch all tags on a repository with a detached head from branch (CircleCI)', async t => {
|
||||
let {cwd, repositoryUrl} = await gitRepo();
|
||||
|
||||
await gitCommits(['First'], {cwd});
|
||||
@@ -128,7 +126,7 @@ test('Fetch all tags on a repository with a detached head from branch (CircleCI)
|
||||
t.deepEqual((await getTags('master', {cwd})).sort(), ['v1.0.0', 'v1.0.1', 'v1.1.0', 'v2.0.0'].sort());
|
||||
});
|
||||
|
||||
test('Fetch all tags on a detached head repository with outdated cached repo (GitLab CI)', async (t) => {
|
||||
test('Fetch all tags on a detached head repository with outdated cached repo (GitLab CI)', async t => {
|
||||
const {cwd, repositoryUrl} = await gitRepo();
|
||||
|
||||
await gitCommits(['First'], {cwd});
|
||||
@@ -156,7 +154,7 @@ test('Fetch all tags on a detached head repository with outdated cached repo (Gi
|
||||
t.deepEqual((await getTags('master', {cwd: cloneCwd})).sort(), ['v1.0.0', 'v1.0.1', 'v1.1.0', 'v1.2.0'].sort());
|
||||
});
|
||||
|
||||
test('Verify if a branch exists', async (t) => {
|
||||
test('Verify if a branch exists', async t => {
|
||||
// Create a git repository, set the current working directory at the root of the repo
|
||||
const {cwd} = await gitRepo();
|
||||
// Add commits to the master branch
|
||||
@@ -171,7 +169,7 @@ test('Verify if a branch exists', async (t) => {
|
||||
t.falsy(await isRefExists('next', {cwd}));
|
||||
});
|
||||
|
||||
test('Get all branches', async (t) => {
|
||||
test('Get all branches', async t => {
|
||||
const {cwd, repositoryUrl} = await gitRepo(true);
|
||||
await gitCommits(['First'], {cwd});
|
||||
await gitPush(repositoryUrl, 'master', {cwd});
|
||||
@@ -185,12 +183,12 @@ test('Get all branches', async (t) => {
|
||||
t.deepEqual((await getBranches(repositoryUrl, {cwd})).sort(), ['master', 'second-branch', 'third-branch'].sort());
|
||||
});
|
||||
|
||||
test('Return empty array if there are no branches', async (t) => {
|
||||
test('Return empty array if there are no branches', async t => {
|
||||
const {cwd, repositoryUrl} = await initGit(true);
|
||||
t.deepEqual(await getBranches(repositoryUrl, {cwd}), []);
|
||||
});
|
||||
|
||||
test('Get the commit sha for a given tag', async (t) => {
|
||||
test('Get the commit sha for a given tag', async t => {
|
||||
// Create a git repository, set the current working directory at the root of the repo
|
||||
const {cwd} = await gitRepo();
|
||||
// Add commits to the master branch
|
||||
@@ -201,7 +199,7 @@ test('Get the commit sha for a given tag', async (t) => {
|
||||
t.is(await getTagHead('v1.0.0', {cwd}), commits[0].hash);
|
||||
});
|
||||
|
||||
test('Return git remote repository url from config', async (t) => {
|
||||
test('Return git remote repository url from config', async t => {
|
||||
// Create a git repository, set the current working directory at the root of the repo
|
||||
const {cwd} = await gitRepo();
|
||||
// Add remote.origin.url config
|
||||
@@ -210,7 +208,7 @@ test('Return git remote repository url from config', async (t) => {
|
||||
t.is(await repoUrl({cwd}), 'git@hostname.com:owner/package.git');
|
||||
});
|
||||
|
||||
test('Return git remote repository url set while cloning', async (t) => {
|
||||
test('Return git remote repository url set while cloning', async t => {
|
||||
// Create a git repository, set the current working directory at the root of the repo
|
||||
let {cwd, repositoryUrl} = await gitRepo();
|
||||
await gitCommits(['First'], {cwd});
|
||||
@@ -220,14 +218,14 @@ test('Return git remote repository url set while cloning', async (t) => {
|
||||
t.is(await repoUrl({cwd}), repositoryUrl);
|
||||
});
|
||||
|
||||
test('Return falsy if git repository url is not set', async (t) => {
|
||||
test('Return falsy if git repository url is not set', async t => {
|
||||
// Create a git repository, set the current working directory at the root of the repo
|
||||
const {cwd} = await gitRepo();
|
||||
|
||||
t.falsy(await repoUrl({cwd}));
|
||||
});
|
||||
|
||||
test('Add tag on head commit', async (t) => {
|
||||
test('Add tag on head commit', async t => {
|
||||
// Create a git repository, set the current working directory at the root of the repo
|
||||
const {cwd} = await gitRepo();
|
||||
const commits = await gitCommits(['Test commit'], {cwd});
|
||||
@@ -237,7 +235,7 @@ test('Add tag on head commit', async (t) => {
|
||||
await t.is(await gitCommitTag(commits[0].hash, {cwd}), 'tag_name');
|
||||
});
|
||||
|
||||
test('Push tag to remote repository', async (t) => {
|
||||
test('Push tag to remote repository', async t => {
|
||||
// Create a git repository with a remote, set the current working directory at the root of the repo
|
||||
const {cwd, repositoryUrl} = await gitRepo(true);
|
||||
const commits = await gitCommits(['Test commit'], {cwd});
|
||||
@@ -248,13 +246,13 @@ test('Push tag to remote repository', async (t) => {
|
||||
t.is(await gitRemoteTagHead(repositoryUrl, 'tag_name', {cwd}), commits[0].hash);
|
||||
});
|
||||
|
||||
test('Push tag to remote repository with remote branch ahead', async (t) => {
|
||||
test('Push tag to remote repository with remote branch ahead', async t => {
|
||||
const {cwd, repositoryUrl} = await gitRepo(true);
|
||||
const commits = await gitCommits(['First'], {cwd});
|
||||
await gitPush(repositoryUrl, 'master', {cwd});
|
||||
const temporaryRepo = await gitShallowClone(repositoryUrl);
|
||||
await gitCommits(['Second'], {cwd: temporaryRepo});
|
||||
await gitPush('origin', 'master', {cwd: temporaryRepo});
|
||||
const tmpRepo = await gitShallowClone(repositoryUrl);
|
||||
await gitCommits(['Second'], {cwd: tmpRepo});
|
||||
await gitPush('origin', 'master', {cwd: tmpRepo});
|
||||
|
||||
await tag('tag_name', 'HEAD', {cwd});
|
||||
await push(repositoryUrl, {cwd});
|
||||
@@ -262,40 +260,40 @@ test('Push tag to remote repository with remote branch ahead', async (t) => {
|
||||
t.is(await gitRemoteTagHead(repositoryUrl, 'tag_name', {cwd}), commits[0].hash);
|
||||
});
|
||||
|
||||
test('Return "true" if in a Git repository', async (t) => {
|
||||
test('Return "true" if in a Git repository', async t => {
|
||||
// Create a git repository with a remote, set the current working directory at the root of the repo
|
||||
const {cwd} = await gitRepo(true);
|
||||
|
||||
t.true(await isGitRepo({cwd}));
|
||||
});
|
||||
|
||||
test('Return falsy if not in a Git repository', async (t) => {
|
||||
test('Return falsy if not in a Git repository', async t => {
|
||||
const cwd = tempy.directory();
|
||||
|
||||
t.falsy(await isGitRepo({cwd}));
|
||||
});
|
||||
|
||||
test('Return "true" for valid tag names', async (t) => {
|
||||
test('Return "true" for valid tag names', async t => {
|
||||
t.true(await verifyTagName('1.0.0'));
|
||||
t.true(await verifyTagName('v1.0.0'));
|
||||
t.true(await verifyTagName('tag_name'));
|
||||
t.true(await verifyTagName('tag/name'));
|
||||
});
|
||||
|
||||
test('Return falsy for invalid tag names', async (t) => {
|
||||
test('Return falsy for invalid tag names', async t => {
|
||||
t.falsy(await verifyTagName('?1.0.0'));
|
||||
t.falsy(await verifyTagName('*1.0.0'));
|
||||
t.falsy(await verifyTagName('[1.0.0]'));
|
||||
t.falsy(await verifyTagName('1.0.0..'));
|
||||
});
|
||||
|
||||
test('Throws error if obtaining the tags fails', async (t) => {
|
||||
test('Throws error if obtaining the tags fails', async t => {
|
||||
const cwd = tempy.directory();
|
||||
|
||||
await t.throwsAsync(getTags('master', {cwd}));
|
||||
});
|
||||
|
||||
test('Return "true" if repository is up to date', async (t) => {
|
||||
test('Return "true" if repository is up to date', async t => {
|
||||
const {cwd, repositoryUrl} = await gitRepo(true);
|
||||
await gitCommits(['First'], {cwd});
|
||||
await gitPush(repositoryUrl, 'master', {cwd});
|
||||
@@ -303,7 +301,7 @@ test('Return "true" if repository is up to date', async (t) => {
|
||||
t.true(await isBranchUpToDate(repositoryUrl, 'master', {cwd}));
|
||||
});
|
||||
|
||||
test('Return falsy if repository is not up to date', async (t) => {
|
||||
test('Return falsy if repository is not up to date', async t => {
|
||||
const {cwd, repositoryUrl} = await gitRepo(true);
|
||||
await gitCommits(['First'], {cwd});
|
||||
await gitCommits(['Second'], {cwd});
|
||||
@@ -311,14 +309,14 @@ test('Return falsy if repository is not up to date', async (t) => {
|
||||
|
||||
t.true(await isBranchUpToDate(repositoryUrl, 'master', {cwd}));
|
||||
|
||||
const temporaryRepo = await gitShallowClone(repositoryUrl);
|
||||
await gitCommits(['Third'], {cwd: temporaryRepo});
|
||||
await gitPush('origin', 'master', {cwd: temporaryRepo});
|
||||
const tmpRepo = await gitShallowClone(repositoryUrl);
|
||||
await gitCommits(['Third'], {cwd: tmpRepo});
|
||||
await gitPush('origin', 'master', {cwd: tmpRepo});
|
||||
|
||||
t.falsy(await isBranchUpToDate(repositoryUrl, 'master', {cwd}));
|
||||
});
|
||||
|
||||
test('Return falsy if detached head repository is not up to date', async (t) => {
|
||||
test('Return falsy if detached head repository is not up to date', async t => {
|
||||
let {cwd, repositoryUrl} = await gitRepo();
|
||||
|
||||
const [commit] = await gitCommits(['First'], {cwd});
|
||||
@@ -330,7 +328,7 @@ test('Return falsy if detached head repository is not up to date', async (t) =>
|
||||
t.falsy(await isBranchUpToDate(repositoryUrl, 'master', {cwd}));
|
||||
});
|
||||
|
||||
test('Get a commit note', async (t) => {
|
||||
test('Get a commit note', async t => {
|
||||
// Create a git repository, set the current working directory at the root of the repo
|
||||
const {cwd} = await gitRepo();
|
||||
// Add commits to the master branch
|
||||
@@ -341,7 +339,7 @@ test('Get a commit note', async (t) => {
|
||||
t.deepEqual(await getNote(commits[0].hash, {cwd}), {note: 'note'});
|
||||
});
|
||||
|
||||
test('Return empty object if there is no commit note', async (t) => {
|
||||
test('Return empty object if there is no commit note', async t => {
|
||||
// Create a git repository, set the current working directory at the root of the repo
|
||||
const {cwd} = await gitRepo();
|
||||
// Add commits to the master branch
|
||||
@@ -350,7 +348,7 @@ test('Return empty object if there is no commit note', async (t) => {
|
||||
t.deepEqual(await getNote(commits[0].hash, {cwd}), {});
|
||||
});
|
||||
|
||||
test('Throw error if a commit note in invalid', async (t) => {
|
||||
test('Throw error if a commit note in invalid', async t => {
|
||||
// Create a git repository, set the current working directory at the root of the repo
|
||||
const {cwd} = await gitRepo();
|
||||
// Add commits to the master branch
|
||||
@@ -361,7 +359,7 @@ test('Throw error if a commit note in invalid', async (t) => {
|
||||
await t.throwsAsync(getNote(commits[0].hash, {cwd}));
|
||||
});
|
||||
|
||||
test('Add a commit note', async (t) => {
|
||||
test('Add a commit note', async t => {
|
||||
// Create a git repository, set the current working directory at the root of the repo
|
||||
const {cwd} = await gitRepo();
|
||||
// Add commits to the master branch
|
||||
@@ -372,7 +370,7 @@ test('Add a commit note', async (t) => {
|
||||
t.is(await gitGetNote(commits[0].hash, {cwd}), '{"note":"note"}');
|
||||
});
|
||||
|
||||
test('Overwrite a commit note', async (t) => {
|
||||
test('Overwrite a commit note', async t => {
|
||||
// Create a git repository, set the current working directory at the root of the repo
|
||||
const {cwd} = await gitRepo();
|
||||
// Add commits to the master branch
|
||||
@@ -384,7 +382,7 @@ test('Overwrite a commit note', async (t) => {
|
||||
t.is(await gitGetNote(commits[0].hash, {cwd}), '{"note":"note2"}');
|
||||
});
|
||||
|
||||
test('Unshallow and fetch repository with notes', async (t) => {
|
||||
test('Unshallow and fetch repository with notes', async t => {
|
||||
// Create a git repository, set the current working directory at the root of the repo
|
||||
let {cwd, repositoryUrl} = await gitRepo();
|
||||
// Add commits to the master branch
|
||||
@@ -403,7 +401,7 @@ test('Unshallow and fetch repository with notes', async (t) => {
|
||||
t.is(await gitGetNote(commits[0].hash, {cwd}), '{"note":"note"}');
|
||||
});
|
||||
|
||||
test('Fetch all notes on a detached head repository', async (t) => {
|
||||
test('Fetch all notes on a detached head repository', async t => {
|
||||
let {cwd, repositoryUrl} = await gitRepo();
|
||||
|
||||
await gitCommits(['First'], {cwd});
|
||||
|
||||
+45
-79
@@ -1,10 +1,10 @@
|
||||
import tempy from 'tempy';
|
||||
import execa from 'execa';
|
||||
import fileUrl from 'file-url';
|
||||
import pEachSeries from 'p-each-series';
|
||||
import gitLogParser from 'git-log-parser';
|
||||
import getStream from 'get-stream';
|
||||
import {GIT_NOTE_REF} from '../../lib/definitions/constants.js';
|
||||
const tempy = require('tempy');
|
||||
const execa = require('execa');
|
||||
const fileUrl = require('file-url');
|
||||
const pEachSeries = require('p-each-series');
|
||||
const gitLogParser = require('git-log-parser');
|
||||
const getStream = require('get-stream');
|
||||
const {GIT_NOTE_REF} = require('../../lib/definitions/constants');
|
||||
|
||||
/**
|
||||
* Commit message information.
|
||||
@@ -25,12 +25,8 @@ import {GIT_NOTE_REF} from '../../lib/definitions/constants.js';
|
||||
*/
|
||||
async function initGit(withRemote) {
|
||||
const cwd = tempy.directory();
|
||||
const args = withRemote ? ['--bare', '--initial-branch=master'] : ['--initial-branch=master'];
|
||||
|
||||
await execa('git', ['init', ...args], {cwd}).catch(() => {
|
||||
const args = withRemote ? ['--bare'] : [];
|
||||
return execa('git', ['init', ...args], {cwd});
|
||||
});
|
||||
await execa('git', ['init', ...(withRemote ? ['--bare'] : [])], {cwd});
|
||||
const repositoryUrl = fileUrl(cwd);
|
||||
return {cwd, repositoryUrl};
|
||||
}
|
||||
@@ -86,13 +82,12 @@ async function initBareRepo(repositoryUrl, branch = 'master') {
|
||||
*
|
||||
* @returns {Array<Commit>} The created commits, in reverse order (to match `git log` order).
|
||||
*/
|
||||
async function gitCommits(messages, execaOptions) {
|
||||
async function gitCommits(messages, execaOpts) {
|
||||
await pEachSeries(
|
||||
messages,
|
||||
async (message) =>
|
||||
(await execa('git', ['commit', '-m', message, '--allow-empty', '--no-gpg-sign'], execaOptions)).stdout
|
||||
async message => (await execa('git', ['commit', '-m', message, '--allow-empty', '--no-gpg-sign'], execaOpts)).stdout
|
||||
);
|
||||
return (await gitGetCommits(undefined, execaOptions)).slice(0, messages.length);
|
||||
return (await gitGetCommits(undefined, execaOpts)).slice(0, messages.length);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -103,16 +98,13 @@ async function gitCommits(messages, execaOptions) {
|
||||
*
|
||||
* @return {Array<Object>} The list of parsed commits.
|
||||
*/
|
||||
async function gitGetCommits(from, execaOptions) {
|
||||
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`},
|
||||
{...execaOptions, env: {...process.env, ...execaOptions.env}}
|
||||
)
|
||||
gitLogParser.parse({_: `${from ? from + '..' : ''}HEAD`}, {...execaOpts, env: {...process.env, ...execaOpts.env}})
|
||||
)
|
||||
).map((commit) => {
|
||||
).map(commit => {
|
||||
commit.message = commit.message.trim();
|
||||
commit.gitTags = commit.gitTags.trim();
|
||||
return commit;
|
||||
@@ -126,8 +118,8 @@ async function gitGetCommits(from, execaOptions) {
|
||||
* @param {Boolean} create to create the branch, `false` to checkout an existing branch.
|
||||
* @param {Object} [execaOpts] Options to pass to `execa`.
|
||||
*/
|
||||
async function gitCheckout(branch, create, execaOptions) {
|
||||
await execa('git', create ? ['checkout', '-b', branch] : ['checkout', branch], execaOptions);
|
||||
async function gitCheckout(branch, create, execaOpts) {
|
||||
await execa('git', create ? ['checkout', '-b', branch] : ['checkout', branch], execaOpts);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -136,8 +128,8 @@ async function gitCheckout(branch, create, execaOptions) {
|
||||
* @param {String} repositoryUrl The repository remote URL.
|
||||
* @param {Object} [execaOpts] Options to pass to `execa`.
|
||||
*/
|
||||
async function gitFetch(repositoryUrl, execaOptions) {
|
||||
await execa('git', ['fetch', repositoryUrl], execaOptions);
|
||||
async function gitFetch(repositoryUrl, execaOpts) {
|
||||
await execa('git', ['fetch', repositoryUrl], execaOpts);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -147,8 +139,8 @@ async function gitFetch(repositoryUrl, execaOptions) {
|
||||
*
|
||||
* @return {String} The sha of the head commit in the current git repository.
|
||||
*/
|
||||
async function gitHead(execaOptions) {
|
||||
return (await execa('git', ['rev-parse', 'HEAD'], execaOptions)).stdout;
|
||||
async function gitHead(execaOpts) {
|
||||
return (await execa('git', ['rev-parse', 'HEAD'], execaOpts)).stdout;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -158,8 +150,8 @@ async function gitHead(execaOptions) {
|
||||
* @param {String} [sha] The commit on which to create the tag. If undefined the tag is created on the last commit.
|
||||
* @param {Object} [execaOpts] Options to pass to `execa`.
|
||||
*/
|
||||
async function gitTagVersion(tagName, sha, execaOptions) {
|
||||
await execa('git', sha ? ['tag', '-f', tagName, sha] : ['tag', tagName], execaOptions);
|
||||
async function gitTagVersion(tagName, sha, execaOpts) {
|
||||
await execa('git', sha ? ['tag', '-f', tagName, sha] : ['tag', tagName], execaOpts);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -215,8 +207,8 @@ async function gitDetachedHeadFromBranch(repositoryUrl, branch, head) {
|
||||
* @param {String} value Config value.
|
||||
* @param {Object} [execaOpts] Options to pass to `execa`.
|
||||
*/
|
||||
async function gitAddConfig(name, value, execaOptions) {
|
||||
await execa('git', ['config', '--add', name, value], execaOptions);
|
||||
async function gitAddConfig(name, value, execaOpts) {
|
||||
await execa('git', ['config', '--add', name, value], execaOpts);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -227,8 +219,8 @@ async function gitAddConfig(name, value, execaOptions) {
|
||||
*
|
||||
* @return {String} The sha of the commit associated with `tagName` on the local repository.
|
||||
*/
|
||||
async function gitTagHead(tagName, execaOptions) {
|
||||
return (await execa('git', ['rev-list', '-1', tagName], execaOptions)).stdout;
|
||||
async function gitTagHead(tagName, execaOpts) {
|
||||
return (await execa('git', ['rev-list', '-1', tagName], execaOpts)).stdout;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -240,11 +232,11 @@ async function gitTagHead(tagName, execaOptions) {
|
||||
*
|
||||
* @return {String} The sha of the commit associated with `tagName` on the remote repository.
|
||||
*/
|
||||
async function gitRemoteTagHead(repositoryUrl, tagName, execaOptions) {
|
||||
return (await execa('git', ['ls-remote', '--tags', repositoryUrl, tagName], execaOptions)).stdout
|
||||
async function gitRemoteTagHead(repositoryUrl, tagName, execaOpts) {
|
||||
return (await execa('git', ['ls-remote', '--tags', repositoryUrl, tagName], execaOpts)).stdout
|
||||
.split('\n')
|
||||
.filter((tag) => Boolean(tag))
|
||||
.map((tag) => tag.match(/^(?<tag>\S+)/)[1])[0];
|
||||
.filter(tag => Boolean(tag))
|
||||
.map(tag => tag.match(/^(?<tag>\S+)/)[1])[0];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -255,8 +247,8 @@ async function gitRemoteTagHead(repositoryUrl, tagName, execaOptions) {
|
||||
*
|
||||
* @return {String} The tag associatedwith the sha in parameter or `null`.
|
||||
*/
|
||||
async function gitCommitTag(gitHead, execaOptions) {
|
||||
return (await execa('git', ['describe', '--tags', '--exact-match', gitHead], execaOptions)).stdout;
|
||||
async function gitCommitTag(gitHead, execaOpts) {
|
||||
return (await execa('git', ['describe', '--tags', '--exact-match', gitHead], execaOpts)).stdout;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -268,8 +260,8 @@ async function gitCommitTag(gitHead, execaOptions) {
|
||||
*
|
||||
* @throws {Error} if the push failed.
|
||||
*/
|
||||
async function gitPush(repositoryUrl, branch, execaOptions) {
|
||||
await execa('git', ['push', '--tags', repositoryUrl, `HEAD:${branch}`], execaOptions);
|
||||
async function gitPush(repositoryUrl, branch, execaOpts) {
|
||||
await execa('git', ['push', '--tags', repositoryUrl, `HEAD:${branch}`], execaOpts);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -278,8 +270,8 @@ async function gitPush(repositoryUrl, branch, execaOptions) {
|
||||
* @param {String} ref The ref to merge.
|
||||
* @param {Object} [execaOpts] Options to pass to `execa`.
|
||||
*/
|
||||
async function merge(ref, execaOptions) {
|
||||
await execa('git', ['merge', '--no-ff', ref], execaOptions);
|
||||
async function merge(ref, execaOpts) {
|
||||
await execa('git', ['merge', '--no-ff', ref], execaOpts);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -288,8 +280,8 @@ async function merge(ref, execaOptions) {
|
||||
* @param {String} ref The ref to merge.
|
||||
* @param {Object} [execaOpts] Options to pass to `execa`.
|
||||
*/
|
||||
async function mergeFf(ref, execaOptions) {
|
||||
await execa('git', ['merge', '--ff', ref], execaOptions);
|
||||
async function mergeFf(ref, execaOpts) {
|
||||
await execa('git', ['merge', '--ff', ref], execaOpts);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -298,8 +290,8 @@ async function mergeFf(ref, execaOptions) {
|
||||
* @param {String} ref The ref to merge.
|
||||
* @param {Object} [execaOpts] Options to pass to `execa`.
|
||||
*/
|
||||
async function rebase(ref, execaOptions) {
|
||||
await execa('git', ['rebase', ref], execaOptions);
|
||||
async function rebase(ref, execaOpts) {
|
||||
await execa('git', ['rebase', ref], execaOpts);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -309,8 +301,8 @@ async function rebase(ref, execaOptions) {
|
||||
* @param {String} ref The ref to add the note to.
|
||||
* @param {Object} [execaOpts] Options to pass to `execa`.
|
||||
*/
|
||||
async function gitAddNote(note, ref, execaOptions) {
|
||||
await execa('git', ['notes', '--ref', GIT_NOTE_REF, 'add', '-m', note, ref], execaOptions);
|
||||
async function gitAddNote(note, ref, execaOpts) {
|
||||
await execa('git', ['notes', '--ref', GIT_NOTE_REF, 'add', '-m', note, ref], execaOpts);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -319,37 +311,11 @@ async function gitAddNote(note, ref, execaOptions) {
|
||||
* @param {String} ref The ref to get the note from.
|
||||
* @param {Object} [execaOpts] Options to pass to `execa`.
|
||||
*/
|
||||
async function gitGetNote(ref, execaOptions) {
|
||||
return (await execa('git', ['notes', '--ref', GIT_NOTE_REF, 'show', ref], execaOptions)).stdout;
|
||||
async function gitGetNote(ref, execaOpts) {
|
||||
return (await execa('git', ['notes', '--ref', GIT_NOTE_REF, 'show', ref], execaOpts)).stdout;
|
||||
}
|
||||
|
||||
const exported = {
|
||||
initGit,
|
||||
gitRepo,
|
||||
initBareRepo,
|
||||
gitCommits,
|
||||
gitGetCommits,
|
||||
gitCheckout,
|
||||
gitFetch,
|
||||
gitHead,
|
||||
gitTagVersion,
|
||||
gitShallowClone,
|
||||
gitDetachedHead,
|
||||
gitDetachedHeadFromBranch,
|
||||
gitAddConfig,
|
||||
gitTagHead,
|
||||
gitRemoteTagHead,
|
||||
gitCommitTag,
|
||||
gitPush,
|
||||
merge,
|
||||
mergeFf,
|
||||
rebase,
|
||||
gitAddNote,
|
||||
gitGetNote,
|
||||
};
|
||||
|
||||
export default exported;
|
||||
export {
|
||||
module.exports = {
|
||||
initGit,
|
||||
gitRepo,
|
||||
initBareRepo,
|
||||
|
||||
+6
-14
@@ -1,7 +1,7 @@
|
||||
import Docker from 'dockerode';
|
||||
import getStream from 'get-stream';
|
||||
import pRetry from 'p-retry';
|
||||
import {initBareRepo, gitShallowClone} from './git-utils.js';
|
||||
const Docker = require('dockerode');
|
||||
const getStream = require('get-stream');
|
||||
const pRetry = require('p-retry');
|
||||
const {initBareRepo, gitShallowClone} = require('./git-utils');
|
||||
|
||||
const IMAGE = 'pvdlg/docker-gitbox:latest';
|
||||
const SERVER_PORT = 80;
|
||||
@@ -63,18 +63,10 @@ async function createRepo(name, branch = 'master', description = `Repository ${n
|
||||
const authUrl = `http://${gitCredential}@${SERVER_HOST}:${HOST_PORT}/git/${name}.git`;
|
||||
|
||||
// Retry as the server might take a few ms to make the repo available push
|
||||
await pRetry(() => initBareRepo(authUrl, branch), {retries: 5, minTimeout: 500, factor: 2});
|
||||
await pRetry(() => initBareRepo(authUrl, branch), {retries: 3, minTimeout: 500, factor: 2});
|
||||
const cwd = await gitShallowClone(authUrl);
|
||||
|
||||
return {cwd, repositoryUrl, authUrl};
|
||||
}
|
||||
|
||||
const exported = {
|
||||
start,
|
||||
stop,
|
||||
gitCredential,
|
||||
createRepo,
|
||||
};
|
||||
|
||||
export default exported;
|
||||
export {start, stop, gitCredential, createRepo};
|
||||
module.exports = {start, stop, gitCredential, createRepo};
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import Docker from 'dockerode';
|
||||
import getStream from 'get-stream';
|
||||
import got from 'got';
|
||||
import pRetry from 'p-retry';
|
||||
import {mockServerClient} from 'mockserver-client';
|
||||
const Docker = require('dockerode');
|
||||
const getStream = require('get-stream');
|
||||
const got = require('got');
|
||||
const pRetry = require('p-retry');
|
||||
const {mockServerClient} = require('mockserver-client');
|
||||
|
||||
const IMAGE = 'jamesdbloom/mockserver:latest';
|
||||
const MOCK_SERVER_PORT = 1080;
|
||||
@@ -100,13 +100,4 @@ function verify(expectation) {
|
||||
return client.verify(expectation);
|
||||
}
|
||||
|
||||
const exported = {
|
||||
start,
|
||||
stop,
|
||||
mock,
|
||||
verify,
|
||||
url,
|
||||
};
|
||||
|
||||
export default exported;
|
||||
export {start, stop, mock, verify, url};
|
||||
module.exports = {start, stop, mock, verify, url};
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import Docker from 'dockerode';
|
||||
import getStream from 'get-stream';
|
||||
import got from 'got';
|
||||
import delay from 'delay';
|
||||
import pRetry from 'p-retry';
|
||||
const Docker = require('dockerode');
|
||||
const getStream = require('get-stream');
|
||||
const got = require('got');
|
||||
const delay = require('delay');
|
||||
const pRetry = require('p-retry');
|
||||
|
||||
const IMAGE = 'semanticrelease/npm-registry-docker:latest';
|
||||
const SERVER_PORT = 15986;
|
||||
@@ -76,12 +76,4 @@ async function stop() {
|
||||
await container.remove();
|
||||
}
|
||||
|
||||
const exported = {
|
||||
start,
|
||||
stop,
|
||||
authEnv,
|
||||
url,
|
||||
};
|
||||
|
||||
export default exported;
|
||||
export {start, stop, authEnv, url};
|
||||
module.exports = {start, stop, authEnv, url};
|
||||
|
||||
@@ -1,12 +1,7 @@
|
||||
import execa from 'execa';
|
||||
const execa = require('execa');
|
||||
|
||||
async function npmView(packageName, env) {
|
||||
return JSON.parse((await execa('npm', ['view', packageName, '--json'], {env})).stdout);
|
||||
}
|
||||
|
||||
const exported = {
|
||||
npmView,
|
||||
};
|
||||
|
||||
export default exported;
|
||||
export {npmView};
|
||||
module.exports = {npmView};
|
||||
|
||||
+12
-39
@@ -1,10 +1,9 @@
|
||||
import test from 'ava';
|
||||
import lodash from 'lodash'
|
||||
const {repeat} = lodash
|
||||
import hideSensitive from '../lib/hide-sensitive.js';
|
||||
import {SECRET_REPLACEMENT, SECRET_MIN_SIZE} from '../lib/definitions/constants.js';
|
||||
const test = require('ava');
|
||||
const {repeat} = require('lodash');
|
||||
const hideSensitive = require('../lib/hide-sensitive');
|
||||
const {SECRET_REPLACEMENT, SECRET_MIN_SIZE} = require('../lib/definitions/constants');
|
||||
|
||||
test('Replace multiple sensitive environment variable values', (t) => {
|
||||
test('Replace multiple sensitive environment variable values', t => {
|
||||
const env = {SOME_PASSWORD: 'password', SOME_TOKEN: 'secret'};
|
||||
t.is(
|
||||
hideSensitive(env)(`https://user:${env.SOME_PASSWORD}@host.com?token=${env.SOME_TOKEN}`),
|
||||
@@ -12,7 +11,7 @@ test('Replace multiple sensitive environment variable values', (t) => {
|
||||
);
|
||||
});
|
||||
|
||||
test('Replace multiple occurences of sensitive environment variable values', (t) => {
|
||||
test('Replace multiple occurences of sensitive environment variable values', t => {
|
||||
const env = {secretKey: 'secret'};
|
||||
t.is(
|
||||
hideSensitive(env)(`https://user:${env.secretKey}@host.com?token=${env.secretKey}`),
|
||||
@@ -20,20 +19,7 @@ test('Replace multiple occurences of sensitive environment variable values', (t)
|
||||
);
|
||||
});
|
||||
|
||||
test('Replace sensitive environment variable matching specific regex for "private"', (t) => {
|
||||
const env = {privateKey: 'secret', GOPRIVATE: 'host.com'};
|
||||
t.is(hideSensitive(env)(`https://host.com?token=${env.privateKey}`), `https://host.com?token=${SECRET_REPLACEMENT}`);
|
||||
});
|
||||
|
||||
test('Replace url-encoded environment variable', (t) => {
|
||||
const env = {privateKey: 'secret '};
|
||||
t.is(
|
||||
hideSensitive(env)(`https://host.com?token=${encodeURI(env.privateKey)}`),
|
||||
`https://host.com?token=${SECRET_REPLACEMENT}`
|
||||
);
|
||||
});
|
||||
|
||||
test('Escape regexp special characters', (t) => {
|
||||
test('Escape regexp special characters', t => {
|
||||
const env = {SOME_CREDENTIALS: 'p$^{.+}\\w[a-z]o.*rd'};
|
||||
t.is(
|
||||
hideSensitive(env)(`https://user:${env.SOME_CREDENTIALS}@host.com`),
|
||||
@@ -41,23 +27,15 @@ test('Escape regexp special characters', (t) => {
|
||||
);
|
||||
});
|
||||
|
||||
test('Escape regexp special characters in url-encoded environment variable', (t) => {
|
||||
const env = {SOME_PASSWORD: 'secret password p$^{.+}\\w[a-z]o.*rd)('};
|
||||
t.is(
|
||||
hideSensitive(env)(`https://user:${encodeURI(env.SOME_PASSWORD)}@host.com`),
|
||||
`https://user:${SECRET_REPLACEMENT}@host.com`
|
||||
);
|
||||
});
|
||||
|
||||
test('Accept "undefined" input', (t) => {
|
||||
test('Accept "undefined" input', t => {
|
||||
t.is(hideSensitive({})(), undefined);
|
||||
});
|
||||
|
||||
test('Return same string if no environment variable has to be replaced', (t) => {
|
||||
test('Return same string if no environment variable has to be replaced', t => {
|
||||
t.is(hideSensitive({})('test'), 'test');
|
||||
});
|
||||
|
||||
test('Exclude empty environment variables from the regexp', (t) => {
|
||||
test('Exclude empty environment variables from the regexp', t => {
|
||||
const env = {SOME_PASSWORD: 'password', SOME_TOKEN: ''};
|
||||
t.is(
|
||||
hideSensitive(env)(`https://user:${env.SOME_PASSWORD}@host.com?token=`),
|
||||
@@ -65,16 +43,11 @@ test('Exclude empty environment variables from the regexp', (t) => {
|
||||
);
|
||||
});
|
||||
|
||||
test('Exclude empty environment variables from the regexp if there is only empty ones', (t) => {
|
||||
test('Exclude empty environment variables from the regexp if there is only empty ones', t => {
|
||||
t.is(hideSensitive({SOME_PASSWORD: '', SOME_TOKEN: ' \n '})(`https://host.com?token=`), 'https://host.com?token=');
|
||||
});
|
||||
|
||||
test('Exclude nonsensitive GOPRIVATE environment variable for Golang projects from the regexp', (t) => {
|
||||
const env = {GOPRIVATE: 'host.com'};
|
||||
t.is(hideSensitive(env)(`https://host.com?token=`), 'https://host.com?token=');
|
||||
});
|
||||
|
||||
test('Exclude environment variables with value shorter than SECRET_MIN_SIZE from the regexp', (t) => {
|
||||
test('Exclude environment variables with value shorter than SECRET_MIN_SIZE from the regexp', t => {
|
||||
const SHORT_TOKEN = repeat('a', SECRET_MIN_SIZE - 1);
|
||||
const LONG_TOKEN = repeat('b', SECRET_MIN_SIZE);
|
||||
const env = {SHORT_TOKEN, LONG_TOKEN};
|
||||
|
||||
+252
-254
File diff suppressed because it is too large
Load Diff
+56
-107
@@ -1,16 +1,13 @@
|
||||
import path from 'path';
|
||||
import test from 'ava';
|
||||
import proxyquire from 'proxyquire';
|
||||
import lodash from 'lodash'
|
||||
const {escapeRegExp} = lodash
|
||||
import {writeJson, readJson} from 'fs-extra';
|
||||
import execa from 'execa';
|
||||
import {WritableStreamBuffer} from 'stream-buffers';
|
||||
import delay from 'delay';
|
||||
import getAuthUrl from '../lib/get-git-auth-url.js';
|
||||
import {SECRET_REPLACEMENT} from '../lib/definitions/constants.js';
|
||||
|
||||
import {
|
||||
const path = require('path');
|
||||
const test = require('ava');
|
||||
const proxyquire = require('proxyquire');
|
||||
const {escapeRegExp} = require('lodash');
|
||||
const {writeJson, readJson} = require('fs-extra');
|
||||
const execa = require('execa');
|
||||
const {WritableStreamBuffer} = require('stream-buffers');
|
||||
const delay = require('delay');
|
||||
const {SECRET_REPLACEMENT} = require('../lib/definitions/constants');
|
||||
const {
|
||||
gitHead,
|
||||
gitTagHead,
|
||||
gitRepo,
|
||||
@@ -20,32 +17,28 @@ import {
|
||||
gitCheckout,
|
||||
merge,
|
||||
gitGetNote,
|
||||
} from './helpers/git-utils.js';
|
||||
|
||||
import {npmView} from './helpers/npm-utils.js';
|
||||
import gitbox from './helpers/gitbox.js';
|
||||
import mockServer from './helpers/mockserver.js';
|
||||
import npmRegistry from './helpers/npm-registry.js';
|
||||
} = require('./helpers/git-utils');
|
||||
const {npmView} = require('./helpers/npm-utils');
|
||||
const gitbox = require('./helpers/gitbox');
|
||||
const mockServer = require('./helpers/mockserver');
|
||||
const npmRegistry = require('./helpers/npm-registry');
|
||||
|
||||
/* eslint camelcase: ["error", {properties: "never"}] */
|
||||
|
||||
const requireNoCache = proxyquire.noPreserveCache();
|
||||
|
||||
// Environment variables used with semantic-release cli (similar to what a user would setup)
|
||||
const {GITHUB_ACTION, GITHUB_TOKEN, ...processEnvWithoutGitHubActionsVariables} = process.env;
|
||||
const env = {
|
||||
...processEnvWithoutGitHubActionsVariables,
|
||||
...npmRegistry.authEnv,
|
||||
CI: 'true',
|
||||
GH_TOKEN: gitbox.gitCredential,
|
||||
GITHUB_URL: mockServer.url,
|
||||
TRAVIS: 'true',
|
||||
CI: 'true',
|
||||
TRAVIS_BRANCH: 'master',
|
||||
TRAVIS_PULL_REQUEST: 'false',
|
||||
GITHUB_API_URL: mockServer.url,
|
||||
};
|
||||
|
||||
// Environment variables used only for the local npm command used to do verification
|
||||
const npmTestEnv = {
|
||||
const testEnv = {
|
||||
...process.env,
|
||||
...npmRegistry.authEnv,
|
||||
npm_config_registry: npmRegistry.url,
|
||||
@@ -65,7 +58,7 @@ test.after.always(async () => {
|
||||
await Promise.all([gitbox.stop(), npmRegistry.stop(), mockServer.stop()]);
|
||||
});
|
||||
|
||||
test('Release patch, minor and major versions', async (t) => {
|
||||
test('Release patch, minor and major versions', async t => {
|
||||
const packageName = 'test-release';
|
||||
const owner = 'git';
|
||||
// Create a git repository, set the current working directory at the root of the repo
|
||||
@@ -80,7 +73,7 @@ test('Release patch, minor and major versions', async (t) => {
|
||||
release: {branches: ['master', 'next'], success: false, fail: false},
|
||||
});
|
||||
// Create a npm-shrinkwrap.json file
|
||||
await execa('npm', ['shrinkwrap'], {env: npmTestEnv, cwd, extendEnv: false});
|
||||
await execa('npm', ['shrinkwrap'], {env: testEnv, cwd});
|
||||
|
||||
/* No release */
|
||||
let verifyMock = await mockServer.mock(
|
||||
@@ -91,7 +84,7 @@ 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, exitCode} = await execa(cli, [], {env, cwd, extendEnv: false});
|
||||
let {stdout, exitCode} = await execa(cli, [], {env, cwd});
|
||||
t.regex(stdout, /There are no relevant changes, so no new version is released/);
|
||||
t.is(exitCode, 0);
|
||||
|
||||
@@ -114,7 +107,7 @@ 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, exitCode} = await execa(cli, [], {env, cwd, extendEnv: false}));
|
||||
({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(exitCode, 0);
|
||||
@@ -126,7 +119,7 @@ test('Release patch, minor and major versions', async (t) => {
|
||||
// Retrieve the published package from the registry and check version and gitHead
|
||||
let {
|
||||
'dist-tags': {latest: releasedVersion},
|
||||
} = await npmView(packageName, npmTestEnv);
|
||||
} = await npmView(packageName, testEnv);
|
||||
let head = await gitHead({cwd});
|
||||
t.is(releasedVersion, version);
|
||||
t.is(await gitTagHead(`v${version}`, {cwd}), head);
|
||||
@@ -155,7 +148,7 @@ test('Release patch, minor and major versions', async (t) => {
|
||||
t.log('Commit a fix');
|
||||
await gitCommits(['fix: bar'], {cwd});
|
||||
t.log('$ semantic-release');
|
||||
({stdout, exitCode} = await execa(cli, [], {env, cwd, extendEnv: false}));
|
||||
({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(exitCode, 0);
|
||||
@@ -167,7 +160,7 @@ test('Release patch, minor and major versions', async (t) => {
|
||||
// Retrieve the published package from the registry and check version and gitHead
|
||||
({
|
||||
'dist-tags': {latest: releasedVersion},
|
||||
} = await npmView(packageName, npmTestEnv));
|
||||
} = await npmView(packageName, testEnv));
|
||||
head = await gitHead({cwd});
|
||||
t.is(releasedVersion, version);
|
||||
t.is(await gitTagHead(`v${version}`, {cwd}), head);
|
||||
@@ -196,7 +189,7 @@ test('Release patch, minor and major versions', async (t) => {
|
||||
t.log('Commit a feature');
|
||||
await gitCommits(['feat: baz'], {cwd});
|
||||
t.log('$ semantic-release');
|
||||
({stdout, exitCode} = await execa(cli, [], {env, cwd, extendEnv: false}));
|
||||
({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(exitCode, 0);
|
||||
@@ -208,7 +201,7 @@ test('Release patch, minor and major versions', async (t) => {
|
||||
// Retrieve the published package from the registry and check version and gitHead
|
||||
({
|
||||
'dist-tags': {latest: releasedVersion},
|
||||
} = await npmView(packageName, npmTestEnv));
|
||||
} = await npmView(packageName, testEnv));
|
||||
head = await gitHead({cwd});
|
||||
t.is(releasedVersion, version);
|
||||
t.is(await gitTagHead(`v${version}`, {cwd}), head);
|
||||
@@ -239,7 +232,7 @@ test('Release patch, minor and major versions', async (t) => {
|
||||
await gitPush('origin', 'next', {cwd});
|
||||
await gitCommits(['feat: foo\n\n BREAKING CHANGE: bar'], {cwd});
|
||||
t.log('$ semantic-release');
|
||||
({stdout, exitCode} = await execa(cli, [], {env: {...env, TRAVIS_BRANCH: 'next'}, cwd, extendEnv: false}));
|
||||
({stdout, exitCode} = await execa(cli, [], {env: {...env, TRAVIS_BRANCH: 'next'}, 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(exitCode, 0);
|
||||
@@ -251,7 +244,7 @@ test('Release patch, minor and major versions', async (t) => {
|
||||
// Retrieve the published package from the registry and check version and gitHead
|
||||
({
|
||||
'dist-tags': {next: releasedVersion},
|
||||
} = await npmView(packageName, npmTestEnv));
|
||||
} = await npmView(packageName, testEnv));
|
||||
head = await gitHead({cwd});
|
||||
t.is(releasedVersion, version);
|
||||
t.is(await gitGetNote(`v${version}`, {cwd}), '{"channels":["next"]}');
|
||||
@@ -289,7 +282,7 @@ test('Release patch, minor and major versions', async (t) => {
|
||||
await merge('next', {cwd});
|
||||
await gitPush('origin', 'master', {cwd});
|
||||
t.log('$ semantic-release');
|
||||
({stdout, exitCode} = await execa(cli, [], {env, cwd, extendEnv: false}));
|
||||
({stdout, exitCode} = await execa(cli, [], {env, cwd}));
|
||||
t.regex(stdout, new RegExp(`Updated GitHub release: release-url/${version}`));
|
||||
t.regex(stdout, new RegExp(`Adding version ${version} to npm registry on dist-tag latest`));
|
||||
t.is(exitCode, 0);
|
||||
@@ -299,7 +292,7 @@ test('Release patch, minor and major versions', async (t) => {
|
||||
// Retrieve the published package from the registry and check version and gitHead
|
||||
({
|
||||
'dist-tags': {latest: releasedVersion},
|
||||
} = await npmView(packageName, npmTestEnv));
|
||||
} = await npmView(packageName, testEnv));
|
||||
t.is(releasedVersion, version);
|
||||
t.is(await gitGetNote(`v${version}`, {cwd}), '{"channels":["next",null]}');
|
||||
t.is(await gitTagHead(`v${version}`, {cwd}), await gitTagHead(`v${version}`, {cwd}));
|
||||
@@ -311,7 +304,7 @@ test('Release patch, minor and major versions', async (t) => {
|
||||
await mockServer.verify(updateReleaseMock);
|
||||
});
|
||||
|
||||
test('Exit with 1 if a plugin is not found', async (t) => {
|
||||
test('Exit with 1 if a plugin is not found', async t => {
|
||||
const packageName = 'test-plugin-not-found';
|
||||
const owner = 'test-repo';
|
||||
// Create a git repository, set the current working directory at the root of the repo
|
||||
@@ -324,12 +317,12 @@ test('Exit with 1 if a plugin is not found', async (t) => {
|
||||
release: {analyzeCommits: 'non-existing-path', success: false, fail: false},
|
||||
});
|
||||
|
||||
const {exitCode, stderr} = await t.throwsAsync(execa(cli, [], {env, cwd, extendEnv: false}));
|
||||
const {exitCode, stderr} = await t.throwsAsync(execa(cli, [], {env, cwd}));
|
||||
t.is(exitCode, 1);
|
||||
t.regex(stderr, /Cannot find module/);
|
||||
});
|
||||
|
||||
test('Exit with 1 if a shareable config is not found', async (t) => {
|
||||
test('Exit with 1 if a shareable config is not found', async t => {
|
||||
const packageName = 'test-config-not-found';
|
||||
const owner = 'test-repo';
|
||||
// Create a git repository, set the current working directory at the root of the repo
|
||||
@@ -342,12 +335,12 @@ test('Exit with 1 if a shareable config is not found', async (t) => {
|
||||
release: {extends: 'non-existing-path', success: false, fail: false},
|
||||
});
|
||||
|
||||
const {exitCode, stderr} = await t.throwsAsync(execa(cli, [], {env, cwd, extendEnv: false}));
|
||||
const {exitCode, stderr} = await t.throwsAsync(execa(cli, [], {env, cwd}));
|
||||
t.is(exitCode, 1);
|
||||
t.regex(stderr, /Cannot find module/);
|
||||
});
|
||||
|
||||
test('Exit with 1 if a shareable config reference a not found plugin', async (t) => {
|
||||
test('Exit with 1 if a shareable config reference a not found plugin', async t => {
|
||||
const packageName = 'test-config-ref-not-found';
|
||||
const owner = 'test-repo';
|
||||
const shareable = {analyzeCommits: 'non-existing-path'};
|
||||
@@ -363,12 +356,12 @@ test('Exit with 1 if a shareable config reference a not found plugin', async (t)
|
||||
});
|
||||
await writeJson(path.resolve(cwd, 'shareable.json'), shareable);
|
||||
|
||||
const {exitCode, stderr} = await t.throwsAsync(execa(cli, [], {env, cwd, extendEnv: false}));
|
||||
const {exitCode, stderr} = await t.throwsAsync(execa(cli, [], {env, cwd}));
|
||||
t.is(exitCode, 1);
|
||||
t.regex(stderr, /Cannot find module/);
|
||||
});
|
||||
|
||||
test('Dry-run', async (t) => {
|
||||
test('Dry-run', async t => {
|
||||
const packageName = 'test-dry-run';
|
||||
const owner = 'git';
|
||||
// Create a git repository, set the current working directory at the root of the repo
|
||||
@@ -393,7 +386,7 @@ test('Dry-run', async (t) => {
|
||||
t.log('Commit a feature');
|
||||
await gitCommits(['feat: Initial commit'], {cwd});
|
||||
t.log('$ semantic-release -d');
|
||||
const {stdout, exitCode} = await execa(cli, ['-d'], {env, cwd, extendEnv: false});
|
||||
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/);
|
||||
@@ -404,8 +397,9 @@ test('Dry-run', async (t) => {
|
||||
await mockServer.verify(verifyMock);
|
||||
});
|
||||
|
||||
test('Allow local releases with "noCi" option', async (t) => {
|
||||
test('Allow local releases with "noCi" option', async t => {
|
||||
const envNoCi = {...env};
|
||||
delete envNoCi.TRAVIS;
|
||||
delete envNoCi.CI;
|
||||
const packageName = 'test-no-ci';
|
||||
const owner = 'git';
|
||||
@@ -440,7 +434,7 @@ 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, exitCode} = await execa(cli, ['--no-ci'], {env: envNoCi, cwd, extendEnv: false});
|
||||
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(exitCode, 0);
|
||||
@@ -449,7 +443,7 @@ test('Allow local releases with "noCi" option', async (t) => {
|
||||
t.is((await readJson(path.resolve(cwd, 'package.json'))).version, version);
|
||||
|
||||
// Retrieve the published package from the registry and check version and gitHead
|
||||
const {version: releasedVersion, gitHead: releasedGitHead} = await npmView(packageName, npmTestEnv);
|
||||
const {version: releasedVersion, gitHead: releasedGitHead} = await npmView(packageName, testEnv);
|
||||
|
||||
const head = await gitHead({cwd});
|
||||
t.is(releasedVersion, version);
|
||||
@@ -462,7 +456,7 @@ test('Allow local releases with "noCi" option', async (t) => {
|
||||
await mockServer.verify(createReleaseMock);
|
||||
});
|
||||
|
||||
test('Pass options via CLI arguments', async (t) => {
|
||||
test('Pass options via CLI arguments', async t => {
|
||||
const packageName = 'test-cli';
|
||||
// Create a git repository, set the current working directory at the root of the repo
|
||||
t.log('Create git repository and package.json');
|
||||
@@ -493,7 +487,7 @@ test('Pass options via CLI arguments', async (t) => {
|
||||
false,
|
||||
'--debug',
|
||||
],
|
||||
{env, cwd, extendEnv: false}
|
||||
{env, cwd}
|
||||
);
|
||||
t.regex(stdout, new RegExp(`Publishing version ${version} to npm registry`));
|
||||
t.is(exitCode, 0);
|
||||
@@ -502,7 +496,7 @@ test('Pass options via CLI arguments', async (t) => {
|
||||
t.is((await readJson(path.resolve(cwd, 'package.json'))).version, version);
|
||||
|
||||
// Retrieve the published package from the registry and check version and gitHead
|
||||
const {version: releasedVersion, gitHead: releasedGitHead} = await npmView(packageName, npmTestEnv);
|
||||
const {version: releasedVersion, gitHead: releasedGitHead} = await npmView(packageName, testEnv);
|
||||
const head = await gitHead({cwd});
|
||||
t.is(releasedVersion, version);
|
||||
t.is(releasedGitHead, head);
|
||||
@@ -511,7 +505,7 @@ test('Pass options via CLI arguments', async (t) => {
|
||||
t.log(`+ released ${releasedVersion} with head ${releasedGitHead}`);
|
||||
});
|
||||
|
||||
test('Run via JS API', async (t) => {
|
||||
test('Run via JS API', async t => {
|
||||
const semanticRelease = requireNoCache('..', {
|
||||
'./lib/logger': {log: () => {}, error: () => {}, stdout: () => {}},
|
||||
'env-ci': () => ({isCi: true, branch: 'master', isPr: false}),
|
||||
@@ -558,7 +552,7 @@ test('Run via JS API', async (t) => {
|
||||
t.is((await readJson(path.resolve(cwd, 'package.json'))).version, version);
|
||||
|
||||
// Retrieve the published package from the registry and check version and gitHead
|
||||
const {version: releasedVersion, gitHead: releasedGitHead} = await npmView(packageName, npmTestEnv);
|
||||
const {version: releasedVersion, gitHead: releasedGitHead} = await npmView(packageName, testEnv);
|
||||
const head = await gitHead({cwd});
|
||||
t.is(releasedVersion, version);
|
||||
t.is(releasedGitHead, head);
|
||||
@@ -570,7 +564,7 @@ test('Run via JS API', async (t) => {
|
||||
await mockServer.verify(createReleaseMock);
|
||||
});
|
||||
|
||||
test('Log unexpected errors from plugins and exit with 1', async (t) => {
|
||||
test('Log unexpected errors from plugins and exit with 1', async t => {
|
||||
const packageName = 'test-unexpected-error';
|
||||
// Create a git repository, set the current working directory at the root of the repo
|
||||
t.log('Create git repository and package.json');
|
||||
@@ -587,7 +581,7 @@ 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, exitCode} = await execa(cli, [], {env, cwd, reject: false, extendEnv: 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
|
||||
@@ -597,7 +591,7 @@ test('Log unexpected errors from plugins and exit with 1', async (t) => {
|
||||
t.is(exitCode, 1);
|
||||
});
|
||||
|
||||
test('Log errors inheriting SemanticReleaseError and exit with 1', async (t) => {
|
||||
test('Log errors inheriting SemanticReleaseError and exit with 1', async t => {
|
||||
const packageName = 'test-inherited-error';
|
||||
// Create a git repository, set the current working directory at the root of the repo
|
||||
t.log('Create git repository and package.json');
|
||||
@@ -614,13 +608,13 @@ 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, exitCode} = await execa(cli, [], {env, cwd, reject: false, extendEnv: 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(exitCode, 1);
|
||||
});
|
||||
|
||||
test('Exit with 1 if missing permission to push to the remote repository', async (t) => {
|
||||
test('Exit with 1 if missing permission to push to the remote repository', async t => {
|
||||
const packageName = 'unauthorized';
|
||||
// Create a git repository, set the current working directory at the root of the repo
|
||||
t.log('Create git repository');
|
||||
@@ -635,14 +629,14 @@ test('Exit with 1 if missing permission to push to the remote repository', async
|
||||
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, extendEnv: false}
|
||||
{env: {...env, GH_TOKEN: 'user:wrong_pass'}, cwd, reject: false}
|
||||
);
|
||||
// Verify the type and message are logged
|
||||
t.regex(stderr, /EGITNOPERMISSION/);
|
||||
t.is(exitCode, 1);
|
||||
});
|
||||
|
||||
test('Hide sensitive environment variable values from the logs', async (t) => {
|
||||
test('Hide sensitive environment variable values from the logs', async t => {
|
||||
const packageName = 'log-secret';
|
||||
// Create a git repository, set the current working directory at the root of the repo
|
||||
t.log('Create git repository');
|
||||
@@ -655,55 +649,10 @@ test('Hide sensitive environment variable values from the logs', async (t) => {
|
||||
});
|
||||
|
||||
t.log('$ semantic-release');
|
||||
const {stdout, stderr} = await execa(cli, [], {
|
||||
env: {...env, MY_TOKEN: 'secret token'},
|
||||
cwd,
|
||||
reject: false,
|
||||
extendEnv: false,
|
||||
});
|
||||
const {stdout, stderr} = await execa(cli, [], {env: {...env, MY_TOKEN: 'secret token'}, cwd, reject: false});
|
||||
|
||||
t.regex(stdout, new RegExp(`Console: Exposing token ${escapeRegExp(SECRET_REPLACEMENT)}`));
|
||||
t.regex(stdout, new RegExp(`Log: Exposing token ${escapeRegExp(SECRET_REPLACEMENT)}`));
|
||||
t.regex(stderr, new RegExp(`Error: Console token ${escapeRegExp(SECRET_REPLACEMENT)}`));
|
||||
t.regex(stderr, new RegExp(`Throw error: Exposing ${escapeRegExp(SECRET_REPLACEMENT)}`));
|
||||
});
|
||||
|
||||
test('Use the valid git credentials when multiple are provided', async (t) => {
|
||||
const {cwd, authUrl} = await gitbox.createRepo('test-auth');
|
||||
|
||||
t.is(
|
||||
await getAuthUrl({
|
||||
cwd,
|
||||
env: {
|
||||
GITHUB_TOKEN: 'dummy',
|
||||
GITLAB_TOKEN: 'trash',
|
||||
BB_TOKEN_BASIC_AUTH: gitbox.gitCredential,
|
||||
GIT_ASKPASS: 'echo',
|
||||
GIT_TERMINAL_PROMPT: 0,
|
||||
},
|
||||
branch: {name: 'master'},
|
||||
options: {repositoryUrl: 'http://toto@localhost:2080/git/test-auth.git'},
|
||||
}),
|
||||
authUrl
|
||||
);
|
||||
});
|
||||
|
||||
test('Use the repository URL as is if none of the given git credentials are valid', async (t) => {
|
||||
const {cwd} = await gitbox.createRepo('test-invalid-auth');
|
||||
const dummyUrl = 'http://toto@localhost:2080/git/test-invalid-auth.git';
|
||||
|
||||
t.is(
|
||||
await getAuthUrl({
|
||||
cwd,
|
||||
env: {
|
||||
GITHUB_TOKEN: 'dummy',
|
||||
GITLAB_TOKEN: 'trash',
|
||||
GIT_ASKPASS: 'echo',
|
||||
GIT_TERMINAL_PROMPT: 0,
|
||||
},
|
||||
branch: {name: 'master'},
|
||||
options: {repositoryUrl: dummyUrl},
|
||||
}),
|
||||
dummyUrl
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1,17 +1,16 @@
|
||||
import test from 'ava';
|
||||
import lodash from 'lodash'
|
||||
const {noop} = lodash
|
||||
import sinon from 'sinon';
|
||||
import normalize from '../../lib/plugins/normalize.js';
|
||||
const test = require('ava');
|
||||
const {noop} = require('lodash');
|
||||
const {stub} = require('sinon');
|
||||
const normalize = require('../../lib/plugins/normalize');
|
||||
|
||||
const cwd = process.cwd();
|
||||
|
||||
test.beforeEach((t) => {
|
||||
test.beforeEach(t => {
|
||||
// Stub the logger functions
|
||||
t.context.log = sinon.stub);
|
||||
t.context.error = sinon.stub);
|
||||
t.context.success = sinon.stub);
|
||||
t.context.stderr = {write: sinon.stub)};
|
||||
t.context.log = stub();
|
||||
t.context.error = stub();
|
||||
t.context.success = stub();
|
||||
t.context.stderr = {write: stub()};
|
||||
t.context.logger = {
|
||||
log: t.context.log,
|
||||
error: t.context.error,
|
||||
@@ -20,7 +19,7 @@ test.beforeEach((t) => {
|
||||
};
|
||||
});
|
||||
|
||||
test('Normalize and load plugin from string', (t) => {
|
||||
test('Normalize and load plugin from string', t => {
|
||||
const plugin = normalize(
|
||||
{cwd, options: {}, logger: t.context.logger},
|
||||
'verifyConditions',
|
||||
@@ -33,7 +32,7 @@ test('Normalize and load plugin from string', (t) => {
|
||||
t.deepEqual(t.context.success.args[0], ['Loaded plugin "verifyConditions" from "./test/fixtures/plugin-noop"']);
|
||||
});
|
||||
|
||||
test('Normalize and load plugin from object', (t) => {
|
||||
test('Normalize and load plugin from object', t => {
|
||||
const plugin = normalize(
|
||||
{cwd, options: {}, logger: t.context.logger},
|
||||
'publish',
|
||||
@@ -46,7 +45,7 @@ test('Normalize and load plugin from object', (t) => {
|
||||
t.deepEqual(t.context.success.args[0], ['Loaded plugin "publish" from "./test/fixtures/plugin-noop"']);
|
||||
});
|
||||
|
||||
test('Normalize and load plugin from a base file path', (t) => {
|
||||
test('Normalize and load plugin from a base file path', t => {
|
||||
const plugin = normalize({cwd, options: {}, logger: t.context.logger}, 'verifyConditions', './plugin-noop', {
|
||||
'./plugin-noop': './test/fixtures',
|
||||
});
|
||||
@@ -58,7 +57,7 @@ test('Normalize and load plugin from a base file path', (t) => {
|
||||
]);
|
||||
});
|
||||
|
||||
test('Wrap plugin in a function that add the "pluginName" to the error"', async (t) => {
|
||||
test('Wrap plugin in a function that add the "pluginName" to the error"', async t => {
|
||||
const plugin = normalize({cwd, options: {}, logger: t.context.logger}, 'verifyConditions', './plugin-error', {
|
||||
'./plugin-error': './test/fixtures',
|
||||
});
|
||||
@@ -68,7 +67,7 @@ test('Wrap plugin in a function that add the "pluginName" to the error"', async
|
||||
t.is(error.pluginName, './plugin-error');
|
||||
});
|
||||
|
||||
test('Wrap plugin in a function that add the "pluginName" to multiple errors"', async (t) => {
|
||||
test('Wrap plugin in a function that add the "pluginName" to multiple errors"', async t => {
|
||||
const plugin = normalize({cwd, options: {}, logger: t.context.logger}, 'verifyConditions', './plugin-errors', {
|
||||
'./plugin-errors': './test/fixtures',
|
||||
});
|
||||
@@ -79,7 +78,7 @@ test('Wrap plugin in a function that add the "pluginName" to multiple errors"',
|
||||
}
|
||||
});
|
||||
|
||||
test('Normalize and load plugin from function', (t) => {
|
||||
test('Normalize and load plugin from function', t => {
|
||||
const pluginFunction = () => {};
|
||||
const plugin = normalize({cwd, options: {}, logger: t.context.logger}, '', pluginFunction, {});
|
||||
|
||||
@@ -87,7 +86,7 @@ test('Normalize and load plugin from function', (t) => {
|
||||
t.is(typeof plugin, 'function');
|
||||
});
|
||||
|
||||
test('Normalize and load plugin that retuns multiple functions', (t) => {
|
||||
test('Normalize and load plugin that retuns multiple functions', t => {
|
||||
const plugin = normalize(
|
||||
{cwd, options: {}, logger: t.context.logger},
|
||||
'verifyConditions',
|
||||
@@ -99,8 +98,8 @@ test('Normalize and load plugin that retuns multiple functions', (t) => {
|
||||
t.deepEqual(t.context.success.args[0], ['Loaded plugin "verifyConditions" from "./test/fixtures/multi-plugin"']);
|
||||
});
|
||||
|
||||
test('Wrap "analyzeCommits" plugin in a function that validate the output of the plugin', async (t) => {
|
||||
const analyzeCommits = sinon.stub).resolves(2);
|
||||
test('Wrap "analyzeCommits" plugin in a function that validate the output of the plugin', async t => {
|
||||
const analyzeCommits = stub().resolves(2);
|
||||
const plugin = normalize(
|
||||
{cwd, options: {}, stderr: t.context.stderr, logger: t.context.logger},
|
||||
'analyzeCommits',
|
||||
@@ -117,8 +116,8 @@ test('Wrap "analyzeCommits" plugin in a function that validate the output of the
|
||||
t.regex(error.details, /2/);
|
||||
});
|
||||
|
||||
test('Wrap "generateNotes" plugin in a function that validate the output of the plugin', async (t) => {
|
||||
const generateNotes = sinon.stub).resolves(2);
|
||||
test('Wrap "generateNotes" plugin in a function that validate the output of the plugin', async t => {
|
||||
const generateNotes = stub().resolves(2);
|
||||
const plugin = normalize(
|
||||
{cwd, options: {}, stderr: t.context.stderr, logger: t.context.logger},
|
||||
'generateNotes',
|
||||
@@ -135,8 +134,8 @@ test('Wrap "generateNotes" plugin in a function that validate the output of the
|
||||
t.regex(error.details, /2/);
|
||||
});
|
||||
|
||||
test('Wrap "publish" plugin in a function that validate the output of the plugin', async (t) => {
|
||||
const publish = sinon.stub).resolves(2);
|
||||
test('Wrap "publish" plugin in a function that validate the output of the plugin', async t => {
|
||||
const publish = stub().resolves(2);
|
||||
const plugin = normalize(
|
||||
{cwd, options: {}, stderr: t.context.stderr, logger: t.context.logger},
|
||||
'publish',
|
||||
@@ -153,8 +152,8 @@ test('Wrap "publish" plugin in a function that validate the output of the plugin
|
||||
t.regex(error.details, /2/);
|
||||
});
|
||||
|
||||
test('Wrap "addChannel" plugin in a function that validate the output of the plugin', async (t) => {
|
||||
const addChannel = sinon.stub).resolves(2);
|
||||
test('Wrap "addChannel" plugin in a function that validate the output of the plugin', async t => {
|
||||
const addChannel = stub().resolves(2);
|
||||
const plugin = normalize(
|
||||
{cwd, options: {}, stderr: t.context.stderr, logger: t.context.logger},
|
||||
'addChannel',
|
||||
@@ -171,8 +170,8 @@ test('Wrap "addChannel" plugin in a function that validate the output of the plu
|
||||
t.regex(error.details, /2/);
|
||||
});
|
||||
|
||||
test('Plugin is called with "pluginConfig" (with object definition) and input', async (t) => {
|
||||
const pluginFunction = sinon.stub).resolves();
|
||||
test('Plugin is called with "pluginConfig" (with object definition) and input', async t => {
|
||||
const pluginFunction = stub().resolves();
|
||||
const pluginConf = {path: pluginFunction, conf: 'confValue'};
|
||||
const options = {global: 'globalValue'};
|
||||
const plugin = normalize({cwd, options, logger: t.context.logger}, '', pluginConf, {});
|
||||
@@ -186,8 +185,8 @@ test('Plugin is called with "pluginConfig" (with object definition) and input',
|
||||
);
|
||||
});
|
||||
|
||||
test('Plugin is called with "pluginConfig" (with array definition) and input', async (t) => {
|
||||
const pluginFunction = sinon.stub).resolves();
|
||||
test('Plugin is called with "pluginConfig" (with array definition) and input', async t => {
|
||||
const pluginFunction = stub().resolves();
|
||||
const pluginConf = [pluginFunction, {conf: 'confValue'}];
|
||||
const options = {global: 'globalValue'};
|
||||
const plugin = normalize({cwd, options, logger: t.context.logger}, '', pluginConf, {});
|
||||
@@ -201,8 +200,8 @@ test('Plugin is called with "pluginConfig" (with array definition) and input', a
|
||||
);
|
||||
});
|
||||
|
||||
test('Prevent plugins to modify "pluginConfig"', async (t) => {
|
||||
const pluginFunction = sinon.stub).callsFake((pluginConfig) => {
|
||||
test('Prevent plugins to modify "pluginConfig"', async t => {
|
||||
const pluginFunction = stub().callsFake(pluginConfig => {
|
||||
pluginConfig.conf.subConf = 'otherConf';
|
||||
});
|
||||
const pluginConf = {path: pluginFunction, conf: {subConf: 'originalConf'}};
|
||||
@@ -214,8 +213,8 @@ test('Prevent plugins to modify "pluginConfig"', async (t) => {
|
||||
t.is(options.globalConf.globalSubConf, 'originalGlobalConf');
|
||||
});
|
||||
|
||||
test('Prevent plugins to modify its input', async (t) => {
|
||||
const pluginFunction = sinon.stub).callsFake((pluginConfig, options) => {
|
||||
test('Prevent plugins to modify its input', async t => {
|
||||
const pluginFunction = stub().callsFake((pluginConfig, options) => {
|
||||
options.param.subParam = 'otherParam';
|
||||
});
|
||||
const input = {param: {subParam: 'originalSubParam'}, options: {}};
|
||||
@@ -225,13 +224,13 @@ test('Prevent plugins to modify its input', async (t) => {
|
||||
t.is(input.param.subParam, 'originalSubParam');
|
||||
});
|
||||
|
||||
test('Return noop if the plugin is not defined', (t) => {
|
||||
test('Return noop if the plugin is not defined', t => {
|
||||
const plugin = normalize({cwd, options: {}, logger: t.context.logger});
|
||||
|
||||
t.is(plugin, noop);
|
||||
});
|
||||
|
||||
test('Always pass a defined "pluginConfig" for plugin defined with string', async (t) => {
|
||||
test('Always pass a defined "pluginConfig" for plugin defined with string', async t => {
|
||||
// Call the normalize function with the path of a plugin that returns its config
|
||||
const plugin = normalize(
|
||||
{cwd, options: {}, logger: t.context.logger},
|
||||
@@ -244,7 +243,7 @@ test('Always pass a defined "pluginConfig" for plugin defined with string', asyn
|
||||
t.deepEqual(pluginResult.pluginConfig, {});
|
||||
});
|
||||
|
||||
test('Always pass a defined "pluginConfig" for plugin defined with path', async (t) => {
|
||||
test('Always pass a defined "pluginConfig" for plugin defined with path', async t => {
|
||||
// Call the normalize function with the path of a plugin that returns its config
|
||||
const plugin = normalize(
|
||||
{cwd, options: {}, logger: t.context.logger},
|
||||
@@ -257,7 +256,7 @@ test('Always pass a defined "pluginConfig" for plugin defined with path', async
|
||||
t.deepEqual(pluginResult.pluginConfig, {});
|
||||
});
|
||||
|
||||
test('Throws an error if the plugin return an object without the expected plugin function', (t) => {
|
||||
test('Throws an error if the plugin return an object without the expected plugin function', t => {
|
||||
const error = t.throws(() =>
|
||||
normalize({cwd, options: {}, logger: t.context.logger}, 'inexistantPlugin', './test/fixtures/multi-plugin', {})
|
||||
);
|
||||
@@ -268,7 +267,7 @@ test('Throws an error if the plugin return an object without the expected plugin
|
||||
t.truthy(error.details);
|
||||
});
|
||||
|
||||
test('Throws an error if the plugin is not found', (t) => {
|
||||
test('Throws an error if the plugin is not found', t => {
|
||||
t.throws(() => normalize({cwd, options: {}, logger: t.context.logger}, 'inexistantPlugin', 'non-existing-path', {}), {
|
||||
message: /Cannot find module 'non-existing-path'/,
|
||||
code: 'MODULE_NOT_FOUND',
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user