Compare commits

...
25 Commits
Author SHA1 Message Date
Christoph Witzko 1db531b511 Merge pull request #30 from boennemann/feat-private-modules
feat(npm-info): support private modules
2015-06-11 19:56:12 +02:00
Stephan Bönnemann db39c796a6 Merge pull request #38 from boennemann/feat-verify-version-placeholder
Not publishing placeholder versions
2015-06-09 20:11:17 +02:00
Stephan Bönnemann 1591608f83 fix(verify): not publishing placeholder versions closes #24 2015-06-09 20:01:14 +02:00
Stephan Bönnemann 3250b9a1af test(verify): not publishing placeholder versions 2015-06-09 19:59:23 +02:00
Stephan Bönnemann 96757ac645 docs(readme): add jsconfbp talk 2015-06-08 12:11:14 +02:00
Christoph Witzko 25828960bc fix(npm-info): support basic auth 2015-05-27 19:05:06 +02:00
Stephan Bönnemann 8244fe9f7d Merge pull request #33 from lewiscowper/patch-2
docs(readme): add travis-after-all note to example .travis.yml
2015-05-27 11:04:28 +02:00
Christoph Witzko d0032de737 test(npm-info): support private modules 2015-05-27 11:01:55 +02:00
Lewis Cowper 6c6562249b docs(readme): add travis-after-all note to example travis.yml 2015-05-27 09:37:55 +01:00
Christoph Witzko 25cb545ea3 feat(npm-info): support private modules
closes #28
2015-05-25 00:25:09 +02:00
Stephan Bönnemann 0930a6e33d Merge pull request #27 from gr2m/patch-1
docs(readme): now also tested on io.js `^2`
2015-05-17 19:09:45 +02:00
Gregor Martynus 890255692f docs(readme): now also tested on io.js ^2 2015-05-17 13:11:44 +02:00
Christoph Witzko fd4c4d1c81 fix(pre): use path instead of name at verification hook object 2015-05-15 20:38:55 +02:00
Christoph Witzko 7ba1cb956f fix(pre): improved logging 2015-05-15 19:41:27 +02:00
Christoph Witzko 1f70215ffe test(pre): verification hook 2015-05-15 19:32:32 +02:00
Stephan BönnemannandChristoph Witzko 245f918201 fix(pre): exit script for failed validation 2015-05-15 19:08:27 +02:00
Christoph Witzko f0f53f207b fix(pre): correct type on initial release 2015-05-14 01:19:54 +02:00
Christoph Witzko 41c90124f6 feat(pre): pass more information to the verification hook 2015-05-12 15:52:47 +02:00
Christoph Witzko 44db677c4a chore: added cracks 2015-05-12 09:28:10 +02:00
Christoph Witzko 127cb673bd feat(pre): verification hook 2015-05-12 09:27:35 +02:00
Christoph Witzko a5dabed534 fix(pre): use raw body instead of subject 2015-05-12 08:53:38 +02:00
Christoph Witzko be9bb40dd2 refactor(pre): inline if 2015-05-12 08:53:38 +02:00
Stephan Bönnemann 4821aff69e fix(npm-info): correctly encode scoped package names
This adds support for releasing scoped packages that are public and and that have an initial version in the registry available.
More fixes needed to support yet unpublished/private scoped modules.
2015-05-12 00:06:03 +02:00
Stephan Bönnemann 773ecf693f docs(readme): document expected error closes #18 2015-05-11 23:34:35 +02:00
Stephan Bönnemann f2dd66f614 chore(travis): test on iojs-v2 2015-05-08 00:49:11 +02:00
13 changed files with 245 additions and 48 deletions
+1
View File
@@ -1,5 +1,6 @@
language: node_js
node_js:
- iojs-v2
- iojs-v1
- '0.10'
- '0.12'
+20 -12
View File
@@ -9,12 +9,14 @@
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.
[![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.
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.
![https://twitter.com/trodrigues/status/509301317467373571](https://cloud.githubusercontent.com/assets/908178/6091690/cc86f58c-aeb8-11e4-94cb-15f15f486cde.png)
## 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.
@@ -40,11 +42,11 @@ 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: 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 an error that you can *safely ignore* [npm/npm#7118](https://github.com/npm/npm/issues/7118).
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
@@ -85,22 +87,28 @@ It would be preferable not to have a version field in the `package.json` at all,
#### 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.
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
```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)
@@ -126,11 +134,9 @@ Note: This isn't tied to a specific service, but example configuration is provid
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: 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).
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
@@ -168,9 +174,11 @@ It is indeed a great idea because it _forces_ you to follow best practices. If y
### 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.
`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
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)
+8 -1
View File
@@ -31,7 +31,14 @@ var npmArgv = process.env.npm_config_argv ?
if (~argv._.indexOf('pre')) {
// see src/restart.js
if (npmArgv['semantic-release-rerun']) process.exit(0)
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 (
+1 -1
View File
@@ -7,7 +7,7 @@ var efh = require('./error').efh
module.exports = function (from, cb) {
var range = (from ? from + '..' : '') + 'HEAD'
exec(
'git log -E --format=%H==SPLIT==%s==END== ' + range,
'git log -E --format=%H==SPLIT==%B==END== ' + range,
efh(cb)(function (stdout) {
cb(null, String(stdout).split('==END==\n')
+38 -20
View File
@@ -1,27 +1,45 @@
'use strict'
var async = require('async')
var npmconf = require('npmconf')
var request = require('request')
var efh = require('./error').efh
module.exports = function (pkgName, cb) {
request(process.env.npm_config_registry + pkgName, efh(cb)(function (response, body) {
var res = {
version: null,
gitHead: null,
pkg: null
var registry = process.env.npm_config_registry
async.waterfall([
npmconf.load,
function (conf, callback) {
var cred = conf.getCredentialsByURI(registry)
var reqopts = {
url: registry + pkgName.replace(/\//g, '%2F'),
headers: {}
}
if (cred.token) {
reqopts.headers.Authorization = 'Bearer ' + cred.token
} else if (cred.auth) {
reqopts.headers.Authorization = 'Basic ' + cred.auth
}
callback(null, reqopts)
},
request,
function (response, body, callback) {
var res = {
version: null,
gitHead: null,
pkg: null
}
if (response.statusCode === 404 || !body) return callback(null, res)
var pkg = JSON.parse(body)
if (pkg.error) return callback(pkg.error)
res.version = pkg['dist-tags'].latest
res.gitHead = pkg.versions[res.version].gitHead
res.pkg = pkg
callback(null, res)
}
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)
}))
], cb)
}
+8 -1
View File
@@ -9,6 +9,7 @@
},
"dependencies": {
"abbrev": "^1.0.5",
"async": "^1.0.0",
"conventional-changelog": "0.0.17",
"error-first-handler": "^1.0.1",
"git-head": "^1.2.1",
@@ -16,17 +17,20 @@
"github-url-from-git": "^1.4.0",
"ini": "^1.3.2",
"minimist": "^1.1.0",
"npmconf": "^2.1.2",
"parse-github-repo-url": "^1.0.0",
"request": "^2.53.0",
"semver": "^4.3.3"
},
"devDependencies": {
"cracks": "^2.0.1",
"github-release-fake-server": "^1.3.0",
"lodash.defaults": "^3.0.0",
"nano-uid": "^0.2.0",
"nixt": "^0.4.1",
"nock": "^2.2.0",
"sinopia": "^1.0.0",
"standard": "^3.2.1",
"standard": "^3.11.1",
"tap-spec": "^3.0.0",
"tape": "^4.0.0"
},
@@ -59,5 +63,8 @@
"test:style": "standard",
"test:integration": "node tests | tap-spec",
"test": "./bin/test"
},
"release": {
"verification": "cracks"
}
}
+38 -3
View File
@@ -20,10 +20,45 @@ module.exports = function (options, plugins, cb) {
var type = analyzer(commits)
if (!type) return cb(null, null)
pkg.version = !res.version ? '1.0.0' : semver.inc(res.version, type)
if (!options.debug) fs.writeFileSync(path, JSON.stringify(pkg, null, 2))
cb(null, pkg.version)
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)
})
}))
}))
}
+1
View File
@@ -1,3 +1,4 @@
_auth=dGVzdDpmb28=
email=test@example.com
registry=http://127.0.0.1:4873/
//registry.npmjs.org/:_authToken=testtoken
+2
View File
@@ -4,7 +4,9 @@ var test = require('tape')
var createModule = require('./lib/create-module')
require('./tap/npm-info')(test)
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)
+5
View File
@@ -0,0 +1,5 @@
'use strict'
module.exports = function (opts, cb) {
cb(null, !(opts.commits.length % 2))
}
+45
View File
@@ -0,0 +1,45 @@
'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')
})
})
})
}))
}
+26 -10
View File
@@ -4,16 +4,15 @@ 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) {
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)
@@ -29,6 +28,23 @@ module.exports = function (test, createModule) {
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')
})
})
}))
})
}
+52
View File
@@ -0,0 +1,52 @@
'use strict'
var nock = require('nock')
var npmInfo = require('../../lib/npm-info.js')
var registry = 'http://registry.npmjs.org/'
var defaultModule = {
'dist-tags': {
latest: '1.0.0'
},
versions: {
'1.0.0': {
gitHead: 'HEAD'
}
}
}
process.env.npm_config_registry = registry
module.exports = function (test) {
test('npm-info', function (t) {
var regMock = nock(registry, {
reqheaders: {
'authorization': 'Bearer testtoken'
}
})
.get('/express')
.reply(200, defaultModule)
.get('/@user%2Fmodule')
.reply(200, defaultModule)
t.test('get unscoped module', function (t) {
t.plan(3)
npmInfo('express', function (err, info) {
t.error(err, 'error')
t.is(info.version, '1.0.0', 'version')
t.is(info.gitHead, 'HEAD', 'gitHead')
})
})
t.test('get scoped module', function (t) {
t.plan(3)
npmInfo('@user/module', function (err, info) {
t.error(err, 'error')
t.is(info.version, '1.0.0', 'version')
t.is(info.gitHead, 'HEAD', 'gitHead')
regMock.done()
})
})
})
}