Merge branch 'master' into shadow

This commit is contained in:
Mike McLean 2008-06-03 16:08:52 -04:00
commit aa664aadd3
10 changed files with 199 additions and 91 deletions

View file

@ -84,6 +84,9 @@ srpm: tarball
rpm: tarball
$(RPM_WITH_DIRS) $(DIST_DEFINES) -bb $(SPECFILE)
test-rpm: tarball
$(RPM_WITH_DIRS) $(DIST_DEFINES) --define "testbuild 1" -bb $(SPECFILE)
tag::
git tag -a $(TAG)
@echo "Tagged with: $(TAG)"

View file

@ -68,11 +68,12 @@ def main():
def shutdown(*args):
raise SystemExit
signal.signal(signal.SIGTERM,shutdown)
taken = False
while 1:
try:
tm.updateBuildroots()
tm.updateTasks()
tm.getNextTask()
taken = tm.getNextTask()
except (SystemExit,ServerExit,KeyboardInterrupt):
logger.warn("Exiting")
break
@ -86,7 +87,11 @@ def main():
# log the exception and continue
logger.error(''.join(traceback.format_exception(*sys.exc_info())))
try:
time.sleep(options.sleeptime)
if not taken:
# Only sleep if we didn't take a task, otherwise retry immediately.
# The load-balancing code in getNextTask() will prevent a single builder
# from getting overloaded.
time.sleep(options.sleeptime)
except (SystemExit,KeyboardInterrupt):
logger.warn("Exiting")
break
@ -786,7 +791,7 @@ class TaskManager(object):
session.host.updateHost(self.task_load,self.ready)
if not self.ready:
self.logger.info("Not ready for task")
return
return False
hosts, tasks = session.host.getLoadData()
self.logger.debug("Load Data:")
self.logger.debug(" hosts: %r" % hosts)
@ -810,10 +815,10 @@ class TaskManager(object):
self.logger.debug("bins: %r" % bins)
if our_avail is None:
self.logger.info("Server did not report this host. Are we disabled?")
return
return False
elif not bins:
self.logger.info("No bins for this host. Missing channel/arch config?")
return
return False
#sort available capacities for each of our bins
avail = {}
for bin in bins.iterkeys():
@ -828,7 +833,7 @@ class TaskManager(object):
if self.host_id == task['host_id']:
#assigned to us, we can take it regardless
if self.takeTask(task['id']):
return
return True
elif task['state'] == koji.TASK_STATES['FREE']:
bin = "%(channel_id)s:%(arch)s" % task
self.logger.debug("task is free, bin=%r" % bin)
@ -844,14 +849,14 @@ class TaskManager(object):
if our_avail < median:
self.logger.debug("Skipping - available capacity in lower half")
#decline for now and give the upper half a chance
return
return False
#otherwise, we attempt to open the task
if self.takeTask(task['id']):
return
return True
else:
#should not happen
raise Exception, "Invalid task state reported by server"
return
return False
def _waitTask(self, task_id, pid=None):
"""Wait (nohang) on the task, return true if finished"""
@ -1892,7 +1897,7 @@ class BuildSRPMFromSCMTask(BaseTaskHandler):
for allowed_scm in options.allowed_scms.split():
scm_tuple = allowed_scm.split(':')
if len(scm_tuple) == 2:
if scm.host == scm_tuple[0] and scm.repository == scm_tuple[1]:
if fnmatch(scm.host, scm_tuple[0]) and fnmatch(scm.repository, scm_tuple[1]):
# SCM host:repository is in the allowed list
break
else:

View file

@ -36,7 +36,8 @@ server=http://hub.example.com/kojihub
pkgurl=http://hub.example.com/packages
; A space-separated list of hostname:repository pairs that kojid is authorized to checkout from (no quotes)
allowed_scms=scm.example.com:/cvs/example git.example.org:/example
; Wildcards (as supported by fnmatch) are allowed
allowed_scms=scm.example.com:/cvs/example git.example.org:/example svn.example.org:/users/*
; The mail host to use for sending email notifications
smtphost=example.com

View file

@ -1,7 +1,7 @@
#!/usr/bin/python
# command line interface for the Koji build system
# Copyright (c) 2005-2007 Red Hat
# Copyright (c) 2005-2008 Red Hat
#
# Koji is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
@ -97,6 +97,8 @@ def get_options():
help=_("show debug output"))
parser.add_option("--debug-xmlrpc", action="store_true", default=False,
help=_("show xmlrpc debug output"))
parser.add_option("-q", "--quiet", action="store_true", default=False,
help=_("run quietly"))
parser.add_option("--skip-main", action="store_true", default=False,
help=_("don't actually run main"))
parser.add_option("-s", "--server", help=_("url of XMLRPC server"))
@ -143,14 +145,15 @@ def get_options():
}
# grab settings from /etc/koji.conf first, and allow them to be
# overridden by user config
progname = os.path.basename(sys.argv[0]) or 'koji'
for configFile in ('/etc/koji.conf', os.path.expanduser(options.configFile)):
if os.access(configFile, os.F_OK):
f = open(configFile)
config = ConfigParser.ConfigParser()
config.readfp(f)
f.close()
if config.has_section('koji'):
for name, value in config.items('koji'):
if config.has_section(progname):
for name, value in config.items(progname):
#note the defaults dictionary also serves to indicate which
#options *can* be set via the config file. Such options should
#not have a default value set in the option parser.
@ -785,12 +788,16 @@ def handle_resubmit(options, session, args):
parser.add_option("--nowait", action="store_true", help=_("Don't wait on task"))
parser.add_option("--nowatch", action="store_true", dest="nowait",
help=_("An alias for --nowait"))
parser.add_option("--quiet", action="store_true", help=_("Do not print the task information"), default=options.quiet)
(options, args) = parser.parse_args(args)
if len(args) < 1:
parser.error(_("Please specify a task ID"))
assert False
activate_session(session)
taskID = args[0]
if not options.quiet:
print "Resubmitting the following task:"
_printTaskInfo(session, int(taskID), 0, False, True)
newID = session.resubmitTask(int(taskID))
print "Resubmitted task %s as new task %s" % (taskID, newID)
if _running_in_bg() or options.nowait:
@ -1682,7 +1689,7 @@ def anon_handle_latest_pkg(options, session, args):
parser = OptionParser(usage=usage)
parser.add_option("--arch", help=_("List all of the latest packages for this arch"))
parser.add_option("--all", action="store_true", help=_("List all of the latest packages for this tag"))
parser.add_option("--quiet", action="store_true", help=_("Do not print the header information"))
parser.add_option("--quiet", action="store_true", help=_("Do not print the header information"), default=options.quiet)
parser.add_option("--paths", action="store_true", help=_("Show the file paths"))
(options, args) = parser.parse_args(args)
if len(args) == 0:
@ -1738,7 +1745,7 @@ def anon_handle_latest_by_tag(options, session, args):
usage = _("usage: %prog latest-by-tag [options] package [package...]")
usage += _("\n(Specify the --help global option for a list of other help options)")
parser = OptionParser(usage=usage)
parser.add_option("--quiet", action="store_true", help=_("Do not print the header information"))
parser.add_option("--quiet", action="store_true", help=_("Do not print the header information"), default=options.quiet)
parser.add_option("--paths", action="store_true", help=_("Show the file paths"))
(options, args) = parser.parse_args(args)
if len(args) == 0:
@ -1807,7 +1814,7 @@ def anon_handle_list_tagged(options, session, args):
parser.add_option("--rpms", action="store_true", help=_("Show rpms instead of builds"))
parser.add_option("--inherit", action="store_true", help=_("Follow inheritance"))
parser.add_option("--latest", action="store_true", help=_("Only show the latest builds/rpms"))
parser.add_option("--quiet", action="store_true", help=_("Do not print the header information"))
parser.add_option("--quiet", action="store_true", help=_("Do not print the header information"), default=options.quiet)
parser.add_option("--paths", action="store_true", help=_("Show the file paths"))
parser.add_option("--sigs", action="store_true", help=_("Show signatures"))
(options, args) = parser.parse_args(args)
@ -1842,7 +1849,6 @@ def anon_handle_list_tagged(options, session, args):
build_idx = dict([(b['id'],b) for b in builds])
for rinfo in data:
build = build_idx[rinfo['build_id']]
build['name'] = build['package_name']
builddir = pathinfo.build(build)
if options.sigs:
sigkey = rinfo['sigkey']
@ -2090,7 +2096,7 @@ def anon_handle_list_hosts(options, session, args):
parser.add_option("--not-ready", action="store_false", dest="ready", help=_("Limit to not ready hosts"))
parser.add_option("--enabled", action="store_true", help=_("Limit to enabled hosts"))
parser.add_option("--not-enabled", action="store_false", dest="enabled", help=_("Limit to not enabled hosts"))
parser.add_option("--quiet", action="store_true", help=_("Do not print header information"))
parser.add_option("--quiet", action="store_true", help=_("Do not print header information"), default=options.quiet)
(options, args) = parser.parse_args(args)
opts = {}
activate_session(session)
@ -2142,7 +2148,7 @@ def anon_handle_list_pkgs(options, session, args):
parser.add_option("--owner", help=_("Specify owner"))
parser.add_option("--tag", help=_("Specify tag"))
parser.add_option("--package", help=_("Specify package"))
parser.add_option("--quiet", action="store_true", help=_("Do not print header information"))
parser.add_option("--quiet", action="store_true", help=_("Do not print header information"), default=options.quiet)
parser.add_option("--noinherit", action="store_true", help=_("Don't follow inheritance"))
parser.add_option("--show-blocked", action="store_true", help=_("Show blocked packages"))
parser.add_option("--show-dups", action="store_true", help=_("Show superseded owners"))
@ -2642,7 +2648,7 @@ def anon_handle_list_targets(options, session, args):
usage += _("\n(Specify the --help global option for a list of other help options)")
parser = OptionParser(usage=usage)
parser.add_option("--name", help=_("Specify the build target name"))
parser.add_option("--quiet", action="store_true", help=_("Do not print the header information"))
parser.add_option("--quiet", action="store_true", help=_("Do not print the header information"), default=options.quiet)
(options, args) = parser.parse_args(args)
if len(args) != 0:
parser.error(_("This command takes no arguments"))
@ -3510,7 +3516,7 @@ def handle_list_tasks(options, session, args):
usage += _("\n(Specify the --help global option for a list of other help options)")
parser = OptionParser(usage=usage)
parser.add_option("--mine", action="store_true", help=_("Just print your tasks"))
parser.add_option("--quiet", action="store_true", help=_("Do not display the column headers"))
parser.add_option("--quiet", action="store_true", help=_("Do not display the column headers"), default=options.quiet)
(options, args) = parser.parse_args(args)
if len(args) != 0:
parser.error(_("This command takes no arguments"))
@ -3794,7 +3800,8 @@ def anon_handle_download_build(options, session, args):
parser.add_option("--latestfrom", dest="latestfrom", help=_("Download the latest build from this tag"))
parser.add_option("--debuginfo", action="store_true", help=_("Also download -debuginfo rpms"))
parser.add_option("--key", help=_("Download rpms signed with the given key"))
parser.add_option("-q", "--quiet", action="store_true", help=_("Do not display progress meter"))
parser.add_option("-q", "--quiet", action="store_true", help=_("Do not display progress meter"),
default=options.quiet)
(suboptions, args) = parser.parse_args(args)
if len(args) < 1:
parser.error(_("Please specify a package N-V-R or build ID"))
@ -3963,7 +3970,8 @@ def list_commands(show_admin=False):
desc = desc[8:]
print " %-20s %s" % (alias, desc)
print _('(Type "koji --help" for help about global options')
print _(' or "koji <command> --help" for help about a particular command\'s options.)')
print _(' or "koji <command> --help" for help about a particular command\'s options')
print _(' or "koji help --admin" for help about privileged administrative commands.)')
def error(msg=None, code=1):
if msg:

View file

@ -911,7 +911,7 @@ def readPackageList(tagID=None, userID=None, pkgID=None, event=None, inherit=Fal
return packages
def readTaggedBuilds(tag,event=None,inherit=False,latest=False,package=None):
def readTaggedBuilds(tag,event=None,inherit=False,latest=False,package=None,owner=None):
"""Returns a list of builds for specified tag
set inherit=True to follow inheritance
@ -954,6 +954,9 @@ def readTaggedBuilds(tag,event=None,inherit=False,latest=False,package=None):
if package:
q += """AND package.name = %(package)s
"""
if owner:
q += """AND users.name = %(owner)s
"""
q += """ORDER BY tag_listing.create_event DESC
"""
# i.e. latest first
@ -980,7 +983,7 @@ def readTaggedBuilds(tag,event=None,inherit=False,latest=False,package=None):
return builds
def readTaggedRPMS(tag, package=None, arch=None, event=None,inherit=False,latest=True,rpmsigs=False):
def readTaggedRPMS(tag, package=None, arch=None, event=None,inherit=False,latest=True,rpmsigs=False,owner=None):
"""Returns a list of rpms for specified tag
set inherit=True to follow inheritance
@ -993,7 +996,7 @@ def readTaggedRPMS(tag, package=None, arch=None, event=None,inherit=False,latest
# (however, it is fairly quick)
taglist += [link['parent_id'] for link in readFullInheritance(tag, event)]
builds = readTaggedBuilds(tag, event=event, inherit=inherit, latest=latest, package=package)
builds = readTaggedBuilds(tag, event=event, inherit=inherit, latest=latest, package=package, owner=owner)
#index builds
build_idx = dict([(b['build_id'],b) for b in builds])
@ -2875,7 +2878,13 @@ def import_rpm(fn,buildinfo=None,brootid=None):
#figure it out for ourselves
if rpminfo['sourcepackage'] == 1:
buildinfo = rpminfo.copy()
buildinfo['id'] = new_build(rpminfo)
build_id = find_build_id(buildinfo)
if build_id:
# build already exists
buildinfo['id'] = build_id
else:
# create a new build
buildinfo['id'] = new_build(rpminfo)
else:
#figure it out from sourcerpm string
buildinfo = get_build(koji.parse_NVRA(rpminfo['sourcerpm']))
@ -4410,22 +4419,23 @@ class RootExports(object):
raise koji.ActionNotAllowed, 'Cannot cancel task, not owner'
task.cancelChildren()
def listTagged(self,tag,event=None,inherit=False,prefix=None,latest=False,package=None):
def listTagged(self,tag,event=None,inherit=False,prefix=None,latest=False,package=None,owner=None):
"""List builds tagged with tag"""
if not isinstance(tag,int):
#lookup tag id
tag = get_tag_id(tag,strict=True)
results = readTaggedBuilds(tag,event,inherit=inherit,latest=latest,package=package)
results = readTaggedBuilds(tag,event,inherit=inherit,latest=latest,package=package,owner=owner)
if prefix:
prefix = prefix.lower()
results = [build for build in results if build['package_name'].lower().startswith(prefix)]
return results
def listTaggedRPMS(self,tag,event=None,inherit=False,latest=False,package=None,arch=None,rpmsigs=False):
def listTaggedRPMS(self,tag,event=None,inherit=False,latest=False,package=None,arch=None,rpmsigs=False,owner=None):
"""List rpms and builds within tag"""
if not isinstance(tag,int):
#lookup tag id
tag = get_tag_id(tag,strict=True)
return readTaggedRPMS(tag,event=event,inherit=inherit,latest=latest,package=package,arch=arch,rpmsigs=rpmsigs)
return readTaggedRPMS(tag,event=event,inherit=inherit,latest=latest,package=package,arch=arch,rpmsigs=rpmsigs,owner=owner)
def listBuilds(self, packageID=None, userID=None, taskID=None, prefix=None, state=None,
completeBefore=None, completeAfter=None, queryOpts=None):

View file

@ -8,7 +8,6 @@ Alias /koji "/usr/share/koji-web/scripts/"
# General settings
PythonDebug On
PythonOption KojiHubURL http://hub.example.com/kojihub
PythonOption KojiWebURL http://www.example.com/koji
PythonOption KojiPackagesURL http://server.example.com/mnt/koji/packages
PythonOption WebPrincipal koji/web@EXAMPLE.COM
PythonOption WebKeytab /etc/httpd.keytab

View file

@ -74,19 +74,43 @@
<tr>
<th>RPMs</th>
<td class="container">
#if $len($rpms) > 0
#if $len($rpmsByArch) > 0
<table class="nested">
#for $rpm in $rpms
#if $rpmsByArch.has_key('src')
<tr><th>src</th><th></th></tr>
#for $rpm in $rpmsByArch['src']
#set $rpmfile = '%(name)s-%(version)s-%(release)s.%(arch)s.rpm' % $rpm
#set $nvrpath = '%(package_name)s/%(version)s/%(release)s' % $build
<tr>
#set $rpmfile = '%(name)s-%(version)s-%(release)s.%(arch)s.rpm' % $rpm
#set $nvrpath = '%(package_name)s/%(version)s/%(release)s' % $build
<td></td>
<td>$rpmfile (<a href="rpminfo?rpmID=$rpm.id">info</a>) (<a href="$downloadBase/$nvrpath/$rpm.arch/$rpmfile">download</a>)</td>
</tr>
#end for
#end if
#set $arches = $rpmsByArch.keys()
#silent $arches.sort()
#for $arch in $arches
#if $arch == 'src'
#silent continue
#end if
<tr>
<th>$arch</th>
<td>
$rpmfile (<a href="rpminfo?rpmID=$rpm.id">info</a>) (<a href="$downloadBase/$nvrpath/$rpm.arch/$rpmfile">download</a>)
#if $task and $rpm.arch != 'src'
(<a href="$downloadBase/$nvrpath/data/logs/$rpm.arch/">build logs</a>)
#if $task
(<a href="$downloadBase/$nvrpath/data/logs/$arch/">build logs</a>)
#end if
</td>
</tr>
#for $rpm in $rpmsByArch[$arch] + $debuginfoByArch.get($arch, [])
<tr>
#set $rpmfile = '%(name)s-%(version)s-%(release)s.%(arch)s.rpm' % $rpm
#set $nvrpath = '%(package_name)s/%(version)s/%(release)s' % $build
<td></td>
<td>
$rpmfile (<a href="rpminfo?rpmID=$rpm.id">info</a>) (<a href="$downloadBase/$nvrpath/$rpm.arch/$rpmfile">download</a>)
</td>
</tr>
#end for
#end for
</table>
#else

View file

@ -11,16 +11,16 @@
#include "includes/header.chtml"
<h4>#if $state != None then $util.stateName($state).capitalize() else ''# Builds#if $prefix then ' starting with "%s"' % $prefix else ''##if $user then ' by <a href="userinfo?userID=%i">%s</a>' % ($user.id, $user.name) else ''##if $tag then ' in tag <a href="taginfo?tagID=%i">%s</a>' % ($tag.id, $tag.name) else ''#</h4>
<h4>#if $state != None then $util.stateName($state).capitalize() else ''# Builds#if $package then ' of <a href="packageinfo?packageID=%i">%s</a>' % ($package.id, $package.name) else ''##if $prefix then ' starting with "%s"' % $prefix else ''##if $user then ' by <a href="userinfo?userID=%i">%s</a>' % ($user.id, $user.name) else ''##if $tag then ' in tag <a href="taginfo?tagID=%i">%s</a>' % ($tag.id, $tag.name) else ''#</h4>
<table class="data-list">
#if $tag
<tr>
<td colspan="6">
#if $inherited
<a href="builds?inherited=0$util.passthrough($self, 'userID', 'tagID', 'order', 'prefix', 'state')">Hide inherited builds</a>
<a href="builds?inherited=0$util.passthrough($self, 'userID', 'tagID', 'packageID', 'order', 'prefix', 'state')">Hide inherited builds</a>
#else
<a href="builds?${util.passthrough($self, 'userID', 'tagID', 'order', 'prefix', 'state')[1:]}">Show inherited builds</a>
<a href="builds?${util.passthrough($self, 'userID', 'tagID', 'packageID', 'order', 'prefix', 'state')[1:]}">Show inherited builds</a>
#end if
</td>
</tr>
@ -28,7 +28,7 @@
<tr>
<td colspan="#if $tag then '6' else '5'#">
<strong>State</strong>:
<select name="state" class="filterlist" onchange="javascript: window.location = 'builds?state=' + this.value + '$util.passthrough($self, 'userID', 'tagID', 'order', 'prefix', 'inherited')';">
<select name="state" class="filterlist" onchange="javascript: window.location = 'builds?state=' + this.value + '$util.passthrough($self, 'userID', 'tagID', 'packageID', 'order', 'prefix', 'inherited')';">
<option value="all">all</option>
#for $stateOpt in ['BUILDING', 'COMPLETE', 'FAILED', 'CANCELED']
<option value="$koji.BUILD_STATES[$stateOpt]" #if $state == $koji.BUILD_STATES[$stateOpt] then 'selected="selected"' else ''#>$stateOpt.lower()</option>
@ -37,7 +37,7 @@
<br/>
<strong>Built by</strong>:
#if $user
<a href="builds?${util.passthrough($self, 'tagID', 'order', 'prefix', 'inherited', 'state')[1:]}">everyone</a>
<a href="builds?${util.passthrough($self, 'tagID', 'packageID', 'order', 'prefix', 'inherited', 'state')[1:]}">everyone</a>
#else
<strong>everyone</strong>
#end if
@ -46,11 +46,11 @@
#if $user and $user.id == $loggedInUser.id
<strong>me</strong>
#else
<a href="builds?userID=$loggedInUser.name$util.passthrough($self, 'tagID', 'order', 'prefix', 'inherited', 'state')">me</a>
<a href="builds?userID=$loggedInUser.name$util.passthrough($self, 'tagID', 'packageID', 'order', 'prefix', 'inherited', 'state')">me</a>
#end if
#end if
|
<select name="userID" class="filterlist" onchange="javascript: window.location = 'builds?userID=' + this.value + '$util.passthrough($self, 'tagID', 'order', 'prefix', 'inherited', 'state')';">
<select name="userID" class="filterlist" onchange="javascript: window.location = 'builds?userID=' + this.value + '$util.passthrough($self, 'tagID', 'packageID', 'order', 'prefix', 'inherited', 'state')';">
<option value="">(select)</option>
#for $userOption in $users
<option value="$userOption.name" #if $userOption.name == ($user and $user.name or None) then 'selected="selected"' else ''#>$userOption.name</option>
@ -64,12 +64,12 @@
#if $prefix == $char
<strong>$char</strong>
#else
<a href="builds?prefix=$char$util.passthrough($self, 'userID', 'tagID', 'order', 'inherited', 'state')">$char</a>
<a href="builds?prefix=$char$util.passthrough($self, 'userID', 'tagID', 'packageID', 'order', 'inherited', 'state')">$char</a>
#end if
|
#end for
#if $prefix
<a href="builds?${util.passthrough($self, 'userID', 'tagID', 'order', 'inherited', 'state')[1:]}">all</a>
<a href="builds?${util.passthrough($self, 'userID', 'tagID', 'packageID', 'order', 'inherited', 'state')[1:]}">all</a>
#else
<strong>all</strong>
#end if
@ -80,7 +80,7 @@
#if $len($buildPages) > 1
<form class="pageJump">
Page:
<select onchange="javascript: window.location = 'builds?start=' + this.value * $buildRange + '$util.passthrough($self, 'userID', 'tagID', 'order', 'prefix', 'inherited', 'state')';">
<select onchange="javascript: window.location = 'builds?start=' + this.value * $buildRange + '$util.passthrough($self, 'userID', 'tagID', 'packageID', 'order', 'prefix', 'inherited', 'state')';">
#for $pageNum in $buildPages
<option value="$pageNum"#if $pageNum == $buildCurrentPage then ' selected' else ''#>#echo $pageNum + 1#</option>
#end for
@ -88,25 +88,25 @@
</form>
#end if
#if $buildStart > 0
<a href="builds?start=#echo $buildStart - $buildRange #$util.passthrough($self, 'userID', 'tagID', 'order', 'prefix', 'inherited', 'state')">&lt;&lt;&lt;</a>
<a href="builds?start=#echo $buildStart - $buildRange #$util.passthrough($self, 'userID', 'tagID', 'packageID', 'order', 'prefix', 'inherited', 'state')">&lt;&lt;&lt;</a>
#end if
#if $totalBuilds != 0
<strong>Builds #echo $buildStart + 1 # through #echo $buildStart + $buildCount # of $totalBuilds</strong>
#end if
#if $buildStart + $buildCount < $totalBuilds
<a href="builds?start=#echo $buildStart + $buildRange#$util.passthrough($self, 'userID', 'tagID', 'order', 'prefix', 'inherited', 'state')">&gt;&gt;&gt;</a>
<a href="builds?start=#echo $buildStart + $buildRange#$util.passthrough($self, 'userID', 'tagID', 'packageID', 'order', 'prefix', 'inherited', 'state')">&gt;&gt;&gt;</a>
#end if
</td>
</tr>
<tr class="list-header">
<th><a href="builds?order=$util.toggleOrder($self, 'build_id')$util.passthrough($self, 'userID', 'tagID', 'prefix', 'inherited', 'state')">ID</a> $util.sortImage($self, 'build_id')</th>
<th><a href="builds?order=$util.toggleOrder($self, 'nvr')$util.passthrough($self, 'userID', 'tagID', 'prefix', 'inherited', 'state')">NVR</a> $util.sortImage($self, 'nvr')</th>
<th><a href="builds?order=$util.toggleOrder($self, 'build_id')$util.passthrough($self, 'userID', 'tagID', 'packageID', 'prefix', 'inherited', 'state')">ID</a> $util.sortImage($self, 'build_id')</th>
<th><a href="builds?order=$util.toggleOrder($self, 'nvr')$util.passthrough($self, 'userID', 'tagID', 'packageID', 'prefix', 'inherited', 'state')">NVR</a> $util.sortImage($self, 'nvr')</th>
#if $tag
<th><a href="builds?order=$util.toggleOrder($self, 'tag_name')$util.passthrough($self, 'userID', 'tagID', 'prefix', 'inherited', 'state')">Tag</a> $util.sortImage($self, 'tag_name')</th>
<th><a href="builds?order=$util.toggleOrder($self, 'tag_name')$util.passthrough($self, 'userID', 'tagID', 'packageID', 'prefix', 'inherited', 'state')">Tag</a> $util.sortImage($self, 'tag_name')</th>
#end if
<th><a href="builds?order=$util.toggleOrder($self, 'owner_name')$util.passthrough($self, 'userID', 'tagID', 'prefix', 'inherited', 'state')">Built by</a> $util.sortImage($self, 'owner_name')</th>
<th><a href="builds?order=$util.toggleOrder($self, 'completion_time')$util.passthrough($self, 'userID', 'tagID', 'prefix', 'inherited', 'state')">Finished</a> $util.sortImage($self, 'completion_time')</th>
<th><a href="builds?order=$util.toggleOrder($self, 'state')$util.passthrough($self, 'userID', 'tagID', 'prefix', 'inherited', 'state')">State</a> $util.sortImage($self, 'state')</th>
<th><a href="builds?order=$util.toggleOrder($self, 'owner_name')$util.passthrough($self, 'userID', 'tagID', 'packageID', 'prefix', 'inherited', 'state')">Built by</a> $util.sortImage($self, 'owner_name')</th>
<th><a href="builds?order=$util.toggleOrder($self, 'completion_time')$util.passthrough($self, 'userID', 'tagID', 'packageID', 'prefix', 'inherited', 'state')">Finished</a> $util.sortImage($self, 'completion_time')</th>
<th><a href="builds?order=$util.toggleOrder($self, 'state')$util.passthrough($self, 'userID', 'tagID', 'packageID', 'prefix', 'inherited', 'state')">State</a> $util.sortImage($self, 'state')</th>
</tr>
#if $len($builds) > 0
#for $build in $builds
@ -132,7 +132,7 @@
#if $len($buildPages) > 1
<form class="pageJump">
Page:
<select onchange="javascript: window.location = 'builds?start=' + this.value * $buildRange + '$util.passthrough($self, 'userID', 'tagID', 'order', 'prefix', 'inherited', 'state')';">
<select onchange="javascript: window.location = 'builds?start=' + this.value * $buildRange + '$util.passthrough($self, 'userID', 'tagID', 'packageID', 'order', 'prefix', 'inherited', 'state')';">
#for $pageNum in $buildPages
<option value="$pageNum"#if $pageNum == $buildCurrentPage then ' selected' else ''#>#echo $pageNum + 1#</option>
#end for
@ -140,13 +140,13 @@
</form>
#end if
#if $buildStart > 0
<a href="builds?start=#echo $buildStart - $buildRange #$util.passthrough($self, 'userID', 'tagID', 'order', 'prefix', 'inherited', 'state')">&lt;&lt;&lt;</a>
<a href="builds?start=#echo $buildStart - $buildRange #$util.passthrough($self, 'userID', 'tagID', 'packageID', 'order', 'prefix', 'inherited', 'state')">&lt;&lt;&lt;</a>
#end if
#if $totalBuilds != 0
<strong>Builds #echo $buildStart + 1 # through #echo $buildStart + $buildCount # of $totalBuilds</strong>
#end if
#if $buildStart + $buildCount < $totalBuilds
<a href="builds?start=#echo $buildStart + $buildRange#$util.passthrough($self, 'userID', 'tagID', 'order', 'prefix', 'inherited', 'state')">&gt;&gt;&gt;</a>
<a href="builds?start=#echo $buildStart + $buildRange#$util.passthrough($self, 'userID', 'tagID', 'packageID', 'order', 'prefix', 'inherited', 'state')">&gt;&gt;&gt;</a>
#end if
</td>
</tr>

View file

@ -17,6 +17,7 @@ def _setUserCookie(req, user):
options = req.get_options()
cookie = mod_python.Cookie.SignedCookie('user', user,
secret=options['Secret'],
secure=True,
path=os.path.dirname(req.uri),
expires=(time.time() + (int(options['LoginTimeout']) * 60 * 60)))
mod_python.Cookie.add_cookie(req, cookie)
@ -110,13 +111,34 @@ def _getServer(req):
req._session = session
return session
def _redirectBack(req, page):
def _getBaseURL(req):
pieces = req.uri.split('/')
base = '/'.join(pieces[:-1])
return req.construct_url(base)
def _redirectBack(req, page, forceSSL):
if page:
mod_python.util.redirect(req, page)
# We'll work with the page we were given
pass
elif req.headers_in.get('Referer'):
mod_python.util.redirect(req, req.headers_in.get('Referer'))
page = req.headers_in.get('Referer')
else:
mod_python.util.redirect(req, 'index')
page = 'index'
# Modify the scheme if necessary
if page.startswith('http'):
pass
elif page.startswith('/'):
page = req.construct_url(page)
else:
page = _getBaseURL(req) + '/' + page
if forceSSL:
page = page.replace('http:', 'https:')
else:
page = page.replace('https:', 'http:')
# and redirect to the page
mod_python.util.redirect(req, page)
def login(req, page=None):
session = _getServer(req)
@ -127,10 +149,10 @@ def login(req, page=None):
req.add_common_vars()
env = req.subprocess_env
if not env.get('HTTPS') == 'on':
https_url = options['KojiWebURL'].replace('http://', 'https://') + '/login'
if req.args:
https_url += '?' + req.args
mod_python.util.redirect(req, https_url)
dest = 'login'
if page:
dest = dest + '?page=' + page
_redirectBack(req, dest, forceSSL=True)
return
if env.get('SSL_CLIENT_VERIFY') != 'SUCCESS':
@ -157,13 +179,13 @@ def login(req, page=None):
raise koji.AuthError, 'KojiWeb is incorrectly configured for authentication, contact the system administrator'
_setUserCookie(req, username)
_redirectBack(req, page)
# To protect the session cookie, we must forceSSL
_redirectBack(req, page, forceSSL=True)
def logout(req, page=None):
_clearUserCookie(req)
_redirectBack(req, page)
_redirectBack(req, page, forceSSL=False)
def index(req, packageOrder='package_name', packageStart=None, buildOrder='-completion_time', buildStart=None, taskOrder='-completion_time', taskStart=None):
values = _initValues(req)
@ -810,6 +832,15 @@ def buildinfo(req, buildID):
rpms = server.listBuildRPMs(build['id'])
rpms.sort(_sortbyname)
rpmsByArch = {}
debuginfoByArch = {}
for rpm in rpms:
canon_arch = koji.canonArch(rpm['arch'])
if rpm['name'].endswith('-debuginfo') or rpm['name'].endswith('-debuginfo-common'):
debuginfoByArch.setdefault(canon_arch, []).append(rpm)
else:
rpmsByArch.setdefault(canon_arch, []).append(rpm)
if build['task_id']:
task = server.getTaskInfo(build['task_id'], request=True)
else:
@ -817,7 +848,8 @@ def buildinfo(req, buildID):
values['build'] = build
values['tags'] = tags
values['rpms'] = rpms
values['rpmsByArch'] = rpmsByArch
values['debuginfoByArch'] = debuginfoByArch
values['task'] = task
if req.currentUser:
values['perms'] = server.getUserPerms(req.currentUser['id'])
@ -839,7 +871,7 @@ def buildinfo(req, buildID):
return _genHTML(req, 'buildinfo.chtml')
def builds(req, userID=None, tagID=None, state=None, order='-completion_time', start=None, prefix=None, inherited='1'):
def builds(req, userID=None, tagID=None, packageID=None, state=None, order='-completion_time', start=None, prefix=None, inherited='1'):
values = _initValues(req, 'Builds', 'builds')
server = _getServer(req)
@ -864,6 +896,14 @@ def builds(req, userID=None, tagID=None, state=None, order='-completion_time', s
values['tagID'] = tagID
values['tag'] = tag
package = None
if packageID != None:
if packageID.isdigit():
packageID = int(packageID)
package = server.getPackage(packageID, strict=True)
values['packageID'] = packageID
values['package'] = package
if state == 'all':
state = None
elif state != None:
@ -880,10 +920,13 @@ def builds(req, userID=None, tagID=None, state=None, order='-completion_time', s
if tag:
# don't need to consider 'state' here, since only completed builds would be tagged
builds = kojiweb.util.paginateResults(server, values, 'listTagged', kw={'tag': tag['id'], 'inherit': bool(inherited), 'prefix': prefix},
builds = kojiweb.util.paginateResults(server, values, 'listTagged', kw={'tag': tag['id'], 'package': (package and package['name'] or None),
'owner': (user and user['name'] or None),
'inherit': bool(inherited), 'prefix': prefix},
start=start, dataName='builds', prefix='build', order=order)
else:
builds = kojiweb.util.paginateMethod(server, values, 'listBuilds', kw={'userID': (user and user['id'] or None), 'state': state, 'prefix': prefix},
builds = kojiweb.util.paginateMethod(server, values, 'listBuilds', kw={'userID': (user and user['id'] or None), 'packageID': (package and package['id'] or None),
'state': state, 'prefix': prefix},
start=start, dataName='builds', prefix='build', order=order)
values['chars'] = [chr(char) for char in range(48, 58) + range(97, 123)]
@ -1259,6 +1302,7 @@ def buildtargetdelete(req, targetID):
mod_python.util.redirect(req, 'buildtargets')
def reports(req):
server = _getServer(req)
values = _initValues(req, 'Reports', 'reports')
return _genHTML(req, 'reports.chtml')
@ -1499,33 +1543,40 @@ def buildsbytarget(req, days='7', start=None, order='-builds'):
return _genHTML(req, 'buildsbytarget.chtml')
def recentbuilds(req, user=None, tag=None, userID=None, tagID=None):
def recentbuilds(req, user=None, tag=None, package=None):
values = _initValues(req, 'Recent Build RSS')
server = _getServer(req)
tagObj = None
if tag != None:
if tag.isdigit():
tag = int(tag)
tagObj = server.getTag(tag)
elif tagID != None:
tagID = int(tagID)
tagObj = server.getTag(tagID)
userObj = None
if user != None:
if user.isdigit():
user = int(user)
userObj = server.getUser(user)
elif userID != None:
userID = int(userID)
userObj = server.getUser(userID)
packageObj = None
if package:
if package.isdigit():
package = int(package)
packageObj = server.getPackage(package)
if tagObj != None:
builds = server.listTagged(tagObj['id'], inherit=True)
builds = server.listTagged(tagObj['id'], inherit=True, package=(packageObj and packageObj['name'] or None),
owner=(userObj and userObj['name'] or None))
builds.sort(kojiweb.util.sortByKeyFunc('-completion_time', noneGreatest=True))
builds = builds[:20]
elif userObj != None:
builds = server.listBuilds(userID=userObj['id'], queryOpts={'order': '-completion_time',
'limit': 20})
else:
builds = server.listBuilds(queryOpts={'order': '-completion_time', 'limit': 20})
kwargs = {}
if userObj:
kwargs['userID'] = userObj['id']
if packageObj:
kwargs['packageID'] = packageObj['id']
builds = server.listBuilds(queryOpts={'order': '-completion_time', 'limit': 20}, **kwargs)
server.multicall = True
for build in builds:
@ -1554,8 +1605,9 @@ def recentbuilds(req, user=None, tag=None, userID=None, tagID=None):
values['tag'] = tagObj
values['user'] = userObj
values['package'] = packageObj
values['builds'] = builds
values['weburl'] = req.get_options().get('KojiWebURL', 'http://localhost/koji')
values['weburl'] = _getBaseURL(req)
req.content_type = 'text/xml'
return _genHTML(req, 'recentbuilds.chtml')

View file

@ -10,6 +10,9 @@
#if $user
#silent $query.append('userID=%i' % $user.id)
#end if
#if $package
#silent $query.append('packageID=%i' % $package.id)
#end if
#if $query
#echo '%s/%s?%s' % ($weburl, 'builds', '&amp;'.join($query))
#else
@ -19,10 +22,13 @@
<rss version="2.0">
<channel>
<title>koji: most recent builds#if $tag then ' into tag ' + $tag.name else ''##if $user then ' by user ' + $user.name else ''#</title>
<title>Koji: recent builds#if $package then ' of package ' + $package.name else ''##if $tag then ' into tag ' + $tag.name else ''##if $user then ' by user ' + $user.name else ''#</title>
<link>$linkURL()</link>
<description>
A list of the most recent builds
#if $package
of package $package.name
#end if
#if $tag
into tag $tag.name
#end if