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/debug/debugger.h"
|
||||||
#include "base/logging.h"
|
#include "base/logging.h"
|
||||||
#include "common/atom_version.h"
|
#include "common/atom_version.h"
|
||||||
|
#include "common/v8_conversions.h"
|
||||||
#include "vendor/node/src/node.h"
|
#include "vendor/node/src/node.h"
|
||||||
|
|
||||||
namespace atom {
|
namespace atom {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
static int kMaxCallStackSize = 200; // Same with WebKit.
|
||||||
|
|
||||||
static uv_async_t dummy_uv_handle;
|
static uv_async_t dummy_uv_handle;
|
||||||
|
|
||||||
void UvNoOp(uv_async_t* handle, int status) {
|
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
|
} // namespace
|
||||||
|
|
||||||
// Defined in atom_extensions.cc.
|
// 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, "crash", Crash);
|
||||||
node::SetMethod(process, "activateUvLoop", ActivateUVLoop);
|
node::SetMethod(process, "activateUvLoop", ActivateUVLoop);
|
||||||
node::SetMethod(process, "log", Log);
|
node::SetMethod(process, "log", Log);
|
||||||
|
node::SetMethod(process, "getCurrentStackTrace", GetCurrentStackTrace);
|
||||||
|
|
||||||
process->Get(v8::String::New("versions"))->ToObject()->
|
process->Get(v8::String::New("versions"))->ToObject()->
|
||||||
Set(v8::String::New("atom-shell"), v8::String::New(ATOM_VERSION_STRING));
|
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();
|
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
|
} // namespace atom
|
||||||
|
|
|
@ -24,6 +24,7 @@ class AtomBindings {
|
||||||
static v8::Handle<v8::Value> Crash(const v8::Arguments& args);
|
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> ActivateUVLoop(const v8::Arguments& args);
|
||||||
static v8::Handle<v8::Value> Log(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);
|
DISALLOW_COPY_AND_ASSIGN(AtomBindings);
|
||||||
};
|
};
|
||||||
|
|
|
@ -77,6 +77,10 @@ inline v8::Handle<v8::Value> ToV8Value(bool b) {
|
||||||
return v8::Boolean::New(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) {
|
inline v8::Handle<v8::Value> ToV8Value(const std::string& s) {
|
||||||
return v8::String::New(s.data(), s.size());
|
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 = [
|
IGNORE_FILES = [
|
||||||
'app/win/resource.h',
|
'app/win/resource.h',
|
||||||
'browser/atom_event_processing_window.h',
|
|
||||||
'browser/atom_application_mac.h',
|
'browser/atom_application_mac.h',
|
||||||
'browser/atom_application_delegate_mac.h',
|
'browser/atom_application_delegate_mac.h',
|
||||||
'browser/native_window_mac.h',
|
'browser/native_window_mac.h',
|
||||||
|
'browser/ui/atom_event_processing_window.h',
|
||||||
'browser/ui/atom_menu_controller_mac.h',
|
'browser/ui/atom_menu_controller_mac.h',
|
||||||
'browser/ui/cocoa/custom_frame_view.h',
|
|
||||||
'browser/ui/nsalert_synchronous_sheet_mac.h',
|
'browser/ui/nsalert_synchronous_sheet_mac.h',
|
||||||
'common/api/api_messages.cc',
|
'common/api/api_messages.cc',
|
||||||
'common/api/api_messages.h',
|
'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 errno
|
||||||
import glob
|
import glob
|
||||||
import os
|
import os
|
||||||
|
import requests
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
import tempfile
|
import tempfile
|
||||||
|
|
||||||
from lib.util import *
|
from lib.util import *
|
||||||
|
from lib.github import GitHub
|
||||||
|
|
||||||
|
|
||||||
TARGET_PLATFORM = {
|
TARGET_PLATFORM = {
|
||||||
|
@ -18,6 +20,7 @@ TARGET_PLATFORM = {
|
||||||
'win32': 'win32',
|
'win32': 'win32',
|
||||||
}[sys.platform]
|
}[sys.platform]
|
||||||
|
|
||||||
|
ATOM_SHELL_REPO = 'atom/atom-shell'
|
||||||
ATOM_SHELL_VRESION = get_atom_shell_version()
|
ATOM_SHELL_VRESION = get_atom_shell_version()
|
||||||
NODE_VERSION = 'v0.10.18'
|
NODE_VERSION = 'v0.10.18'
|
||||||
|
|
||||||
|
@ -32,18 +35,26 @@ def main():
|
||||||
|
|
||||||
if not dist_newer_than_head():
|
if not dist_newer_than_head():
|
||||||
create_dist = os.path.join(SOURCE_ROOT, 'script', 'create-dist.py')
|
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()
|
bucket, access_key, secret_key = s3_config()
|
||||||
upload(bucket, access_key, secret_key)
|
upload_node(bucket, access_key, secret_key, NODE_VERSION)
|
||||||
if not args.no_update_version:
|
|
||||||
update_version(bucket, access_key, secret_key)
|
|
||||||
|
|
||||||
|
|
||||||
def parse_args():
|
def parse_args():
|
||||||
parser = argparse.ArgumentParser(description='upload distribution file')
|
parser = argparse.ArgumentParser(description='upload distribution file')
|
||||||
parser.add_argument('-n', '--no-update-version',
|
parser.add_argument('-v', '--version', help='Specify the version',
|
||||||
help='Do not update the latest version file',
|
default=ATOM_SHELL_VRESION)
|
||||||
|
parser.add_argument('-n', '--no-publish-release',
|
||||||
|
help='Do not publish the release',
|
||||||
action='store_true')
|
action='store_true')
|
||||||
return parser.parse_args()
|
return parser.parse_args()
|
||||||
|
|
||||||
|
@ -62,36 +73,88 @@ def dist_newer_than_head():
|
||||||
return dist_time > int(head_time)
|
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)
|
os.chdir(DIST_DIR)
|
||||||
|
|
||||||
s3put(bucket, access_key, secret_key, DIST_DIR,
|
s3put(bucket, access_key, secret_key, DIST_DIR,
|
||||||
'atom-shell/{0}'.format(version), [DIST_NAME])
|
'atom-shell/dist/{0}'.format(version), glob.glob('node-*.tar.gz'))
|
||||||
s3put(bucket, access_key, secret_key, DIST_DIR,
|
|
||||||
'atom-shell/dist/{0}'.format(NODE_VERSION), glob.glob('node-*.tar.gz'))
|
|
||||||
|
|
||||||
if TARGET_PLATFORM == 'win32':
|
if TARGET_PLATFORM == 'win32':
|
||||||
# Generate the node.lib.
|
# Generate the node.lib.
|
||||||
build = os.path.join(SOURCE_ROOT, 'script', 'build.py')
|
build = os.path.join(SOURCE_ROOT, 'script', 'build.py')
|
||||||
subprocess.check_call([sys.executable, build, '-c', 'Release',
|
subprocess.check_output([sys.executable, build, '-c', 'Release',
|
||||||
'-t', 'generate_node_lib'])
|
'-t', 'generate_node_lib'])
|
||||||
|
|
||||||
# Upload the 32bit node.lib.
|
# Upload the 32bit node.lib.
|
||||||
node_lib = os.path.join(OUT_DIR, 'node.lib')
|
node_lib = os.path.join(OUT_DIR, 'node.lib')
|
||||||
s3put(bucket, access_key, secret_key, OUT_DIR,
|
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.
|
# Upload the fake 64bit node.lib.
|
||||||
touch_x64_node_lib()
|
touch_x64_node_lib()
|
||||||
node_lib = os.path.join(OUT_DIR, 'x64', 'node.lib')
|
node_lib = os.path.join(OUT_DIR, 'x64', 'node.lib')
|
||||||
s3put(bucket, access_key, secret_key, OUT_DIR,
|
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):
|
def auth_token():
|
||||||
prefix = os.path.join(SOURCE_ROOT, 'dist')
|
token = os.environ.get('ATOM_SHELL_GITHUB_TOKEN')
|
||||||
version = os.path.join(prefix, 'version')
|
message = ('Error: Please set the $ATOM_SHELL_GITHUB_TOKEN '
|
||||||
s3put(bucket, access_key, secret_key, prefix, 'atom-shell', [version])
|
'environment variable, which is your personal token')
|
||||||
|
assert token, message
|
||||||
|
return token
|
||||||
|
|
||||||
|
|
||||||
def s3_config():
|
def s3_config():
|
||||||
|
@ -116,7 +179,7 @@ def s3put(bucket, access_key, secret_key, prefix, key_prefix, files):
|
||||||
'--grant', 'public-read'
|
'--grant', 'public-read'
|
||||||
] + files
|
] + files
|
||||||
|
|
||||||
subprocess.check_call(args)
|
subprocess.check_output(args)
|
||||||
|
|
||||||
|
|
||||||
def touch_x64_node_lib():
|
def touch_x64_node_lib():
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue