Skip to content

Commit 755e7bf

Browse files
committed
Merge branch 'release/0.1.0'
2 parents 6f3ed9d + 38af12f commit 755e7bf

File tree

16 files changed

+413
-1
lines changed

16 files changed

+413
-1
lines changed

.babelrc

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"presets": [
3+
["env", {
4+
"targets": {
5+
"node": true
6+
}
7+
}]
8+
]
9+
}

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ logs
99
*.log
1010
npm-debug.log*
1111

12+
# Generated code
13+
dist
14+
1215
# Runtime data
1316
pids
1417
*.pid

.npmignore

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
## Ignore source code for published code
2+
src
3+
test
4+
5+
## Ignore project configuration files
6+
.babelrc
7+
.editorconfig
8+
.git*
9+
10+
# IDE generated files and directories
11+
.project
12+
.vs
13+
.vscode
14+
nbproject
15+
16+
# Logs
17+
logs
18+
*.log
19+
npm-debug.log*
20+
21+
# Runtime data
22+
pids
23+
*.pid
24+
*.seed
25+
26+
# Directory for instrumented libs generated by jscoverage/JSCover
27+
lib-cov
28+
29+
# Coverage directory used by tools like istanbul
30+
coverage
31+
32+
# nyc test coverage
33+
.nyc_output
34+
35+
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
36+
.grunt
37+
38+
# node-waf configuration
39+
.lock-wscript
40+
41+
# Compiled binary addons (http://nodejs.org/api/addons.html)
42+
build/Release
43+
44+
# Dependency directories
45+
node_modules
46+
jspm_packages
47+
48+
# Optional npm cache directory
49+
.npm
50+
51+
# Optional REPL history
52+
.node_repl_history

.travis.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
language: node_js
2+
node_js:
3+
- "node"
4+
- "7"
5+
- "6"
6+
7+
after_success:
8+
- './node_modules/.bin/nyc report --reporter=text-lcov | ./node_modules/.bin/coveralls'

README.md

Lines changed: 93 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,94 @@
11
# build-revision
2-
Generate a version number for your continuous builds
2+
3+
[![Build Status](https://travis-ci.org/abhishekdev/build-revision.svg?branch=master)](https://travis-ci.org/abhishekdev/build-revision) [![Coverage Status](https://coveralls.io/repos/github/abhishekdev/build-revision/badge.svg?branch=master)](https://coveralls.io/github/abhishekdev/build-revision?branch=master)
4+
5+
Generate semver compatible version to uniquely identify project build using [build metadata](http://semver.org/#spec-item-10)
6+
7+
## Usage
8+
9+
### Install
10+
11+
```sh
12+
$ npm install --save-dev build-revision
13+
```
14+
15+
### Example
16+
17+
#### ES5
18+
19+
```javascript
20+
var buildRevision = require('build-revision');
21+
22+
buildRevision().then(function(version){
23+
console.log(version);
24+
});
25+
```
26+
27+
#### ES7
28+
29+
```javascript
30+
import buildRevision from 'build-revision';
31+
32+
const fn = aync() => {
33+
const version = await buildRevision();
34+
console.log(version);
35+
}
36+
```
37+
38+
## Results
39+
40+
### Working copy has no changes (_CI/CD Tools_)
41+
42+
Version | Build Version
43+
---------------------- | ----------------------
44+
0.1.0 | 0.1.0+SHA.abcd123
45+
0.1.0-pre | 0.1.0-pre+SHA.abcd123
46+
0.1.0-pre+SHA.01234567 | 0.1.0-pre+SHA.01234567
47+
48+
### Working copy has no changes (_Developer Machine_)
49+
50+
Version | Build Version
51+
---------------------- | ---------------------------------------------------
52+
0.1.0 | 0.1.0+SHA.abcd123.currentuser.20170101T000000Z
53+
0.1.0-pre | 0.1.0-pre+SHA.abcd123.currentuser.20170101T000000Z
54+
0.1.0-pre+SHA.01234567 | 0.1.0-pre+SHA.01234567.currentuser.20170101T000000Z
55+
56+
## API
57+
58+
### buildRevision(options)
59+
60+
- Appends `prefix.githash` to the version for a repo with no local changes
61+
- Appends `prefix.githash.username.timestamp` to the version for repo with local changes
62+
- The timestamp is a ISO 8601 UTC string
63+
64+
```
65+
Type: Promise
66+
Throws: Error
67+
- if the package version is not resolved
68+
- if the package version is not a valid semver
69+
- if the project is not a git repository
70+
Returns:
71+
- semver compatible version with a build metadata part
72+
```
73+
74+
### Options
75+
76+
#### options.prefix
77+
78+
Build metadata Prefix
79+
80+
```
81+
Type: `String`
82+
Default: `SHA`
83+
```
84+
85+
#### options.cwd
86+
87+
Search for the closest package.json starting from this directory
88+
89+
```
90+
Type: `String`
91+
Default: `.`
92+
```
93+
94+
## License [MIT](LICENSE)

index.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
'use strict';
2+
3+
module.exports = require('./dist/version').default;

package.json

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
{
2+
"name": "build-revision",
3+
"version": "0.1.0",
4+
"description": "Generate semver compatible version to uniquely identify project build using build metadata",
5+
"main": "index.js",
6+
"license": "MIT",
7+
"author": {
8+
"name": "Abhishek Dev",
9+
"url": "https://github.com/abhishekdev"
10+
},
11+
"repository": {
12+
"type": "git",
13+
"url": "git+https://github.com/abhishekdev/build-revision.git"
14+
},
15+
"bugs": {
16+
"url": "https://github.com/abhishekdev/build-revision/issues"
17+
},
18+
"homepage": "https://github.com/abhishekdev/build-revision#readme",
19+
"scripts": {
20+
"build": "babel src -d dist",
21+
"prepublish": "npm run build",
22+
"test": "nyc ava"
23+
},
24+
"keywords": [
25+
"build",
26+
"version",
27+
"revision",
28+
"hash",
29+
"SHA"
30+
],
31+
"dependencies": {
32+
"moment": "^2.17.1",
33+
"read-pkg-up": "^2.0.0",
34+
"semver": "^5.3.0",
35+
"username": "^2.3.0"
36+
},
37+
"devDependencies": {
38+
"ava": "^0.17.0",
39+
"babel-cli": "^6.22.2",
40+
"babel-preset-env": "^1.1.8",
41+
"coveralls": "^2.11.15",
42+
"nyc": "^10.1.2"
43+
}
44+
}

src/repo.js

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
'use strict';
2+
3+
import {exec} from 'child_process';
4+
5+
const git = (command, option) => {
6+
const config = Object.assign({}, option);
7+
return new Promise((resolve, reject) => {
8+
exec('git ' + command, {
9+
cwd: config.cwd
10+
}, (err, stdout) => {
11+
if (err) {
12+
reject(err);
13+
return;
14+
}
15+
resolve(stdout.trimRight());
16+
});
17+
});
18+
};
19+
20+
const git__isDirty = async(option) => {
21+
const status = await git('status --porcelain', option);
22+
return status !== '';
23+
};
24+
25+
const git__getHash = async(option) => {
26+
const hash = await git('rev-parse --short HEAD', option);
27+
if (hash !== '') {
28+
return hash;
29+
}
30+
throw new Error('Could not find Git SHA');
31+
};
32+
33+
export default {
34+
isDirty: git__isDirty,
35+
hash: git__getHash
36+
}

src/version.js

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
'use strict';
2+
3+
import path from 'path';
4+
import moment from 'moment';
5+
import semver from 'semver';
6+
import username from 'username';
7+
import readPkgUp from 'read-pkg-up';
8+
import repo from './repo';
9+
10+
const REGEX_NOT_APLHA_NUMERIC = /\W/g;
11+
const REGEX_ISODATE_MILLISECONDS = /\.[0]{3}/g;
12+
13+
// Replace demarcators in a string, so that it is compatible as a semver fragment
14+
const semverString = (str) => {
15+
return str.replace(REGEX_NOT_APLHA_NUMERIC, '');
16+
};
17+
18+
// Generate an ISO 8601 UTC timestamp that is safe for use in a semver string
19+
// NOTE: The generated date should be ISO 8601 compatible.
20+
// e.g. 2017-Jan-01, 15:00:01.100 => "20170130T1500Z"
21+
const semverMoment = () => {
22+
// Get ISO date after ignoring the millisecond precision
23+
const isoDate = moment().milliseconds(0).toISOString();
24+
25+
// Replace millisecond information as the 'dot' notation for it is incompatible with semver
26+
return semverString(isoDate.replace(REGEX_ISODATE_MILLISECONDS, ''));
27+
};
28+
29+
// Read version key from the nearest package
30+
const pkgVersion = async(o) => {
31+
const data = await readPkgUp({cwd: o.cwd});
32+
if (data && data.pkg && data.pkg.version) {
33+
return semver.valid(data.pkg.version);
34+
}
35+
throw new TypeError('Could not read a valid version from: ' + data.path
36+
? path.relative('', data.path)
37+
: 'package.json');
38+
};
39+
40+
// Help identify dirty builds, those that differ from the latest commit.
41+
// If the working directory is dirty, append the username and date to
42+
// the version string, to make it stand out.
43+
const suffix = async(version, option) => {
44+
const startMarker = '+';
45+
const fieldSeparator = '.';
46+
const prefix = version.includes(startMarker)
47+
? fieldSeparator
48+
: startMarker;
49+
const dirty = await repo.isDirty(option);
50+
const hash = await repo.hash(option);
51+
let buildmeta = [
52+
option.prefix || 'SHA',
53+
hash
54+
];
55+
56+
if (dirty) {
57+
const name = await username();
58+
buildmeta.push(semverString(name));
59+
buildmeta.push(semverMoment());
60+
}
61+
62+
// Attach or append to "build data", as defined by semver.
63+
return version + prefix + buildmeta.join(fieldSeparator);
64+
};
65+
66+
const getRevision = async(option) => {
67+
const version = await pkgVersion(option);
68+
const revision = await suffix(version, option);
69+
70+
return revision;
71+
};
72+
73+
const version = async(o) => {
74+
const option = Object.assign({}, o);
75+
const cwd = path.resolve(option.cwd || '');
76+
const revision = await getRevision(option);
77+
78+
return revision;
79+
};
80+
81+
export default version;
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"name": "sample-package"
3+
}

0 commit comments

Comments
 (0)