| Index: packagerChrome.py |
| =================================================================== |
| new file mode 100644 |
| --- /dev/null |
| +++ b/packagerChrome.py |
| @@ -0,0 +1,166 @@ |
| +# coding: utf-8 |
| + |
| +# This Source Code is subject to the terms of the Mozilla Public License |
| +# version 2.0 (the "License"). You can obtain a copy of the License at |
| +# http://mozilla.org/MPL/2.0/. |
| + |
| +import sys, os, subprocess, re, json, codecs, struct |
| +from ConfigParser import SafeConfigParser |
| +from StringIO import StringIO |
| +from zipfile import ZipFile, ZIP_DEFLATED |
| + |
| +defaultLocale = 'en_US' |
| + |
| +def getDefaultFileName(baseDir, metadata, version, ext): |
| + return os.path.join(baseDir, '%s-%s.%s' % (metadata.get('general', 'baseName'), version, ext)) |
| + |
| +def getMetadataPath(baseDir): |
| + return os.path.join(baseDir, 'metadata') |
| + |
| +def getBuildNum(baseDir): |
| + try: |
| + (result, dummy) = subprocess.Popen(['hg', 'id', '-n'], stdout=subprocess.PIPE).communicate() |
| + return re.sub(r'\D', '', result) |
| + except Exception: |
| + return '0' |
| + |
| +def readMetadata(baseDir): |
| + metadata = SafeConfigParser() |
| + file = codecs.open(getMetadataPath(baseDir), 'rb', encoding='utf-8') |
| + metadata.readfp(file) |
| + file.close() |
| + return metadata |
| + |
| +def readVersion(baseDir): |
| + file = open(os.path.join(baseDir, 'manifest.json')) |
| + data = json.load(file) |
| + file.close() |
| + return data['version'] |
| + |
| +def setUpdateURL(updateName, zip, dir, fileName, fileData): |
| + if fileName == 'manifest.json': |
| + data = json.loads(fileData) |
| + data['update_url'] = 'https://adblockplus.org/devbuilds/%s/updates.xml' % updateName |
| + return json.dumps(data, sort_keys=True, indent=2) |
| + return fileData |
| + |
| +def setExperimentalSettings(zip, dir, fileName, fileData): |
| + if fileName == 'manifest.json': |
| + data = json.loads(fileData) |
| + data['permissions'] += ['experimental'] |
| + data['name'] += ' experimental build' |
| + return json.dumps(data, sort_keys=True, indent=2) |
| + return fileData |
| + |
| +def addBuildNumber(revision, zip, dir, fileName, fileData): |
| + if fileName == 'manifest.json': |
| + if len(revision) > 0: |
| + data = json.loads(fileData) |
| + while data['version'].count('.') < 2: |
| + data['version'] += '.0' |
| + data['version'] += '.' + revision |
| + return json.dumps(data, sort_keys=True, indent=2) |
| + return fileData |
| + |
| +def mergeContentScripts(zip, dir, fileName, fileData): |
| + if fileName == 'manifest.json': |
| + data = json.loads(fileData) |
| + if 'content_scripts' in data: |
| + scriptIndex = 1 |
| + for contentScript in data['content_scripts']: |
| + if 'js' in contentScript: |
| + scriptData = '' |
| + for scriptFile in contentScript['js']: |
| + parts = [dir] + scriptFile.split('/') |
| + scriptPath = os.path.join(*parts) |
| + handle = open(scriptPath, 'rb') |
| + scriptData += handle.read() |
| + handle.close() |
| + contentScript['js'] = ['contentScript' + str(scriptIndex) + '.js'] |
| + zip.writestr('contentScript' + str(scriptIndex) + '.js', scriptData) |
| + scriptIndex += 1 |
| + return json.dumps(data, sort_keys=True, indent=2) |
| + return fileData |
| + |
| +def addToZip(zip, filters, dir, baseName): |
| + for file in os.listdir(dir): |
| + filelc = file.lower() |
| + if (file.startswith('.') or |
| + file == 'buildtools' or file == 'qunit' or file == 'metadata' or |
| + filelc.endswith('.py') or filelc.endswith('.pyc') or |
| + filelc.endswith('.crx') or filelc.endswith('.zip') or |
| + filelc.endswith('.sh') or filelc.endswith('.bat') or |
| + filelc.endswith('.txt')): |
| + # skip special files, scripts, existing archives |
| + continue |
| + if file.startswith('include.'): |
| + # skip includes, they will be added by other means |
| + continue |
| + |
| + filePath = os.path.join(dir, file) |
| + if os.path.isdir(filePath): |
| + addToZip(zip, filters, filePath, baseName + file + '/') |
| + else: |
| + handle = open(filePath, 'rb') |
| + fileData = handle.read() |
| + handle.close() |
| + |
| + for filter in filters: |
| + fileData = filter(zip, dir, baseName + file, fileData) |
| + zip.writestr(baseName + file, fileData) |
| + |
| +def packDirectory(dir, filters): |
| + buffer = StringIO() |
| + zip = ZipFile(buffer, 'w', ZIP_DEFLATED) |
| + addToZip(zip, filters, dir, '') |
| + zip.close() |
| + return buffer.getvalue() |
| + |
| +def signBinary(zipdata, keyFile): |
| + import M2Crypto |
| + if not os.path.exists(keyFile): |
| + M2Crypto.RSA.gen_key(1024, 65537, callback=lambda x: None).save_key(keyFile, cipher=None) |
| + key = M2Crypto.EVP.load_key(keyFile) |
| + key.sign_init() |
| + key.sign_update(zipdata) |
| + return key.final() |
| + |
| +def getPublicKey(keyFile): |
| + import M2Crypto |
| + return M2Crypto.EVP.load_key(keyFile).as_der() |
| + |
| +def writePackage(outputFile, pubkey, signature, zipdata): |
| + file = open(outputFile, 'wb') |
| + if pubkey != None and signature != None: |
| + file.write(struct.pack('<4sIII', 'Cr24', 2, len(pubkey), len(signature))) |
| + file.write(pubkey) |
| + file.write(signature) |
| + file.write(zipdata) |
| + file.close() |
| + |
| +def createBuild(baseDir, outFile=None, buildNum=None, releaseBuild=False, keyFile=None, experimentalAPI=False): |
| + metadata = readMetadata(baseDir) |
| + version = readVersion(baseDir) |
| + if outFile == None: |
| + outFile = getDefaultFileName(baseDir, metadata, version, 'crx' if keyFile else 'zip') |
| + |
| + filters = [] |
| + if not releaseBuild: |
| + if buildNum == None: |
| + buildNum = getBuildNum(baseDir) |
| + filters.append(lambda zip, dir, fileName, fileData: addBuildNumber(buildNum, zip, dir, fileName, fileData)) |
| + |
| + baseName = metadata.get('general', 'baseName') |
| + updateName = baseName + '-experimental' if experimentalAPI else baseName |
| + filters.append(lambda zip, dir, fileName, fileData: setUpdateURL(updateName, zip, dir, fileName, fileData)) |
| + if experimentalAPI: |
| + filters.append(setExperimentalSettings) |
| + filters.append(mergeContentScripts) |
| + |
| + zipdata = packDirectory(baseDir, filters) |
| + signature = None |
| + pubkey = None |
| + if keyFile != None: |
| + signature = signBinary(zipdata, keyFile) |
| + pubkey = getPublicKey(keyFile) |
| + writePackage(outFile, pubkey, signature, zipdata) |