Merge pull request #103 from atom/releases-api
Upload atom-shell's binaries with Releases API
This commit is contained in:
commit
c029ff2055
6 changed files with 196 additions and 21 deletions
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
|
|
@ -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
3
script/cpplint.py
vendored
|
@ -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
70
script/lib/github.py
Normal 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)
|
101
script/upload.py
101
script/upload.py
|
@ -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():
|
||||
|
|
Loading…
Reference in a new issue