Compare commits

..
11 Commits
Author SHA1 Message Date
greenkeeper[bot]andPierre Vanduynslager d28b7e3e07 fix(package): update @semantic-release/github to version 3.0.0 2017-12-22 13:26:00 -05:00
greenkeeper[bot]andPierre Vanduynslager e00dd160c7 chore(package): update @semantic-release/github to version 2.2.3 2017-12-17 00:25:54 -05:00
Pierre Vanduynslager 3316f1a79e test: add a delay after starting the npm registry
Attempt at improving test reliability on CI
2017-12-14 11:44:24 -05:00
Pierre Vanduynslager 8942093c18 test: limit information stored in context
Store only objects set in `beforeEach` used in `test`.
Avoid logging useless info on test failure.
2017-12-14 11:44:24 -05:00
greenkeeper[bot]andPierre Vanduynslager 1b8aa95a6e chore(package): update prettier to version 1.9.2 2017-12-12 00:01:12 -05:00
greenkeeper[bot]andPierre Vanduynslager 624ee88abd chore(package): update fs-extra to version 5.0.0 2017-12-11 15:02:29 -05:00
Pierre Vanduynslager 4053d8f7fb fix: log plugin type in addition of path 2017-12-10 04:03:32 -05:00
greenkeeper[bot]andPierre Vanduynslager 88f33ec482 chore(package): update mockserver-client to version 5.1.1 2017-12-06 16:29:19 -05:00
Pierre Vanduynslager 0761ef378a style: update to prettier@1.9.0 2017-12-05 10:03:18 -05:00
greenkeeper[bot]andPierre Vanduynslager f30fb6c8af chore(package): update prettier to version 1.9.0 2017-12-05 10:03:18 -05:00
Hutson BettsandGregor Martynus 0113db2e06 docs(README): add node support policy
Add a _Node Support Policy_ section to the project's `README.md` file
to indicate what this project, and its core team, are promising in terms
of Node runtime support.

Establishing a support policy provides reassurance to the community that
they can expect a level of functionality from `semantic-release`, while
providing guidance to the core maintainers, and everyone else that
contributes, the level of support they should espire to.

Closes #485
2017-12-01 15:39:21 -08:00
13 changed files with 169 additions and 163 deletions
+16
View File
@@ -287,6 +287,8 @@ Being able to write code for just the most recent node versions greatly simplifi
For a special purpose tool like `semantic-release`, that's only meant to be used in controlled CI environments, we think it's okay to have such a high version requirement. As `semantic-release` handles package publishing we expect almost every project to have at least one build job running node 8 already – and that's all it takes. Even if that's not that case `semantic-release` can still be executed with the help of [npx](https://www.npmjs.com/package/npx) (`npx -p node@8 npm run semantic-release`).
Please see our [Node Support Policy](#node-support-policy) for our long-term promise for supporting Node.
## Badge
Use this in one of your projects? Include one of these badges in your README.md to let people know that your package is published using `semantic-release`.
@@ -309,6 +311,20 @@ Use this in one of your projects? Include one of these badges in your README.md
[![semantic-release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg?style=flat-square)](https://github.com/semantic-release/semantic-release)
```
## Node Support Policy
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.
It's possible this package will work correctly on newer versions of Node. It may even be possible to use this package on older versions of Node, though that's more unlikely as we'll make every effort to take advantage of features available in the oldest LTS version we support.
As each Node LTS version reaches its end-of-life we will remove that version from the node engines property of our package's package.json file. Removing a Node version is considered a breaking change and will entail the publishing of a new major version of this package. We will not accept any requests to support an end-of-life version of Node. Any merge requests or issues supporting an end-of-life version of Node will be closed.
We will accept code that allows this package to run on newer, non-LTS, versions of Node. Furthermore, we will attempt to ensure our own changes work on the latest version of Node. To help in that commitment, our continuous integration setup runs against all LTS versions of Node in addition the most recent Node release; called current.
JavaScript package managers should allow you to install this package with any version of Node, with, at most, a warning if your version of Node does not fall within the range specified by our node engines property. If you encounter issues installing this package, please report the issue to your package manager.
## License
MIT License
+3 -9
View File
@@ -84,24 +84,18 @@ function noGitHeadMessage(branch, version) {
This means semantic-release can not extract the commits between now and then.
This is usually caused by releasing from outside the repository directory or with innaccessible git metadata.
You can recover from this error by creating a tag for the version "${
version
}" on the commit corresponding to this release:
You can recover from this error by creating a tag for the version "${version}" on the commit corresponding to this release:
$ git tag -f v${version} <commit sha1 corresponding to last release>
$ git push -f --tags origin ${branch}
`;
}
function notInHistoryMessage(gitHead, branch, version) {
return `The commit the last release of this package was derived from is not in the direct history of the "${
branch
}" branch.
return `The commit the last release of this package was derived from is not in the direct history of the "${branch}" branch.
This means semantic-release can not extract the commits between now and then.
This is usually caused by force pushing, releasing from an unrelated branch, or using an already existing package name.
You can recover from this error by restoring the commit "${gitHead}" or by creating a tag for the version "${
version
}" on the commit corresponding to this release:
You can recover from this error by restoring the commit "${gitHead}" or by creating a tag for the version "${version}" on the commit corresponding to this release:
$ git tag -f v${version || '<version>'} <commit sha1 corresponding to last release>
$ git push -f --tags origin ${branch}
`;
+1 -1
View File
@@ -8,7 +8,7 @@ module.exports = (pluginType, pluginConfig, logger, validator) => {
}
const {path, ...config} = isString(pluginConfig) || isFunction(pluginConfig) ? {path: pluginConfig} : pluginConfig;
if (!isFunction(pluginConfig)) {
logger.log('Load plugin %s', path);
logger.log('Load plugin %s from %s', pluginType, path);
}
const plugin = isFunction(path) ? path : importFrom.silent(__dirname, path) || importFrom(process.cwd(), path);
+5 -4
View File
@@ -18,7 +18,7 @@
"@semantic-release/commit-analyzer": "^5.0.0",
"@semantic-release/condition-travis": "^7.0.0",
"@semantic-release/error": "^2.1.0",
"@semantic-release/github": "^2.0.0",
"@semantic-release/github": "^3.0.0",
"@semantic-release/npm": "^2.0.0",
"@semantic-release/release-notes-generator": "^6.0.0",
"chalk": "^2.3.0",
@@ -41,18 +41,19 @@
"codecov": "^3.0.0",
"commitizen": "^2.9.6",
"cz-conventional-changelog": "^2.0.0",
"delay": "^2.0.0",
"dockerode": "^2.5.2",
"eslint-config-prettier": "^2.5.0",
"eslint-plugin-prettier": "^2.3.0",
"file-url": "^2.0.2",
"fs-extra": "^4.0.2",
"fs-extra": "^5.0.0",
"got": "^8.0.0",
"js-yaml": "^3.10.0",
"mockserver-client": "^2.0.0",
"mockserver-client": "^5.1.1",
"nock": "^9.0.2",
"nyc": "^11.2.1",
"p-retry": "^1.0.0",
"prettier": "~1.8.0",
"prettier": "~1.9.2",
"proxyquire": "^1.8.0",
"sinon": "^4.0.0",
"tempy": "^0.2.1",
+14 -13
View File
@@ -12,18 +12,19 @@ import {
gitDetachedHead,
} from './helpers/git-utils';
// Save the current working diretory
const cwd = process.cwd();
test.beforeEach(t => {
// Save the current working diretory
t.context.cwd = process.cwd();
// Stub the logger functions
t.context.log = stub();
t.context.error = stub();
t.context.logger = {log: t.context.log, error: t.context.error};
});
test.afterEach.always(t => {
test.afterEach.always(() => {
// Restore the current working directory
process.chdir(t.context.cwd);
process.chdir(cwd);
});
test.serial('Get all commits when there is no last release', async t => {
@@ -411,7 +412,7 @@ test.serial('Throws ENOGITHEAD error if the gitHead of the last release cannot b
t.is(error.name, 'SemanticReleaseError');
// Verify the log function has been called with a message explaining the error
t.regex(
t.context.error.firstCall.args[0],
t.context.error.args[0][0],
/The commit the last release of this package was derived from cannot be determined from the release metadata nor from the repository tags/
);
});
@@ -429,9 +430,9 @@ test.serial('Throws ENOTINHISTORY error if gitHead is not in history', async t =
t.is(error.code, 'ENOTINHISTORY');
t.is(error.name, 'SemanticReleaseError');
// Verify the log function has been called with a message mentionning the branch
t.regex(t.context.error.firstCall.args[0], /history of the "master" branch/);
t.regex(t.context.error.args[0][0], /history of the "master" branch/);
// Verify the log function has been called with a message mentionning the missing gitHead
t.regex(t.context.error.firstCall.args[0], /restoring the commit "notinhistory"/);
t.regex(t.context.error.args[0][0], /restoring the commit "notinhistory"/);
});
test.serial('Throws ENOTINHISTORY error if gitHead is not in branch history but present in others', async t => {
@@ -454,9 +455,9 @@ test.serial('Throws ENOTINHISTORY error if gitHead is not in branch history but
t.is(error.code, 'ENOTINHISTORY');
t.is(error.name, 'SemanticReleaseError');
// Verify the log function has been called with a message mentionning the branch
t.regex(t.context.error.firstCall.args[0], /history of the "master" branch/);
t.regex(t.context.error.args[0][0], /history of the "master" branch/);
// Verify the log function has been called with a message mentionning the missing gitHead
t.regex(t.context.error.firstCall.args[0], new RegExp(`restoring the commit "${commitsBranch[0].hash}"`));
t.regex(t.context.error.args[0][0], new RegExp(`restoring the commit "${commitsBranch[0].hash}"`));
});
test.serial('Throws ENOTINHISTORY error if gitHead is not in detached head but present in other branch', async t => {
@@ -483,9 +484,9 @@ test.serial('Throws ENOTINHISTORY error if gitHead is not in detached head but p
t.is(error.code, 'ENOTINHISTORY');
t.is(error.name, 'SemanticReleaseError');
// Verify the log function has been called with a message mentionning the branch
t.regex(t.context.error.firstCall.args[0], /history of the "master" branch/);
t.regex(t.context.error.args[0][0], /history of the "master" branch/);
// Verify the log function has been called with a message mentionning the missing gitHead
t.regex(t.context.error.firstCall.args[0], new RegExp(`restoring the commit "${commitsBranch[0].hash}"`));
t.regex(t.context.error.args[0][0], new RegExp(`restoring the commit "${commitsBranch[0].hash}"`));
});
test.serial('Throws ENOTINHISTORY error when a tag is not in branch history but present in others', async t => {
@@ -509,7 +510,7 @@ test.serial('Throws ENOTINHISTORY error when a tag is not in branch history but
t.is(error.code, 'ENOTINHISTORY');
t.is(error.name, 'SemanticReleaseError');
// Verify the log function has been called with a message mentionning the branch
t.regex(t.context.error.firstCall.args[0], /history of the "master" branch/);
t.regex(t.context.error.args[0][0], /history of the "master" branch/);
// Verify the log function has been called with a message mentionning the missing gitHead
t.regex(t.context.error.firstCall.args[0], new RegExp(`restoring the commit "${shaTag}"`));
t.regex(t.context.error.args[0][0], new RegExp(`restoring the commit "${shaTag}"`));
});
+15 -14
View File
@@ -5,20 +5,21 @@ import {stub} from 'sinon';
import yaml from 'js-yaml';
import {gitRepo, gitCommits, gitShallowClone, gitAddConfig} from './helpers/git-utils';
// Save the current process.env
const envBackup = Object.assign({}, process.env);
// Save the current working diretory
const cwd = process.cwd();
test.beforeEach(t => {
// Save the current process.env
t.context.env = Object.assign({}, process.env);
// Save the current working diretory
t.context.cwd = process.cwd();
t.context.plugins = stub().returns({});
t.context.getConfig = proxyquire('../lib/get-config', {'./plugins': t.context.plugins});
});
test.afterEach.always(t => {
// Restore the current working directory
process.chdir(t.context.cwd);
test.afterEach.always(() => {
// Restore process.env
process.env = Object.assign({}, t.context.env);
process.env = envBackup;
// Restore the current working directory
process.chdir(cwd);
});
test.serial('Default values, reading repositoryUrl from package.json', async t => {
@@ -84,7 +85,7 @@ test.serial('Read options from package.json', async t => {
// Verify the options contains the plugin config from package.json
t.deepEqual(options, release);
// Verify the plugins module is called with the plugin options from package.json
t.deepEqual(t.context.plugins.firstCall.args[0], release);
t.deepEqual(t.context.plugins.args[0][0], release);
});
test.serial('Read options from .releaserc.yml', async t => {
@@ -104,7 +105,7 @@ test.serial('Read options from .releaserc.yml', async t => {
// Verify the options contains the plugin config from package.json
t.deepEqual(options, release);
// Verify the plugins module is called with the plugin options from package.json
t.deepEqual(t.context.plugins.firstCall.args[0], release);
t.deepEqual(t.context.plugins.args[0][0], release);
});
test.serial('Read options from .releaserc.json', async t => {
@@ -124,7 +125,7 @@ test.serial('Read options from .releaserc.json', async t => {
// Verify the options contains the plugin config from package.json
t.deepEqual(options, release);
// Verify the plugins module is called with the plugin options from package.json
t.deepEqual(t.context.plugins.firstCall.args[0], release);
t.deepEqual(t.context.plugins.args[0][0], release);
});
test.serial('Read options from .releaserc.js', async t => {
@@ -144,7 +145,7 @@ test.serial('Read options from .releaserc.js', async t => {
// Verify the options contains the plugin config from package.json
t.deepEqual(options, release);
// Verify the plugins module is called with the plugin options from package.json
t.deepEqual(t.context.plugins.firstCall.args[0], release);
t.deepEqual(t.context.plugins.args[0][0], release);
});
test.serial('Read options from release.config.js', async t => {
@@ -164,7 +165,7 @@ test.serial('Read options from release.config.js', async t => {
// Verify the options contains the plugin config from package.json
t.deepEqual(options, release);
// Verify the plugins module is called with the plugin options from package.json
t.deepEqual(t.context.plugins.firstCall.args[0], release);
t.deepEqual(t.context.plugins.args[0][0], release);
});
test.serial('Prioritise cli parameters over file configuration and git repo', async t => {
@@ -191,5 +192,5 @@ test.serial('Prioritise cli parameters over file configuration and git repo', as
// Verify the options contains the plugin config from cli
t.deepEqual(result.options, options);
// Verify the plugins module is called with the plugin options from cli
t.deepEqual(t.context.plugins.firstCall.args[0], options);
t.deepEqual(t.context.plugins.args[0][0], options);
});
+4 -6
View File
@@ -11,14 +11,12 @@ import {
gitAddConfig,
} from './helpers/git-utils';
test.beforeEach(t => {
// Save the current working diretory
t.context.cwd = process.cwd();
});
// Save the current working diretory
const cwd = process.cwd();
test.afterEach.always(t => {
test.afterEach.always(() => {
// Restore the current working directory
process.chdir(t.context.cwd);
process.chdir(cwd);
});
test.serial('Get the last commit sha', async t => {
+5 -3
View File
@@ -7,8 +7,10 @@ import nock from 'nock';
* @param {String} [githubUrl='https://api.github.com'] The url on which to intercept http requests.
* @return {Object} A `nock` object ready to respond to a github authentication request.
*/
export default function authenticate(
{githubToken = 'GH_TOKEN', githubUrl = 'https://api.github.com', githubApiPathPrefix = ''} = {}
) {
export default function authenticate({
githubToken = 'GH_TOKEN',
githubUrl = 'https://api.github.com',
githubApiPathPrefix = '',
} = {}) {
return nock(`${githubUrl}/${githubApiPathPrefix}`, {reqheaders: {Authorization: `token ${githubToken}`}});
}
+2
View File
@@ -1,6 +1,7 @@
import Docker from 'dockerode';
import getStream from 'get-stream';
import got from 'got';
import delay from 'delay';
import pRetry from 'p-retry';
const IMAGE = 'npmjs/npm-docker-couchdb:1.6.1';
@@ -28,6 +29,7 @@ async function start() {
});
await container.start();
await delay(3000);
try {
// Wait for the registry to be ready
+66 -68
View File
@@ -6,29 +6,27 @@ import DEFINITIONS from '../lib/plugins/definitions';
import {gitHead as getGitHead} from '../lib/git';
import {gitRepo, gitCommits, gitTagVersion} from './helpers/git-utils';
// Save the current process.env
const envBackup = Object.assign({}, process.env);
// Save the current working diretory
const cwd = process.cwd();
stub(process.stdout, 'write');
stub(process.stderr, 'write');
test.beforeEach(t => {
// Save the current process.env
t.context.env = Object.assign({}, process.env);
// Save the current working diretory
t.context.cwd = process.cwd();
// Stub the logger functions
t.context.log = stub();
t.context.error = stub();
t.context.logger = {log: t.context.log, error: t.context.error};
t.context.semanticRelease = proxyquire('../index', {'./lib/logger': t.context.logger});
t.context.stdout = stub(process.stdout, 'write');
t.context.stderr = stub(process.stderr, 'write');
});
test.afterEach.always(t => {
test.afterEach.always(() => {
// Restore process.env
process.env = Object.assign({}, t.context.env);
process.env = envBackup;
// Restore the current working directory
process.chdir(t.context.cwd);
t.context.stdout.restore();
t.context.stderr.restore();
process.chdir(cwd);
});
test.serial('Plugins are called with expected values', async t => {
@@ -65,44 +63,44 @@ test.serial('Plugins are called with expected values', async t => {
await t.context.semanticRelease(options);
t.true(verifyConditions1.calledOnce);
t.deepEqual(verifyConditions1.firstCall.args[1], {options, logger: t.context.logger});
t.true(verifyConditions2.calledOnce);
t.deepEqual(verifyConditions2.firstCall.args[1], {options, logger: t.context.logger});
t.is(verifyConditions1.callCount, 1);
t.deepEqual(verifyConditions1.args[0][1], {options, logger: t.context.logger});
t.is(verifyConditions2.callCount, 1);
t.deepEqual(verifyConditions2.args[0][1], {options, logger: t.context.logger});
t.true(getLastRelease.calledOnce);
t.deepEqual(getLastRelease.firstCall.args[1], {options, logger: t.context.logger});
t.is(getLastRelease.callCount, 1);
t.deepEqual(getLastRelease.args[0][1], {options, logger: t.context.logger});
t.true(analyzeCommits.calledOnce);
t.deepEqual(analyzeCommits.firstCall.args[1].options, options);
t.deepEqual(analyzeCommits.firstCall.args[1].logger, t.context.logger);
t.deepEqual(analyzeCommits.firstCall.args[1].lastRelease, lastRelease);
t.deepEqual(analyzeCommits.firstCall.args[1].commits[0].hash.substring(0, 7), commits[0].hash);
t.deepEqual(analyzeCommits.firstCall.args[1].commits[0].message, commits[0].message);
t.is(analyzeCommits.callCount, 1);
t.deepEqual(analyzeCommits.args[0][1].options, options);
t.deepEqual(analyzeCommits.args[0][1].logger, t.context.logger);
t.deepEqual(analyzeCommits.args[0][1].lastRelease, lastRelease);
t.deepEqual(analyzeCommits.args[0][1].commits[0].hash.substring(0, 7), commits[0].hash);
t.deepEqual(analyzeCommits.args[0][1].commits[0].message, commits[0].message);
t.true(verifyRelease.calledOnce);
t.deepEqual(verifyRelease.firstCall.args[1].options, options);
t.deepEqual(verifyRelease.firstCall.args[1].logger, t.context.logger);
t.deepEqual(verifyRelease.firstCall.args[1].lastRelease, lastRelease);
t.deepEqual(verifyRelease.firstCall.args[1].commits[0].hash.substring(0, 7), commits[0].hash);
t.deepEqual(verifyRelease.firstCall.args[1].commits[0].message, commits[0].message);
t.deepEqual(verifyRelease.firstCall.args[1].nextRelease, nextRelease);
t.is(verifyRelease.callCount, 1);
t.deepEqual(verifyRelease.args[0][1].options, options);
t.deepEqual(verifyRelease.args[0][1].logger, t.context.logger);
t.deepEqual(verifyRelease.args[0][1].lastRelease, lastRelease);
t.deepEqual(verifyRelease.args[0][1].commits[0].hash.substring(0, 7), commits[0].hash);
t.deepEqual(verifyRelease.args[0][1].commits[0].message, commits[0].message);
t.deepEqual(verifyRelease.args[0][1].nextRelease, nextRelease);
t.true(generateNotes.calledOnce);
t.deepEqual(generateNotes.firstCall.args[1].options, options);
t.deepEqual(generateNotes.firstCall.args[1].logger, t.context.logger);
t.deepEqual(generateNotes.firstCall.args[1].lastRelease, lastRelease);
t.deepEqual(generateNotes.firstCall.args[1].commits[0].hash.substring(0, 7), commits[0].hash);
t.deepEqual(generateNotes.firstCall.args[1].commits[0].message, commits[0].message);
t.deepEqual(generateNotes.firstCall.args[1].nextRelease, nextRelease);
t.is(generateNotes.callCount, 1);
t.deepEqual(generateNotes.args[0][1].options, options);
t.deepEqual(generateNotes.args[0][1].logger, t.context.logger);
t.deepEqual(generateNotes.args[0][1].lastRelease, lastRelease);
t.deepEqual(generateNotes.args[0][1].commits[0].hash.substring(0, 7), commits[0].hash);
t.deepEqual(generateNotes.args[0][1].commits[0].message, commits[0].message);
t.deepEqual(generateNotes.args[0][1].nextRelease, nextRelease);
t.true(publish.calledOnce);
t.deepEqual(publish.firstCall.args[1].options, options);
t.deepEqual(publish.firstCall.args[1].logger, t.context.logger);
t.deepEqual(publish.firstCall.args[1].lastRelease, lastRelease);
t.deepEqual(publish.firstCall.args[1].commits[0].hash.substring(0, 7), commits[0].hash);
t.deepEqual(publish.firstCall.args[1].commits[0].message, commits[0].message);
t.deepEqual(publish.firstCall.args[1].nextRelease, Object.assign({}, nextRelease, {notes}));
t.is(publish.callCount, 1);
t.deepEqual(publish.args[0][1].options, options);
t.deepEqual(publish.args[0][1].logger, t.context.logger);
t.deepEqual(publish.args[0][1].lastRelease, lastRelease);
t.deepEqual(publish.args[0][1].commits[0].hash.substring(0, 7), commits[0].hash);
t.deepEqual(publish.args[0][1].commits[0].message, commits[0].message);
t.deepEqual(publish.args[0][1].nextRelease, Object.assign({}, nextRelease, {notes}));
});
test.serial('Use new gitHead, and recreate release notes if a publish plugin create a commit', async t => {
@@ -138,16 +136,16 @@ test.serial('Use new gitHead, and recreate release notes if a publish plugin cre
await t.context.semanticRelease(options);
t.true(generateNotes.calledTwice);
t.deepEqual(generateNotes.firstCall.args[1].nextRelease, nextRelease);
t.true(publish1.calledOnce);
t.deepEqual(publish1.firstCall.args[1].nextRelease, Object.assign({}, nextRelease, {notes}));
t.is(generateNotes.callCount, 2);
t.deepEqual(generateNotes.args[0][1].nextRelease, nextRelease);
t.is(publish1.callCount, 1);
t.deepEqual(publish1.args[0][1].nextRelease, Object.assign({}, nextRelease, {notes}));
nextRelease.gitHead = await getGitHead();
t.deepEqual(generateNotes.secondCall.args[1].nextRelease, Object.assign({}, nextRelease, {notes}));
t.true(publish2.calledOnce);
t.deepEqual(publish2.firstCall.args[1].nextRelease, Object.assign({}, nextRelease, {notes}));
t.is(publish2.callCount, 1);
t.deepEqual(publish2.args[0][1].nextRelease, Object.assign({}, nextRelease, {notes}));
});
test.serial('Dry-run skips verifyConditions and publish', async t => {
@@ -185,12 +183,12 @@ test.serial('Dry-run skips verifyConditions and publish', async t => {
await t.context.semanticRelease(options);
t.true(verifyConditions.notCalled);
t.true(getLastRelease.calledOnce);
t.true(analyzeCommits.calledOnce);
t.true(verifyRelease.calledOnce);
t.true(generateNotes.calledOnce);
t.true(publish.notCalled);
t.is(verifyConditions.callCount, 0);
t.is(getLastRelease.callCount, 1);
t.is(analyzeCommits.callCount, 1);
t.is(verifyRelease.callCount, 1);
t.is(generateNotes.callCount, 1);
t.is(publish.callCount, 0);
});
test.serial('Accept "undefined" values for the "getLastRelease" and "generateNotes" plugins', async t => {
@@ -225,20 +223,20 @@ test.serial('Accept "undefined" values for the "getLastRelease" and "generateNot
await t.context.semanticRelease(options);
t.true(getLastRelease.calledOnce);
t.is(getLastRelease.callCount, 1);
t.true(analyzeCommits.calledOnce);
t.deepEqual(analyzeCommits.firstCall.args[1].lastRelease, lastRelease);
t.is(analyzeCommits.callCount, 1);
t.deepEqual(analyzeCommits.args[0][1].lastRelease, lastRelease);
t.true(verifyRelease.calledOnce);
t.deepEqual(verifyRelease.firstCall.args[1].lastRelease, lastRelease);
t.is(verifyRelease.callCount, 1);
t.deepEqual(verifyRelease.args[0][1].lastRelease, lastRelease);
t.true(generateNotes.calledOnce);
t.deepEqual(generateNotes.firstCall.args[1].lastRelease, lastRelease);
t.is(generateNotes.callCount, 1);
t.deepEqual(generateNotes.args[0][1].lastRelease, lastRelease);
t.true(publish.calledOnce);
t.deepEqual(publish.firstCall.args[1].lastRelease, lastRelease);
t.falsy(publish.firstCall.args[1].nextRelease.notes);
t.is(publish.callCount, 1);
t.deepEqual(publish.args[0][1].lastRelease, lastRelease);
t.falsy(publish.args[0][1].nextRelease.notes);
});
test.serial('Throw SemanticReleaseError if not running from a git repository', async t => {
+20 -30
View File
@@ -23,9 +23,16 @@ const testEnv = Object.assign({}, process.env, {
NPM_EMAIL: 'integration@test.com',
LEGACY_TOKEN: Buffer.from(`${process.env.NPM_USERNAME}:${process.env.NPM_PASSWORD}`, 'utf8').toString('base64'),
});
// Save the current process.env
const envBackup = Object.assign({}, process.env);
const cli = require.resolve('../bin/semantic-release');
const pluginError = require.resolve('./fixtures/plugin-error');
const pluginInheritedError = require.resolve('./fixtures/plugin-error-inherited');
// Save the current working diretory
const cwd = process.cwd();
// Disable logs during tests
stub(process.stdout, 'write');
stub(process.stderr, 'write');
test.before(async () => {
// Start the local NPM registry
@@ -34,11 +41,7 @@ test.before(async () => {
await mockServer.start();
});
test.beforeEach(t => {
// Save the current process.env
t.context.env = Object.assign({}, process.env);
// Save the current working diretory
t.context.cwd = process.cwd();
test.beforeEach(() => {
// Delete env paramaters that could have been set on the machine running the tests
delete process.env.NPM_TOKEN;
delete process.env.NPM_USERNAME;
@@ -56,23 +59,13 @@ test.beforeEach(t => {
delete process.env[keys[i]];
}
}
// Disable logs during tests
t.context.log = stub(console, 'log');
t.context.error = stub(console, 'error');
t.context.stdout = stub(process.stdout, 'write');
t.context.stderr = stub(process.stderr, 'write');
});
test.afterEach.always(t => {
test.afterEach.always(() => {
// Restore process.env
process.env = Object.assign({}, t.context.env);
process.env = envBackup;
// Restore the current working directory
process.chdir(t.context.cwd);
// Restore the logs
t.context.log.restore();
t.context.error.restore();
t.context.stdout.restore();
t.context.stderr.restore();
process.chdir(cwd);
});
test.after.always(async () => {
@@ -492,9 +485,7 @@ test.serial('Create a tag as a recovery solution for "ENOTINHISTORY" error', asy
t.regex(
stderr,
new RegExp(
`You can recover from this error by restoring the commit "${head}" or by creating a tag for the version "${
version
}" on the commit corresponding to this release`
`You can recover from this error by restoring the commit "${head}" or by creating a tag for the version "${version}" on the commit corresponding to this release`
)
);
@@ -611,7 +602,6 @@ test.serial('Pass options via CLI arguments', async t => {
test.serial('Run via JS API', async t => {
const packageName = 'test-js-api';
const owner = 'test-repo';
const githubToken = 'OTHER_TOKEN';
// Create a git repository, set the current working directory at the root of the repo
t.log('Create git repository and package.json');
await gitRepo();
@@ -627,19 +617,22 @@ test.serial('Run via JS API', async t => {
const version = '1.0.0';
const verifyMock = await mockServer.mock(
`/repos/${owner}/${packageName}`,
{headers: [{name: 'Authorization', values: [`token ${githubToken}`]}]},
{headers: [{name: 'Authorization', values: [`token ${env.GH_TOKEN}`]}]},
{body: {permissions: {push: true}}, method: 'GET'}
);
const createRefMock = await mockServer.mock(
`/repos/${owner}/${packageName}/git/refs`,
{body: {ref: `refs/tags/v${version}`}, headers: [{name: 'Authorization', values: [`token ${githubToken}`]}]},
{
body: {ref: `refs/tags/v${version}`},
headers: [{name: 'Authorization', values: [`token ${env.GH_TOKEN}`]}],
},
{body: {ref: `refs/tags/${version}`}}
);
const createReleaseMock = await mockServer.mock(
`/repos/${owner}/${packageName}/releases`,
{
body: {tag_name: `v${version}`, target_commitish: 'master', name: `v${version}`},
headers: [{name: 'Authorization', values: [`token ${githubToken}`]}],
headers: [{name: 'Authorization', values: [`token ${env.GH_TOKEN}`]}],
},
{body: {html_url: `release-url/${version}`}}
);
@@ -650,14 +643,11 @@ test.serial('Run via JS API', async t => {
await gitCommits(['feat: Initial commit']);
t.log('$ Call semantic-release via API');
await semanticRelease({
verifyConditions: [{path: '@semantic-release/github', githubToken}, '@semantic-release/npm'],
publish: [{path: '@semantic-release/github', githubToken}, '@semantic-release/npm'],
verifyConditions: [{path: '@semantic-release/github'}, '@semantic-release/npm'],
publish: [{path: '@semantic-release/github'}, '@semantic-release/npm'],
debug: true,
});
t.true(t.context.log.calledWithMatch(/Published Github release: /, new RegExp(`release-url/${version}`)));
t.true(t.context.log.calledWithMatch(/Publishing version .* to npm registry/, version));
// Verify package.json and has been updated
t.is((await readJson('./package.json')).version, version);
+12 -9
View File
@@ -1,5 +1,5 @@
import test from 'ava';
import {stub, match} from 'sinon';
import {stub} from 'sinon';
import logger from '../lib/logger';
test.beforeEach(t => {
@@ -16,8 +16,8 @@ test.serial('Basic log', t => {
logger.log('test log');
logger.error('test error');
t.true(t.context.log.calledWithMatch(/.*test log/));
t.true(t.context.error.calledWithMatch(/.*test error/));
t.regex(t.context.log.args[0][0], /.*test log/);
t.regex(t.context.error.args[0][0], /.*test error/);
});
test.serial('Log object', t => {
@@ -25,16 +25,18 @@ test.serial('Log object', t => {
logger.log(obj);
logger.error(obj);
t.true(t.context.log.calledWithMatch(match.string, obj));
t.true(t.context.error.calledWithMatch(match.string, obj));
t.is(t.context.log.args[0][1], obj);
t.is(t.context.error.args[0][1], obj);
});
test.serial('Log with string formatting', t => {
logger.log('test log %s', 'log value');
logger.error('test error %s', 'error value');
t.true(t.context.log.calledWithMatch(/.*test log/, 'log value'));
t.true(t.context.error.calledWithMatch(/.*test error/, 'error value'));
t.regex(t.context.log.args[0][0], /.*test log/);
t.regex(t.context.error.args[0][0], /.*test error/);
t.is(t.context.log.args[0][1], 'log value');
t.is(t.context.error.args[0][1], 'error value');
});
test.serial('Log with error stacktrace and properties', t => {
@@ -43,6 +45,7 @@ test.serial('Log with error stacktrace and properties', t => {
const otherError = new Error('other error message');
logger.error('test error %O', otherError);
t.true(t.context.error.calledWithMatch(match.string, error));
t.true(t.context.error.calledWithMatch(/.*test error/, otherError));
t.is(t.context.error.args[0][1], error);
t.regex(t.context.error.args[1][0], /.*test error/);
t.is(t.context.error.args[1][1], otherError);
});
+6 -6
View File
@@ -1,6 +1,6 @@
import test from 'ava';
import {noop} from 'lodash';
import {stub, match} from 'sinon';
import {stub} from 'sinon';
import normalize from '../../lib/plugins/normalize';
test.beforeEach(t => {
@@ -10,17 +10,17 @@ test.beforeEach(t => {
});
test('Normalize and load plugin from string', t => {
const plugin = normalize('', './test/fixtures/plugin-noop', t.context.logger);
const plugin = normalize('verifyConditions', './test/fixtures/plugin-noop', t.context.logger);
t.is(typeof plugin, 'function');
t.true(t.context.log.calledWith(match.string, './test/fixtures/plugin-noop'));
t.deepEqual(t.context.log.args[0], ['Load plugin %s from %s', 'verifyConditions', './test/fixtures/plugin-noop']);
});
test('Normalize and load plugin from object', t => {
const plugin = normalize('', {path: './test/fixtures/plugin-noop'}, t.context.logger);
const plugin = normalize('publish', {path: './test/fixtures/plugin-noop'}, t.context.logger);
t.is(typeof plugin, 'function');
t.true(t.context.log.calledWith(match.string, './test/fixtures/plugin-noop'));
t.deepEqual(t.context.log.args[0], ['Load plugin %s from %s', 'publish', './test/fixtures/plugin-noop']);
});
test('Normalize and load plugin from function', t => {
@@ -33,7 +33,7 @@ test('Normalize and load plugin that retuns multiple functions', t => {
const plugin = normalize('verifyConditions', './test/fixtures/multi-plugin', t.context.logger);
t.is(typeof plugin, 'function');
t.true(t.context.log.calledWith(match.string, './test/fixtures/multi-plugin'));
t.deepEqual(t.context.log.args[0], ['Load plugin %s from %s', 'verifyConditions', './test/fixtures/multi-plugin']);
});
test('Wrap plugin in a function that validate the output of the plugin', async t => {