動機
最近、Electronのアプリを作り始めました。
出来上がった機能をすぐに公開したいので、CircleCIでmac用のappファイルを作成し、GitHubのReleaseに上げる事にしました。
ただ、masterの変更の度にバージョン番号を上げるのも面倒だし、Releaseのタグが大量にあるのも微妙な気がしました。
(なにより、毎回tagを手動で作るのもめんどい)
そこで、Releaseは毎回"更新"し、package.jsonのversion
が変わっている場合のみReleaseを"追加"することにしました。
普通は逆というか、普通のフローとは違いますね。。
例:
- Releaseに
v0.0.1
が存在するとき、version
が0.0.1
のままmasterが更新
以前のタグ・Releaseを破棄、最新のmasterでv0.0.1
を再作成。 - Releaseに
v0.0.1
が存在するとき、version
を0.0.2
にしてmasterを更新
v0.0.1
はそのままで、最新のmasterでv0.0.2
を作成。
手順
CircleCIの環境変数を設定
GitHub上のリソースを変更するため、access tokenを設定します。
こちらより、repo
の権限を持つtokenを取得します。
CircleCI上のEnvironment variablesの設定( https://circleci.com/gh/:user/:repo/edit#env-vars 形式のURL)にて、GITHUB_API_TOKEN
というkeyで設定します。
CircleCIの鍵を更新
後述のスクリプトで、remoteのgitよりタグの削除・追加を行う必要があるので、CircleCIの持つ鍵を更新します。(標準ではread権限のみ)
Checkout SSH keysの設定( https://circleci.com/gh/:user/:repo/edit#checkout 形式のURL)にて、現在の鍵をRemoveし、"Add deploy key"より新たな鍵を追加します。(ボタンをポチポチするだけで出来るのは便利ですね。)
上記の設定が完了したら、 https://github.com/settings/ssh のページに"CircleCI: :user/:repo"といった鍵が追加されているはずです。
scriptの作成
次に、実際に処理を行うスクリプトです。
user
やrepo
、中にあるメールアドレスなどは環境に合わせて設定する必要があります。
(user/repoぐらいは、CircleCI上から取れますね。。手抜きです。)
下記を、今回はbin/release
として置きました。
また、chmod a+x bin/release
として実行権限を与えてあります。
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import os
import sys
import subprocess
import httplib
import urllib2
import json
import copy
user = 'noboru-i'
repo = 'issue-hub'
token = os.getenv('GITHUB_API_TOKEN')
branch = os.getenv('CIRCLE_BRANCH')
host = 'api.github.com'
headers = {
'Authorization' : 'token %s' % token,
'Content-Type' : 'application/json',
'User-Agent' : 'issue-hub release'
}
# get file name from arg
argvs = sys.argv
argc = len(argvs)
if (argc != 2):
print 'Usage: # python %s filename' % argvs[0]
quit()
file_name = argvs[1]
# get version from package.json
package_info = json.loads(open('package.json').read())
tag_version = package_info['version']
# get current release id or None
def get_release_id():
print 'get_release_id'
path = '/repos/%s/%s/releases/tags/v%s' % (user, repo, tag_version)
conn = httplib.HTTPSConnection(host)
conn.request('GET', path, None, headers)
resp = conn.getresponse()
print resp.status
if resp.status == 404:
return None
res = json.loads(resp.read())
return res['id']
# delete current release by id
def delete_release(id):
print 'delete_release'
path = '/repos/%s/%s/releases/%s' % (user, repo, id)
conn = httplib.HTTPSConnection(host)
conn.request('DELETE', path, None, headers)
resp = conn.getresponse()
print resp.status
# delete tag
subprocess.call('git config --global user.email ishikura.noboru@gmail.com'.split(' '))
subprocess.call('git config --global user.name "CircleCI"'.split(' '))
subprocess.call(('git tag -d v%s' % tag_version).split(' '))
subprocess.call(('git push origin :v%s' % tag_version).split(' '))
# create new release
def create_release():
print 'create_release'
path = '/repos/%s/%s/releases' % (user, repo)
params = {
'tag_name' : 'v%s' % tag_version,
'target_commitish' : branch,
'name' : 'v%s' % tag_version,
'prerelease' : True
}
conn = httplib.HTTPSConnection(host)
conn.request('POST', path, json.dumps(params), headers)
resp = conn.getresponse()
print resp.status
res = json.loads(resp.read())
return res
# upload binary file
def upload_file(upload_url, file_name):
print 'upload_file'
name = os.path.basename(file_name)
url = upload_url.replace('{?name,label}', '?name=%s' % name)
print url
length = os.path.getsize(file_name)
data = open(file_name, 'rb')
req = urllib2.Request(url, data)
req.add_header('Content-Length', '%d' % length)
req.add_header('Content-Type', 'application/octet-stream')
req.add_header('Authorization', 'token %s' % token)
res = urllib2.urlopen(req).read()
return res
id = get_release_id()
print id
if id is not None:
delete_release(id)
# sometime create draft release
import time
time.sleep(5)
create_result = create_release()
print create_result['id']
print create_result['upload_url']
upload_result = upload_file(create_result['upload_url'], file_name)
circle.ymlの編集
私の場合は、npmのタスクを叩き、tarコマンドでdist/issue-hub-darwin-x64.tar.gz
を作っているので、それを引数として実行します。
私の実際のcircle.ymlはこちらです。
deployment:
master:
branch: master
commands:
- # ここでバイナリの作成
- ./bin/release dist/issue-hub-darwin-x64.tar.gz