Skip to content

Commit

Permalink
Merge commit '00a192c' into pull568
Browse files Browse the repository at this point in the history
  • Loading branch information
djmitche committed Nov 18, 2012
2 parents c72936b + 00a192c commit 02a5432
Show file tree
Hide file tree
Showing 47 changed files with 1,018 additions and 993 deletions.
10 changes: 7 additions & 3 deletions master/buildbot/config.py
Expand Up @@ -545,10 +545,14 @@ def load_www(self, filename, config_dict, errors):

if not self.www['url'].endswith('/'):
self.www['url'] += '/'

if "extra_js" in self.www and not type(self.www['extra_js'])==list:
raise TypeError("BuildmasterConfig['www']['extra_js'] param must be a list of path")
public_html = self.www.get('public_html')
if public_html and not os.path.isdir(public_html):
errors.addError("public_html directory '%s' does not exist" %
if not public_html:
public_html = self.www["public_html"] = os.path.join(os.path.dirname(filename),
"public_html")
if not os.path.isdir(public_html):
errors.addError("www needs an existing public_html directory config, instead of %s" %
(public_html,))


Expand Down
6 changes: 4 additions & 2 deletions master/buildbot/data/changes.py
Expand Up @@ -15,6 +15,7 @@

from twisted.internet import defer
from twisted.python import log
import datetime
from buildbot.data import base, exceptions
from buildbot.process import metrics
from buildbot.process.users import users
Expand Down Expand Up @@ -77,14 +78,15 @@ def addChange(self, files=None, comments=None, author=None, revision=None,
author, src)
else:
uid = None

if isinstance(when_timestamp, datetime.datetime):
when_timestamp = datetime2epoch(when_timestamp)
change = {
'changeid': None, # not known yet
'author': author,
'files': files,
'comments': comments,
'revision': revision,
'when_timestamp': datetime2epoch(when_timestamp),
'when_timestamp': when_timestamp,
'branch': branch,
'category': category,
'revlink': revlink,
Expand Down
2 changes: 1 addition & 1 deletion master/buildbot/data/connector.py
Expand Up @@ -30,7 +30,7 @@ class RTypes(object):


class Root(base.Endpoint):
pathPattern = ('',)
pathPattern = ()

def get(self, options, kwargs):
return defer.succeed(self.master.data.rootLinks)
Expand Down
2 changes: 2 additions & 0 deletions master/buildbot/scripts/create_master.py
Expand Up @@ -24,6 +24,7 @@
from buildbot.master import BuildMaster
from buildbot import config as config_module
from buildbot import monkeypatches
from buildbot.scripts.update_js import updateJS

def makeBasedir(config):
if os.path.exists(config['basedir']):
Expand Down Expand Up @@ -142,6 +143,7 @@ def createMaster(config):
makeSampleConfig(config)
makePublicHtml(config)
makeTemplatesDir(config)
yield updateJS(config)
yield createDB(config)

if not config['quiet']:
Expand Down
12 changes: 12 additions & 0 deletions master/buildbot/scripts/runner.py
Expand Up @@ -34,6 +34,7 @@ class UpgradeMasterOptions(base.BasedirMixin, base.SubcommandOptions):
subcommandFunction = "buildbot.scripts.upgrade_master.upgradeMaster"
optFlags = [
["quiet", "q", "Do not emit the commands being run"],
["develop", "d", "link to buildbot dir rather than copy, and dont perform javascript optimization (only work on unix)"],
["replace", "r", "Replace any modified files without confirmation."],
]
optParameters = [
Expand Down Expand Up @@ -76,6 +77,7 @@ class CreateMasterOptions(base.BasedirMixin, base.SubcommandOptions):
"Re-use an existing directory (will not overwrite master.cfg file)"],
["relocatable", "r",
"Create a relocatable buildbot.tac"],
["develop", "d", "link to buildbot dir rather than copy, and dont perform javascript optimization (only work on unix)"],
["no-logrotate", "n",
"Do not permit buildmaster rotate logs by itself"]
]
Expand Down Expand Up @@ -129,6 +131,14 @@ def postOptions(self):
raise usage.UsageError("log-count parameter needs to be an int "+
" or None")

class UpdateJSOptions(base.BasedirMixin, base.SubcommandOptions):
subcommandFunction = "buildbot.scripts.update_js.updateJSFunc"
optFlags = [
["quiet", "q", "Do not emit the commands being run"],
["develop", "d", "link to buildbot dir rather than copy, and dont perform javascript optimization (only work on unix)"],
]
def getSynopsis(self):
return "Usage: buildbot update_js [<basedir>]"

class StopOptions(base.BasedirMixin, base.SubcommandOptions):
subcommandFunction = "buildbot.scripts.stop.stop"
Expand Down Expand Up @@ -639,6 +649,8 @@ class Options(usage.Options):
"Create and populate a directory for a new buildmaster"],
['upgrade-master', None, UpgradeMasterOptions,
"Upgrade an existing buildmaster directory for the current version"],
['updatejs', None, UpdateJSOptions,
"update the js directory from buildbot sources, and minify the js"],
['start', None, StartOptions,
"Start a buildmaster"],
['stop', None, StopOptions,
Expand Down
197 changes: 197 additions & 0 deletions master/buildbot/scripts/update_js.py
@@ -0,0 +1,197 @@
# This file is part of Buildbot. Buildbot is free software: you can
# redistribute it and/or modify it under the terms of the GNU General Public
# License as published by the Free Software Foundation, version 2.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along with
# this program; if not, write to the Free Software Foundation, Inc., 51
# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
# Copyright Buildbot Team Members

import os, shutil
import urllib2
import zipfile
import platform
from twisted.internet import defer, threads
from buildbot.util import in_reactor

js_deps = [("http://download.dojotoolkit.org/release-1.8.1/dojo-release-1.8.1-src.zip",
"dojo-release-1.8.1-src/", "."),
("https://github.com/kriszyp/xstyle/archive/v0.0.5.zip",
"xstyle-0.0.5/","xstyle"),
("https://github.com/kriszyp/put-selector/archive/v0.3.2.zip",
"put-selector-0.3.2/","put-selector"),
("https://github.com/SitePen/dgrid/archive/v0.3.3.zip",
"dgrid-0.3.3/","dgrid"),
("https://github.com/timrwood/moment/archive/1.7.2.zip","moment-1.7.2/min/moment.min.js","moment.js")
]
def syncStatic(config,www, workdir, olddir):
"""Synchronize the static directory"""
if not config['quiet']:
print "copying the javascript UI to base directory %s" % (config['basedir'],)
source_dir = os.path.join(os.path.dirname(__file__), "..","www","static")
extra_js = www.get('extra_js', [])
if os.path.exists(workdir):
shutil.rmtree(workdir)
if os.path.exists(olddir):
shutil.rmtree(olddir)
if not config['develop'] or platform.system() == "Windows":
shutil.copytree(source_dir, workdir)
for d in extra_js:
if os.path.exists(d) and os.path.isdir(d):
shutil.copytree(d, os.path.join(workdir, "js", os.path.basename(d)))
else:
os.mkdir(workdir)
for d in "img css".split():
os.symlink(os.path.join(source_dir, d), os.path.join(workdir, d))
os.mkdir(os.path.join(workdir, "js"))
for f in os.listdir(os.path.join(source_dir,"js")):
os.symlink(os.path.join(source_dir, "js", f), os.path.join(workdir, "js",f))
for d in extra_js:
if os.path.exists(d) and os.path.isdir(d):
os.symlink(d, os.path.join(workdir, "js", os.path.basename(d)))

def downloadJSDeps(config, workdir):
if not config['quiet']:
print "Downloading JS dependancies %s" % (config['basedir'],)
depsdir = os.path.join(config['basedir'], ".jsdeps_tarballs")
if not os.path.isdir(depsdir):
os.mkdir(depsdir)
for url, archivedir, archivedest in js_deps:
fn = os.path.join(depsdir, os.path.basename(url))
if not os.path.exists(fn):
f = urllib2.urlopen(url)
o = open(fn, "wb")
total_size = int(f.info().getheader('Content-Length').strip())
chunk_size = 1024*1024
bytes_so_far = 0
while True:
if not config['quiet']:
print "Downloading %s: %d%%" % (url,100*bytes_so_far/total_size)
chunk = f.read(chunk_size)
bytes_so_far += len(chunk)
if not chunk:
break
o.write(chunk)
o.close()
z = zipfile.ZipFile(fn)
for member in z.infolist():
if not member.filename.startswith(archivedir):
continue
isdir = member.external_attr & 16
dest = os.path.join(workdir, "js", archivedest)
if member.filename[len(archivedir):]:
dest = os.path.join(dest, member.filename[len(archivedir):])
if isdir:
if not os.path.exists(dest):
os.makedirs(dest)
else:
if not config['quiet']:
print "extracting %s" % (member.filename)
o = open(dest,"wb")
o.write(z.read(member))
o.close()
build_js = """
dependencies = (function(){
var _packages = "dojox dijit put-selector lib dgrid xstyle moment %(extra_pkg)s".split(" ");
var packages = [];
for (var i = 0;i<_packages.length; i+=1) {
if (_packages[i].length >1) {
packages.push([ _packages[i], "../"+_packages[i]]);
}
}
return {
basePath: "%(basePath)s",
releaseDir: "%(releaseDir)s",
prefixes:packages,
layers: [
{
name: "dojo.js",
customBase: true,
dependencies: [
"dojo/_base/declare",
]
},
{
name: "../lib/router.js",
dependencies: [
"lib.router"
]
}
]
};
}());
console.log(JSON.stringify(dependencies,null, " "))
"""
def minifyJS(config, www, workdir):
skip_minify = False
if os.system("java -version"):
print "you need a working version of java for dojo build system to work"
skip_minify = True

if platform.system() != "Windows" and os.system("node -v"):
print "you need a working version of node.js in your path for dojo build system to work"
skip_minify = True

if config['develop']:
skip_minify = True

# Todo: need to find out the best way to distribute minified code in buildbot's sdist.
# we'll sort this out once we have more code and requirements for the whole sdist picture
# Perhaps its even only needed for large scale buildbot where we can require installation of
# node and java in the master, and where people dont care about sdists.
if skip_minify:
os.symlink("js", os.path.join(workdir, "js.built"))
return

# create the build.js config file that dojo build system is needing
o = open(os.path.join(workdir,"js","build.js"), "w")
extra_js = www.get('extra_js', [])
o.write(build_js% dict(extra_pkg=" ".join([os.path.basename(js) for js in extra_js]),
basePath = os.path.join(workdir,"js"),
releaseDir = os.path.join(workdir,"jsrelease")))
o.close()
os.chdir(os.path.join(workdir,"js"))

# Those scripts are part of the dojo tarball that we previously downloaded
if platform.system() == "Windows":
os.system("util/buildscripts/build.bat --bin java -p build.js --release")
else:
os.system("sh util/buildscripts/build.sh --bin node -p build.js --release")
os.rename(os.path.join(workdir,"jsrelease", "dojo"),os.path.join(workdir,"js.built"))
shutil.rmtree(os.path.join(workdir,"jsrelease"))
if not config['quiet']:
print "optimizing the javascript UI for better performance in %s" % (config['basedir'],)
pass

@defer.inlineCallbacks
def updateJS(config, master_cfg=None):
if not master_cfg:
from upgrade_master import loadConfig # avoid recursive import
master_cfg = loadConfig(config)
if not master_cfg:
www = {}
else:
www = master_cfg.www
workdir = os.path.join(config['basedir'], "public_html", "static.new")
olddir = os.path.join(config['basedir'], "public_html", "static.old")
static = os.path.join(config['basedir'], "public_html", "static")
yield threads.deferToThread(lambda :syncStatic(config, www, workdir, olddir))
yield threads.deferToThread(lambda :downloadJSDeps(config, workdir))
yield threads.deferToThread(lambda :minifyJS(config, www, workdir))
if os.path.exists(static):
os.rename(static, olddir)
os.rename(workdir, static)
if not config['quiet']:
print "javascript UI configured in %s" % (config['basedir'],)

defer.returnValue(0)

@in_reactor
def updateJSFunc(config):
return updateJS(config)
2 changes: 2 additions & 0 deletions master/buildbot/scripts/upgrade_master.py
Expand Up @@ -26,6 +26,7 @@
from buildbot.master import BuildMaster
from buildbot.util import in_reactor
from buildbot.scripts import base
from buildbot.scripts.update_js import updateJS

def checkBasedir(config):
if not config['quiet']:
Expand Down Expand Up @@ -166,6 +167,7 @@ def upgradeMaster(config, _noMonkey=False):
return

upgradeFiles(config)
yield updateJS(config, master_cfg)
yield upgradeDatabase(config, master_cfg)

if not config['quiet']:
Expand Down
58 changes: 8 additions & 50 deletions master/buildbot/test/unit/test_www_ui.py
Expand Up @@ -13,67 +13,25 @@
#
# Copyright Buildbot Team Members

import os
from buildbot.www import ui, service
from buildbot.www import ui
from buildbot.test.util import www
from twisted.trial import unittest
from twisted.internet import defer, reactor
from twisted.python import failure

class Test(www.WwwTestMixin, unittest.TestCase):
def test_render(self):
master = self.make_master(url='h:/a/b/')
rsrc = ui.UIResource(master)
rsrc = ui.UIResource(master, extra_routes=[])

d = self.render_resource(rsrc, [''])
@d.addCallback
def check(rv):
self.assertIn('base_url:"h:/a/b/"', rv)
return d

try:
from buildbot.test.util.txghost import Ghost
has_ghost= Ghost != None
except ImportError:
# if $REQUIRE_GHOST is set, then fail if it's not found
if os.environ.get('REQUIRE_GHOST'):
raise
has_ghost=False

class TestGhostPy(www.WwwTestMixin, unittest.TestCase):
if not has_ghost:
skip = "Need Ghost.py to run most of www_ui tests"

@defer.inlineCallbacks
def setUp(self):
# hack to prevent twisted.web.http to setup a 1 sec callback at init
import twisted
#twisted.internet.base.DelayedCall.debug = True
twisted.web.http._logDateTimeUsers = 1
# lets resolve the tested port unicity later...
port = 8010
self.url = 'http://localhost:'+str(port)+"/"
self.master = self.make_master(url=self.url, port=port)
self.svc = service.WWWService(self.master)
yield self.svc.startService()
yield self.svc.reconfigService(self.master.config)
self.ghost = Ghost()

@defer.inlineCallbacks
def tearDown(self):
from twisted.internet.tcp import Server
del self.ghost
yield self.svc.stopService()
# webkit has the bad habbit on not closing the persistent
# connections, so we need to hack them away to make trial happy
for reader in reactor.getReaders():
if isinstance(reader, Server):
f = failure.Failure(Exception("test end"))
reader.connectionLost(f)

@defer.inlineCallbacks
class TestGhostPy(www.WwwTestMixin,www.WwwGhostTestMixin, unittest.TestCase):
def test_home(self):
yield self.ghost.open(self.url)
yield self.ghost.wait_for_selector("ul.breadcrumb")
base_url, resources = self.ghost.evaluate("bb_router.base_url")
assert(base_url== self.url)
return self.doPageLoadTest("/",
( "tests.assertEqual(bb_router.base_url, '%(url)s')"%self.__dict__,))
def test_doh_dojo_tests_colors(self):
"""simple test to make sure our doh tester work. tests an already working dojo unit test"""
return self.doDohPageLoadRunnerTests()

0 comments on commit 02a5432

Please sign in to comment.