Compare commits

..
53 Commits
Author SHA1 Message Date
Stephan Bönnemann ddaec4f2bf test(integration): make em at least run locally 2015-07-19 16:21:40 +02:00
Stephan Bönnemann 22a283a027 chore: prepare to move back to main repo#next 2015-07-19 16:05:03 +02:00
Stephan Bönnemann d9eeb3fcae refactor: use one config object instead of passing arguments to plugins on their own
This commit does a lot but it's all connected and tries to make everything more extensible and future proof.

1. CLI arguments and options from the "package.json" are no longer treated as two different things. You can now pass options either way.

BREAKING CHANGE: cli arguments are now normalized to camelCase, so e.g. `options['github-url']` is now `options.githubUrl`

2. Plugins no longer receive config they need one by one, but in one huge object. This way it's easier to pass more info in the future w/o breaking existing plugins that rely on the position of the callback in the arguments array.

BREAKING CHANGE: Plugins now need to read their passed options from one huge config object.

Old:
```js
module.exports = function (pluginConfig, foo, bar, cb) {…}
```

New:
```js
// ES5
module.exports = function(pluginConfig, config, cb) {
  var foo = config.foo
  var bar = config.bar
  …
}

// ES6
module.exports = function (pluginConfig, {foo, bar}, cb) {…}
```
2015-07-19 15:34:30 +02:00
Stephan Bönnemann 8892ec7f7a fix(plugins): typo in plugin name 2015-07-17 16:51:06 +02:00
Stephan Bönnemann cf27cd16cc chore(package): ignore config files 2015-07-17 15:10:10 +02:00
Stephan Bönnemann 152d1f6874 chore(travis): simplify config 2015-07-17 14:56:56 +02:00
Stephan Bönnemann 626315e9fe feat(package): update condition-travis 2015-07-17 14:56:44 +02:00
Stephan Bönnemann f5573f7494 chore(package): bump condition-travis 2015-07-17 10:01:52 +02:00
Stephan Bönnemann 2d3506cda1 chore: setup semantic-release 2015-07-17 09:35:53 +02:00
Stephan Bönnemann 72f9f8ea7c chore: rename to scripts 2015-07-16 19:36:42 +02:00
Stephan Bönnemann 59d852d6fb feat(post): initial 2015-07-16 17:30:23 +02:00
Stephan Bönnemann ed05101a61 test(post): initial 2015-07-16 17:30:14 +02:00
Stephan Bönnemann 431babb42e fix(verify): ensure repo url in package.json is well formed 2015-07-16 14:07:52 +02:00
Stephan Bönnemann dbea5cc1b1 feat(last-release): add custom dist-tag 2015-07-16 11:51:33 +02:00
Stephan Bönnemann 6cf1711151 test(last-release): add custom dist-tag 2015-07-16 11:51:22 +02:00
Stephan Bönnemann ba5222af52 chore(package): update semver 2015-07-14 21:13:36 +02:00
Stephan Bönnemann b1d9985ba3 test(plugins): test exported plugins 2015-07-14 21:13:36 +02:00
Stephan Bönnemann 13e2f5ce78 feat(cli): add verifyConditions hook 2015-07-14 21:13:36 +02:00
Stephan Bönnemann af15fb4c61 fix(last-release): normalized registry url 2015-07-14 21:13:36 +02:00
Stephan Bönnemann caf09c9be0 fix(cli): only write npmrc when necessary 2015-07-14 18:30:30 +02:00
Stephan Bönnemann 0316babf7a refactor(error): use error module 2015-07-14 12:25:36 +02:00
Stephan Bönnemann 43c8df0bfe fix(plugins): require user plugins relative to the cwd 2015-07-13 12:03:05 +02:00
Stephan Bönnemann 150273c8fe fix(plugins): rename plugins 2015-07-13 11:53:32 +02:00
Stephan Bönnemann f247ed8741 chore(package): remove integration tests from default test command 2015-07-09 15:33:59 +02:00
Stephan Bönnemann 2eff4a4467 style(type): add dot at end of sentence 2015-07-09 02:10:39 +02:00
Stephan Bönnemann e1d5da5864 fix(plugins): pass right path to generateNotes 2015-07-09 02:10:39 +02:00
Stephan Bönnemann a0190b7ad3 feat(bin): implement cli 2015-07-09 02:10:39 +02:00
Stephan Bönnemann c6f95e4870 test(pre): initial cli/integration suite 2015-07-09 02:10:39 +02:00
Stephan Bönnemann 6d84b66c05 test(test-module): creates integration test env on disk 2015-07-09 02:10:38 +02:00
Stephan Bönnemann 4d58af7145 test(base-scenario): creates nixt testing base 2015-07-09 02:10:38 +02:00
Stephan Bönnemann 9a3d9a9280 chore: add editorconfig 2015-07-09 02:10:38 +02:00
Stephan Bönnemann 32c1dcf75b test(registry): lib to start and stop an npm-registry-couchapp 2015-07-09 02:10:38 +02:00
Stephan Bönnemann 4093bb2dbe feat: initial 2015-07-08 20:41:36 +02:00
Stephan Bönnemann c59b5cac9c feat(nerf-dart): copy in nerf-dart module from npm 2015-06-19 18:35:25 -07:00
Stephan Bönnemann 44a18fd5cf chore(package): install commit analyzer and release note generator 2015-06-19 17:33:27 -07:00
Stephan Bönnemann e24c7c3833 chore(coverage): switch to coveralls 2015-06-18 16:48:28 -07:00
Stephan Bönnemann 057e8e9759 feat(plugins): normalize plugins and have unified function signature 2015-06-18 16:48:28 -07:00
Stephan Bönnemann da23c15ea5 test(plugins): initial suite 2015-06-18 16:10:55 -07:00
Stephan Bönnemann b63a43fa53 feat(verify): initial verification for pkg, options and env 2015-06-17 15:42:14 -07:00
Stephan Bönnemann c47ff97ab3 test(verify): initial suite 2015-06-17 15:41:26 -07:00
Stephan Bönnemann 689f37ae2e test(specs): how about running ALL tests? 2015-06-14 21:50:53 -07:00
Stephan Bönnemann a4fe18cade feat(pre): plugging last-release, commits and type together 2015-06-14 21:49:50 -07:00
Stephan Bönnemann 7412401d55 test(pre): initial suite 2015-06-14 21:49:50 -07:00
Stephan Bönnemann 6df67828fe refactor(commits,type): simplify interfaces 2015-06-14 21:49:39 -07:00
Stephan Bönnemann 66bf8ff710 refactor(mocks): extract from specs 2015-06-14 21:07:39 -07:00
Stephan Bönnemann a405021d08 feat(type): initial functionality 2015-06-14 19:40:05 -07:00
Stephan Bönnemann f79e26dcd6 test(type): initial suite 2015-06-14 19:39:38 -07:00
Stephan Bönnemann 861debb85e feat(commits): initial functionality 2015-06-14 18:17:25 -07:00
Stephan Bönnemann d6172b8767 test(commits): initial suite 2015-06-14 18:17:25 -07:00
Stephan Bönnemann f62525ad7e feat(last-release): initial functionality 2015-06-14 18:17:25 -07:00
Stephan Bönnemann cce860ec03 test(last-release): initial suite 2015-06-14 18:17:25 -07:00
Stephan Bönnemann 56238887d3 chore: config files 2015-06-14 18:17:25 -07:00
Stephan Bönnemann ac7037d948 chore: root 2015-06-12 18:55:23 -07:00
61 changed files with 1175 additions and 1228 deletions
+11
View File
@@ -0,0 +1,11 @@
root = true
[*]
end_of_line = lf
insert_final_newline = true
indent_style = space
indent_size = 2
trim_trailing_whitespace = true
[*.md]
trim_trailing_whitespace = false
+11
View File
@@ -1,3 +1,14 @@
# common
coverage
node_modules
test/registry/couch
test/registry/data
*.log
*.dump
.DS_Store
.nyc_output
.test
.tmp
# build-artifacts
dist
+18
View File
@@ -0,0 +1,18 @@
# common
coverage
node_modules
test/registry/couch
test/registry/data
*.log
*.dump
.DS_Store
.nyc_output
.test
.tmp
# source/config
src
*.yml
.gitignore
.editorconfig
.npmrc
+9 -24
View File
@@ -1,37 +1,22 @@
language: node_js
node_js:
- iojs-v2
- iojs-v1
- '0.10'
- '0.12'
sudo: false
language: node_js
cache:
directories:
- node_modules
notifications:
email: false
node_js:
- iojs-v2
- iojs-v1
- '0.12'
- '0.10'
before_install:
- npm i -g npm@^2.0.0
before_script:
- npm prune
- curl -Lo travis_after_all.py https://raw.github.com/dmakhno/travis_after_all/master/travis_after_all.py
- curl -Lo travis_after_all.py https://git.io/vLSON
after_success:
- python travis_after_all.py
- export $(cat .to_export_back)
after_failure:
- python travis_after_all.py
- export $(cat .to_export_back)
before_deploy:
- rm -f travis_after_all.py .to_export_back
after_script:
- echo leader=$BUILD_LEADER status=$BUILD_AGGREGATE_STATUS
deploy:
provider: npm
email: stephan@boennemann.me
skip_cleanup: true
api_key:
secure: Lq1FZweaNr/Ah8M7h3oWQ59HTswzvcksTUfzWb/dpqNn5FECN9I+4gi79EQPD62ZbJDCuDHY0EEEDLdnaB1z8N7kOj1RtmvC1k0teTEXFsGBPS4evXakRj+AfoBAGsTaXHqBYBOvYBxJT2Md/UoOgPSQjtAdZSNsmoFb1xkngQ0=
on:
branch: master
repo: boennemann/semantic-release
condition: "$BUILD_LEADER$BUILD_AGGREGATE_STATUS = YESothers_succeeded"
- if [[ $BUILD_LEADER = YES ]]; then npm run coverage:upload; fi
- npm run semantic-release
+10 -174
View File
@@ -1,184 +1,20 @@
# semantic-release
[![Build Status](https://travis-ci.org/boennemann/semantic-release.svg?branch=master)](https://travis-ci.org/boennemann/semantic-release)
[![Dependency Status](https://david-dm.org/boennemann/semantic-release.svg)](https://david-dm.org/boennemann/semantic-release)
[![devDependency Status](https://david-dm.org/boennemann/semantic-release/dev-status.svg)](https://david-dm.org/boennemann/semantic-release#info=devDependencies)
[![Build Status](https://travis-ci.org/semantic-release/semantic-release.svg?branch=next)](https://travis-ci.org/semantic-release/semantic-release)
[![Coverage Status](https://coveralls.io/repos/semantic-release/semantic-release/badge.svg?branch=next&service=github)](https://coveralls.io/github/semantic-release/semantic-release?branch=next)
[![Dependency Status](https://david-dm.org/semantic-release/semantic-release/next.svg)](https://david-dm.org/semantic-release/semantic-release/next)
[![devDependency Status](https://david-dm.org/semantic-release/semantic-release/next/dev-status.svg)](https://david-dm.org/semantic-release/semantic-release/next#info=devDependencies)
[![js-standard-style](https://img.shields.io/badge/code%20style-standard-brightgreen.svg?style=flat)](https://github.com/feross/standard)
[![NPM](https://nodei.co/npm/semantic-release.png?downloads=true&downloadRank=true&stars=true)](https://nodei.co/npm/semantic-release/)
## What is `semantic-release` about?
It fully automates your package's releases. It will determine not only which version to release, but also when all without you wasting [cognitive resources](https://www.youtube.com/watch?v=FKTxC9pl-WM) on it.
At its core `semantic-release` is a set of conventions that gives you entirely automated, semver-compliant package publishing. _Coincidentally_ these conventions make sense on their own, like having meaningful commit messages.
[![JSConfBP Talk](https://cloud.githubusercontent.com/assets/908178/8032541/e9bf6300-0dd6-11e5-92c9-8a39211368af.png)](https://www.youtube.com/watch?v=tc2UgG5L7WM&index=6&list=PLFZ5NyC0xHDaaTy6tY9p0C0jd_rRRl5Zm)
> This talk gives you a complete introduction to the underlying concepts plus a live demo at the end.
> This talk gives you a complete introduction to the underlying concepts
It is fully integrated into the `npm` lifecycle, so all you need to do is to configure your CI to regularly `npm publish` (i.e. for every commit).
It removes human decisions and emotions from version numbers and releases suddenly, strictly following the [SemVer](http://semver.org/) spec isn't a problem anymore.
## How does it work?
Meaningful conventions. Instead of dumping [lols](http://whatthecommit.com/) into our commit messages, we can take some time to think about what we changed in the codebase and write it down. Following formalized conventions it this then possible to not only generate a helpful changelog, but to determine whether a new version should be released.
This module ships with the [AngularJS Commit Message Conventions](https://docs.google.com/document/d/1QrDFcIiPjSLDn3EL15IJygNPiHORgU1_OOAqWjiDU5Y/edit), but you can [define your own](https://github.com/boennemann/semantic-release/wiki/commit-analysis).
### The `prepublish` step
Before `npm` actually gets to publish a new version `semantic-release`'s `prepublish` step does the following:
- Extract all commits since the last version was published
- Determine the release type (`major`|`minor`|`patch`) or abort if nothing relevant changed
- Increase the version number accordingly
### The `publish` step
`npm` does its thing.
### The `postpublish` step
After `npm` published the new version the `postpublish` step does this:
- Generate release notes
- Create a new [GitHub Release](https://help.github.com/articles/about-releases/) (including a git tag) with the release notes
Note: The default release notes are a [changelog](https://github.com/ajoslin/conventional-changelog) that is generated from git metadata, using the AngularJS commit conventions. You can [specify your own release note generator](https://github.com/boennemann/semantic-release/wiki/release-note-generation).
Note: This is tied to GitHub, feel free to send PRs for other services.
Note: `semantic-release` works around a limitation in `npm`'s `prepublish` step. Once a version is published it prints a "Could not pack" error that you can *safely ignore* [npm/npm#7118](https://github.com/npm/npm/issues/7118).
## How you can set it up
### Installation
First of all you need to install `semantic-release` and save it as a `devDependency`.
```bash
npm install --save-dev semantic-release
# Btw, if you're feeling lazy you can just type this it's the same thing.
npm i -D semantic-release
```
### Package
```bash
./node_modules/.bin/semantic-release setup
```
What this does:
#### Scripts
The setup command configures `scripts` inside the `package.json`:
```json
"scripts": {
"prepublish": "semantic-release pre",
"postpublish": "semantic-release post"
}
```
Note: If you have already configured `scripts` for `prepublish` or `postpublish` they're just executed one after another. For example: `"npm run babelify && semantic-release pre"`.
#### Version
It would be preferable not to have a version field in the `package.json` at all, but due to an `npm` limitation it is required to have a _not yet published_ version in there [npm/npm#7118](https://github.com/npm/npm/issues/7118). Because of this the version gets changed to `"0.0.0-semantically-released"` until `npm` _hopefully_ removes its limitations.
#### Repository
If you haven't defined your GitHub repository in the `package.json`s [repository field](https://docs.npmjs.com/files/package.json#repository) the remote `origin`'s repository is used.
### CI Server
Inside your `.travis.yml`:
```yml
language: node_js
node_js:
- iojs-v1
# If you have a more sophisticated build with multiple jobs you should have a look at
# https://github.com/dmakhno/travis_after_all which is also configured for this
# package. (Check the .travis.yml)
sudo: false
cache:
directories:
- node_modules
notifications:
email: false
# See https://github.com/boennemann/semantic-release/issues/18
before_deploy:
- npm config set spin false --global
env:
# Get your token here: https://github.com/settings/tokens/new
# Grant the token repo/public_repo scope (all others can be deselected)
# You should encrypt this:
# `travis encrypt GH_TOKEN=<token> --add`
global: GH_TOKEN=<github-access-token-with-access-to-your-repo>
deploy:
provider: npm
email: <your-npm-mail@example.com>
# Very important. Don't forget this one.
skip_cleanup: true
# Travis currently only supports the old auth key format.
# Do `echo -n "<username>:<password>" | base64` to get it.
# You should encrypt this:
# `travis encrypt $(echo -n "<username>:<password>" | base64) --add deploy.api_key`
api_key: <npm-api-key>
on:
branch: master
repo: <user>/<repo>
```
Note: This isn't tied to a specific service, but example configuration is provided for [Travis CI](https://travis-ci.org/). Feel free to contribute configuration for other servers or services.
Note: You should [encrypt](http://docs.travis-ci.com/user/environment-variables/#sts=Secure Variables) your api keys and tokens.
Note: Your CI environment has to export `CI=true` in order for `semantic-release` not to automatically perform a dry run. Travis CI does this by default.
Note: It is crucial that your CI server also fetches all tags when checking out your repository. Travis CI does this by default.
## ITYM*FAQ*LT
> I think you might frequently ask questions like these
### Why is the `package.json`'s version not updated in my repository?
The `npm` docs even state:
> The most important things in your package.json are the name and version fields. Those are actually required, and your package won't install without them.
> -- [npm docs](https://docs.npmjs.com/files/package.json#version)
While this entirely true the version number doesn't have to be checked into source control. `semantic-release` takes care of the version field right before `npm publish` uses it and this is the only point where it _really_ is required.
### Is there a way to preview which version would currently get published?
If you're running `npm publish` locally `semantic-release` automatically performs a dry run. This does log the version that would currently get published, but only if you `git fetch --tags` before.
### Can I run this on my own machine rather than on a CI server?
Of course you can, but this doesn't necessarly mean you should. Running your tests on an independent machine before releasing software is a crucial part of this workflow. Also it is a pain to set this up locally, with GitHub tokens lying around and everything. That said, you can either set the environment variable `CI=true`, or run the scripts with `--debug=false` explicitly. Don't forget to export `GH_TOKEN=your_token` as well.
### Can I manually trigger the release of a specific version?
You can trigger a release by pushing to your repository. You deliberately can not trigger a _specific_ version release, because this is the whole point of `semantic-release`. Start your packages with `1.0.0` and semver on.
Note: The default commit message conventions do not support pre-release flags, but you can build this in yourself using [custom analyzers](https://github.com/boennemann/semantic-release/wiki/commit-analysis).
### How do I get back to good ol' `npm publish`?
`npm` offers the `--ignore-scripts` flag. Doing `npm publish --ignore-scripts` doesn't execute the `prepublish` and `postpublish` scripts.
### Is it _really_ a good idea to release on every push?
It is indeed a great idea because it _forces_ you to follow best practices. If you don't feel comfortable making every passing feature or fix on your master branch addressable via `npm` you might not treat your master right. Have a look at [branch workflows](https://guides.github.com/introduction/flow/index.html). If you still think you should have control over the exact point in time of your release, e.g. because you are following a release schedule, configure your CI server to release only on the `production`/`deploy`/`release` branch and push your code there in certain intervals.
### Why should I trust `semantic-release` with my releases? What if it breaks?
`semantic-release` has a full integration-test suite that tests _actual_ `npm` publishes and _actual_ GitHub Releases (with private registry/API) on node.js `^0.10`, `^0.12` and io.js `^1`, `^2`. A new version won't get published if it doesn't pass on all these engines.
## License
MIT License
2015 © Stephan Bönnemann and [contributors](https://github.com/boennemann/semantic-release/graphs/contributors)
![https://twitter.com/trodrigues/status/509301317467373571](https://cloud.githubusercontent.com/assets/908178/6091690/cc86f58c-aeb8-11e4-94cb-15f15f486cde.png)
The next branch is a completely rewritten version of `semantic-release`, that isn't fully tested and documented yet.
It has lots and lots of great new features, but also breaks a lot of things.
Unless you're adventurous you should look at the [stable master branch](https://github.com/semantic-release/semantic-release/tree/master).
+9 -90
View File
@@ -1,94 +1,13 @@
#!/usr/bin/env node
'use strict'
var readFile = require('fs').readFileSync
var abbrev = require('abbrev')
var minimist = require('minimist')
var efh = require('../lib/error').standard
var argv = minimist(process.argv.slice(2), {
alias: {
d: 'debug',
dry: 'debug',
t: 'token',
g: 'github-url'
},
booleans: ['debug'],
default: {
debug: !process.env.CI,
token: process.env.GH_TOKEN || process.env.TOKEN || process.env.GITHUB_TOKEN,
'github-url': process.env.GH_URL
/* istanbul ignore next */
try {
require('../dist')
} catch (err) {
if (err.code === 'MODULE_NOT_FOUND') {
require('babel/register')
require('../src')
} else {
console.log(err)
}
})
var plugins = JSON.parse(readFile('./package.json')).release || {}
var npmArgv = process.env.npm_config_argv ?
minimist(JSON.parse(process.env.npm_config_argv).cooked) :
{_: []}
if (~argv._.indexOf('pre')) {
// see src/restart.js
if (npmArgv['semantic-release-rerun']) {
if (!/semantically-released/.test(process.env.npm_package_version)) process.exit(0)
console.log('There is something wrong with your setup, as a placeholder version is about to be released.')
console.log('Please verify that your setup is correct.')
console.log('If you think this is a problem with semantic-release please open an issue.')
process.exit(1)
}
// the `prepublish` hook is also executed when the package is installed
// in this case we abort the command and do nothing.
if (
isAbbrev(npmArgv, 'install') ||
isAbbrev(npmArgv, 'link') ||
isAbbrev(npmArgv, 'pack')
) process.exit(0)
if (argv.debug) console.log('This is a dry run')
console.log('Determining new version')
var publish = false
if (isAbbrev(npmArgv, 'publish')) publish = true
// require a correct setup during publish
if (publish && !argv.debug && !require('../src/verify')(argv)) process.exit(1)
require('../src/pre')(argv, plugins, efh(function (result) {
if (!result) {
console.log('Nothing changed. Not publishing.')
process.exit(1)
}
console.log('Publishing v' + result)
if (!publish) process.exit(0)
if (argv.debug) process.exit(1)
require('../src/restart')(efh(function () {
process.exit(1)
}))
}))
}
if (~argv._.indexOf('post')) {
require('../src/post')(argv, plugins, efh(function () {
// see src/restart.js
if (npmArgv['semantic-release-rerun']) {
console.log('Everything is alright :) npm will now print an error message that you can safely ignore.')
}
}))
}
if (~argv._.indexOf('setup')) {
require('../src/setup')()
console.log('"package.json" is set up properly. Now configure your CI server.')
console.log('https://github.com/boennemann/semantic-release#ci-server')
}
function isAbbrev (argv, command) {
return argv._.some(Object.prototype.hasOwnProperty.bind(abbrev(command)))
}
-34
View File
@@ -1,34 +0,0 @@
#!/bin/bash
set -e
npm run test:style
function cleanup {
set +e
cp .tmp/.npmrc ~/.npmrc
cat .tmp/sinopia.pid | xargs kill
cat .tmp/ghrs.pid | xargs kill
rm -rf .tmp
}
trap cleanup EXIT
mkdir -p .tmp/modules
export DEBUG=ghrs:cli
./node_modules/.bin/sinopia ./tests/config.yml & echo $! >> .tmp/sinopia.pid
./node_modules/.bin/github-release-fake-server -p 4343 & echo $! >> .tmp/ghrs.pid
sleep 0.5
cp ~/.npmrc .tmp/.npmrc
cp tests/.npmrc ~/.npmrc
npm run test:integration
-32
View File
@@ -1,32 +0,0 @@
'use strict'
var parseRawCommit = require('conventional-changelog/lib/git').parseRawCommit
module.exports = function (commits) {
var type = null
commits
.map(function (commit) {
return parseRawCommit(commit.hash + '\n' + commit.message)
})
.filter(function (commit) {
return !!commit
})
.every(function (commit) {
if (commit.breaks.length) {
type = 'major'
return false
}
if (commit.type === 'feat') type = 'minor'
if (!type && commit.type === 'fix') type = 'patch'
return true
})
return type
}
-27
View File
@@ -1,27 +0,0 @@
'use strict'
var exec = require('child_process').exec
var efh = require('./error').efh
module.exports = function (from, cb) {
var range = (from ? from + '..' : '') + 'HEAD'
exec(
'git log -E --format=%H==SPLIT==%B==END== ' + range,
efh(cb)(function (stdout) {
cb(null, String(stdout).split('==END==\n')
.filter(function (raw) {
return !!raw.trim()
})
.map(function (raw) {
var data = raw.split('==SPLIT==')
return {
hash: data[0],
message: data[1]
}
}))
})
)
}
-14
View File
@@ -1,14 +0,0 @@
'use strict'
var efh = require('error-first-handler')
module.exports = {
efh: efh,
standard: efh(function (err) {
console.log('Something went wrong:')
if (typeof err === 'string') return console.log(err)
if (err instanceof Error) return console.log(err.message, err.stack)
if (err.message) return console.log(err.message)
console.log(err)
})
}
-28
View File
@@ -1,28 +0,0 @@
'use strict'
var request = require('request')
var efh = require('./error').efh
module.exports = function (pkgName, cb) {
var encodedPkgName = pkgName.replace(/\//g, '%2F')
request(process.env.npm_config_registry + encodedPkgName, efh(cb)(function (response, body) {
var res = {
version: null,
gitHead: null,
pkg: null
}
if (response.statusCode === 404 || !body) return cb(null, res)
var pkg = JSON.parse(body)
if (pkg.error) return cb(pkg.error)
res.version = pkg['dist-tags'].latest
res.gitHead = pkg.versions[res.version].gitHead
res.pkg = pkg
cb(null, res)
}))
}
-17
View File
@@ -1,17 +0,0 @@
'use strict'
var readFile = require('fs').readFileSync
var changelog = require('conventional-changelog')
var parseUrl = require('github-url-from-git')
module.exports = function (cb) {
var pkg = JSON.parse(readFile('./package.json'))
var repository = pkg.repository ? parseUrl(pkg.repository.url) : null
changelog({
version: pkg.version,
repository: repository,
file: false
}, cb)
}
+69 -43
View File
@@ -1,67 +1,93 @@
{
"name": "semantic-release",
"description": "automated semver compliant package publishing",
"version": "0.0.0-semantically-released",
"author": "Stephan Bönnemann <stephan@boennemann.me>",
"bin": "./bin/semantic-release.js",
"author": "Stephan Bönnemann <stephan@boennemann.me> (http://boennemann.me)",
"bin": {
"semantic-release": "./bin/semantic-release.js"
},
"bugs": {
"url": "https://github.com/boennemann/semantic-release/issues"
"url": "https://github.com/semantic-release/semantic-release/issues"
},
"bundleDependencies": [
"@semantic-release/commit-analyzer",
"@semantic-release/condition-travis",
"@semantic-release/error",
"@semantic-release/release-notes-generator"
],
"config": {
"nyc": {
"exclude": [
".test",
"node_modules"
]
}
},
"dependencies": {
"abbrev": "^1.0.5",
"conventional-changelog": "0.0.17",
"error-first-handler": "^1.0.1",
"@semantic-release/commit-analyzer": "^2.0.0",
"@semantic-release/condition-travis": "^4.0.0",
"@semantic-release/error": "^1.0.0",
"@semantic-release/release-notes-generator": "^2.0.0",
"git-head": "^1.2.1",
"github": "0.2.4",
"github-url-from-git": "^1.4.0",
"ini": "^1.3.2",
"minimist": "^1.1.0",
"github": "^0.2.4",
"lodash": "^3.9.3",
"nopt": "^3.0.3",
"npm-registry-client": "^6.4.0",
"npmconf": "^2.1.2",
"npmlog": "^1.2.1",
"parse-github-repo-url": "^1.0.0",
"request": "^2.53.0",
"semver": "^4.3.3"
"require-relative": "^0.8.7",
"run-auto": "^1.1.2",
"semver": "^5.0.1"
},
"devDependencies": {
"cracks": "^2.0.1",
"github-release-fake-server": "^1.3.0",
"lodash.defaults": "^3.0.0",
"nano-uid": "^0.2.0",
"babel": "^5.5.8",
"coveralls": "^2.11.2",
"mkdirp": "^0.5.1",
"nixt": "^0.4.1",
"sinopia": "^1.0.0",
"standard": "^3.2.1",
"tap-spec": "^3.0.0",
"tape": "^4.0.0"
"nock": "^2.5.0",
"npm-registry-couchapp": "^2.6.11",
"nyc": "^3.0.0",
"proxyquire": "^1.5.0",
"rimraf": "^2.4.0",
"standard": "^4.2.1",
"tap": "^1.3.1"
},
"engines": {
"iojs": "^1",
"node": "^0.10",
"npm": "^2"
},
"homepage": "https://github.com/boennemann/semantic-release",
"homepage": "https://github.com/semantic-release/semantic-release/tree/next#readme",
"keywords": [
"author",
"automation",
"release",
"publish",
"changelog",
"module",
"package",
"publish",
"release",
"semver",
"version",
"changelog"
"version"
],
"license": "MIT",
"main": "index.js",
"repository": {
"type": "git",
"url": "https://github.com/boennemann/semantic-release.git"
},
"scripts": {
"postpublish": "./bin/semantic-release.js post",
"prepublish": "./bin/semantic-release.js pre",
"test:style": "standard",
"test:integration": "node tests | tap-spec",
"test": "./bin/test"
"main": "dist/index.js",
"publishConfig": {
"tag": "next"
},
"release": {
"verification": "cracks"
"branch": "next"
},
"repository": {
"type": "git",
"url": "git+https://github.com/semantic-release/semantic-release.git"
},
"scripts": {
"build": "rimraf dist && mkdirp dist && babel src --out-dir dist",
"coverage": "nyc report",
"coverage:upload": "npm run coverage -- --reporter=lcovonly && coveralls < coverage/lcov.info",
"prepublish": "npm run build",
"pretest:integration": "npm run build && npm run test:build",
"pretest:unit": "npm run build && npm run test:build",
"semantic-release": "./bin/semantic-release.js pre && npm dedupe && npm publish && ./bin/semantic-release.js post",
"test": "npm run test:style && npm run test:unit",
"test:build": "rimraf .test && mkdirp .test && babel test --out-dir .test",
"test:integration": "tap --no-cov .test/scenarios/*.js",
"test:style": "standard",
"test:unit": "nyc tap --no-cov .test/specs/*.js"
}
}
+134
View File
@@ -0,0 +1,134 @@
const { readFileSync, writeFileSync } = require('fs')
const path = require('path')
const _ = require('lodash')
const log = require('npmlog')
const nopt = require('nopt')
const npmconf = require('npmconf')
const PREFIX = 'semantic-release'
const env = process.env
const pkg = JSON.parse(readFileSync('./package.json'))
const knownOptions = {
branch: String,
debug: Boolean,
'github-token': String,
'github-url': String,
'analyze-commits': [path, String],
'generate-notes': [path, String],
'verify-conditions': [path, String],
'verify-release': [path, String]
}
const options = _.defaults(
_.mapKeys(nopt(knownOptions), (value, key) => _.camelCase(key)),
pkg.release,
{
branch: 'master',
debug: !env.CI,
githubToken: env.GH_TOKEN || env.GITHUB_TOKEN,
githubUrl: env.GH_URL
}
)
const plugins = require('./lib/plugins')(options)
npmconf.load({}, (err, conf) => {
if (err) {
log.error(PREFIX, 'Failed to load npm config.', err)
process.exit(1)
}
let npm = {
auth: {
token: env.NPM_TOKEN
},
loglevel: conf.get('loglevel'),
registry: conf.get('registry'),
tag: (pkg.publishConfig || {}).tag || conf.get('tag') || 'latest'
}
if (npm.registry[npm.registry.length - 1] !== '/') npm.registry += '/'
log.level = npm.loglevel
const config = {PREFIX, log, env, pkg, options, plugins, npm}
log.verbose(PREFIX, 'options:', _.assign({
githubToken: options.githubToken ? '***' : undefined
}), options)
log.verbose(PREFIX, 'Verifying config.')
const errors = require('./lib/verify')(config)
errors.forEach((err) => log.error(PREFIX, `${err.message} ${err.code}`))
if (errors.length) process.exit(1)
if (options.argv.remain[0] === 'pre') {
log.verbose(PREFIX, 'Running pre-script.')
log.verbose(PREFIX, 'Veriying conditions.')
plugins.verifyConditions(config, (err) => {
if (err) {
log[options.debug ? 'warn' : 'error'](PREFIX, err.message)
if (!options.debug) process.exit(1)
}
const nerfDart = require('./lib/nerf-dart')(npm.registry)
let wroteNpmRc = false
if (env.NPM_TOKEN) {
conf.set(`${nerfDart}:_authToken`, '${NPM_TOKEN}', 'project')
wroteNpmRc = true
} else if (env.NPM_OLD_TOKEN && env.NPM_EMAIL) {
// Using the old auth token format is not considered part of the public API
// This might go away anytime (i.e. once we have a better testing strategy)
conf.set('_auth', '${NPM_OLD_TOKEN}', 'project')
conf.set('email', '${NPM_EMAIL}', 'project')
wroteNpmRc = true
}
conf.save('project', (err) => {
if (err) return log.error(PREFIX, 'Failed to save npm config.', err)
if (wroteNpmRc) log.verbose(PREFIX, 'Wrote authToken to .npmrc.')
require('./pre')(config, (err, release) => {
if (err) {
log.error(PREFIX, 'Failed to determine new version.')
const args = [PREFIX, (err.code ? `${err.code} ` : '') + err.message]
if (err.stack) args.push(err.stack)
log.error(...args)
process.exit(1)
}
const message = `Determined version ${release.version} as "${npm.tag}".`
log.verbose(PREFIX, message)
if (options.debug) {
log.error(PREFIX, `${message} Not publishing in debug mode.`, release)
process.exit(1)
}
writeFileSync('./package.json', JSON.stringify(_.assign(pkg, {
version: release.version
}), null, 2))
log.verbose(PREFIX, `Wrote version ${release.version} to package.json.`)
})
})
})
} else if (options.argv.remain[0] === 'post') {
log.verbose(PREFIX, 'Running post-script.')
require('./post')(config, (err, published, release) => {
if (err) {
log.error(PREFIX, 'Failed to publish release notes.', err)
process.exit(1)
}
log.verbose(PREFIX, `${published ? 'Published' : 'Generated'} release notes.`, release)
})
} else {
log.error(PREFIX, `Command "${options.argv.remain[0]}" not recognized. User either "pre" or "post"`)
}
})
+25
View File
@@ -0,0 +1,25 @@
const { exec } = require('child_process')
module.exports = function ({lastRelease}, cb) {
const from = lastRelease.gitHead
const range = (from ? from + '..' : '') + 'HEAD'
exec(
`git log -E --format=%H==SPLIT==%B==END== ${range}`,
(err, stdout) => {
if (err) return cb(err)
cb(null, String(stdout).split('==END==\n')
.filter((raw) => !!raw.trim())
.map((raw) => {
const data = raw.split('==SPLIT==')
return {
hash: data[0],
message: data[1]
}
}))
}
)
}
+26
View File
@@ -0,0 +1,26 @@
const SemanticReleaseError = require('@semantic-release/error')
const npmlog = require('npmlog')
const RegClient = require('npm-registry-client')
module.exports = function ({pkg, npm}, cb) {
npmlog.level = npm.loglevel || 'error'
const client = new RegClient({log: npmlog})
client.get(`${npm.registry}${pkg.name}`, {
auth: npm.auth
}, (err, data) => {
if (err && err.statusCode === 404) return cb(null, {})
if (err) return cb(err)
const version = data['dist-tags'][npm.tag]
if (!version) return cb(new SemanticReleaseError(`There is no release with the dist-tag "${npm.tag}" yet. Tag a version first.`, 'ENODISTTAG'))
cb(null, {
version,
gitHead: data.versions[version].gitHead,
tag: npm.tag
})
})
}
+14
View File
@@ -0,0 +1,14 @@
// borrowed from https://github.com/npm/npm/blob/master/lib/config/nerf-dart.js
const url = require('url')
/* istanbul ignore next */
module.exports = function toNerfDart (uri) {
let parsed = url.parse(uri)
delete parsed.protocol
delete parsed.auth
delete parsed.query
delete parsed.search
delete parsed.hash
return url.resolve(url.format(parsed), '.')
}
+4
View File
@@ -0,0 +1,4 @@
/* istanbul ignore next */
module.exports = function (config, options, cb) {
cb(null)
}
+20
View File
@@ -0,0 +1,20 @@
const relative = require('require-relative')
let exports = module.exports = function (options) {
return {
analyzeCommits: exports.normalize(options.analyzeCommits, '@semantic-release/commit-analyzer'),
generateNotes: exports.normalize(options.generateNotes, '@semantic-release/release-notes-generator'),
verifyConditions: exports.normalize(options.verifyConditions, '@semantic-release/condition-travis'),
verifyRelease: exports.normalize(options.verifyRelease, './plugin-noop')
}
}
exports.normalize = function (pluginConfig, fallback) {
if (typeof pluginConfig === 'string') return relative(pluginConfig).bind(null, {})
if (pluginConfig && (typeof pluginConfig.path === 'string')) {
return relative(pluginConfig.path).bind(null, pluginConfig)
}
return require(fallback).bind(null, pluginConfig)
}
+20
View File
@@ -0,0 +1,20 @@
const SemanticReleaseError = require('@semantic-release/error')
module.exports = function (config, cb) {
const { plugins, lastRelease } = config
plugins.analyzeCommits(config, (err, type) => {
if (err) return cb(err)
if (!type) {
return cb(new SemanticReleaseError(
'There are no relevant changes, so no new version is released.',
'ENOCHANGE'
))
}
if (!lastRelease.version) return cb(null, 'initial')
cb(null, type)
})
}
+44
View File
@@ -0,0 +1,44 @@
const parseSlug = require('parse-github-repo-url')
const SemanticReleaseError = require('@semantic-release/error')
module.exports = function ({pkg, options, env}) {
let errors = []
if (!pkg.name) {
errors.push(new SemanticReleaseError(
'No "name" found in package.json.',
'ENOPKGNAME'
))
}
if (!pkg.repository || !pkg.repository.url) {
errors.push(new SemanticReleaseError(
'No "repository" found in package.json.',
'ENOPKGREPO'
))
} else if (!parseSlug(pkg.repository.url)) {
errors.push(new SemanticReleaseError(
'The "repository" field in the package.json is malformed.',
'EMALFORMEDPKGREPO'
))
}
if (options.debug) return errors
if (!options.githubToken) {
errors.push(new SemanticReleaseError(
'No github token specified.',
'ENOGHTOKEN'
))
}
if (!(env.NPM_TOKEN || (env.NPM_OLD_TOKEN && env.NPM_EMAIL))) {
errors.push(new SemanticReleaseError(
'No npm token specified.',
'ENONPMTOKEN'
))
}
return errors
}
+34 -34
View File
@@ -1,51 +1,51 @@
'use strict'
const url = require('url')
var readFile = require('fs').readFileSync
var url = require('url')
const gitHead = require('git-head')
const GitHubApi = require('github')
const parseSlug = require('parse-github-repo-url')
var gitHead = require('git-head')
var GitHubApi = require('github')
var parseSlug = require('parse-github-repo-url')
module.exports = function (config, cb) {
const { pkg, options, plugins } = config
const ghConfig = options.githubUrl ? url.parse(options.githubUrl) : {}
var efh = require('../lib/error').efh
module.exports = function (options, plugins, cb) {
var pkg = JSON.parse(readFile('./package.json'))
var repository = pkg.repository ? pkg.repository.url : null
if (!repository) return cb(new Error('Package must have a repository'))
var notesGenerator = require(plugins.notes || '../lib/release-notes')
var config = options['github-url'] ? url.parse(options['github-url']) : {}
var github = new GitHubApi({
const github = new GitHubApi({
version: '3.0.0',
port: config.port,
protocol: (config.protocol || '').split(':')[0] || null,
host: config.hostname
port: ghConfig.port,
protocol: (ghConfig.protocol || '').split(':')[0] || null,
host: ghConfig.hostname
})
notesGenerator(efh(cb)(function (log) {
gitHead(efh(cb)(function (hash) {
var ghRepo = parseSlug(repository)
var release = {
plugins.generateNotes(config, (err, log) => {
if (err) return cb(err)
gitHead((err, hash) => {
if (err) return cb(err)
const ghRepo = parseSlug(pkg.repository.url)
const release = {
owner: ghRepo[0],
repo: ghRepo[1],
tag_name: 'v' + pkg.version,
name: `v${pkg.version}`,
tag_name: `v${pkg.version}`,
target_commitish: hash,
draft: options.debug,
draft: !!options.debug,
body: log
}
if (options.debug && !options.githubToken) {
return cb(null, false, release)
}
github.authenticate({
type: 'oauth',
token: options.token
token: options.githubToken
})
github.releases.createRelease(release, efh(cb)(function () {
cb(null, true)
}))
}))
}))
github.releases.createRelease(release, (err) => {
if (err) return cb(err)
cb(null, true, release)
})
})
})
}
+40 -59
View File
@@ -1,64 +1,45 @@
'use strict'
const _ = require('lodash')
const auto = require('run-auto')
const semver = require('semver')
var fs = require('fs')
const getLastRelease = require('./lib/last-release')
const getCommits = require('./lib/commits')
const getType = require('./lib/type')
var semver = require('semver')
module.exports = function (config, cb) {
const {plugins} = config
auto({
lastRelease: getLastRelease.bind(null, config),
commits: ['lastRelease', (cb, results) => {
getCommits(_.assign({
lastRelease: results.lastRelease
}, config),
cb)
}],
type: ['commits', 'lastRelease', (cb, results) => {
getType(_.assign({
commits: results.commits,
lastRelease: results.lastRelease
}, config),
cb)
}]
}, (err, results) => {
if (err) return cb(err)
var getCommits = require('../lib/commits')
var npmInfo = require('../lib/npm-info')
var efh = require('../lib/error').efh
const nextRelease = {
type: results.type,
version: results.type === 'initial' ?
'1.0.0' :
semver.inc(results.lastRelease.version, results.type)
}
module.exports = function (options, plugins, cb) {
var path = './package.json'
var pkg = JSON.parse(fs.readFileSync(path))
if (!pkg.name) return cb(new Error('Package must have a name'))
npmInfo(pkg.name, efh(cb)(function (res) {
getCommits(res.gitHead, efh(cb)(function (commits) {
var analyzer = require(plugins.analyzer || '../lib/analyzer')
var type = analyzer(commits)
if (!type) return cb(null, null)
if (res.version) {
pkg.version = semver.inc(res.version, type)
} else {
type = 'major'
pkg.version = '1.0.0'
}
var writePkg = function () {
if (!options.debug) fs.writeFileSync(path, JSON.stringify(pkg, null, 2) + '\n')
cb(null, pkg.version)
}
if (!plugins.verification) return writePkg()
var opts = {}
if (typeof plugins.verification === 'string') {
opts.path = plugins.verification
}
if (typeof plugins.verification === 'object') {
opts = plugins.verification
opts.path = opts.path || opts.name
}
opts.type = type
opts.commits = commits
opts.version = res.version
opts.nextVersion = pkg.version
var verification = require(opts.path)
console.log('Running verification hook...')
verification(opts, function (error, ok) {
if (!error && ok) return writePkg()
console.log('Verification failed' + (error ? ': ' + error : ''))
process.exit(1)
})
}))
}))
plugins.verifyRelease(_.assign({
commits: results.commits,
lastRelease: results.lastRelease,
nextRelease
}, config), (err) => {
if (err) return cb(err)
cb(null, nextRelease)
})
})
}
-30
View File
@@ -1,30 +0,0 @@
'use strict'
var spawn = require('child_process').spawn
var exports = module.exports = function (cb) {
// npm loads package.json data before running the `prepublish` hook
// changing the version on `prepublish` has no effect
// see https://github.com/npm/npm/issues/7118
// to circumvent this behavior we are calling `npm publish` inside `prepublish`
// the package.json is then loaded again and the correct version will be published
var child = spawn('npm', ['publish', '--semantic-release-rerun'])
var handler = exports.handleCloseAndExit.bind(null, cb)
child.stdout.pipe(process.stdout)
child.stderr.pipe(process.stderr)
child.on('close', handler)
child.on('exit', handler)
child.on('error', cb)
}
exports.handleCloseAndExit = function (cb, code, signal) {
if (code === 0) return cb(null)
cb({
code: code,
signal: signal,
message: 'npm publish failed'
})
}
-42
View File
@@ -1,42 +0,0 @@
'use strict'
var fs = require('fs')
var ini = require('ini')
var ghUrl = require('github-url-from-git')
module.exports = function () {
var pkg = JSON.parse(fs.readFileSync('./package.json') + '')
// ensure a yet unpublished version
pkg.version = '0.0.0-semantically-released'
// set up scripts
var pre = 'semantic-release pre'
var post = 'semantic-release post'
if (!pkg.scripts) pkg.scripts = {}
if (!pkg.scripts.prepublish) pkg.scripts.prepublish = pre
else if (!(new RegExp(pre).test(pkg.scripts.prepublish))) pkg.scripts.prepublish += ' && ' + pre
if (!pkg.scripts.postpublish) pkg.scripts.postpublish = post
else if (!(new RegExp(post).test(pkg.scripts.postpublish))) pkg.scripts.postpublish += ' && ' + post
// set up repository
if (!pkg.repository || !pkg.repository.url) {
var config = ini.decode(fs.readFileSync('./.git/config') + '')
var repo = config['remote "origin"'].url
if (repo) pkg.repository = { type: 'git', url: ghUrl(repo) }
}
// set up devDependency
if (!pkg.devDependencies) pkg.devDependencies = {}
if (!pkg.devDependencies['semantic-release']) {
pkg.devDependencies['semantic-release'] = '^' + require('../package.json').version
}
fs.writeFileSync('./package.json', JSON.stringify(pkg, null, 2) + '\n')
}
-71
View File
@@ -1,71 +0,0 @@
'use strict'
var fs = require('fs')
var exports = module.exports = function (input) {
var options = exports.verifyOptions(input)
var pkg = exports.verifyPackage()
var travis = exports.verifyTravis()
return options && pkg && travis
}
exports.verifyTravis = function () {
try {
var travis = fs.readFileSync('.travis.yml') + ''
} catch (e) {
return true
}
var passed = true
if (!/\sdeploy:/m.test(travis)) {
console.error('You should configure deployments inside the ".travis.yml".')
passed = false
}
if (!/skip_cleanup:/m.test(travis)) {
console.error('You must set "skip_cleanup" to "true" inside the ".travis.yml".')
passed = false
}
return passed
}
exports.verifyPackage = function () {
var passed = true
try {
var pkg = fs.readFileSync('./package.json') + ''
} catch (e) {
console.error('You must have a "package.json" present.')
passed = false
pkg = '{}'
}
try {
pkg = JSON.parse(pkg)
} catch (e) {
console.error('You must have a "package.json" that is valid JSON.')
return false
}
if (!pkg.repository || !pkg.repository.url) {
console.error('You must define your GitHub "repository" inside the "package.json".')
passed = false
}
if (!pkg.scripts || !pkg.scripts.prepublish || !pkg.scripts.postpublish) {
console.error('You must define your "scripts" inside the "package.json".')
passed = false
}
return passed
}
exports.verifyOptions = function (options) {
if (!options) return true
if (options.token) return true
console.error('You must define a GitHub token.')
return false
}
+14
View File
@@ -0,0 +1,14 @@
const nixt = require('nixt')
module.exports = function (cwd, uri) {
return nixt()
.cwd(cwd)
.env('NPM_OLD_TOKEN', 'aW50ZWdyYXRpb246c3VjaHNlY3VyZQ==')
.env('NPM_EMAIL', 'integration@test.com')
.env('GH_TOKEN', 'ghtoken')
.env('CI', 'true')
.env('TRAVIS', 'true')
.env('TRAVIS_BRANCH', 'master')
.env('npm_config_registry', uri)
.clone()
}
+45
View File
@@ -0,0 +1,45 @@
const { exec } = require('child_process')
const { join } = require('path')
const { writeFileSync } = require('fs')
const mkdirp = require('mkdirp')
module.exports = function (name, cb) {
const cwd = join(__dirname, '../tmp', name)
mkdirp.sync(cwd)
writeFileSync(join(cwd, '.npmrc'), `
//localhost:1337/registry/_design/app/_rewrite/:username=integration
//localhost:1337/registry/_design/app/_rewrite/:email=integration@test.com`, null, 2)
writeFileSync(join(cwd, 'package.json'), JSON.stringify({
name,
repository: {
url: 'git+https://github.com/semantic-release/test'
},
_npmUser: {
name: 'integration',
email: 'integration@test.com'
},
maintainers: [{
name: 'integration',
email: 'integration@test.com'
}]
}, null, 2))
exec(`
git init &&
git config user.email "integration@test" &&
git config user.name "Integration Test" &&
git add . &&
git commit -m "chore: root"`
, {cwd}, (err, stdout, stderr) => {
if (err) {
console.log(stdout, stderr)
return cb(err)
}
cb(null, cwd)
})
}
+16
View File
@@ -0,0 +1,16 @@
const rawCommits = [
'hash-one==SPLIT==commit-one==END==\n',
'hash-two==SPLIT==commit-two==END==\n'
]
module.exports = {
exec: (command, cb) => {
cb(
null,
/\.\.HEAD/.test(command) ?
rawCommits[0] :
rawCommits.join()
)
},
'@noCallThru': true
}
+3
View File
@@ -0,0 +1,3 @@
module.exports = function (cb) {
cb(null, 'bar')
}
+6
View File
@@ -0,0 +1,6 @@
module.exports = () => ({
authenticate: () => true,
releases: {
createRelease: (release, cb) => cb(null)
}
})
+28
View File
@@ -0,0 +1,28 @@
const nock = require('nock')
const availableModule = {
'dist-tags': {
latest: '1.33.7',
foo: '0.8.15'
},
versions: {
'0.8.15': {
gitHead: 'bar'
},
'1.33.7': {
gitHead: 'HEAD'
}
}
}
module.exports = nock('http://registry.npmjs.org')
.get('/available')
.reply(200, availableModule)
.get('/tagged')
.reply(200, availableModule)
.get('/untagged')
.reply(200, availableModule)
.get('/@scoped/available')
.reply(200, availableModule)
.get('/unavailable')
.reply(404, {})
+12
View File
@@ -0,0 +1,12 @@
const { exec } = require('child_process')
const { join } = require('path')
const opts = {
cwd: join(__dirname, '../../test/registry')
}
module.exports = {
start: exec.bind(null, './start.sh', opts),
stop: exec.bind(null, './stop.sh', opts),
uri: 'http://localhost:15986/registry/_design/app/_rewrite/'
}
+19
View File
@@ -0,0 +1,19 @@
[couchdb]
database_dir = data
view_index_dir = data
delayed_commits = false
[couch_httpd_auth]
public_fields = appdotnet, avatar, avatarMedium, avatarLarge, date, email, fields, freenode, fullname, github, homepage, name, roles, twitter, type, _id, _rev
users_db_public = true
[httpd]
port = 15986
bind_address = 127.0.0.1
secure_rewrites = false
[log]
file = couch/couch.log
[admins]
admin = -pbkdf2-89582b49cd2e8443e29a841f5a76d367956a8e58,1afa2f1531a17ac97f2ac9e334293753,10
+36
View File
@@ -0,0 +1,36 @@
#!/bin/bash
# exit if an error occurs
set -e
cd $(dirname $0)
mkdir -p couch
# start couchdb as a background process, load local config, specify writable logfiles
if [[ $TRAVIS = true ]]
then
echo 'starting couch with sudo'
sudo couchdb -b -a local.ini -p couch/pid -o couch/stdout.log -e couch/stderr.log
else
couchdb -b -a local.ini -p couch/pid -o couch/stdout.log -e couch/stderr.log
fi
# wait for couch to start
sleep 5
COUCH=http://admin:password@127.0.0.1:15986
# create "registry" database
curl -X PUT $COUCH/registry
# create sample npm user
curl -X PUT $COUCH/_users/org.couchdb.user:integration -H Content-Type:application/json --data-binary '{"_id": "org.couchdb.user:integration","name": "integration","roles": [],"type": "user","password": "suchsecure","email": "integration@test.com"}'
# npm-registry-couchpp needs this variable set to run
export DEPLOY_VERSION=nope
# setup npm-registry-couchapp
npm explore npm-registry-couchapp -- npm start --npm-registry-couchapp:couch=$COUCH/registry
npm explore npm-registry-couchapp -- npm run load --npm-registry-couchapp:couch=$COUCH/registry
npm explore npm-registry-couchapp -- NO_PROMPT=yes npm run copy --npm-registry-couchapp:couch=$COUCH/registry
+12
View File
@@ -0,0 +1,12 @@
#!/bin/bash
# close couchdb background process
couchdb -d
# delete data and logs
cd $(dirname $0)
cat couch/{couch,stdout,stderr}.log
rm -rf couch
rm -rf data
+98
View File
@@ -0,0 +1,98 @@
const { join } = require('path')
const { test, tearDown } = require('tap')
const rimraf = require('rimraf')
const registry = require('../registry')
const testModule = require('../lib/test-module')
const baseScenario = require('../lib/base-scenario')
test('change version', (t) => {
t.plan(7)
registry.start((err) => {
t.error(err, 'registry started')
if (err) {
t.end()
t.bailout('registry not started')
}
testModule('change-version', (err, cwd) => {
t.error(err, 'test-module created')
if (err) {
t.end()
t.bailout('test-module not created')
}
t.test('no version', (tt) => {
tt.plan(1)
baseScenario(cwd, registry.uri)
.env('npm_config_loglevel', 'info')
.run('node ../../../bin/semantic-release.js pre')
.stderr(/ENOCHANGE/)
.code(1)
.end(tt.error)
})
t.test('initial version', (tt) => {
tt.plan(1)
baseScenario(cwd, registry.uri)
.exec('git commit -m "feat: initial" --allow-empty')
.exec('node ../../../bin/semantic-release.js pre')
.run('npm publish')
.stdout(/1\.0\.0/)
.code(0)
.end(tt.error)
})
t.test('patch version', (tt) => {
tt.plan(1)
baseScenario(cwd, registry.uri)
.exec('git commit -m "fix: foo" --allow-empty')
.exec('node ../../../bin/semantic-release.js pre')
.run('npm publish')
.stdout(/1\.0\.1/)
.code(0)
.end(tt.error)
})
t.test('feature version', (tt) => {
tt.plan(1)
baseScenario(cwd, registry.uri)
.exec('git commit -m "feat: foo" --allow-empty')
.exec('node ../../../bin/semantic-release.js pre')
.run('npm publish')
.code(0)
.stdout(/1\.1\.0/)
.end(tt.error)
})
t.test('breaking version', (tt) => {
tt.plan(1)
baseScenario(cwd, registry.uri)
.exec('git commit -m "feat: foo\n\n BREAKING CHANGE: bar" --allow-empty')
.exec('node ../../../bin/semantic-release.js pre')
.run('npm publish')
.code(0)
.stdout(/2\.0\.0/)
.end(tt.error)
})
})
})
})
tearDown(() => {
function cb (err, stdout, stderr) {
if (err) console.log(err)
if (stdout) console.log(stdout)
if (stderr) console.log(stderr)
}
rimraf(join(__dirname, '../tmp'), cb)
registry.stop(cb)
})
+32
View File
@@ -0,0 +1,32 @@
const test = require('tap').test
const proxyquire = require('proxyquire')
const commits = proxyquire('../../dist/lib/commits', {
'child_process': require('../mocks/child-process')
})
test('commits since last release', (t) => {
t.test('get all commits', (tt) => {
commits({lastRelease: {}}, (err, commits) => {
tt.error(err)
tt.is(commits.length, 2, 'all commits')
tt.is(commits[0].hash, 'hash-one', 'parsed hash')
tt.is(commits[1].message, 'commit-two', 'parsed message')
tt.end()
})
})
t.test('get commits since hash', (tt) => {
commits({lastRelease: {gitHead: 'hash'}}, (err, commits) => {
tt.error(err)
tt.is(commits.length, 1, 'specified commits')
tt.is(commits[0].hash, 'hash-one', 'parsed hash')
tt.is(commits[0].message, 'commit-one', 'parsed message')
tt.end()
})
})
t.end()
})
+79
View File
@@ -0,0 +1,79 @@
const { defaults } = require('lodash')
const test = require('tap').test
require('../mocks/registry')
const lastRelease = require('../../dist/lib/last-release')
const npm = {
registry: 'http://registry.npmjs.org/',
tag: 'latest'
}
test('last release from registry', (t) => {
t.plan(5)
t.test('get release from package name', (tt) => {
lastRelease({
pkg: {name: 'available'},
npm
}, (err, release) => {
tt.error(err)
tt.is(release.version, '1.33.7', 'version')
tt.is(release.gitHead, 'HEAD', 'gitHead')
tt.is(release.tag, 'latest', 'dist-tag')
tt.end()
})
})
t.test('get release from a tagged package\'s name', (tt) => {
lastRelease({
pkg: {name: 'tagged'},
npm: defaults({tag: 'foo'}, npm)
}, (err, release) => {
tt.error(err)
tt.is(release.version, '0.8.15', 'version')
tt.is(release.gitHead, 'bar', 'gitHead')
tt.is(release.tag, 'foo', 'dist-tag')
tt.end()
})
})
t.test('get error from an untagged package\'s name', (tt) => {
lastRelease({
pkg: {name: 'untagged'},
npm: defaults({tag: 'bar'}, npm)
}, (err) => {
tt.is(err.code, 'ENODISTTAG', 'error')
tt.end()
})
})
t.test('get release from scoped package name', (tt) => {
lastRelease({
pkg: {name: '@scoped/available'},
npm
}, (err, release) => {
tt.error(err)
tt.is(release.version, '1.33.7', 'version')
tt.is(release.gitHead, 'HEAD', 'gitHead')
tt.is(release.tag, 'latest', 'dist-tag')
tt.end()
})
})
t.test('get nothing from not yet published package name', (tt) => {
lastRelease({
pkg: {name: 'unavailable'},
npm
}, (err, release) => {
tt.error(err)
tt.is(release.version, undefined, 'no version')
tt.end()
})
})
})
+43
View File
@@ -0,0 +1,43 @@
const test = require('tap').test
const plugins = require('../../dist/lib/plugins')
test('export plugins', (t) => {
t.plan(4)
const defaultPlugins = plugins({})
t.is(typeof defaultPlugins.analyzeCommits, 'function')
t.is(typeof defaultPlugins.generateNotes, 'function')
t.is(typeof defaultPlugins.verifyConditions, 'function')
t.is(typeof defaultPlugins.verifyRelease, 'function')
})
test('normalize and load plugin', (t) => {
t.test('load from string', (tt) => {
const plugin = plugins.normalize('./dist/lib/plugin-noop')
tt.is(typeof plugin, 'function')
tt.end()
})
t.test('load from object', (tt) => {
const plugin = plugins.normalize({
path: './dist/lib/plugin-noop'
})
tt.is(typeof plugin, 'function')
tt.end()
})
t.test('load from object', (tt) => {
const plugin = plugins.normalize(null, '../../dist/lib/plugin-noop')
tt.is(typeof plugin, 'function')
tt.end()
})
})
+68
View File
@@ -0,0 +1,68 @@
const { defaults } = require('lodash')
const test = require('tap').test
const proxyquire = require('proxyquire')
const post = proxyquire('../../dist/post', {
'git-head': require('../mocks/git-head'),
github: require('../mocks/github')
})
const pkg = {
version: '1.0.0',
repository: {url: 'http://github.com/whats/up.git'}
}
const plugins = {generateNotes: (pkg, cb) => cb(null, 'the log')}
const defaultRelease = {
owner: 'whats',
repo: 'up',
name: 'v1.0.0',
tag_name: 'v1.0.0',
target_commitish: 'bar',
body: 'the log'
}
test('full post run', (t) => {
t.test('in debug mode w/o token', (tt) => {
post({
options: {debug: true},
pkg,
plugins
}, (err, published, release) => {
tt.error(err)
tt.is(published, false)
tt.match(release, defaults({draft: true}, defaultRelease))
tt.end()
})
})
t.test('in debug mode w/token', (tt) => {
post({
options: {debug: true, githubToken: 'yo'},
pkg,
plugins
}, (err, published, release) => {
tt.error(err)
tt.is(published, true)
tt.match(release, defaults({draft: true}, defaultRelease))
tt.end()
})
})
t.test('production', (tt) => {
post({
options: {githubToken: 'yo'},
pkg,
plugins
}, (err, published, release) => {
tt.error(err)
tt.is(published, true)
tt.match(release, defaultRelease)
tt.end()
})
})
})
+50
View File
@@ -0,0 +1,50 @@
const test = require('tap').test
const proxyquire = require('proxyquire')
require('../mocks/registry')
const pre = proxyquire('../../dist/pre', {
'child_process': require('../mocks/child-process')
})
const plugins = {
verifyRelease: (release, cb) => cb(null, release),
analyzeCommits: (commits, cb) => cb(null, 'major')
}
const npm = {
registry: 'http://registry.npmjs.org/',
tag: 'latest'
}
test('full pre run', (t) => {
t.test('increase version', (tt) => {
tt.plan(3)
pre({
npm,
pkg: {name: 'available'},
plugins
}, (err, release) => {
tt.error(err)
tt.is(release.type, 'major')
tt.is(release.version, '2.0.0')
})
})
t.test('increase version', (tt) => {
tt.plan(3)
pre({
npm,
pkg: {name: 'unavailable'},
plugins
}, (err, release) => {
tt.error(err)
tt.is(release.type, 'initial')
tt.is(release.version, '1.0.0')
})
})
t.end()
})
+48
View File
@@ -0,0 +1,48 @@
const test = require('tap').test
const type = require('../../dist/lib/type')
test('get type from commits', (t) => {
t.test('get type from plugin', (tt) => {
tt.plan(2)
type({
commits: [{
hash: '0',
message: 'a'
}],
lastRelease: {version: '1.0.0'},
plugins: {analyzeCommits: (config, cb) => cb(null, 'major')}
}, (err, type) => {
tt.error(err)
tt.is(type, 'major')
})
})
t.test('error when no changes', (tt) => {
tt.plan(1)
type({
commits: [],
lastRelease: {},
plugins: {analyzeCommits: (config, cb) => cb(null, null)}
}, (err) => {
tt.is(err.code, 'ENOCHANGE')
})
})
t.test('initial version', (tt) => {
tt.plan(2)
type({
commits: [],
lastRelease: {},
plugins: {analyzeCommits: (config, cb) => cb(null, 'major')}
}, (err, type) => {
tt.error(err)
tt.is(type, 'initial')
})
})
t.end()
})
+68
View File
@@ -0,0 +1,68 @@
const test = require('tap').test
const verify = require('../../dist/lib/verify')
test('verify pkg, options and env', (t) => {
t.test('dry run verification', (tt) => {
const noErrors = verify({
options: {debug: true},
pkg: {
name: 'package',
repository: {
url: 'http://github.com/whats/up.git'
}
}
})
tt.is(noErrors.length, 0)
const errors = verify({
options: {debug: true},
pkg: {}
})
tt.is(errors.length, 2)
tt.is(errors[0].code, 'ENOPKGNAME')
tt.is(errors[1].code, 'ENOPKGREPO')
const errors2 = verify({
options: {debug: true},
pkg: {
name: 'package',
repository: {
url: 'lol'
}
}
})
tt.is(errors2.length, 1)
tt.is(errors2[0].code, 'EMALFORMEDPKGREPO')
tt.end()
})
t.test('publish verification', (tt) => {
const noErrors = verify({
env: {NPM_TOKEN: 'yo'},
options: {githubToken: 'sup'},
pkg: {
name: 'package',
repository: {
url: 'http://github.com/whats/up.git'
}
}
})
tt.is(noErrors.length, 0)
const errors = verify({env: {}, options: {}, pkg: {}})
tt.is(errors.length, 4)
tt.is(errors[0].code, 'ENOPKGNAME')
tt.is(errors[1].code, 'ENOPKGREPO')
tt.is(errors[2].code, 'ENOGHTOKEN')
tt.is(errors[3].code, 'ENONPMTOKEN')
tt.end()
})
})
-1
View File
@@ -1 +0,0 @@
test:{SHA}C+7Hteo/D9vJXQ3UfzxbwnXaijM=
-3
View File
@@ -1,3 +0,0 @@
_auth=dGVzdDpmb28=
email=test@example.com
registry=http://127.0.0.1:4873/
-17
View File
@@ -1,17 +0,0 @@
storage: ../.tmp/.storage
auth:
htpasswd:
file: ./.htpasswd
uplinks:
npmjs:
url: https://registry.npmjs.org/
logs:
- {type: stdout, format: pretty, level: silent}
packages:
'*':
allow_access: all
allow_publish: all
-14
View File
@@ -1,14 +0,0 @@
'use strict'
var test = require('tape')
var createModule = require('./lib/create-module')
require('./scenarios/custom-analyzer')(test, createModule)
require('./scenarios/custom-verification')(test, createModule)
require('./scenarios/ignore')(test, createModule)
require('./scenarios/prepublish')(test, createModule)
require('./scenarios/postpublish')(test, createModule)
require('./scenarios/publish')(test, createModule)
require('./scenarios/verify')(test, createModule)
require('./scenarios/setup')(test, createModule)
-37
View File
@@ -1,37 +0,0 @@
'use strict'
var fs = require('fs')
var exec = require('child_process').exec
var nixt = require('nixt')
module.exports = function (t, message, version, code, name, cwd) {
t.test(name, function (t) {
t.plan(3)
nixt()
.cwd(cwd)
.env('CI', true)
.env('npm_config_registry', 'http://127.0.0.1:4873/')
.exec('git commit --allow-empty -m "' + message + '"')
.run('npm run prepublish')
.code(code)
.stdout(/> semantic-release pre\n\nDetermining new version\n/m)
.end(function (err) {
t.error(err, 'nixt')
var pkg = JSON.parse(fs.readFileSync(cwd + '/package.json'))
t.is(pkg.version, version, 'version')
if (code === 1) {
return t.error(null, 'no publish')
}
exec('npm publish --ignore-scripts', {cwd: cwd}, function (err) {
setTimeout(function () {
t.error(err, 'publish')
}, 300)
})
})
})
}
-45
View File
@@ -1,45 +0,0 @@
'use strict'
var exec = require('child_process').exec
var join = require('path').join
var efh = require('error-first-handler')
var defaults = require('lodash.defaults')
var uid = require('nano-uid')()
module.exports = function (input) {
var cb = Array.prototype.pop.call(arguments)
uid.generate(5, efh(cb)(function (id) {
var pkg = defaults((typeof input === 'object' ? input : {}), {
name: id,
version: '0.0.0',
devDependencies: {
'semantic-release': 'file:../../../'
},
scripts: {
prepublish: 'semantic-release pre',
postpublish: 'semantic-release post'
},
publishConfig: {
registry: 'http://localhost:4873/'
}
})
id = pkg.name
var cwd = join(__dirname, '../../.tmp/modules', id)
exec(
'mkdir ' + cwd + ' && ' +
'cd ' + cwd + ' && ' +
'git init && ' +
'echo \'' + JSON.stringify(pkg, null, 2) + '\' >> package.json && ' +
'git add . && ' +
'git config user.email "integration@test" && ' +
'git config user.name "Integration Test" && ' +
'git commit -m "initial" && ' +
'npm install'
, efh(cb)(function (stdout) {
cb(null, id, cwd)
}))
}))
}
-20
View File
@@ -1,20 +0,0 @@
'use strict'
module.exports = function (commits) {
var type = null
commits.every(function (commit) {
if (/FOO/.test(commit.message)) {
type = 'major'
return false
}
if (/BAR/.test(commit.message)) type = 'minor'
if (!type && /BAZ/.test(commit.message)) type = 'patch'
return true
})
return type
}
-5
View File
@@ -1,5 +0,0 @@
'use strict'
module.exports = function (cb) {
cb(null, 'custom log')
}
-5
View File
@@ -1,5 +0,0 @@
'use strict'
module.exports = function (opts, cb) {
cb(null, !(opts.commits.length % 2))
}
-23
View File
@@ -1,23 +0,0 @@
'use strict'
var path = require('path')
var efh = require('error-first-handler')
var commitToVersionTest = require('../lib/commit-to-version-test')
module.exports = function (test, createModule) {
createModule({
release: {
analyzer: path.join(__dirname, '../lib/custom-analyzer')
}
}, efh()(function (name, cwd) {
test('custom-analyzer', function (t) {
commitToVersionTest(t, 'HO', '0.0.0', 1, 'abort publish w/o changes', cwd)
commitToVersionTest(t, 'BAZ', '1.0.0', 0, 'release 1.0.0 if unpublished', cwd)
commitToVersionTest(t, 'BAZ', '1.0.1', 0, 'bump patch for fix', cwd)
commitToVersionTest(t, 'BAR', '1.1.0', 0, 'bump minor for feature', cwd)
commitToVersionTest(t, 'FOO', '2.0.0', 0, 'bump major for breaking change', cwd)
})
}))
}
-45
View File
@@ -1,45 +0,0 @@
'use strict'
var path = require('path')
var efh = require('error-first-handler')
var nixt = require('nixt')
module.exports = function (test, createModule) {
createModule({
release: {
verification: path.join(__dirname, '../lib/custom-verification')
}
}, efh()(function (name, cwd) {
test('custom-verification', function (t) {
t.test('even commit count', function (t) {
t.plan(1)
nixt()
.cwd(cwd)
.env('CI', true)
.env('npm_config_registry', 'http://127.0.0.1:4873/')
.exec('git commit --allow-empty -m "feat: commit"')
.run('npm run prepublish')
.code(0)
.end(function (err) {
t.error(err, 'nixt')
})
})
t.test('odd commit count', function (t) {
t.plan(1)
nixt()
.cwd(cwd)
.env('CI', true)
.env('npm_config_registry', 'http://127.0.0.1:4873/')
.exec('git commit --allow-empty -m "feat: commit"')
.run('npm run prepublish')
.code(1)
.stdout(/Verification failed/)
.end(function (err) {
t.error(err, 'nixt')
})
})
})
}))
}
-37
View File
@@ -1,37 +0,0 @@
'use strict'
var fs = require('fs')
var efh = require('error-first-handler')
var nixt = require('nixt')
module.exports = function (test, createModule) {
createModule(efh()(function (name, cwd) {
test('ignore', function (t) {
ignoreTest(t, 'npm install', 'not doing anything when the module is installed')
ignoreTest(t, 'npm i', 'not doing anything when the module is installed with abbrevd command')
ignoreTest(t, 'npm link', 'not doing anything when the module is linked')
ignoreTest(t, 'npm lin', 'not doing anything when the module is linked with abbrevd command')
ignoreTest(t, 'npm pack', 'not doing anything when the module is packed')
ignoreTest(t, 'npm pa', 'not doing anything when the module is packed with abbrevd command')
})
function ignoreTest (t, command, name, last) {
t.test(name, function (t) {
t.plan(2)
var pkg = fs.readFileSync(cwd + '/package.json')
nixt()
.cwd(cwd)
.run(command)
.code(0)
.stdout(/> semantic-release pre\n$/m)
.end(function (err) {
t.is(pkg + '', fs.readFileSync(cwd + '/package.json') + '', 'package')
t.error(err, 'nixt')
})
})
}
}))
}
-115
View File
@@ -1,115 +0,0 @@
'use strict'
var path = require('path')
var efh = require('error-first-handler')
var GitHubApi = require('github')
var nixt = require('nixt')
var github = new GitHubApi({
version: '3.0.0',
port: 4343,
protocol: 'http',
host: '127.0.0.1'
})
github.authenticate({
type: 'oauth',
token: '***'
})
module.exports = function (test, createModule) {
createModule({
version: '2.0.0',
repository: {
type: 'git',
url: 'http://github.com/user/repo'
}
}, efh()(function (name, cwd) {
test('postpublish', function (t) {
var base = getBase(cwd)
t.test('publish new version to github releases', function (t) {
t.plan(1)
base.clone()
.stdout(/> semantic-release post\n\nGenerating changelog from.*\nParsed/m)
.run('npm run postpublish')
.end(function (err) {
t.error(err, 'nixt')
})
})
t.test('publish new version (with detached HEAD) to github releases', function (t) {
t.plan(1)
base.clone()
.stdout(/> semantic-release post\n\nGenerating changelog from.*\nParsed/m)
.exec('git checkout `git rev-parse HEAD`')
.run('npm run postpublish')
.end(function (err) {
t.error(err, 'nixt')
})
})
t.test('correct data published', function (t) {
t.plan(4)
github.releases.getRelease({ owner: 'user', repo: 'repo', id: 1}, function (err, raw) {
var res = JSON.parse(raw)
t.error(err, 'github')
t.is(res.tag_name, 'v2.0.0', 'version')
t.is(res.author.login, 'user', 'user')
t.ok(/\n\n\n#### Features\n\n\* \*\*cool:\*\*\n.*the next big thing/.test(res.body), 'body')
})
})
})
}))
createModule({
version: '2.0.0',
repository: {
type: 'git',
url: 'http://github.com/user/repo'
},
release: {
notes: path.join(__dirname, '../lib/custom-release-notes')
}
}, efh()(function (name, cwd) {
test('custom-release-notes', function (t) {
var base = getBase(cwd)
t.test('publish new version (with custom notes) to github releases', function (t) {
t.plan(1)
base.clone()
.run('npm run postpublish')
.end(function (err) {
t.error(err, 'nixt')
})
})
t.test('custom notes published', function (t) {
t.plan(4)
github.releases.getRelease({ owner: 'user', repo: 'repo', id: 3}, function (err, raw) {
var res = JSON.parse(raw)
t.error(err, 'github')
t.is(res.tag_name, 'v2.0.0', 'version')
t.is(res.author.login, 'user', 'user')
t.ok(/custom log/.test(res.body), 'body')
})
})
})
}))
}
function getBase (cwd) {
return nixt()
.cwd(cwd)
.env('CI', true)
.env('GH_URL', 'http://127.0.0.1:4343/')
.env('GH_TOKEN', '***')
.exec('git commit --allow-empty -m "feat(cool): the next big thing"')
.code(0)
}
-17
View File
@@ -1,17 +0,0 @@
'use strict'
var efh = require('error-first-handler')
var commitToVersionTest = require('../lib/commit-to-version-test')
module.exports = function (test, createModule) {
createModule(efh()(function (name, cwd) {
test('prepublish', function (t) {
commitToVersionTest(t, 'refactor: change', '0.0.0', 1, 'abort publish w/o changes', cwd)
commitToVersionTest(t, 'fix: change', '1.0.0', 0, 'release 1.0.0 if unpublished', cwd)
commitToVersionTest(t, 'fix: change', '1.0.1', 0, 'bump patch for fix', cwd)
commitToVersionTest(t, 'feat: change', '1.1.0', 0, 'bump minor for feature', cwd)
commitToVersionTest(t, 'fix: BREAKING CHANGE: change', '2.0.0', 0, 'bump major for breaking change', cwd)
})
}))
}
-37
View File
@@ -1,37 +0,0 @@
'use strict'
var efh = require('error-first-handler')
var nixt = require('nixt')
module.exports = function (test, createModule) {
test('publish', function (t) {
publishTest(t, 'npm publish', 'pre and post hooks work as a part of publish')
publishTest(t, 'npm pub', 'pre and post hooks work as a part of publish with abbrevd command')
function publishTest (t, command, testname, last) {
createModule({
repository: {
type: 'git',
url: 'http://github.com/user/repo'
}
}, efh()(function (name, cwd) {
t.test(testname, function (t) {
t.plan(1)
nixt()
.cwd(cwd)
.env('CI', true)
.env('GH_URL', 'http://127.0.0.1:4343/')
.env('GH_TOKEN', '***')
.exec('git commit --allow-empty -m "feat: super"')
.run(command)
.code(1)
.stdout(/Everything is alright/)
.end(function (err) {
t.error(err, 'nixt')
})
})
}))
}
})
}
-38
View File
@@ -1,38 +0,0 @@
'use strict'
var join = require('path').join
var readFile = require('fs').readFileSync
var efh = require('error-first-handler')
var nixt = require('nixt')
module.exports = function (test, createModule) {
createModule({
repository: {},
scripts: {
postpublish: 'npm run gh-pages'
}
}, efh()(function (name, cwd) {
test('setup', function (t) {
t.test('setup "package.json"', function (t) {
t.plan(5)
nixt()
.cwd(cwd)
.exec('git remote add origin git@github.com:user/repo.git')
.run('./node_modules/.bin/semantic-release setup')
.code(0)
.end(function (err) {
t.error(err, 'nixt')
var pkg = JSON.parse(readFile(join(cwd, 'package.json')))
t.is(pkg.version, '0.0.0-semantically-released', 'version')
t.is(pkg.repository.url, 'https://github.com/user/repo', 'repo')
t.is(pkg.scripts.prepublish, 'semantic-release pre', 'pre')
t.is(pkg.scripts.postpublish, 'npm run gh-pages && semantic-release post', 'post')
})
})
})
}))
}
-50
View File
@@ -1,50 +0,0 @@
'use strict'
var efh = require('error-first-handler')
var nixt = require('nixt')
module.exports = function (test, createModule) {
test('verify', function (t) {
createModule({
repository: {},
scripts: {
prepublish: 'semantic-release pre --no-token'
}
}, efh()(function (name, cwd) {
t.test('verify package and options before publishing', function (t) {
t.plan(1)
nixt()
.cwd(cwd)
.env('CI', true)
.run('npm publish')
.stderr(new RegExp(
'You must define a GitHub token\.\n' +
'You must define your GitHub "repository" inside the "package.json"\.\n' +
'You must define your "scripts" inside the "package.json"\.'
, 'm'
))
.code(1)
.end(function (err) {
t.error(err, 'nixt')
})
})
}))
createModule({
version: '1.0.0-semantically-released'
}, efh()(function (name, cwd) {
t.test('not publishing placeholder versions', function (t) {
t.plan(1)
nixt()
.cwd(cwd)
.env('CI', true)
.run('npm publish --semantic-release-rerun')
.code(1)
.end(function (err) {
t.error(err, 'nixt')
})
})
}))
})
}