npmのパッケージをpublishするついでにversion番号のインクリメントとGithubのTag切りまでやってくれるスクリプトを書く

npmコマンドを使うとnodejsのモジュールを簡単に[npm](https://www.npmjs.com/)にpublishできる。
そのついでに

+ [バージョン番号をインクリメントするスクリプト](#version-up-js)
+ [Github上のtagを生成するスクリプト](#git-tag-js)

があると便利なので書いてみた。

<a name="version-up-js"></a>
## バージョン番号をインクリメントするスクリプト

package.jsonを読み込んで、versionのマイナー番号の数字を一個増やして上書きする。

例： `"version":"1.0.12"` -> `"version":"1.0.13"`


```js

#!/usr/bin/env node

/**
 * Version up this package.
 */

"use strict";


var filename = require.resolve('../../../package.json'),  //Your project package.json path.
    async = require('async'),
    fs = require('fs');

/**
 * Increment miner version.
 * @param {string} version - Version number to work wit.
 * @returns {string} - Incremented number
 * @private
 */
function _incrementVersion(version) {
    var versions = version.split('.');
    versions.push(Number(versions.pop()) + 1);
    return versions.join('.');
}

/**
 * Rewrite package.json to update version.
 * @param {string} filename - Package.json file path.
 * @param {function} callback - Callback when done.
 */
function updateVersion(filename, callback) {
    var data = require(filename);
    data.version = _incrementVersion(data.version);
    var content = JSON.stringify(data, null, 4);
    fs.writeFile(filename, content, function (err) {
        callback(err, data);
    });
}

updateVersion(filename, function (err, data) {
    if (err) {
        console.error(err);
    } else {
        console.log('Package version incremented to:', data.version);
    }
});

```


<a name="git-tag-js"></a>
## Github上のtagを生成するスクリプト

package.jsonの情報をもとにgithub上にtagを作る

例えば`"version":"1.0.3"`のpackageなら`v1.0.3`のtagを作ってpushする。すでに存在していたら何もしない。


```js
#!/usr/bin/env node

/**
 * Tag on github.
 */

"use strict";

var pkgPath = require.resolve('../../../package.json'); // Your project package.json path.

var fs = require('fs'),
    util = require('util'),
    async = require('async'),
    childProcess = require('child_process');

/**
 * Spawn a process and pip stdout/stderr
 * @param {string} command - Command to spawn.
 * @param {string[]} args - Command arguments.
 * @param {function} callback - Callback when done.
 * @returns {*} - Spawned process.
 * @private
 */
function _spawn(command, args, callback) {
    var spwaned = childProcess.spawn(command, args, {});
    spwaned.stdout.pipe(process.stdout);
    spwaned.stderr.pipe(process.stderr);
    spwaned.on('close', function (exitCode) {
        var success = exitCode === 0,
            err = success ? null : new Error(['Spawn', command, 'failed.'].join(' '));
        callback(err);
    });
    return spwaned;
}


/**
 * Read and parse json file.
 * @param {string} filename - Filename to read.
 * @param {function} callback - Callback when done.
 * @private
 */
function _readJsonFile(filename, callback) {
    async.waterfall([
        function (callback) {
            fs.readFile(filename, callback);
        },
        function (buffer, callback) {
            var data = JSON.parse(buffer.toString());
            callback(null, data);
        }
    ], callback);
}

/**
 * Tag name of a package json.
 * @param {string} packageJsonPath - File path of package.json
 * @param {function} callback - Callback when done.
 * @private
 */
function _tagNameForPackage(packageJsonPath, callback) {
    async.waterfall([
        function (callback) {
            _readJsonFile(packageJsonPath, callback);
        },
        function (data, callback) {
            var tagName = 'v' + data.version;
            callback(null, tagName);
        }
    ], callback);
}

/**
 * Check if tag alreay exists.
 * @param {string} tagName - Tag name to work with.
 * @param {function} callback - Callback when done.
 * @private
 */
function _tagExists(tagName, callback) {
    var command = util.format('git tag -l %s', tagName);
    childProcess.exec(command, function (err, stdOut, stdErr) {
        callback(err || stdErr || null, !!stdOut);
    });
}

/**
 * Add git tag.
 * @param {string} tagName - Tag name to add.
 * @param {function} callback - Callback when done.
 * @private
 */
function _addTag(tagName, callback) {
    _spawn('git', ['tag', tagName], callback);
}

/**
 * Push tag to remote server.
 * @param {function} callback - Callback when done.
 * @private
 */
function _pushTags(callback) {
    _spawn('git', ['push', '--tags'], callback);
}

async.waterfall([
    function (callback) {
        _tagNameForPackage(pkgPath, callback);
    },
    function (tagName, callback) {
        async.waterfall([
            function (callback) {
                _tagExists(tagName, callback);
            },
            function (exists, callback) {
                var err = exists ? new Error('Tag already exists.') : null;
                callback(err);
            },
            function (callback) {
                _addTag(tagName, callback);
            },
            function (callback) {
                _pushTags(callback);
            }
        ], callback);
    }
], function (err) {
    if (err) {
        console.error(err);
    } else {
        console.log('Tag on git done!');
    }
});
```