Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ddaec4f2bf | ||
|
|
22a283a027 | ||
|
|
d9eeb3fcae | ||
|
|
8892ec7f7a | ||
|
|
cf27cd16cc | ||
|
|
152d1f6874 | ||
|
|
626315e9fe | ||
|
|
f5573f7494 | ||
|
|
2d3506cda1 | ||
|
|
72f9f8ea7c | ||
|
|
59d852d6fb | ||
|
|
ed05101a61 | ||
|
|
431babb42e | ||
|
|
dbea5cc1b1 | ||
|
|
6cf1711151 | ||
|
|
ba5222af52 | ||
|
|
b1d9985ba3 | ||
|
|
13e2f5ce78 | ||
|
|
af15fb4c61 | ||
|
|
caf09c9be0 | ||
|
|
0316babf7a | ||
|
|
43c8df0bfe | ||
|
|
150273c8fe | ||
|
|
f247ed8741 | ||
|
|
2eff4a4467 | ||
|
|
e1d5da5864 | ||
|
|
a0190b7ad3 | ||
|
|
c6f95e4870 | ||
|
|
6d84b66c05 | ||
|
|
4d58af7145 | ||
|
|
9a3d9a9280 | ||
|
|
32c1dcf75b | ||
|
|
4093bb2dbe | ||
|
|
c59b5cac9c | ||
|
|
44a18fd5cf | ||
|
|
e24c7c3833 | ||
|
|
057e8e9759 | ||
|
|
da23c15ea5 | ||
|
|
b63a43fa53 | ||
|
|
c47ff97ab3 | ||
|
|
689f37ae2e | ||
|
|
a4fe18cade | ||
|
|
7412401d55 | ||
|
|
6df67828fe | ||
|
|
66bf8ff710 | ||
|
|
a405021d08 | ||
|
|
f79e26dcd6 | ||
|
|
861debb85e | ||
|
|
d6172b8767 | ||
|
|
f62525ad7e | ||
|
|
cce860ec03 | ||
|
|
56238887d3 | ||
|
|
ac7037d948 |
@@ -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
@@ -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
@@ -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
-23
@@ -1,36 +1,22 @@
|
||||
language: node_js
|
||||
node_js:
|
||||
- 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
|
||||
|
||||
@@ -1,207 +1,20 @@
|
||||
# semantic-release
|
||||
[](https://travis-ci.org/boennemann/semantic-release)
|
||||
[](https://david-dm.org/boennemann/semantic-release)
|
||||
[](https://david-dm.org/boennemann/semantic-release#info=devDependencies)
|
||||
[](https://travis-ci.org/semantic-release/semantic-release)
|
||||
[](https://coveralls.io/github/semantic-release/semantic-release?branch=next)
|
||||
[](https://david-dm.org/semantic-release/semantic-release/next)
|
||||
[](https://david-dm.org/semantic-release/semantic-release/next#info=devDependencies)
|
||||
[](https://github.com/feross/standard)
|
||||
|
||||
[](https://nodei.co/npm/semantic-release/)
|
||||
|
||||
## What is this thing even?
|
||||
## What is `semantic-release` about?
|
||||
|
||||
`semantic-release` is a toolset to fully automate your package's releases. This will determine not only which version to release, but also when – all without you having to care about it ever again.
|
||||
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.
|
||||
|
||||
This is fully integrated with the `npm` lifecycle, so all you have to do is to setup your CI to `npm publish`.
|
||||
[](https://www.youtube.com/watch?v=tc2UgG5L7WM&index=6&list=PLFZ5NyC0xHDaaTy6tY9p0C0jd_rRRl5Zm)
|
||||
|
||||
The goal of this package is to remove humans from version numbers and releases. The [SemVer](http://semver.org/) spec clearly and unambiguously defines when to increase the major, minor or patch part and still we tend to think we're clever when we ignore this, because marketing or something.
|
||||
> This talk gives you a complete introduction to the underlying concepts
|
||||
|
||||

|
||||
|
||||
## How does this work?
|
||||
|
||||
Conventions, conventions, conventions. Instead of dumping [funny 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 meaningful changelog, but to determine the next semantic version to release. Currently the only supported style is the [AngularJS Commit Message Convention](https://docs.google.com/document/d/1QrDFcIiPjSLDn3EL15IJygNPiHORgU1_OOAqWjiDU5Y/edit) style, but feel free to formalize your own style, write a parser for it, and send a PR to this package.
|
||||
|
||||
```
|
||||
<type>(<scope>): <subject>
|
||||
<BLANK LINE>
|
||||
<body>
|
||||
```
|
||||
|
||||
Examples:
|
||||
|
||||
```
|
||||
feat(ruler): add inches as well as centimeters
|
||||
```
|
||||
|
||||
```
|
||||
fix(protractor): fix 90 degrees counting as 91 degrees
|
||||
```
|
||||
|
||||
```
|
||||
fix(pen): use blue ink instead of red ink
|
||||
|
||||
BREAKING CHANGE: Pen now uses blue ink instead of red.
|
||||
|
||||
To migrate, change your code from the following:
|
||||
|
||||
`pen.draw('blue')`
|
||||
|
||||
To:
|
||||
|
||||
`pen.draw('red')`
|
||||
```
|
||||
|
||||
[Synopsis](https://github.com/ajoslin/conventional-changelog/blob/master/CONVENTIONS.md)
|
||||
|
||||
The preferred configuration is the "try to release on every push" mode. What it does is that everytime a build passes `npm publish` is executed.
|
||||
|
||||
### The `prepublish` step
|
||||
|
||||
Before `npm` actually gets to publish a new version `semantic-release`'s `prepublish` step does the following:
|
||||
|
||||
- Analyze the commits since the last version was published
|
||||
- Decide on the release type (`major`|`minor`|`patch`) or abort if nothing changed
|
||||
- Get the last published version from the registry
|
||||
- Increase the last version with the determined type
|
||||
- Write the new version to the package
|
||||
|
||||
### The `publish` step
|
||||
|
||||
`npm` does its thing.
|
||||
|
||||
### The `postpublish` step
|
||||
|
||||
After `npm` published the new version the `postpublish` step does this:
|
||||
|
||||
- Generate a changelog
|
||||
- Create a new [GitHub Release](https://help.github.com/articles/about-releases/) with the changelog as body
|
||||
|
||||
Note: The GitHub Release automatically creates a tag, too.
|
||||
|
||||
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 an error that you can *safely ignore* [npm/npm#7118](https://github.com/npm/npm/issues/7118).
|
||||
|
||||
## How do I set this up?
|
||||
|
||||
First of all you need to install `semantic-release` and save it as a `devDependency`.
|
||||
|
||||
### Installation
|
||||
|
||||
```bash
|
||||
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 6to5 && 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
|
||||
sudo: false
|
||||
cache:
|
||||
directories:
|
||||
- node_modules
|
||||
notifications:
|
||||
email: false
|
||||
env:
|
||||
# Get your token here: https://github.com/settings/tokens/new
|
||||
# You should encrypt this:
|
||||
# `travis encrypt GH_TOKEN=<token> --add`
|
||||
global: GH_TOKEN=<github-access-token-with-acceess-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: For once this isn't tied to a specific service, but example configuration is shown for [Travis CI](https://travis-ci.org/). Feel free to contribute configuration of 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.
|
||||
|
||||
Note: If you have a more sophisticated build with multiple jobs you should have a look at [travis-after-all](https://github.com/dmakhno/travis_after_all), which is also configured for this [package](.travis.yml).
|
||||
|
||||
## 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 when 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 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: pre-release flags are kind of an exeption here and a solution for them is being thought of. If you have one please open an issue. For the time being: Have a look at the next question.
|
||||
|
||||
### How do I get back to good ol' `npm publish`?
|
||||
|
||||
`npm` offers the `--no-scripts` flag. Doing `npm publish --no-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`. 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)
|
||||
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).
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
cat .tmp/sinopia.pid | xargs kill
|
||||
|
||||
cat .tmp/ghrs.pid | xargs kill
|
||||
|
||||
cp .tmp/.npmrc ~/.npmrc
|
||||
|
||||
rm -rf .tmp
|
||||
@@ -1,16 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
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
|
||||
+9
-75
@@ -1,79 +1,13 @@
|
||||
#!/usr/bin/env node
|
||||
'use strict'
|
||||
|
||||
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 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']) process.exit(0)
|
||||
// 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')) 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, 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, 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)))
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
if (err.message) return console.log(err.message)
|
||||
console.log(err)
|
||||
})
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
'use strict'
|
||||
|
||||
module.exports = function (commits) {
|
||||
var type = null
|
||||
|
||||
commits.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
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
'use strict'
|
||||
|
||||
var git = require('conventional-changelog/lib/git')
|
||||
|
||||
var efh = require('../error').efh
|
||||
|
||||
module.exports = function (cb) {
|
||||
git.latestTag(efh(cb)(function (from) {
|
||||
git.getCommits({from: from}, efh(cb)(function (commits) {
|
||||
cb(null, commits)
|
||||
}))
|
||||
}))
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
'use strict'
|
||||
|
||||
var analyze = require('./analyze')
|
||||
var commits = require('./commits')
|
||||
var efh = require('../error').efh
|
||||
|
||||
module.exports = function (cb) {
|
||||
commits(efh(cb)(function (commits) {
|
||||
cb(null, analyze(commits))
|
||||
}))
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
'use strict'
|
||||
|
||||
var request = require('request')
|
||||
|
||||
var efh = require('./error').efh
|
||||
|
||||
module.exports = function (pkg, cb) {
|
||||
if (!pkg.name) return cb(new Error('Package must have a name'))
|
||||
|
||||
request(process.env.npm_config_registry + pkg.name, efh(cb)(function (response, body) {
|
||||
var pkg = JSON.parse(body)
|
||||
|
||||
if (response.statusCode === 404 || pkg.error) return cb(null, null, true)
|
||||
|
||||
cb(null, pkg['dist-tags'].latest)
|
||||
}))
|
||||
}
|
||||
+68
-37
@@ -1,62 +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.11",
|
||||
"error-first-handler": "^1.0.1",
|
||||
"github": "^0.2.3",
|
||||
"github-url-from-git": "^1.4.0",
|
||||
"ini": "^1.3.2",
|
||||
"minimist": "^1.1.0",
|
||||
"@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",
|
||||
"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.2.0"
|
||||
"require-relative": "^0.8.7",
|
||||
"run-auto": "^1.1.2",
|
||||
"semver": "^5.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"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": "^2.3.1",
|
||||
"tap-spec": "^2.2.0",
|
||||
"tape": "^3.5.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",
|
||||
"main": "dist/index.js",
|
||||
"publishConfig": {
|
||||
"tag": "next"
|
||||
},
|
||||
"release": {
|
||||
"branch": "next"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/boennemann/semantic-release.git"
|
||||
"url": "git+https://github.com/semantic-release/semantic-release.git"
|
||||
},
|
||||
"scripts": {
|
||||
"postpublish": "./bin/semantic-release.js post",
|
||||
"posttest": "./bin/post-test",
|
||||
"prepublish": "./bin/semantic-release.js pre",
|
||||
"pretest": "./bin/pre-test",
|
||||
"test": "standard && node tests | tap-spec"
|
||||
"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
@@ -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"`)
|
||||
}
|
||||
})
|
||||
@@ -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]
|
||||
}
|
||||
}))
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -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
|
||||
})
|
||||
})
|
||||
}
|
||||
@@ -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), '.')
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
/* istanbul ignore next */
|
||||
module.exports = function (config, options, cb) {
|
||||
cb(null)
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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)
|
||||
})
|
||||
}
|
||||
@@ -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
-38
@@ -1,55 +1,51 @@
|
||||
'use strict'
|
||||
const url = require('url')
|
||||
|
||||
var exec = require('child_process').exec
|
||||
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 changelog = require('conventional-changelog')
|
||||
var GitHubApi = require('github')
|
||||
var parseSlug = require('parse-github-repo-url')
|
||||
var parseUrl = require('github-url-from-git')
|
||||
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, 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 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
|
||||
})
|
||||
|
||||
changelog({
|
||||
version: pkg.version,
|
||||
repository: parseUrl(repository),
|
||||
file: false
|
||||
}, efh(cb)(function (log) {
|
||||
exec('git rev-parse HEAD', 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
-19
@@ -1,24 +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 type = require('../lib/type')
|
||||
var version = require('../lib/version')
|
||||
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, cb) {
|
||||
type(efh(cb)(function (type) {
|
||||
if (!type) return cb(null, null)
|
||||
|
||||
var path = './package.json'
|
||||
var pkg = JSON.parse(fs.readFileSync(path))
|
||||
version(pkg, efh(cb)(function (version, unpublished) {
|
||||
pkg.version = unpublished ? '1.0.0' : semver.inc(version, type)
|
||||
if (!options.debug) fs.writeFileSync(path, JSON.stringify(pkg, null, 2))
|
||||
|
||||
cb(null, pkg.version)
|
||||
}))
|
||||
}))
|
||||
plugins.verifyRelease(_.assign({
|
||||
commits: results.commits,
|
||||
lastRelease: results.lastRelease,
|
||||
nextRelease
|
||||
}, config), (err) => {
|
||||
if (err) return cb(err)
|
||||
cb(null, nextRelease)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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'
|
||||
})
|
||||
}
|
||||
@@ -1,45 +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))
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
@@ -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)
|
||||
})
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
module.exports = function (cb) {
|
||||
cb(null, 'bar')
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
module.exports = () => ({
|
||||
authenticate: () => true,
|
||||
releases: {
|
||||
createRelease: (release, cb) => cb(null)
|
||||
}
|
||||
})
|
||||
@@ -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, {})
|
||||
@@ -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/'
|
||||
}
|
||||
@@ -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
|
||||
Executable
+36
@@ -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
|
||||
Executable
+12
@@ -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
|
||||
@@ -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)
|
||||
})
|
||||
@@ -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()
|
||||
})
|
||||
@@ -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()
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -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()
|
||||
})
|
||||
})
|
||||
@@ -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()
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -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()
|
||||
})
|
||||
@@ -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()
|
||||
})
|
||||
@@ -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 +0,0 @@
|
||||
test:{SHA}C+7Hteo/D9vJXQ3UfzxbwnXaijM=
|
||||
@@ -1,3 +0,0 @@
|
||||
_auth=dGVzdDpmb28=
|
||||
email=test@example.com
|
||||
registry=http://127.0.0.1:4873/
|
||||
@@ -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
|
||||
@@ -1,12 +0,0 @@
|
||||
'use strict'
|
||||
|
||||
var test = require('tape')
|
||||
|
||||
var createModule = require('./lib/create-module')
|
||||
|
||||
require('./scenarios/install')(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)
|
||||
@@ -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)
|
||||
}))
|
||||
}))
|
||||
}
|
||||
@@ -1,33 +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('install', function (t) {
|
||||
installTest(t, 'npm install', 'not doing anything when the module is installed')
|
||||
installTest(t, 'npm i', 'not doing anything when the module is installed with abbrevd command')
|
||||
})
|
||||
|
||||
function installTest (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')
|
||||
})
|
||||
})
|
||||
}
|
||||
}))
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
'use strict'
|
||||
|
||||
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) {
|
||||
t.test('publish new version to github releases', 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(cool): the next big thing"')
|
||||
.run('npm run postpublish')
|
||||
.code(0)
|
||||
.stdout(/> semantic-release post\n\nGenerating changelog from.*\nParsed/m)
|
||||
.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, res) {
|
||||
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:\*\* the next big thing/.test(res.body), 'body')
|
||||
})
|
||||
})
|
||||
})
|
||||
}))
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
'use strict'
|
||||
|
||||
var fs = require('fs')
|
||||
var exec = require('child_process').exec
|
||||
|
||||
var efh = require('error-first-handler')
|
||||
var nixt = require('nixt')
|
||||
|
||||
module.exports = function (test, createModule) {
|
||||
createModule(efh()(function (name, cwd) {
|
||||
test('prepublish', function (t) {
|
||||
prepublishTest(t, 'refactor: change', '0.0.0', 1, 'abort publish w/o changes')
|
||||
prepublishTest(t, 'fix: change', '1.0.0', 0, 'release 1.0.0 if unpublished')
|
||||
prepublishTest(t, 'fix: change', '1.0.1', 0, 'bump patch for fix')
|
||||
prepublishTest(t, 'feat: change', '1.1.0', 0, 'bump minor for feature')
|
||||
prepublishTest(t, 'fix: BREAKING CHANGE: change', '2.0.0', 0, 'bump major for breaking change')
|
||||
})
|
||||
|
||||
function prepublishTest (t, message, version, code, name) {
|
||||
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)
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
}))
|
||||
}
|
||||
@@ -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')
|
||||
})
|
||||
})
|
||||
}))
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -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')
|
||||
})
|
||||
})
|
||||
})
|
||||
}))
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
'use strict'
|
||||
|
||||
var efh = require('error-first-handler')
|
||||
var nixt = require('nixt')
|
||||
|
||||
module.exports = function (test, createModule) {
|
||||
createModule({
|
||||
repository: {},
|
||||
scripts: {
|
||||
prepublish: 'semantic-release pre --no-token'
|
||||
}
|
||||
}, efh()(function (name, cwd) {
|
||||
test('verify', function (t) {
|
||||
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')
|
||||
})
|
||||
})
|
||||
})
|
||||
}))
|
||||
}
|
||||
Reference in New Issue
Block a user