Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code

Unified Diff: sitescripts/extensions/bin/createNightlies.py

Issue 29352919: Issue 4419 - Use proper API in order to upload devbuilds to AMO (Closed) Base URL: https://hg.adblockplus.org/sitescripts
Patch Set: Addressed another nit Created Sept. 13, 2016, 3:45 p.m.
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « .sitescripts.example ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: sitescripts/extensions/bin/createNightlies.py
===================================================================
--- a/sitescripts/extensions/bin/createNightlies.py
+++ b/sitescripts/extensions/bin/createNightlies.py
@@ -19,24 +19,25 @@ Nightly builds generation script
================================
This script generates nightly builds of extensions, together
with changelogs and documentation.
"""
import ConfigParser
-import cookielib
+import base64
from datetime import datetime
import hashlib
-import HTMLParser
+import hmac
import json
import logging
import os
import pipes
+import random
import shutil
import struct
import subprocess
import sys
import tempfile
import time
from urllib import urlencode
import urllib2
@@ -386,115 +387,64 @@ class NightlyBuild(object):
link['changelog'] = changelogFile
links.append(link)
template = get_template(get_config().get('extensions', 'nightlyIndexPage'))
template.stream({'config': self.config, 'links': links}).dump(outputPath)
def uploadToMozillaAddons(self):
import urllib3
- username = get_config().get('extensions', 'amo_username')
- password = get_config().get('extensions', 'amo_password')
-
- slug = self.config.galleryID
- login_url = 'https://addons.mozilla.org/en-US/firefox/users/login'
- upload_url = 'https://addons.mozilla.org/en-US/developers/addon/%s/upload' % slug
- add_url = 'https://addons.mozilla.org/en-US/developers/addon/%s/versions/add' % slug
-
- cookie_jar = cookielib.CookieJar()
- opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cookie_jar))
-
- def load_url(url, data=None):
- content_type = 'application/x-www-form-urlencoded'
- if isinstance(data, dict):
- if any(isinstance(v, tuple) for v in data.itervalues()):
- data, content_type = urllib3.filepost.encode_multipart_formdata(data)
- else:
- data = urlencode(data.items())
-
- request = urllib2.Request(url, data, headers={'Content-Type': content_type})
- response = opener.open(request)
- try:
- return response.read()
- finally:
- response.close()
-
- class CSRFParser(HTMLParser.HTMLParser):
- result = None
- dummy_exception = Exception()
-
- def __init__(self, data):
- HTMLParser.HTMLParser.__init__(self)
- try:
- self.feed(data)
- self.close()
- except Exception, e:
- if e != self.dummy_exception:
- raise
-
- if not self.result:
- raise Exception('Failed to extract CSRF token')
-
- def set_result(self, value):
- self.result = value
- raise self.dummy_exception
+ header = {
+ 'alg': 'HS256', # HMAC-SHA256
+ 'typ': 'JWT',
+ }
- def handle_starttag(self, tag, attrs):
- attrs = dict(attrs)
- if tag == 'meta' and attrs.get('name') == 'csrf':
- self.set_result(attrs.get('content'))
- if tag == 'input' and attrs.get('name') == 'csrfmiddlewaretoken':
- self.set_result(attrs.get('value'))
-
- # Extract anonymous CSRF token
- login_page = load_url(login_url)
- csrf_token = CSRFParser(login_page).result
+ issued = int(time.time())
+ payload = {
+ 'iss': get_config().get('extensions', 'amo_key'),
+ 'jti': random.random(),
+ 'iat': issued,
+ 'exp': issued + 60,
+ }
- # Log in and get session's CSRF token
- main_page = load_url(
- login_url,
- {
- 'csrfmiddlewaretoken': csrf_token,
- 'username': username,
- 'password': password,
- }
+ input = '{}.{}'.format(
+ base64.b64encode(json.dumps(header)),
+ base64.b64encode(json.dumps(payload))
)
- csrf_token = CSRFParser(main_page).result
- # Upload build
+ signature = hmac.new(get_config().get('extensions', 'amo_secret'),
+ msg=input,
+ digestmod=hashlib.sha256).digest()
+ token = '{}.{}'.format(input, base64.b64encode(signature))
+
+ upload_url = ('https://addons.mozilla.org/api/v3/addons/{}/'
+ 'versions/{}/').format(self.extensionID, self.version)
+
with open(self.path, 'rb') as file:
- upload_response = json.loads(load_url(
- upload_url,
- {
- 'csrfmiddlewaretoken': csrf_token,
- 'upload': (os.path.basename(self.path), file.read(), 'application/x-xpinstall'),
- }
- ))
+ data, content_type = urllib3.filepost.encode_multipart_formdata({
+ 'upload': (
+ os.path.basename(self.path),
+ file.read(),
+ 'application/x-xpinstall'
+ )
+ })
- # Wait for validation to finish
- while not upload_response.get('validation'):
- time.sleep(2)
- upload_response = json.loads(load_url(
- upload_url + '/' + upload_response.get('upload')
- ))
-
- if upload_response['validation'].get('errors', 0):
- raise Exception('Build failed AMO validation, see https://addons.mozilla.org%s' % upload_response.get('full_report_url'))
+ request = urllib2.Request(upload_url, data=data)
+ request.add_header('Content-Type', content_type)
+ request.add_header('Authorization', 'JWT ' + token)
+ request.get_method = lambda: 'PUT'
- # Add version
- add_response = json.loads(load_url(
- add_url,
- {
- 'csrfmiddlewaretoken': csrf_token,
- 'upload': upload_response.get('upload'),
- 'source': ('', '', 'application/octet-stream'),
- 'beta': 'on',
- 'supported_platforms': 1, # PLATFORM_ANY.id
- }
- ))
+ try:
+ urllib2.urlopen(request).close()
+ except urllib2.HTTPError as e:
+ try:
+ logging.error(e.read())
+ finally:
+ e.close()
+ raise
def uploadToChromeWebStore(self):
# Google APIs use HTTP error codes with error message in body. So we add
# the response body to the HTTPError to get more meaningful error messages.
class HTTPErrorBodyHandler(urllib2.HTTPDefaultErrorHandler):
def http_error_default(self, req, fp, code, msg, hdrs):
raise urllib2.HTTPError(req.get_full_url(), code, '%s\n%s' % (msg, fp.read()), hdrs, fp)
@@ -595,17 +545,17 @@ class NightlyBuild(object):
self.writeIEUpdateManifest(versions)
# update index page
self.updateIndex(versions)
# update nightlies config
self.config.latestRevision = self.revision
- if self.config.type == 'gecko' and self.config.galleryID and get_config().get('extensions', 'amo_username'):
+ if self.config.type == 'gecko' and self.config.galleryID and get_config().has_option('extensions', 'amo_key'):
self.uploadToMozillaAddons()
elif self.config.type == 'chrome' and self.config.clientID and self.config.clientSecret and self.config.refreshToken:
self.uploadToChromeWebStore()
finally:
# clean up
if self.tempdir:
shutil.rmtree(self.tempdir, ignore_errors=True)
« no previous file with comments | « .sitescripts.example ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld