Compare commits

..
19 Commits
Author SHA1 Message Date
Stephan Bönnemann df5f8337e8 Merge pull request #85 from semantic-release/fix-git-contains
Fix git contains
2015-09-16 09:56:36 +02:00
Stephan Bönnemann cb3992595e fix(commits): handle failing git command correctly and show meaningful error
Closes #83
2015-09-16 09:52:09 +02:00
Stephan Bönnemann a3f561d1d1 test(mocks): correctly fail git command when ref not in history 2015-09-16 09:51:26 +02:00
Stephan Bönnemann 143457e03d docs(README): smaller fixes and clarifications 2015-09-16 09:25:04 +02:00
Stephan Bönnemann 2c17773c75 docs(README): add egghead.io tutorial 2015-09-08 10:07:39 +01:00
Stephan Bönnemann 0a17e8e780 chore: make repo "commitizen friendly"
Closes #74
2015-08-28 10:29:48 +02:00
Stephan Bönnemann a7270bb84c fix: write version to shrinkwrap if existent
Closes #11
2015-08-24 15:56:55 +02:00
Stephan Bönnemann 0e013c2dea docs(README): fix badge links 2015-08-24 14:59:24 +02:00
Stephan Bönnemann e1baf1892a chore: setup release branch 2015-08-24 12:00:59 +02:00
Stephan Bönnemann ef1623210b docs: breaking change
BREAKING CHANGE: If you run travis_after_all and travis-after-all at the same
time they'll keep waiting for each other until forever. You have to apply
this patch to your `.travis.yml`.

```diff
---
 .travis.yml | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/.travis.yml b/.travis.yml
index a5fbddb..c76091b 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -14,9 +14,6 @@ before_install:
 - npm i -g npm@^2.0.0
 before_script:
 - npm prune
-- curl -Lo travis_after_all.py https://git.io/vLSON
 after_success:
-- python travis_after_all.py
-- export $(cat .to_export_back)
 - npm run semantic-release
--
```
2015-08-23 22:42:55 +02:00
Stephan Bönnemann ad9501540e docs(README): use equally sized, optimized talk thumbnails 2015-08-23 14:26:47 +02:00
Stephan Bönnemann 7af30f0ad4 docs(README): list plugins and more talks 2015-08-23 14:16:09 +02:00
Stephan Bönnemann 4aa908804c fix(condition-travis): remove need for python script travis_after_all 2015-08-23 00:53:04 +02:00
Stephan Bönnemann 451378117d fix(registry): be better at using the correct registry (relevant for npme)
Closes #53
2015-08-22 23:32:49 +02:00
Stephan Bönnemann 23221cc044 chore(package): update last-release-npm to 1.2.1 2015-08-22 21:48:00 +02:00
Stephan Bönnemann 7a4c89f131 feat: add fallbackTag functionality and map "next" to "latest"
You can now map dist-tags onto "fallbackTags". E.g. if you want a
development channel like "next" and no version is currently published as "next"
semantic-release will fallback to "latest" to determine the new "next" version.
This is now the default for "next" -> "latest", but you can specify your own
mappings by adding "fallbackTags" to your `package.json`'s "release" field.

BREAKING CHANGE: In cases where you pushed a new commit with a "dist-tag"
configuration of "next" and no version was previously published as "next",
semanitc-release failed and did not publish a new version. It will now
automatically fall back to to "latest".
2015-08-22 21:36:11 +02:00
Stephan Bönnemann 2405453f2d docs(README): nice badge pyramid 2015-08-22 19:53:36 +02:00
Stephan Bönnemann a16c36ed4d style: branch available in options not root of config 2015-08-22 19:49:09 +02:00
Stephan Bönnemann 5cc7da6035 fix(commits): add helpful error when lastRelease not in history
Closes #61, Closes #50
2015-08-22 19:31:29 +02:00
10 changed files with 244 additions and 83 deletions
+1 -4
View File
@@ -14,9 +14,6 @@ before_install:
- npm i -g npm@^2.0.0
before_script:
- npm prune
- curl -Lo travis_after_all.py https://git.io/vLSON
after_success:
- python travis_after_all.py
- export $(cat .to_export_back)
- if [[ $BUILD_LEADER = YES ]]; then npm run coverage:upload; fi
- npm run coverage:upload
- npm run semantic-release
+98 -50
View File
@@ -1,56 +1,87 @@
# semantic-release
# :package::rocket: semantic-release
**fully automated package publishing**
> **Trust us, this will change your workflow for the better.**
> [egghead.io](https://egghead.io/lessons/javascript-how-to-write-a-javascript-library-automating-releases-with-semantic-release)
[![Join the chat at https://gitter.im/semantic-release/semantic-release](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/semantic-release/semantic-release?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[![semantic-release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg)](https://github.com/semantic-release/semantic-release)
[![js-standard-style](https://img.shields.io/badge/code%20style-standard-brightgreen.svg?style=flat)](https://github.com/feross/standard)
[![Join the chat at https://gitter.im/semantic-release/semantic-release](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/semantic-release/semantic-release?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[![Commitizen friendly](https://img.shields.io/badge/commitizen-friendly-brightgreen.svg)](http://commitizen.github.io/cz-cli/)
[![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/caribou.svg)](https://david-dm.org/semantic-release/semantic-release/caribou)
[![devDependency Status](https://david-dm.org/semantic-release/semantic-release/caribou/dev-status.svg)](https://david-dm.org/semantic-release/semantic-release/caribou#info=devDependencies)
[![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)
[![Build Status](https://travis-ci.org/semantic-release/semantic-release.svg?branch=caribou)](https://travis-ci.org/semantic-release/semantic-release)
[![Coverage Status](https://coveralls.io/repos/semantic-release/semantic-release/badge.svg?branch=caribou&service=github)](https://coveralls.io/github/semantic-release/semantic-release?branch=caribou)
`semantic-release` builds upon a set of conventions to give you **fully automated, semver-compliant package publishing**. It's mostly about _commit-messages_, but you can define more _heuristics_.
Out of the box this is just about _commit-messages_, but you can do so much more.
* **Detect breaking changes** using the test suite of your last release: [cracks](https://github.com/semantic-release/cracks)
* Detect breaking changes using your dependents test suites: [Help out! Implement the **dont-break** plugin](https://github.com/semantic-release/semantic-release/issues/65)
* Detect breaking changes diffing your JSDoc interface: [Help out! Implement the **india** plugin](https://github.com/semantic-release/semantic-release/issues/66)
* Abort releases with **insufficient test coverage**: [Help out! Implement the **istanbul** plugin](https://github.com/semantic-release/semantic-release/issues/68)
* Abort releases with **vulnerable dependencies** in the tree: [Help out! Implement the **nsp** plugin](https://github.com/semantic-release/semantic-release/issues/67)
* Everything you can imagine: [Build Plugins!](https://github.com/semantic-release/semantic-release#plugins)
  | Commands | Comment
--- | --- | ---
| **manual** | <pre><code><div>npm version major</div><div>git push origin master --tags</div><div>npm publish</div></code></pre> | You **manually decide** what the **next version** is. You have to remember what major, minor and patch means. You have to remember to push both commits and tags. You have to wait for the CI to pass. |
| **semantic-release** | <pre><code><div>git commit -m "fix: &lt;message&gt;"</div><div>git push</div></code></pre> | You **think about and describe the changes** you've made. A new version is automatically published with the correct version number.
| **manual/before** | <pre><code><div>npm version major</div><div>git push origin master --tags</div><div>npm publish</div></code></pre> | You **manually decide** what the **next version** is. You have to remember what major, minor and patch means. You have to remember to push both commits and tags. You have to wait for the CI to pass. |
| **semantic-release/after** | <pre><code><div>git commit -m "fix: &lt;message&gt;"</div><div>git push</div></code></pre> | You **describe the changes** youve made. A new version is automatically published with the correct version number.
This removes the immediate connection between human emotions and version numbers, so strictly following the [SemVer](http://semver.org/) spec is not a problem anymore  and thats ultimately `semantic-release`s goal.
> ### “We fail to follow SemVer and why it neednt matter”
> #### JSConf Budapest 2015
> [![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 of this module
<table>
<tr>
<th colspan="2">
“How to Write a JavaScript Library - Automating Releases with semantic-release”  egghead.io
</th>
</tr>
<tr>
<td colspan="2">
<a href="https://egghead.io/lessons/javascript-how-to-write-a-javascript-library-automating-releases-with-semantic-release"><img src="https://cloud.githubusercontent.com/assets/908178/9730739/7b1da5d8-5610-11e5-88b6-5c75fdda7ee2.png" alt="egghead.io session"></a>
</td>
</tr>
<tr>
<td colspan="2">
A free egghead.io tutorial series on how to write an open source library featuring semantic-release.
</td>
</tr>
<tr>
<th>
“We fail to follow SemVer and why it neednt matter”
</th>
<th>
“semantic-release Q&A with Kent C. Dodds”
</th>
</tr>
<tr>
<td>
<a href="https://www.youtube.com/watch?v=tc2UgG5L7WM&amp;index=6&amp;list=PLFZ5NyC0xHDaaTy6tY9p0C0jd_rRRl5Zm"><img alt="JSConfBP Talk" src="https://cloud.githubusercontent.com/assets/908178/9428178/c337ed26-49a2-11e5-8ad0-a602500a65bf.png"></a>
</td>
<td>
<a href="https://www.youtube.com/watch?v=g6y3DnhkjrI"><img alt="Hangout on Air with Kent C. Dodds" src="https://cloud.githubusercontent.com/assets/908178/9428179/c3387318-49a2-11e5-9f5f-dccd3fbb0c1d.png"></a>
</td>
</tr>
<tr>
<td>
This talk gives you a complete introduction to the underlying concepts of this module. 38:30
</td>
<td>
A “Hangouts on Air” conversation with hands on questions and answers about how to use semantic-release. 53:52
</td>
</tr>
</table>
## How does it work?
Instead of writing [meaningless commit messages](http://whatthecommit.com/), we can take our time to think about the changes in the codebase and write them down. Following formalized conventions it this then possible to generate a helpful changelog and to derive the next semantic version number from them.
_Note: This module ships with the [AngularJS Commit Message Conventions](https://docs.google.com/document/d/1QrDFcIiPjSLDn3EL15IJygNPiHORgU1_OOAqWjiDU5Y/edit) and changelog generator, but you can [define your own](#plugins) style_.
> ### Commit Message Format
> Each commit message consists of a **header**, a **body** and a **footer**. The header has a special
format that includes a **type**, a **scope** and a **subject**:
> ```
<type>(<scope>): <subject>
<BLANK LINE>
<body>
<BLANK LINE>
<footer>
```
> [Full explanation](https://github.com/ajoslin/conventional-changelog/blob/master/conventions/angular.md)
When `semantic-release` got setup it will do that after every successful continuous integration build of your master branch (or any other branch you specify) and publish the new version for you. That way no human is directly involved in the release process and your releases are guaranteed to be [unromantic and unsentimental](http://sentimentalversioning.org/).
If you fear the loss of control over timing and marketing implications of software releases you should know that `semantic-release` supports [release channels](https://github.com/npm/npm/issues/2718) using `npm`'s [dist-tags](https://docs.npmjs.com/cli/tag). This way you can keep control over what your users end up using by default, you can decide when to promote an automatically released version to the stable channel and you can choose which versions to write blogposts and tweets about. You can use the same mechanism to support older versions of your software, for example with important security fixes.
If you fear the loss of control over timing and marketing implications of software releases you should know that `semantic-release` supports [release channels](https://github.com/npm/npm/issues/2718) using `npm`s [dist-tags](https://docs.npmjs.com/cli/tag). This way you can keep control over what your users end up using by default, you can decide when to promote an automatically released version to the stable channel and you can choose which versions to write blogposts and tweets about. You can use the same mechanism to [support older versions of your software](https://gist.github.com/boennemann/54042374e49c7ade8910), for example with important security fixes.
This is what happens in series:
@@ -60,6 +91,23 @@ This is what happens in series:
_Note:_ The current release/tag implementation is tied to GitHub, but could be opened up to Bitbucket, GitLab, et al. Feel free to send PRs for these services.
## Default Commit Message Format
This module ships with the [AngularJS Commit Message Conventions](https://docs.google.com/document/d/1QrDFcIiPjSLDn3EL15IJygNPiHORgU1_OOAqWjiDU5Y/edit) and changelog generator, but you can [define your own](#plugins) style.
Each commit message consists of a **header**, a **body** and a **footer**. The header has a special
format that includes a **type**, a **scope** and a **subject**:
```
<type>(<scope>): <subject>
<BLANK LINE>
<body>
<BLANK LINE>
<footer>
```
[Full explanation](https://github.com/ajoslin/conventional-changelog/blob/master/conventions/angular.md)
## Setup
[![NPM](https://nodei.co/npm/semantic-release.png?downloads=true&downloadRank=true&stars=true)](https://nodei.co/npm/semantic-release/)
@@ -71,7 +119,7 @@ cd your-module
semantic-release-cli setup
```
![dialogue](https://cloud.githubusercontent.com/assets/908178/8766357/f3eadaca-2e34-11e5-8ebb-d40b9ae613d7.png)
![dialogue](https://cloud.githubusercontent.com/assets/908178/9428123/3628dfec-499f-11e5-8bdd-8f3042dd95ed.png)
_[This is what happens under the hood.](https://github.com/semantic-release/cli#manual-setup)_
@@ -86,22 +134,22 @@ You can pass options either via command line (in [kebab-case](https://lodash.com
These options are currently available:
- `branch`: The branch on which releases should happen. Default: `'master'`
- `debug`: If true doesn't actually publish to npm or write things to file. Default: `!process.env.CI`
- `debug`: If true doesnt actually publish to npm or write things to file. Default: `!process.env.CI`
- `githubToken`: The token used to authenticate with GitHub. Default: `process.env.GH_TOKEN`
- `githubUrl`: Optional. Pass your GitHub Enterprise endpoint.
_A few notes on `npm` config_:
1. The `npm` token can only be defined in the environment as `NPM_TOKEN`, because that's where `npm` itself is going to read it from.
1. The `npm` token can only be defined in the environment as `NPM_TOKEN`, because thats where `npm` itself is going to read it from.
2. In order to publish to a different `npm` registry you can just configure that how you would usually do it and `semantic-release` will respect that setting.
2. In order to publish to a different `npm` registry you can specify that inside the `package.json`s [`publishConfig`](https://docs.npmjs.com/files/package.json#publishconfig) field.
3. If you want to use another dist-tag for your publishes than `'latest'` you can specify that inside the `package.json`'s [`publishConfig`](https://docs.npmjs.com/files/package.json#publishconfig) field.
3. If you want to use another dist-tag for your publishes than `'latest'` you can specify that inside the `package.json`s [`publishConfig`](https://docs.npmjs.com/files/package.json#publishconfig) field.
4. `semantic-release` generally tries to orientate itself towards `npm` it inherits the loglevel for example.
## Plugins
There are numerous steps where you can customize `semantic-release`'s behaviour using plugins. A plugin is a regular [option](#options), but passed inside the `release` block of `package.json`:
There are numerous steps where you can customize `semantic-release`s behaviour using plugins. A plugin is a regular [option](#options), but passed inside the `release` block of `package.json`:
```json
{
@@ -125,7 +173,7 @@ A plugin itself is an async function that always receives three arguments.
module.exports = function (pluginConfig, config, callback) {}
```
- `pluginConfig`: If the user of your plugin specifies additional plugin config in the `package.json` (see the `verifyConditions` example above) then it's this object.
- `pluginConfig`: If the user of your plugin specifies additional plugin config in the `package.json` (see the `verifyConditions` example above) then its this object.
- `config`: A config object containing a lot of information to act upon.
- `env`: All environment variables
- `npm`: Select npm configuration bits like `registry`, `tag` and `auth`
@@ -145,7 +193,7 @@ This plugin is responsible for generating release notes. Call the callback with
### `verifyConditions`
This plugins is responsible for verifying that a release should happen in the first place. For example, the [default implementation](https://github.com/semantic-release/condition-travis/) verifies that the publish is happening on Travis, that it's the right branch, and that all other build jobs succeeded. There are more use cases for this, e.g. verifying that test coverage is above a certain threshold or that there are no [vulnerabilities](https://nodesecurity.io/) in your dependencies. Be creative.
This plugins is responsible for verifying that a release should happen in the first place. For example, the [default implementation](https://github.com/semantic-release/condition-travis/) verifies that the publish is happening on Travis, that its the right branch, and that all other build jobs succeeded. There are more use cases for this, e.g. verifying that test coverage is above a certain threshold or that there are no [vulnerabilities](https://nodesecurity.io/) in your dependencies. Be creative.
Passing an array of plugins will run them in series.
@@ -157,19 +205,19 @@ Passing an array of plugins will run them in series.
### `getLastRelease`
This plugin is responsible for determining a package's last release version. The [default implementation](https://github.com/semantic-release/last-release-npm) uses the last published version on a npm registry.
This plugin is responsible for determining a packages last release version. The [default implementation](https://github.com/semantic-release/last-release-npm) uses the last published version on a npm registry.
## ITYM*FAQ*LT
> I think you might frequently ask questions like these
### Why is the `package.json`'s version not updated in my repository?
### 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.
> The most important things in your package.json are the name and version fields. Those are actually required, and your package wont 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.
While this entirely true the version number doesnt 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?
@@ -177,7 +225,7 @@ If you run `npm run semantic-release` locally a dry run gets performed, which lo
### Can I run this on my own machine rather than on a CI server?
Of course you can, but this doesn't necessarily 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 tokens lying around and everything. That said, you can run the scripts with `--debug=false` explicitly. You have to export `GH_TOKEN=<your_token>` and `NPM_TOKEN=<your_other_token>`.
Of course you can, but this doesnt necessarily 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 tokens lying around and everything. That said, you can run the scripts with `--debug=false` explicitly. You have to export `GH_TOKEN=<your_token>` and `NPM_TOKEN=<your_other_token>`.
### Can I manually trigger the release of a specific version?
@@ -185,13 +233,13 @@ You can trigger a release by pushing to your GitHub repository. You deliberately
### 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, you can release only on the `production`/`deploy`/`release` branch and push your code there in certain intervals, or better yet use [dist-tags](https://docs.npmjs.com/cli/dist-tag).
It is indeed a great idea because it _forces_ you to follow best practices. If you dont 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, you can release only on the `production`/`deploy`/`release` branch and push your code there in certain intervals, or better yet use [dist-tags](https://docs.npmjs.com/cli/dist-tag).
### Why should I trust `semantic-release` with my releases?
`semantic-release` has a full unit- and integration-test-suite that tests _actual_ `npm` publishes against the [npm-registry-couchapp](https://github.com/npm/npm-registry-couchapp/) 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.
`semantic-release` has a full unit- and integration-test-suite that tests _actual_ `npm` publishes against the [npm-registry-couchapp](https://github.com/npm/npm-registry-couchapp/) on node.js `^0.10`, `^0.12` and io.js `^1`, `^2`. A new version wont get published if it doesnt pass on all these engines.
Note: Currently integration-tests don't run on Travis CI. If you know stuff about npm/Travis/Couch: Please help!
Note: Currently integration-tests dont run on Travis CI. If you know stuff about npm/Travis/Couch: Please help!
## Badge
@@ -220,4 +268,4 @@ Use this in one of your projects? Include one of these badges in your README.md
MIT License
2015 © Stephan Bönnemann and [contributors](https://github.com/semantic-release/semantic-release/graphs/contributors)
![https://twitter.com/trodrigues/status/509301317467373571](https://cloud.githubusercontent.com/assets/908178/6091690/cc86f58c-aeb8-11e4-94cb-15f15f486cde.png)
[![](https://cloud.githubusercontent.com/assets/908178/6091690/cc86f58c-aeb8-11e4-94cb-15f15f486cde.png)](https://twitter.com/trodrigues/status/509301317467373571)
+11 -7
View File
@@ -8,10 +8,6 @@
"bugs": {
"url": "https://github.com/semantic-release/semantic-release/issues"
},
"engines": {
"node": ">=0.10",
"npm": ">=2"
},
"config": {
"nyc": {
"exclude": [
@@ -20,11 +16,14 @@
]
}
},
"czConfig": {
"path": "node_modules/cz-conventional-changelog/"
},
"dependencies": {
"@semantic-release/commit-analyzer": "^2.0.0",
"@semantic-release/condition-travis": "^4.0.0",
"@semantic-release/condition-travis": "^4.1.0",
"@semantic-release/error": "^1.0.0",
"@semantic-release/last-release-npm": "^1.1.1",
"@semantic-release/last-release-npm": "^1.2.1",
"@semantic-release/release-notes-generator": "^2.0.0",
"git-head": "^1.2.1",
"github": "^0.2.4",
@@ -42,6 +41,7 @@
"devDependencies": {
"babel": "^5.5.8",
"coveralls": "^2.11.2",
"cz-conventional-changelog": "^1.1.0",
"mkdirp": "^0.5.1",
"nixt": "^0.4.1",
"nock": "^2.5.0",
@@ -52,6 +52,10 @@
"standard": "^5.0.2",
"tap": "^1.3.1"
},
"engines": {
"node": ">=0.10",
"npm": ">=2"
},
"homepage": "https://github.com/semantic-release/semantic-release/tree/next#readme",
"keywords": [
"author",
@@ -70,7 +74,7 @@
"tag": "next"
},
"release": {
"branch": "next"
"branch": "caribou"
},
"repository": {
"type": "git",
+16 -2
View File
@@ -1,5 +1,6 @@
const { readFileSync, writeFileSync } = require('fs')
const path = require('path')
const url = require('url')
const _ = require('lodash')
const log = require('npmlog')
@@ -24,6 +25,9 @@ const options = _.defaults(
pkg.release,
{
branch: 'master',
fallbackTags: {
next: 'latest'
},
debug: !env.CI,
githubToken: env.GH_TOKEN || env.GITHUB_TOKEN,
githubUrl: env.GH_URL
@@ -42,11 +46,12 @@ npmconf.load({}, (err, conf) => {
token: env.NPM_TOKEN
},
loglevel: conf.get('loglevel'),
registry: conf.get('registry'),
registry: require('./lib/get-registry')(pkg, conf),
tag: (pkg.publishConfig || {}).tag || conf.get('tag') || 'latest'
}
if (npm.registry[npm.registry.length - 1] !== '/') npm.registry += '/'
// normalize trailing slash
npm.registry = url.format(url.parse(npm.registry))
log.level = npm.loglevel
@@ -110,6 +115,15 @@ npmconf.load({}, (err, conf) => {
process.exit(1)
}
try {
let shrinkwrap = JSON.parse(readFileSync('./npm-shrinkwrap.json'))
shrinkwrap.version = release.version
writeFileSync('./npm-shrinkwrap.json', JSON.stringify(shrinkwrap, null, 2))
log.verbose('pre', `Wrote version ${release.version} to npm-shrinkwrap.json.`)
} catch (e) {
log.silly('pre', `Couldn't find npm-shrinkwrap.json.`)
}
writeFileSync('./package.json', JSON.stringify(_.assign(pkg, {
version: release.version
}), null, 2))
+55 -15
View File
@@ -1,25 +1,65 @@
const { exec } = require('child_process')
module.exports = function ({lastRelease}, cb) {
const log = require('npmlog')
const SemanticReleaseError = require('@semantic-release/error')
module.exports = function ({lastRelease, options}, cb) {
const branch = options.branch
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)
if (!from) return extract()
cb(null, String(stdout).split('==END==\n')
exec(`git branch --contains ${from}`, (err, stdout) => {
let inHistory = false
let branches
.filter((raw) => !!raw.trim())
.map((raw) => {
const data = raw.split('==SPLIT==')
return {
hash: data[0],
message: data[1]
if (!err && stdout) {
branches = stdout.split('\n')
.map((result) => {
if (branch === result.replace('*', '').trim()) {
inHistory = true
return null
}
}))
return result.trim()
})
.filter(branch => !!branch)
}
)
if (!inHistory) {
log.error('commits',
`The commit the last release of this package was derived from is not in the direct history of the "${branch}" branch.
This means semantic-release can not extract the commits between now and then.
This is usually caused by force pushing, releasing from an unrelated branch, or using an already existing package name.
You can recover from this error by publishing manually or restoring the commit "${from}".` + (branches && branches.length ?
`\nHere is a list of branches that still contain the commit in question: \n * ${branches.join('\n * ')}` :
''
))
return cb(new SemanticReleaseError('Commit not in history', 'ENOTINHISTORY'))
}
extract()
})
function extract () {
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]
}
}))
}
)
}
}
+12
View File
@@ -0,0 +1,12 @@
module.exports = function (pkg, conf) {
if (pkg.publishConfig && pkg.publishConfig.registry) return pkg.publishConfig.registry
if (pkg.name[0] !== '@') return conf.get('registry') || 'https://registry.npmjs.org/'
const scope = pkg.name.split('/')[0]
const scopedRegistry = conf.get(`${scope}/registry`)
if (scopedRegistry) return scopedRegistry
return conf.get('registry') || 'https://registry.npmjs.org/'
}
+5
View File
@@ -5,6 +5,11 @@ const rawCommits = [
module.exports = {
exec: (command, cb) => {
if (/contains/.test(command)) {
if (/notinhistory/.test(command)) return cb(new Error())
return cb(null, `whatever\nmaster\n`)
}
cb(
null,
/\.\.HEAD/.test(command) ?
+10 -2
View File
@@ -7,7 +7,7 @@ const commits = proxyquire('../../dist/lib/commits', {
test('commits since last release', (t) => {
t.test('get all commits', (tt) => {
commits({lastRelease: {}}, (err, commits) => {
commits({lastRelease: {}, options: {branch: 'master'}}, (err, commits) => {
tt.error(err)
tt.is(commits.length, 2, 'all commits')
tt.is(commits[0].hash, 'hash-one', 'parsed hash')
@@ -18,7 +18,7 @@ test('commits since last release', (t) => {
})
t.test('get commits since hash', (tt) => {
commits({lastRelease: {gitHead: 'hash'}}, (err, commits) => {
commits({lastRelease: {gitHead: 'hash'}, options: {branch: 'master'}}, (err, commits) => {
tt.error(err)
tt.is(commits.length, 1, 'specified commits')
tt.is(commits[0].hash, 'hash-one', 'parsed hash')
@@ -28,5 +28,13 @@ test('commits since last release', (t) => {
})
})
t.test('get commits since hash', (tt) => {
commits({lastRelease: {gitHead: 'notinhistory'}, options: {branch: 'notmaster'}}, (err, commits) => {
tt.ok(err)
tt.is(err.code, 'ENOTINHISTORY')
tt.end()
})
})
t.end()
})
+30
View File
@@ -0,0 +1,30 @@
const test = require('tap').test
const getRegistry = require('../../dist/lib/get-registry')
test('get correct registry', (t) => {
t.is(getRegistry({
name: 'publish-config',
publishConfig: {
registry: 'a'
}},
{}), 'a')
t.is(getRegistry({name: 'normal'}, {get: () => 'b'}), 'b')
t.is(getRegistry({name: 'normal'}, {get: () => null}), 'https://registry.npmjs.org/')
t.is(getRegistry({name: '@scoped/foo'}, {
get: (input) => input === '@scoped/registry' ? 'c' : 'd'
}), 'c')
t.is(getRegistry({name: '@scoped/bar'}, {
get: () => 'e'
}), 'e')
t.is(getRegistry({name: '@scoped/baz'}, {
get: () => null
}), 'https://registry.npmjs.org/')
t.end()
})
+6 -3
View File
@@ -3,7 +3,9 @@ const proxyquire = require('proxyquire')
require('../mocks/registry')
const pre = proxyquire('../../dist/pre', {
'child_process': require('../mocks/child-process')
'./lib/commits': proxyquire('../../dist/lib/commits', {
'child_process': require('../mocks/child-process')
})
})
const versions = {
@@ -14,14 +16,13 @@ const plugins = {
verifyRelease: (release, cb) => cb(null, release),
analyzeCommits: (commits, cb) => cb(null, 'major'),
getLastRelease: ({ pkg }, cb) => {
cb(null, { version: versions[pkg.name] || null, gitHead: 'HEAD' })
cb(null, {version: versions[pkg.name] || null, gitHead: 'HEAD'})
}
}
const npm = {
registry: 'http://registry.npmjs.org/',
tag: 'latest'
}
test('full pre run', (t) => {
@@ -29,6 +30,7 @@ test('full pre run', (t) => {
tt.plan(3)
pre({
options: {branch: 'master'},
npm,
pkg: {name: 'available'},
plugins
@@ -43,6 +45,7 @@ test('full pre run', (t) => {
tt.plan(3)
pre({
options: {branch: 'master'},
npm,
pkg: {name: 'unavailable'},
plugins