Merge pull request #103 from atom/releases-api

Upload atom-shell's binaries with Releases API
This commit is contained in:
Cheng Zhao 2013-09-26 18:46:35 -07:00
commit c029ff2055
6 changed files with 196 additions and 21 deletions

View file

@ -7,17 +7,35 @@
#include "base/debug/debugger.h"
#include "base/logging.h"
#include "common/atom_version.h"
#include "common/v8_conversions.h"
#include "vendor/node/src/node.h"
namespace atom {
namespace {
static int kMaxCallStackSize = 200; // Same with WebKit.
static uv_async_t dummy_uv_handle;
void UvNoOp(uv_async_t* handle, int status) {
}
v8::Handle<v8::Object> DumpStackFrame(v8::Handle<v8::StackFrame> stack_frame) {
v8::Local<v8::Object> result = v8::Object::New();
result->Set(ToV8Value("line"), ToV8Value(stack_frame->GetLineNumber()));
result->Set(ToV8Value("column"), ToV8Value(stack_frame->GetColumn()));
v8::Handle<v8::String> script = stack_frame->GetScriptName();
if (!script.IsEmpty())
result->Set(ToV8Value("script"), script);
v8::Handle<v8::String> function = stack_frame->GetScriptNameOrSourceURL();
if (!function.IsEmpty())
result->Set(ToV8Value("function"), function);
return result;
}
} // namespace
// Defined in atom_extensions.cc.
@ -37,6 +55,7 @@ void AtomBindings::BindTo(v8::Handle<v8::Object> process) {
node::SetMethod(process, "crash", Crash);
node::SetMethod(process, "activateUvLoop", ActivateUVLoop);
node::SetMethod(process, "log", Log);
node::SetMethod(process, "getCurrentStackTrace", GetCurrentStackTrace);
process->Get(v8::String::New("versions"))->ToObject()->
Set(v8::String::New("atom-shell"), v8::String::New(ATOM_VERSION_STRING));
@ -111,4 +130,23 @@ v8::Handle<v8::Value> AtomBindings::Log(const v8::Arguments& args) {
return v8::Undefined();
}
// static
v8::Handle<v8::Value> AtomBindings::GetCurrentStackTrace(
const v8::Arguments& args) {
v8::HandleScope scope;
int stack_limit = kMaxCallStackSize;
FromV8Arguments(args, &stack_limit);
v8::Local<v8::StackTrace> stack_trace = v8::StackTrace::CurrentStackTrace(
stack_limit, v8::StackTrace::kDetailed);
int frame_count = stack_trace->GetFrameCount();
v8::Local<v8::Array> result = v8::Array::New(frame_count);
for (int i = 0; i < frame_count; ++i)
result->Set(i, DumpStackFrame(stack_trace->GetFrame(i)));
return scope.Close(result);
}
} // namespace atom

View file

@ -24,6 +24,7 @@ class AtomBindings {
static v8::Handle<v8::Value> Crash(const v8::Arguments& args);
static v8::Handle<v8::Value> ActivateUVLoop(const v8::Arguments& args);
static v8::Handle<v8::Value> Log(const v8::Arguments& args);
static v8::Handle<v8::Value> GetCurrentStackTrace(const v8::Arguments& args);
DISALLOW_COPY_AND_ASSIGN(AtomBindings);
};

View file

@ -77,6 +77,10 @@ inline v8::Handle<v8::Value> ToV8Value(bool b) {
return v8::Boolean::New(b);
}
inline v8::Handle<v8::Value> ToV8Value(const char* s) {
return v8::String::New(s);
}
inline v8::Handle<v8::Value> ToV8Value(const std::string& s) {
return v8::String::New(s.data(), s.size());
}

3
script/cpplint.py vendored
View file

@ -7,12 +7,11 @@ import sys
IGNORE_FILES = [
'app/win/resource.h',
'browser/atom_event_processing_window.h',
'browser/atom_application_mac.h',
'browser/atom_application_delegate_mac.h',
'browser/native_window_mac.h',
'browser/ui/atom_event_processing_window.h',
'browser/ui/atom_menu_controller_mac.h',
'browser/ui/cocoa/custom_frame_view.h',
'browser/ui/nsalert_synchronous_sheet_mac.h',
'common/api/api_messages.cc',
'common/api/api_messages.h',

70
script/lib/github.py Normal file
View file

@ -0,0 +1,70 @@
#!/usr/bin/env python
import json
import re
import requests
GITHUB_URL = 'https://api.github.com'
GITHUB_UPLOAD_ASSET_URL = 'https://uploads.github.com'
class GitHub:
def __init__(self, access_token):
self._authorization = 'token %s' % access_token
pattern = '^/repos/{0}/{0}/releases/{1}/assets$'.format('[^/]+', '[0-9]+')
self._releases_upload_api_pattern = re.compile(pattern)
def __getattr__(self, attr):
return _Callable(self, '/%s' % attr)
def _http(self, method, path, **kw):
if not 'headers' in kw:
kw['headers'] = dict()
headers = kw['headers']
headers['Authorization'] = self._authorization
headers['Accept'] = 'application/vnd.github.manifold-preview'
# Data are sent in JSON format.
if 'data' in kw:
kw['data'] = json.dumps(kw['data'])
# Switch to a different domain for the releases uploading API.
if self._releases_upload_api_pattern.match(path):
url = '%s%s' % (GITHUB_UPLOAD_ASSET_URL, path)
else:
url = '%s%s' % (GITHUB_URL, path)
r = getattr(requests, method)(url, **kw).json()
if 'message' in r:
raise Exception(json.dumps(r, indent=2, separators=(',', ': ')))
return r
class _Executable:
def __init__(self, gh, method, path):
self._gh = gh
self._method = method
self._path = path
def __call__(self, **kw):
return self._gh._http(self._method, self._path, **kw)
class _Callable(object):
def __init__(self, gh, name):
self._gh = gh
self._name = name
def __call__(self, *args):
if len(args) == 0:
return self
name = '%s/%s' % (self._name, '/'.join([str(arg) for arg in args]))
return _Callable(self._gh, name)
def __getattr__(self, attr):
if attr in ['get', 'put', 'post', 'patch', 'delete']:
return _Executable(self._gh, attr, self._name)
name = '%s/%s' % (self._name, attr)
return _Callable(self._gh, name)

View file

@ -4,11 +4,13 @@ import argparse
import errno
import glob
import os
import requests
import subprocess
import sys
import tempfile
from lib.util import *
from lib.github import GitHub
TARGET_PLATFORM = {
@ -18,6 +20,7 @@ TARGET_PLATFORM = {
'win32': 'win32',
}[sys.platform]
ATOM_SHELL_REPO = 'atom/atom-shell'
ATOM_SHELL_VRESION = get_atom_shell_version()
NODE_VERSION = 'v0.10.18'
@ -32,18 +35,26 @@ def main():
if not dist_newer_than_head():
create_dist = os.path.join(SOURCE_ROOT, 'script', 'create-dist.py')
subprocess.check_call([sys.executable, create_dist])
subprocess.check_output([sys.executable, create_dist])
# Upload atom-shell with GitHub Releases API.
github = GitHub(auth_token())
release_id = create_or_get_release_draft(github, args.version)
upload_atom_shell(github, release_id, os.path.join(DIST_DIR, DIST_NAME))
if not args.no_publish_release:
publish_release(github, release_id)
# Upload node's headers to S3.
bucket, access_key, secret_key = s3_config()
upload(bucket, access_key, secret_key)
if not args.no_update_version:
update_version(bucket, access_key, secret_key)
upload_node(bucket, access_key, secret_key, NODE_VERSION)
def parse_args():
parser = argparse.ArgumentParser(description='upload distribution file')
parser.add_argument('-n', '--no-update-version',
help='Do not update the latest version file',
parser.add_argument('-v', '--version', help='Specify the version',
default=ATOM_SHELL_VRESION)
parser.add_argument('-n', '--no-publish-release',
help='Do not publish the release',
action='store_true')
return parser.parse_args()
@ -62,36 +73,88 @@ def dist_newer_than_head():
return dist_time > int(head_time)
def upload(bucket, access_key, secret_key, version=ATOM_SHELL_VRESION):
def get_text_with_editor():
editor = os.environ.get('EDITOR','nano')
initial_message = '\n# Please enter the body of your release note.'
t = tempfile.NamedTemporaryFile(suffix='.tmp', delete=False)
t.write(initial_message)
t.close()
subprocess.call([editor, t.name])
text = ''
for line in open(t.name, 'r'):
if len(line) == 0 or line[0] != '#':
text += line
os.unlink(t.name)
return text
def create_or_get_release_draft(github, tag):
name = 'atom-shell %s' % tag
releases = github.repos(ATOM_SHELL_REPO).releases.get()
for release in releases:
# The untagged commit doesn't have a matching tag_name, so also check name.
if release['tag_name'] == tag or release['name'] == name:
return release['id']
return create_release_draft(github, tag)
def create_release_draft(github, tag):
name = 'atom-shell %s' % tag
body = get_text_with_editor()
print body
data = dict(tag_name=tag, target_commitish=tag, name=name, body=body,
draft=True)
r = github.repos(ATOM_SHELL_REPO).releases.post(data=data)
return r['id']
def upload_atom_shell(github, release_id, file_path):
params = {'name': os.path.basename(file_path)}
headers = {'Content-Type': 'application/zip'}
files = {'file': open(file_path, 'rb')}
github.repos(ATOM_SHELL_REPO).releases(release_id).assets.post(
params=params, headers=headers, files=files, verify=False)
def publish_release(github, release_id):
data = dict(draft=False)
github.repos(ATOM_SHELL_REPO).releases(release_id).patch(data=data)
def upload_node(bucket, access_key, secret_key, version):
os.chdir(DIST_DIR)
s3put(bucket, access_key, secret_key, DIST_DIR,
'atom-shell/{0}'.format(version), [DIST_NAME])
s3put(bucket, access_key, secret_key, DIST_DIR,
'atom-shell/dist/{0}'.format(NODE_VERSION), glob.glob('node-*.tar.gz'))
'atom-shell/dist/{0}'.format(version), glob.glob('node-*.tar.gz'))
if TARGET_PLATFORM == 'win32':
# Generate the node.lib.
build = os.path.join(SOURCE_ROOT, 'script', 'build.py')
subprocess.check_call([sys.executable, build, '-c', 'Release',
'-t', 'generate_node_lib'])
subprocess.check_output([sys.executable, build, '-c', 'Release',
'-t', 'generate_node_lib'])
# Upload the 32bit node.lib.
node_lib = os.path.join(OUT_DIR, 'node.lib')
s3put(bucket, access_key, secret_key, OUT_DIR,
'atom-shell/dist/{0}'.format(NODE_VERSION), [node_lib])
'atom-shell/dist/{0}'.format(version), [node_lib])
# Upload the fake 64bit node.lib.
touch_x64_node_lib()
node_lib = os.path.join(OUT_DIR, 'x64', 'node.lib')
s3put(bucket, access_key, secret_key, OUT_DIR,
'atom-shell/dist/{0}'.format(NODE_VERSION), [node_lib])
'atom-shell/dist/{0}'.format(version), [node_lib])
def update_version(bucket, access_key, secret_key):
prefix = os.path.join(SOURCE_ROOT, 'dist')
version = os.path.join(prefix, 'version')
s3put(bucket, access_key, secret_key, prefix, 'atom-shell', [version])
def auth_token():
token = os.environ.get('ATOM_SHELL_GITHUB_TOKEN')
message = ('Error: Please set the $ATOM_SHELL_GITHUB_TOKEN '
'environment variable, which is your personal token')
assert token, message
return token
def s3_config():
@ -116,7 +179,7 @@ def s3put(bucket, access_key, secret_key, prefix, key_prefix, files):
'--grant', 'public-read'
] + files
subprocess.check_call(args)
subprocess.check_output(args)
def touch_x64_node_lib():