Commit 622b0d07 authored by dtseng's avatar dtseng Committed by Commit bot

Add scripts to semi-automate publishing of ChromeVox webstore extension using webstore API.

TEST=manually run through release workflow and verify api endpoints return success.
BUG=none

Review URL: https://codereview.chromium.org/417703002

Cr-Commit-Position: refs/heads/master@{#292021}
parent 8a3e99b8
......@@ -18,7 +18,11 @@
'variables': {
'generate_manifest_script_path': 'tools/generate_manifest.py',
'is_guest_manifest%': 0,
'key': 'MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDltVl1k15pjRzuZfMc3B69inxwm2bZeZ2O8/zFO+NluHnBm3GJ3fzdOoFGJd+M16I8p7zxxQyHeDMfWYASyCeB8XnUEDKjqNLQfCnncsANzHsYoEbYj2nEUML2P13b9q+AAvpCBpAJ4cZp81e9n1y/vbSXHE4385cgkKueItzikQIDAQAB',
},
'includes': [
'../../../../../build/util/version.gypi',
],
'actions': [
{
'action_name': 'generate_manifest',
......@@ -34,7 +38,9 @@
'python',
'<(generate_manifest_script_path)',
'--is_guest_manifest=<(is_guest_manifest)',
'--key=<(key)',
'--use_chromevox_next=<(use_chromevox_next)',
'--set_version=<(version_full)',
'-o', '<(output_manifest_path)',
'<(template_manifest_path)',
],
......
{
"key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDltVl1k15pjRzuZfMc3B69inxwm2bZeZ2O8/zFO+NluHnBm3GJ3fzdOoFGJd+M16I8p7zxxQyHeDMfWYASyCeB8XnUEDKjqNLQfCnncsANzHsYoEbYj2nEUML2P13b9q+AAvpCBpAJ4cZp81e9n1y/vbSXHE4385cgkKueItzikQIDAQAB",
{%if key is defined %}
"key": "{{key}}",
{% endif %}
"manifest_version": 2,
"name": "ChromeVox",
"version": "1.0",
"version": "{{set_version}}",
"description": "ChromeVox - Giving Voice to Chrome.",
{% if is_guest_manifest == '1' %}
"incognito": "split",
......
#!/usr/bin/env python
# Copyright 2014 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
'''A set of utilities to interface with the Chrome Webstore API.'''
import SimpleHTTPServer
import SocketServer
import httplib
import json
import os
import re
import sys
import thread
import urllib
import webbrowser
PROJECT_ARGS = {
'client_id': ('937534751394-gbj5334v9144c57qjqghl7d283plj5r4'
'.apps.googleusercontent.com'),
'grant_type': 'authorization_code',
'redirect_uri': 'http://localhost:8000'
}
PORT = 8000
APP_ID = 'kgejglhpjiefppelpmljglcjbhoiplfn'
OAUTH_DOMAIN = 'accounts.google.com'
OAUTH_AUTH_COMMAND = '/o/oauth2/auth'
OAUTH_TOKEN_COMMAND = '/o/oauth2/token'
WEBSTORE_API_SCOPE = 'https://www.googleapis.com/auth/chromewebstore'
API_ENDPOINT_DOMAIN = 'www.googleapis.com'
COMMAND_GET_UPLOAD_STATUS = (
'/chromewebstore/v1.1/items/%s?projection=draft' % APP_ID)
COMMAND_POST_PUBLISH = '/chromewebstore/v1.1/items/%s/publish' % APP_ID
COMMAND_POST_UPLOAD = '/upload/chromewebstore/v1.1/items/%s' % APP_ID
class CodeRequestHandler(SocketServer.StreamRequestHandler):
def handle(self):
content = self.rfile.readline()
self.server.code = re.search('code=(.*) ', content).groups()[0]
self.rfile.close()
def GetAuthCode():
Handler = CodeRequestHandler
httpd = SocketServer.TCPServer(("", PORT), Handler)
query = '&'.join(['response_type=code',
'scope=%s' % WEBSTORE_API_SCOPE,
'client_id=%(client_id)s' % PROJECT_ARGS,
'redirect_uri=%(redirect_uri)s' % PROJECT_ARGS])
auth_url = ' https://%s%s?%s' % (OAUTH_DOMAIN, OAUTH_AUTH_COMMAND, query)
print 'Navigating to %s' % auth_url
webbrowser.open(auth_url)
httpd.handle_request()
return httpd.code
def GetOauthToken(code, client_secret):
PROJECT_ARGS['code'] = code
PROJECT_ARGS['client_secret'] = client_secret
body = urllib.urlencode(PROJECT_ARGS)
conn = httplib.HTTPSConnection(OAUTH_DOMAIN)
conn.putrequest('POST', OAUTH_TOKEN_COMMAND)
conn.putheader('content-type', 'application/x-www-form-urlencoded')
conn.putheader('content-length', len(body))
conn.endheaders()
conn.send(body)
content = conn.getresponse().read()
return json.loads(content)
def GetPopulatedHeader(client_secret):
code = GetAuthCode()
access_token = GetOauthToken(code, client_secret)
url = 'www.googleapis.com'
return {'Authorization': 'Bearer %(access_token)s' % access_token,
'x-goog-api-version': 2,
'Content-Length': 0
}
def SendGetCommand(command, client_secret):
headers = GetPopulatedHeader(client_secret)
conn = httplib.HTTPSConnection(API_ENDPOINT_DOMAIN)
conn.request('GET', command, '', headers)
return conn.getresponse()
def SendPostCommand(command, client_secret, header_additions = {}, body=None):
headers = GetPopulatedHeader(client_secret)
headers = dict(headers.items() + header_additions.items())
conn = httplib.HTTPSConnection(API_ENDPOINT_DOMAIN)
conn.request('PUT', command, body, headers)
return conn.getresponse()
def GetUploadStatus(client_secret):
'''Gets the status of a previous upload.
Args:
client_secret ChromeVox's client secret creds.
'''
return SendGetCommand(COMMAND_GET_UPLOAD_STATUS, client_secret)
# httplib fails to persist the connection during upload; use curl instead.
def PostUpload(file, client_secret):
'''Posts an uploaded version of ChromeVox.
Args:
file A string path to the ChromeVox extension zip.
client_secret ChromeVox's client secret creds.
'''
header = GetPopulatedHeader(client_secret)
curl_command = ' '.join(['curl',
'-H "Authorization: %(Authorization)s"' % header,
'-H "x-goog-api-version: 2"',
'-X PUT',
'-T %s' % file,
'-v',
'https://%s%s' % (API_ENDPOINT_DOMAIN,
COMMAND_POST_UPLOAD)])
print 'Running %s' % curl_command
if os.system(curl_command) != 0:
sys.exit(-1)
def PostPublishTrustedTesters(client_secret):
'''Publishes a previously uploaded ChromeVox extension to trusted testers.
Args:
client_secret ChromeVox's client secret creds.
'''
return SendPostCommand(COMMAND_POST_PUBLISH,
client_secret,
{ 'publishTarget': 'trustedTesters'})
def PostPublish(client_secret):
'''Publishes a previously uploaded ChromeVox extension publically.
Args:
client_secret ChromeVox's client secret creds.
'''
return SendPostCommand(COMMAND_POST_PUBLISH, client_secret)
......@@ -48,6 +48,12 @@ def main():
parser.add_option(
'--use_chromevox_next', action='store', metavar='CHROMEVOX2',
help='Generate a ChromeVox next manifest')
parser.add_option(
'--set_version', action='store', metavar='SET_VERSION',
help='Set the extension version')
parser.add_option(
'--key', action='store', metavar='KEY',
help='Set the extension key')
options, args = parser.parse_args()
if len(args) != 1:
......
#!/usr/bin/env python
# Copyright 2014 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
'''Publishes ChromeVox to the webstore.
Given an unpacked extension, compresses and sends to the Chrome webstore.
Releasing to the webstore should involve the following manual steps before
running this script:
1. clean the output directory.
2. make a release build.
3. run manual smoke tests.
4. run automated ChromeVox tests.
'''
import chromevox_webstore_util
import json
import optparse
import os
import sys
import tempfile
from zipfile import ZipFile
# A list of files to exclude from the webstore build.
EXCLUDE_FILES = ['manifest_guest.json']
def CreateOptionParser():
parser = optparse.OptionParser(description=__doc__)
parser.usage = '%prog <extension_path> <output_path> <client_secret'
return parser
def MakeManifestEdits(root, old):
'''Customize a manifest for the webstore.
Args:
root: The directory containing file.
old: A json file.
Returns:
File of the new manifest.
'''
new_file = tempfile.NamedTemporaryFile()
new = new_file.name
with open(os.path.join(root, old)) as old_file:
new_contents = json.loads(old_file.read())
new_contents.pop('key', '')
new_file.write(json.dumps(new_contents))
return new_file
def RunInteractivePrompt(client_secret, output_path):
input = ''
while True:
print 'u upload'
print 'g get upload status'
print 't publish trusted tester'
print 'p publish public'
print 'q quit'
input = raw_input('Please select an option: ')
input = input.strip()
if input == 'g':
chromevox_webstore_util.GetUploadStatus(client_secret)
elif input == 'u':
chromevox_webstore_util.PostUpload(output_path, client_secret)
elif input == 't':
chromevox_webstore_util.PostPublishTrustedTesters(client_secret)
elif input == 'p':
chromevox_webstore_util.PostPublish(client_secret)
elif input == 'q':
sys.exit()
else:
print 'Unrecognized option: %s' % input
def main():
_, args = CreateOptionParser().parse_args()
if len(args) != 3:
print 'Expected exactly three arguments'
sys.exit(1)
extension_path = args[0]
output_path = args[1]
client_secret = args[2]
with ZipFile(output_path, 'w') as zip:
for root, dirs, files in os.walk(extension_path):
rel_path = os.path.join(os.path.relpath(root, extension_path), '')
for extension_file in files:
if extension_file in EXCLUDE_FILES:
continue
if extension_file == 'manifest.json':
new_file = MakeManifestEdits(root, extension_file)
zip.write(
new_file.name, os.path.join(rel_path, extension_file))
continue
zip.write(os.path.join(root, extension_file),
os.path.join(rel_path, extension_file))
RunInteractivePrompt(client_secret, output_path)
if __name__ == '__main__':
main()
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment