こういうのを作ります。
自動化したいこと
GitHubのリリースタスクをgulpで自動化できるようにします。
- GitHub上にバージョンに応じたリリースを作成する
- 作成したリリースに
my-package-v0.1.0.zip
のようなファイルを添付する
配布したいバイナリやzipがある場合、リポジトリに含めるのではなくリリースの添付アセットにしておくと、ダンロード数が計測できたり、https://github.com/<user>/<repo>/releases/latest
というURLで最新リリースへと誘導できたりしてなにかと便利です。
ワークフロー
実際のワークフローは以下のようになると思いますが、この記事で扱うのは (4) と (5) です。
- package.jsonの
version
を更新する - ビルドして配布用のバイナリを生成する
- master(とタグ)をGitHubにプッシュする
- GitHubにリリースを作成する
- リリースにファイルを添付する
リリース用のGitタグは (3) の段階で付けておいてもいいですが、作らなかった場合でも (4) を実行すればリモートには自動的にタグが設定されます。
【追記】(1)〜(3)は、npm-versionを使えば自動化できます。
参考 : npm version, preversion, postversionの使い分け
サンプルコード
紹介する内容は、デモプロジェクトとしてGitHubに公開しています。
https://github.com/htanjo/github-release-demo
パッケージ情報の取得
リリースのバージョンや配布するファイル名は、package.jsonの値を使います。
package.json:
{
"name": "my-package",
"version": "0.1.0",
...
}
require('./package.json')
だとキャッシュされることがあるため、fs.readFileSync()
で取得します。
var fs = require('fs');
var pkg = JSON.parse(fs.readFileSync('package.json', 'utf8'));
GitHub APIの利用
GitHub APIを使うことで、リリースの作成やファイルのアップロードができます。
Node.js上でGitHub APIを扱うためにgithub4をインストールします。(オリジナルのgithubモジュールはメンテが止まっていました。)
$ npm install github4 --save-dev
var GitHubApi = require('github4');
var github = new GitHubApi();
github.authenticate({
type: 'oauth',
token: 'xxxxxxxxxxxxxxxx'
});
// リリースの作成
github.repos.createRelease(...);
// ファイルのアップロード
github.repos.uploadAsset(...);
APIへのアクセストークンはPersonal Access Tokensのページから生成することができます。トークンは作成直後に一度しか表示されないので注意!
実際のプロジェクトでは、.authenticate()
に渡す値は環境変数やconfigモジュールを使い、認証情報がソースコードに残らないようにします。
リリースの作成
github4の.createRelease()
メソッドを使います。
標準ではcallbackでレスポンスを返しますが、Promiseで扱った方がgulpタスクにしやすいため、pifyでラップしました。
$ npm install pify --save-dev
var pify = require('pify');
pify(github.repos.createRelease)({
user: 'htanjo',
repo: 'github-release-demo',
tag_name: 'v' + pkg.version,
body: 'Release v' + pkg.version // リリースノート(Markdownに対応)
})
.then(function (res) {
console.log(res); // APIからのレスポンス
console.log(res.id); // リリースID(ファイルアップロード時に必要になる)
});
ファイルのアップロード
github4の.uploadAsset()
を使います。
どのリリースに添付するかは、リリースIDでの指定が必要です。タグ名ではダメなようです。
ファイルの指定はfilePath
オプションを使います。この例では"dist/my-package.zip"がすでに生成されているとします。
pify(github.repos.uploadAsset)({
user: 'htanjo',
repo: 'github-release-demo',
id: id, // .createRelease()のレスポンスで取得したID
name: pkg.name + '-v' + pkg.version + '.zip', // 配布時のファイル名
filePath: 'dist/' + pkg.name + '.zip' // アップロードするファイル
})
.then(function (res) {
console.log(res); // APIからのレスポンス
});
gulpタスクの作成
以上をまとめてgulpタスクにします。
非同期処理がいくつもありますが、Promise化してあるのでそのままgulp.task()
にreturn
すればOKです。
npm install gulp gulp-util --save-dev
gulpfile.js:
var gulp = require('gulp');
var gutil = require('gulp-util');
var GitHubApi = require('github4');
var pify = require('pify');
var fs = require('fs');
var pkg = JSON.parse(fs.readFileSync('package.json', 'utf8'));
var github = new GitHubApi();
github.authenticate({
type: 'oauth',
token: 'xxxxxxxxxxxxxxxx'
});
gulp.task('release', function () {
return pify(github.repos.createRelease)({
user: 'htanjo',
repo: 'github-release-demo',
tag_name: 'v' + pkg.version,
body: 'Release v' + pkg.version
})
.then(function (res) {
gutil.log('Release "' + res.tag_name + '" created');
return res.id;
})
.then(function (id) {
return pify(github.repos.uploadAsset)({
user: 'htanjo',
repo: 'github-release-demo',
id: id,
name: pkg.name + '-v' + pkg.version + '.zip',
filePath: 'dist/' + pkg.name + '.zip'
})
})
.then(function (res) {
gutil.log('Asset "' + res.name + '" uploaded');
});
});
リリース作成/ファイルアップロードを2つのタスクに分けておくと、後からファイル添付だけできるようになったり便利ですが、別途リリースIDを取得する必要があります。その場合は.getReleaseByTag()
を使えばタグ名からIDが取得できます。
リリースしてみる
gulpタスクを実行!
$ gulp release
[18:01:42] Using gulpfile ~/lab/github-release-demo/gulpfile.js
[18:01:42] Starting 'release'...
[18:01:43] Release "v0.1.0" created
[18:01:44] Asset "my-package-v0.1.0.zip" uploaded
[18:01:44] Finished 'release' after 1.9 s
リリース完了、ファイルも添付されました!
デモプロジェクト
https://github.com/htanjo/github-release-demo
実際のリリースページ
https://github.com/htanjo/github-release-demo/releases