build.py
author Vladan Djeric <vdjeric@mozilla.com>
Mon, 21 Dec 2015 18:26:03 -0500
changeset 64 14a8a6325cc3d002da6715fa1d883e51713c3285
parent 46 d2683a9117496e57bdbdb26f75746846c1b19105
child 75 455cd6114b968cfbcfc1b15fa79a86097c602168
permissions -rw-r--r--
Added tag release_tag for changeset 38d7b06615d9

# Copyright 2014 The Mozilla Foundation <http://www.mozilla.org/>
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import json
import os
import shutil
import sys
from experiment import Experiment
from urllib import quote
import hashlib
import subprocess

import genshi.template

this_dir = os.path.abspath(os.path.dirname(__file__))
template_dir = os.path.join(this_dir, 'templates')

def sha256(path):
    fd = open(path)
    hasher = hashlib.sha256()
    while True:
        s = fd.read(hasher.block_size)
        if not len(s):
            break
        hasher.update(s)
    return hasher.hexdigest()

def get_experiments():
    l = []
    for leaf in os.listdir(os.path.join(this_dir, "experiments")):
        experiment_path = os.path.join(this_dir, "experiments", leaf)
        if os.path.exists(os.path.join(experiment_path, "manifest.json")):
            e = Experiment(experiment_path)
            if e.publish:
                l.append(e)

    l.sort(key=lambda e: (e.priority, e.manifest["id"]))

    return l

def build(dest_path, baseurl):
    if baseurl[-1] != "/":
        baseurl = baseurl + "/"

    if os.path.exists(dest_path):
        shutil.rmtree(dest_path)

    os.mkdir(dest_path)
    loader = genshi.template.TemplateLoader([template_dir])

    def write_template(template_name, path, **kwargs):
        t = loader.load(template_name)
        s = t.generate(encoding='utf-8', **kwargs)
        with open(os.path.join(dest_path, path), 'wb') as fh:
            for item in s.render('html'):
                fh.write(item.encode('utf-8'))

    def copy_template(template_name, path):
        shutil.copyfile(os.path.join(template_dir, template_name),
                        os.path.join(dest_path, path))

    jdata = {'version': 1, 'experiments': []}

    experiments = get_experiments()
    for e in (e for e in experiments if not e.finished):
        # Experiments have two modes for building:
        # 1) Copy experiment.xpi (presumably from source control)
        # 2) Run an install script (build.py) to generate the .xpi
        # Only 1 can be present. If both are present, we error.
        input_xpi = os.path.join(e.path, 'experiment.xpi')
        build_path = os.path.join(e.path, 'build.py')
        output_xpi = os.path.join(dest_path, e.manifest['id'], 'experiment.xpi')

        os.mkdir(os.path.join(dest_path, e.manifest["id"]))

        # If both an .xpi and a build script exist, use the XPI. The logic
        # here is that the build process may not be idempotent and/or we
        # may wish to check in a built XPI along with its build script (so
        # it can be rebuilt later). Keep in mind that experiments are
        # keyed to the SHA-1 of their XPI.
        if os.path.exists(input_xpi):
            shutil.copyfile(input_xpi, output_xpi)
        else:
            subprocess.check_call([sys.executable, build_path, output_xpi],
                cwd=e.path)

            if not os.path.exists(output_xpi):
                raise Exception('Build script did not produce an .xpi: %s' %
                    build_path)

        d = e.manifest.copy()
        d["xpiURL"] = "%s%s/experiment.xpi" % (baseurl, quote(e.manifest["id"]))
        d["xpiHash"] = "sha256:%s" % (sha256(output_xpi),)
        jdata['experiments'].append(d)

    experiments.sort(key=lambda e: (e.finished or e.disabled, -e.manifest["startTime"]))

    write_template('index.xhtml', 'index.html', experiments=experiments)
    copy_template('htaccess', '.htaccess')

    with open(os.path.join(dest_path, 'firefox-manifest.json'), 'wb') as fh:
        json.dump(jdata, fh)

if __name__ == "__main__":
    path, baseurl = sys.argv[1:]
    build(os.path.normpath(os.path.abspath(path)), baseurl)