From 71fb065e65489956a4a4f627f68a7521d971eabb Mon Sep 17 00:00:00 2001 From: Mike McLean Date: Tue, 15 Jul 2008 15:54:38 -0400 Subject: [PATCH 01/56] honor group dependencies by expanding in comps --- cli/koji | 4 +++- hub/kojihub.py | 2 +- koji/__init__.py | 60 +++++++++++++++++++++++++++++++++++++++--------- 3 files changed, 53 insertions(+), 13 deletions(-) diff --git a/cli/koji b/cli/koji index d694a7c5..56edb298 100755 --- a/cli/koji +++ b/cli/koji @@ -3486,6 +3486,8 @@ def anon_handle_show_groups(options, session, args): usage += _("\n(Specify the --help global option for a list of other help options)") parser = OptionParser(usage=usage) parser.add_option("--comps", action="store_true", help=_("Print in comps format")) + parser.add_option("-x", "--expand", action="store_true", default=False, + help=_("Expand groups in comps format")) parser.add_option("--spec", action="store_true", help=_("Print build spec")) (options, args) = parser.parse_args(args) if len(args) != 1: @@ -3495,7 +3497,7 @@ def anon_handle_show_groups(options, session, args): tag = args[0] groups = session.getTagGroups(tag) if options.comps: - print koji.generate_comps(groups) + print koji.generate_comps(groups, expand_groups=options.expand) elif options.spec: print koji.make_groups_spec(groups,name='buildgroups',buildgroup='build') else: diff --git a/hub/kojihub.py b/hub/kojihub.py index e2357a46..07feb021 100644 --- a/hub/kojihub.py +++ b/hub/kojihub.py @@ -1723,7 +1723,7 @@ def repo_init(tag, with_src=False, with_debuginfo=False): #generate comps and groups.spec groupsdir = "%s/groups" % (repodir) koji.ensuredir(groupsdir) - comps = koji.generate_comps(groups) + comps = koji.generate_comps(groups, expand_groups=True) fo = file("%s/comps.xml" % groupsdir,'w') fo.write(comps) fo.close() diff --git a/koji/__init__.py b/koji/__init__.py index fe44cc4a..f6a4662d 100644 --- a/koji/__init__.py +++ b/koji/__init__.py @@ -788,7 +788,7 @@ This is a meta-package that requires a defined group of packages """) return ''.join(data) -def generate_comps(groups): +def generate_comps(groups, expand_groups=False): """Generate comps content from groups data""" def boolean_text(x): if x: @@ -803,6 +803,7 @@ def generate_comps(groups): """ ] groups = list(groups) + group_idx = dict([(g['name'],g) for g in groups]) groups.sort(lambda a,b:cmp(a['name'],b['name'])) for g in groups: group_id = g['name'] @@ -825,7 +826,7 @@ def generate_comps(groups): """ % boolean_text(True)) #print grouplist, if any - if g['grouplist']: + if g['grouplist'] and not expand_groups: data.append( """ """) @@ -851,6 +852,15 @@ def generate_comps(groups): """) #print packagelist, if any + def package_entry(pkg): + #p['package_id','type','basearchonly','requires','name'] + name = pkg['package'] + opts = 'type="%s"' % pkg['type'] + if pkg['basearchonly']: + opts += ' basearchonly="%s"' % boolean_text(True) + if pkg['requires']: + opts += ' requires="%s"' % pkg['requires'] + return "%(name)s" % locals() if g['packagelist']: data.append( """ @@ -858,16 +868,44 @@ def generate_comps(groups): packagelist = list(g['packagelist']) packagelist.sort(lambda a,b:cmp(a['package'],b['package'])) for p in packagelist: - #['package_id','type','basearchonly','requires','name'] - name = p['package'] - opts = 'type="%s"' % p['type'] - if p['basearchonly']: - opts += ' basearchonly="%s"' % boolean_text(True) - if p['requires']: - opts += ' requires="%s"' % p['requires'] data.append( -""" %(name)s -""" % locals()) +""" %s +""" % package_entry(p)) + # also include expanded list, if needed + if expand_groups and g['grouplist']: + #add a requires entry for all packages in groups required by buildgroup + need = [req['name'] for req in g['grouplist']] + seen_grp = { g['name'] : 1} + seen_pkg = {} + for p in g['packagelist']: + seen_pkg[p['package']] = 1 + for group_name in need: + if seen_grp.has_key(group_name): + continue + seen_grp[group_name] = 1 + group = group_idx.get(group_name) + if group is None: + data.append( +""" +""" % group_name) + continue + data.append( +""" +""" % group_name) + pkglist = list(group['packagelist']) + pkglist.sort(lambda a,b: cmp(a['package'], b['package'])) + for pkg in pkglist: + pkg_name = pkg['package'] + if seen_pkg.has_key(pkg_name): + continue + data.append( +""" %s +""" % package_entry(pkg)) + for req in group['grouplist']: + req_name = req['name'] + if seen_grp.has_key(req_name): + continue + need.append(req_name) data.append( """ """) From c7d868b4b6293f69a2a35c92596059acdfb96a52 Mon Sep 17 00:00:00 2001 From: Mike Bonnet Date: Tue, 12 Aug 2008 12:49:33 -0400 Subject: [PATCH 02/56] add the authtype config option and command-line option to manually specify the authentication method (patch by Dennis Gilmore) --- cli/koji | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/cli/koji b/cli/koji index 56edb298..af91c1d0 100755 --- a/cli/koji +++ b/cli/koji @@ -93,6 +93,7 @@ def get_options(): help=_("do not authenticate")) parser.add_option("--force-auth", action="store_true", default=False, help=_("authenticate even for read-only operations")) + parser.add_option("--authtype", help=_("force use of a type of authentication, options: noauth, ssl, password, or kerberos")) parser.add_option("-d", "--debug", action="store_true", default=False, help=_("show debug output")) parser.add_option("--debug-xmlrpc", action="store_true", default=False, @@ -141,7 +142,8 @@ def get_options(): 'topdir' : '/mnt/koji', 'cert': '~/.koji/client.crt', 'ca': '~/.koji/clientca.crt', - 'serverca': '~/.koji/serverca.crt' + 'serverca': '~/.koji/serverca.crt', + 'authtype': None } # grab settings from /etc/koji.conf first, and allow them to be # overridden by user config @@ -4046,16 +4048,16 @@ def has_krb_creds(): def activate_session(session): """Test and login the session is applicable""" global options - if options.noauth: + if options.authtype == "noauth" or options.noauth: #skip authentication pass - elif os.path.isfile(options.cert): + elif options.authtype == "ssl" or os.path.isfile(options.cert) and options.authtype is None: # authenticate using SSL client cert session.ssl_login(options.cert, options.ca, options.serverca, proxyuser=options.runas) - elif options.user: + elif options.authtype == "password" or options.user and options.authtype is None: # authenticate using user/password session.login() - elif has_krb_creds(): + elif options.authtype == "kerberos" or has_krb_creds() and options.authtype is None: try: if options.keytab and options.principal: session.krb_login(principal=options.principal, keytab=options.keytab, proxyuser=options.runas) @@ -4065,7 +4067,7 @@ def activate_session(session): error(_("Kerberos authentication failed: %s (%s)") % (e.args[1], e.args[0])) except socket.error, e: warn(_("Could not connect to Kerberos authentication service: %s") % e.args[1]) - if not options.noauth and not session.logged_in: + if not options.noauth and options.authtype != "noauth" and not session.logged_in: error(_("Unable to log in, no authentication methods available")) ensure_connection(session) if options.debug: From 3967f413c8c1d350887e11bf04ac2e025315dd49 Mon Sep 17 00:00:00 2001 From: Mike Bonnet Date: Tue, 12 Aug 2008 13:03:29 -0400 Subject: [PATCH 03/56] allow the checkout of the common/ directory from source control to be configured via the allowed_scms option in kojid.conf --- builder/kojid | 10 ++++++++-- builder/kojid.conf | 9 ++++++--- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/builder/kojid b/builder/kojid index 7d4cd9c1..ac788c9b 100755 --- a/builder/kojid +++ b/builder/kojid @@ -1822,11 +1822,17 @@ class BuildSRPMFromSCMTask(BaseTaskHandler): # will throw a BuildError if the url is invalid scm = SCM(url) + use_common = True + for allowed_scm in options.allowed_scms.split(): scm_tuple = allowed_scm.split(':') - if len(scm_tuple) == 2: + if len(scm_tuple) in (2, 3): if fnmatch(scm.host, scm_tuple[0]) and fnmatch(scm.repository, scm_tuple[1]): # SCM host:repository is in the allowed list + # check if we specify a value for use_common + if len(scm_tuple) == 3: + if scm_tuple[2].lower() in ('false', 'no', '0'): + use_common = False break else: self.logger.warn('Ignoring incorrectly formatted SCM host:repository: %s' % allowed_scm) @@ -1841,7 +1847,7 @@ class BuildSRPMFromSCMTask(BaseTaskHandler): uploadpath = self.getUploadDir() # Check out spec file, etc. from SCM - sourcedir = scm.checkout(scmdir, uploadpath, logfile, use_common=True) + sourcedir = scm.checkout(scmdir, uploadpath, logfile, use_common=use_common) # Find and verify that there is only one spec file. spec_files = glob.glob("%s/*.spec" % sourcedir) diff --git a/builder/kojid.conf b/builder/kojid.conf index 49541228..a0d6fbe1 100644 --- a/builder/kojid.conf +++ b/builder/kojid.conf @@ -35,9 +35,12 @@ server=http://hub.example.com/kojihub ; The URL for the packages tree pkgurl=http://hub.example.com/packages -; A space-separated list of hostname:repository pairs that kojid is authorized to checkout from (no quotes) -; Wildcards (as supported by fnmatch) are allowed -allowed_scms=scm.example.com:/cvs/example git.example.org:/example svn.example.org:/users/* +; A space-separated list of hostname:repository[:use_common] tuples that kojid is authorized to checkout from (no quotes). +; Wildcards (as supported by fnmatch) are allowed. +; If use_common is specified and is one of "false", "no", or "0" (without quotes), then kojid will not attempt to checkout +; a common/ dir when checking out sources from the source control system. Otherwise, it will attempt to checkout a common/ +; dir, and will raise an exception if it cannot. +allowed_scms=scm.example.com:/cvs/example git.example.org:/example svn.example.org:/users/*:no ; The mail host to use for sending email notifications smtphost=example.com From 680521851469ad31b33fae74f13a8fef63992611 Mon Sep 17 00:00:00 2001 From: Mike Bonnet Date: Tue, 12 Aug 2008 15:54:19 -0400 Subject: [PATCH 04/56] change the way git urls are parsed - the concatenation of server and repository now constitute the full url, and are passed to "git clone" - module is now considered a subdirectory of the git checkout, where the Makefile and specfile are expected to be found - module can now be empty or absent in a scm url, meaning that the Makefile and specfile are in the root directory of the checkout --- builder/kojid | 34 +++++++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/builder/kojid b/builder/kojid index ac788c9b..fcd2662c 100755 --- a/builder/kojid +++ b/builder/kojid @@ -2409,8 +2409,8 @@ class SCM(object): if query.endswith('/'): query = query[:-1] - # check for validity: params should be empty, everything else should be populated - if params or not (scheme and netloc and path and query and fragment): + # check for validity: params should be empty, query may be empty, everything else should be populated + if params or not (scheme and netloc and path and fragment): raise koji.GenericError, 'Unable to parse SCM URL: %s' % self.url # return parsed values @@ -2432,6 +2432,7 @@ class SCM(object): sourcedir = '%s/%s' % (scmdir, self.module) update_checkout_cmd = None + update_checkout_dir = None env = None if self.scmtype == 'CVS': @@ -2452,16 +2453,30 @@ class SCM(object): scheme = self.scheme if '+' in scheme: scheme = scheme.split('+')[1] - common_path = 'common' - if self.module.endswith('/.git'): + gitrepo = '%s%s%s' % (scheme, self.host, self.repository) + commonrepo = os.path.dirname(gitrepo) + '/common' + checkout_path = os.path.basename(self.repository) + if self.repository.endswith('/.git'): # If we're referring to the .git subdirectory of the main module, # assume we need to do the same for the common module - common_path = 'common/.git' + checkout_path = os.path.basename(self.repository[:-5]) + commonrepo = os.path.dirname(gitrepo[:-5]) + '/common/.git' + elif self.repository.endswith('.git'): + # If we're referring to a bare repository for the main module, + # assume we need to do the same for the common module + checkout_path = os.path.basename(self.repository[:-4]) + commonrepo = os.path.dirname(gitrepo[:-4]) + '/common.git' - gitserver = '%s%s%s' % (scheme, self.host, self.repository) - module_checkout_cmd = ['git', 'clone', '-n', '%s/%s' % (gitserver, self.module), self.module] - common_checkout_cmd = ['git', 'clone', '%s/%s' % (gitserver, common_path)] + module_checkout_cmd = ['git', 'clone', '-n', gitrepo, checkout_path] + common_checkout_cmd = ['git', 'clone', commonrepo, 'common'] update_checkout_cmd = ['git', 'checkout', self.revision] + update_checkout_dir = '%s/%s' % (scmdir, checkout_path) + + sourcedir = '%s/%s' % (scmdir, checkout_path) + # self.module may be empty, in which case the specfile should be in the top-level directory + if self.module: + # Treat the module as a directory inside the git repository + sourcedir = '%s/%s' % (sourcedir, self.module) elif self.scmtype == 'GIT+SSH': if not self.user: @@ -2505,7 +2520,8 @@ class SCM(object): if update_checkout_cmd: # Currently only required for GIT checkouts # Run the command in the directory the source was checked out into - if log_output(update_checkout_cmd[0], update_checkout_cmd, logfile, uploadpath, cwd=sourcedir, logerror=1, append=1, env=env): + if log_output(update_checkout_cmd[0], update_checkout_cmd, logfile, uploadpath, cwd=update_checkout_dir, + logerror=1, append=1, env=env): raise koji.BuildError, 'Error running %s update command "%s", see %s for details' % \ (self.scmtype, ' '.join(update_checkout_cmd), os.path.basename(logfile)) From a9a82be465ad2d3e4fc39eb4d68a11db1bc0b838 Mon Sep 17 00:00:00 2001 From: Mike Bonnet Date: Tue, 12 Aug 2008 15:56:52 -0400 Subject: [PATCH 05/56] change taskLabel() to show the repository path, module path (when present), and revision information - the additional information is helpful when dealing with multiple source repositories in the same Koji instance --- koji/__init__.py | 35 ++++++++++++++++++++++------------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/koji/__init__.py b/koji/__init__.py index f6a4662d..a94f7dcc 100644 --- a/koji/__init__.py +++ b/koji/__init__.py @@ -1588,6 +1588,24 @@ def buildLabel(buildInfo, showEpoch=False): return '%s%s-%s-%s' % (epochStr, buildInfo['package_name'], buildInfo['version'], buildInfo['release']) +def _module_info(url): + module_info = '' + if '?' in url: + # extract the module path + module_info = url[url.find('?') + 1:url.find('#')] + # Find the first / after the scheme:// + repo_start = url.find('/', url.find('://') + 3) + # Find the ? if present, otherwise find the # + repo_end = url.find('?') + if repo_end == -1: + repo_end = url.find('#') + repo_info = url[repo_start:repo_end] + rev_info = url[url.find('#') + 1:] + if module_info: + return '%s:%s:%s' % (repo_info, module_info, rev_info) + else: + return '%s:%s' % (repo_info, rev_info) + def taskLabel(taskInfo): """Format taskInfo (dict) into a descriptive label.""" method = taskInfo['method'] @@ -1597,23 +1615,14 @@ def taskLabel(taskInfo): if taskInfo.has_key('request'): source, target = taskInfo['request'][:2] if '://' in source: - source = source[source.rfind('?') + 1:] - source = source.replace('/.git', '') - source = source.replace('/#', '#') - source = source.replace('#', ':') - source = source[source.rfind('/') + 1:] + module_info = _module_info(source) else: - source = os.path.basename(source) - extra = '%s, %s' % (target, source) + module_info = os.path.basename(source) + extra = '%s, %s' % (target, module_info) elif method in ('buildSRPMFromSCM', 'buildSRPMFromCVS'): if taskInfo.has_key('request'): url = taskInfo['request'][0] - url = url[url.rfind('?') + 1:] - url = url.replace('/.git', '') - url = url.replace('/#', '#') - url = url.replace('#', ':') - url = url[url.rfind('/') + 1:] - extra = url + extra = _module_info(url) elif method == 'buildArch': if taskInfo.has_key('request'): srpm, tagID, arch = taskInfo['request'][:3] From c08807d8a23c5d15df712ee120e60d8cb30d5bea Mon Sep 17 00:00:00 2001 From: Mike Bonnet Date: Tue, 12 Aug 2008 16:49:03 -0400 Subject: [PATCH 06/56] add links to the headers on the info pages --- www/kojiweb/buildinfo.chtml | 2 +- www/kojiweb/buildrootinfo.chtml | 2 +- www/kojiweb/buildtargetinfo.chtml | 2 +- www/kojiweb/channelinfo.chtml | 2 +- www/kojiweb/fileinfo.chtml | 3 ++- www/kojiweb/hostinfo.chtml | 2 +- www/kojiweb/packageinfo.chtml | 2 +- www/kojiweb/rpminfo.chtml | 2 +- www/kojiweb/taginfo.chtml | 2 +- www/kojiweb/taskinfo.chtml | 2 +- www/kojiweb/userinfo.chtml | 2 +- 11 files changed, 12 insertions(+), 11 deletions(-) diff --git a/www/kojiweb/buildinfo.chtml b/www/kojiweb/buildinfo.chtml index 1cf066d6..02d2e1e5 100644 --- a/www/kojiweb/buildinfo.chtml +++ b/www/kojiweb/buildinfo.chtml @@ -4,7 +4,7 @@ #include "includes/header.chtml" -

Information for build $koji.buildLabel($build)

+

Information for build $koji.buildLabel($build)

diff --git a/www/kojiweb/buildrootinfo.chtml b/www/kojiweb/buildrootinfo.chtml index de1c2c9f..4369ddc2 100644 --- a/www/kojiweb/buildrootinfo.chtml +++ b/www/kojiweb/buildrootinfo.chtml @@ -3,7 +3,7 @@ #include "includes/header.chtml" -

Information for buildroot $buildroot.tag_name-$buildroot.id-$buildroot.repo_id

+

Information for buildroot $buildroot.tag_name-$buildroot.id-$buildroot.repo_id

diff --git a/www/kojiweb/buildtargetinfo.chtml b/www/kojiweb/buildtargetinfo.chtml index e1275510..97f5ff32 100644 --- a/www/kojiweb/buildtargetinfo.chtml +++ b/www/kojiweb/buildtargetinfo.chtml @@ -2,7 +2,7 @@ #include "includes/header.chtml" -

Information for target $target.name

+

Information for target $target.name

diff --git a/www/kojiweb/channelinfo.chtml b/www/kojiweb/channelinfo.chtml index 75ac09cf..16def058 100644 --- a/www/kojiweb/channelinfo.chtml +++ b/www/kojiweb/channelinfo.chtml @@ -2,7 +2,7 @@ #include "includes/header.chtml" -

Information for channel $channel.name

+

Information for channel $channel.name

diff --git a/www/kojiweb/fileinfo.chtml b/www/kojiweb/fileinfo.chtml index dcc1aea1..a7bb15bc 100644 --- a/www/kojiweb/fileinfo.chtml +++ b/www/kojiweb/fileinfo.chtml @@ -1,7 +1,8 @@ #from kojiweb import util +#import urllib #include "includes/header.chtml" -

Information for file $file.name

+

Information for file $file.name

diff --git a/www/kojiweb/hostinfo.chtml b/www/kojiweb/hostinfo.chtml index e1a198ad..42536363 100644 --- a/www/kojiweb/hostinfo.chtml +++ b/www/kojiweb/hostinfo.chtml @@ -2,7 +2,7 @@ #include "includes/header.chtml" -

Information for host $host.name

+

Information for host $host.name

diff --git a/www/kojiweb/packageinfo.chtml b/www/kojiweb/packageinfo.chtml index 2563282b..7ae06bd9 100644 --- a/www/kojiweb/packageinfo.chtml +++ b/www/kojiweb/packageinfo.chtml @@ -2,7 +2,7 @@ #include "includes/header.chtml" -

Information for package $package.name

+

Information for package $package.name

diff --git a/www/kojiweb/rpminfo.chtml b/www/kojiweb/rpminfo.chtml index e8ff4237..896ccd61 100644 --- a/www/kojiweb/rpminfo.chtml +++ b/www/kojiweb/rpminfo.chtml @@ -4,7 +4,7 @@ #include "includes/header.chtml" #set $epoch = ($rpm.epoch != None and $str($rpm.epoch) + ':' or '') -

Information for RPM $rpm.name-$epoch$rpm.version-$rpm.release.${rpm.arch}.rpm

+

Information for RPM $rpm.name-$epoch$rpm.version-$rpm.release.${rpm.arch}.rpm

diff --git a/www/kojiweb/taginfo.chtml b/www/kojiweb/taginfo.chtml index 9cdd3f3b..b1e1d354 100644 --- a/www/kojiweb/taginfo.chtml +++ b/www/kojiweb/taginfo.chtml @@ -2,7 +2,7 @@ #include "includes/header.chtml" -

Information for tag $tag.name

+

Information for tag $tag.name

#if $child and 'admin' in $perms diff --git a/www/kojiweb/taskinfo.chtml b/www/kojiweb/taskinfo.chtml index f98657ce..d7dd98e8 100644 --- a/www/kojiweb/taskinfo.chtml +++ b/www/kojiweb/taskinfo.chtml @@ -53,7 +53,7 @@ #include "includes/header.chtml" -

Information for task $task.id

+

Information for task $task.id

diff --git a/www/kojiweb/userinfo.chtml b/www/kojiweb/userinfo.chtml index 5e6860b1..42f40099 100644 --- a/www/kojiweb/userinfo.chtml +++ b/www/kojiweb/userinfo.chtml @@ -2,7 +2,7 @@ #include "includes/header.chtml" -

Information for user $user.name

+

Information for user $user.name

From 9e81274a9d1032e51ae888cbfa3a8e9a1859c593 Mon Sep 17 00:00:00 2001 From: Mike Bonnet Date: Tue, 12 Aug 2008 17:45:48 -0400 Subject: [PATCH 07/56] put identification information into the title on the *info pages --- www/kojiweb/index.py | 36 +++++++++++++++++++++++++++++++++--- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/www/kojiweb/index.py b/www/kojiweb/index.py index 59efaf3d..7655432a 100644 --- a/www/kojiweb/index.py +++ b/www/kojiweb/index.py @@ -413,7 +413,9 @@ def taskinfo(req, taskID): task = server.getTaskInfo(taskID, request=True) if not task: raise koji.GenericError, 'invalid task ID: %s' % taskID - + + values['title'] += ': ' + koji.taskLabel(task) + values['task'] = task params = task['request'] values['params'] = params @@ -610,6 +612,9 @@ def packageinfo(req, packageID, tagOrder='name', tagStart=None, buildOrder='-com package = server.getPackage(packageID) if package == None: raise koji.GenericError, 'invalid package ID: %s' % packageID + + values['title'] += ': ' + package['name'] + values['package'] = package values['packageID'] = package['id'] @@ -628,6 +633,8 @@ def taginfo(req, tagID, all='0', packageOrder='package_name', packageStart=None, tagID = int(tagID) tag = server.getTag(tagID, strict=True) + values['title'] += ': ' + tag['name'] + all = int(all) numPackages = server.count('listPackages', tagID=tag['id'], inherited=True) @@ -827,6 +834,9 @@ def buildinfo(req, buildID): buildID = int(buildID) build = server.getBuild(buildID) + + values['title'] += ': ' + koji.buildLabel(build) + tags = server.listTags(build['id']) tags.sort(_sortbyname) rpms = server.listBuildRPMs(build['id']) @@ -957,7 +967,9 @@ def userinfo(req, userID, packageOrder='package_name', packageStart=None, buildO if userID.isdigit(): userID = int(userID) user = server.getUser(userID, strict=True) - + + values['title'] += ': ' + user['name'] + values['user'] = user values['userID'] = userID @@ -975,6 +987,13 @@ def rpminfo(req, rpmID, fileOrder='name', fileStart=None): rpmID = int(rpmID) rpm = server.getRPM(rpmID) + + values['title'] += ': %(name)s-%%s%(version)s-%(release)s.%(arch)s.rpm' % rpm + epochStr = '' + if rpm['epoch'] != None: + epochStr = '%s:' % rpm['epoch'] + values['title'] = values['title'] % epochStr + build = server.getBuild(rpm['build_id']) builtInRoot = None if rpm['buildroot_id'] != None: @@ -1017,6 +1036,8 @@ def fileinfo(req, rpmID, filename): if not file: raise koji.GenericError, 'no file %s in RPM %i' % (filename, rpmID) + values['title'] += ': ' + file['name'] + values['rpm'] = rpm values['file'] = file @@ -1077,7 +1098,9 @@ def hostinfo(req, hostID=None, userID=None): raise koji.GenericError, 'invalid host ID: %s' % userID else: raise koji.GenericError, 'hostID or userID must be provided' - + + values['title'] += ': ' + host['name'] + channels = server.listChannels(host['id']) channels.sort(_sortbyname) buildroots = server.listBuildroots(hostID=host['id'], @@ -1124,6 +1147,8 @@ def channelinfo(req, channelID): if channel == None: raise koji.GenericError, 'invalid channel ID: %i' % channelID + values['title'] += ': ' + channel['name'] + hosts = server.listHosts(channelID=channelID) hosts.sort(_sortbyname) @@ -1138,6 +1163,9 @@ def buildrootinfo(req, buildrootID, builtStart=None, builtOrder=None, componentS buildrootID = int(buildrootID) buildroot = server.getBuildroot(buildrootID) + + values['title'] += ': %(tag_name)s-%(id)i-%(repo_id)i' % buildroot + if buildroot == None: raise koji.GenericError, 'unknown buildroot ID: %i' % buildrootID @@ -1201,6 +1229,8 @@ def buildtargetinfo(req, targetID=None, name=None): if target == None: raise koji.GenericError, 'invalid build target: %s' % (targetID or name) + values['title'] += ': ' + target['name'] + buildTag = server.getTag(target['build_tag']) destTag = server.getTag(target['dest_tag']) From 097b3cc01856c17a1c403805c1f4459f3518291b Mon Sep 17 00:00:00 2001 From: Mike Bonnet Date: Tue, 12 Aug 2008 18:03:29 -0400 Subject: [PATCH 08/56] change the title format to put the important bits first (format suggested by Will Woods) --- www/kojiweb/index.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/www/kojiweb/index.py b/www/kojiweb/index.py index 7655432a..0a68d987 100644 --- a/www/kojiweb/index.py +++ b/www/kojiweb/index.py @@ -414,7 +414,7 @@ def taskinfo(req, taskID): if not task: raise koji.GenericError, 'invalid task ID: %s' % taskID - values['title'] += ': ' + koji.taskLabel(task) + values['title'] = koji.taskLabel(task) + ' | Task Info' values['task'] = task params = task['request'] @@ -613,7 +613,7 @@ def packageinfo(req, packageID, tagOrder='name', tagStart=None, buildOrder='-com if package == None: raise koji.GenericError, 'invalid package ID: %s' % packageID - values['title'] += ': ' + package['name'] + values['title'] = package['name'] + ' | Package Info' values['package'] = package values['packageID'] = package['id'] @@ -633,7 +633,7 @@ def taginfo(req, tagID, all='0', packageOrder='package_name', packageStart=None, tagID = int(tagID) tag = server.getTag(tagID, strict=True) - values['title'] += ': ' + tag['name'] + values['title'] = tag['name'] + ' | Tag Info' all = int(all) @@ -835,7 +835,7 @@ def buildinfo(req, buildID): build = server.getBuild(buildID) - values['title'] += ': ' + koji.buildLabel(build) + values['title'] = koji.buildLabel(build) + ' | Build Info' tags = server.listTags(build['id']) tags.sort(_sortbyname) @@ -968,7 +968,7 @@ def userinfo(req, userID, packageOrder='package_name', packageStart=None, buildO userID = int(userID) user = server.getUser(userID, strict=True) - values['title'] += ': ' + user['name'] + values['title'] = user['name'] + ' | User Info' values['user'] = user values['userID'] = userID @@ -988,7 +988,7 @@ def rpminfo(req, rpmID, fileOrder='name', fileStart=None): rpmID = int(rpmID) rpm = server.getRPM(rpmID) - values['title'] += ': %(name)s-%%s%(version)s-%(release)s.%(arch)s.rpm' % rpm + values['title'] = '%(name)s-%%s%(version)s-%(release)s.%(arch)s.rpm' % rpm + ' | RPM Info' epochStr = '' if rpm['epoch'] != None: epochStr = '%s:' % rpm['epoch'] @@ -1036,7 +1036,7 @@ def fileinfo(req, rpmID, filename): if not file: raise koji.GenericError, 'no file %s in RPM %i' % (filename, rpmID) - values['title'] += ': ' + file['name'] + values['title'] = file['name'] + ' | File Info' values['rpm'] = rpm values['file'] = file @@ -1099,7 +1099,7 @@ def hostinfo(req, hostID=None, userID=None): else: raise koji.GenericError, 'hostID or userID must be provided' - values['title'] += ': ' + host['name'] + values['title'] = host['name'] + ' | Host Info' channels = server.listChannels(host['id']) channels.sort(_sortbyname) @@ -1147,7 +1147,7 @@ def channelinfo(req, channelID): if channel == None: raise koji.GenericError, 'invalid channel ID: %i' % channelID - values['title'] += ': ' + channel['name'] + values['title'] = channel['name'] + ' | Channel Info' hosts = server.listHosts(channelID=channelID) hosts.sort(_sortbyname) @@ -1164,7 +1164,7 @@ def buildrootinfo(req, buildrootID, builtStart=None, builtOrder=None, componentS buildrootID = int(buildrootID) buildroot = server.getBuildroot(buildrootID) - values['title'] += ': %(tag_name)s-%(id)i-%(repo_id)i' % buildroot + values['title'] = '%(tag_name)s-%(id)i-%(repo_id)i' % buildroot + ' | Buildroot Info' if buildroot == None: raise koji.GenericError, 'unknown buildroot ID: %i' % buildrootID @@ -1229,7 +1229,7 @@ def buildtargetinfo(req, targetID=None, name=None): if target == None: raise koji.GenericError, 'invalid build target: %s' % (targetID or name) - values['title'] += ': ' + target['name'] + values['title'] = target['name'] + ' | Build Target Info' buildTag = server.getTag(target['build_tag']) destTag = server.getTag(target['dest_tag']) From aa2e4f76c4be29221d536eead2085de9d88ce327 Mon Sep 17 00:00:00 2001 From: Mike Bonnet Date: Tue, 12 Aug 2008 18:07:42 -0400 Subject: [PATCH 09/56] - put the task label into the taskinfo header - move the task ID into the main table --- www/kojiweb/taskinfo.chtml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/www/kojiweb/taskinfo.chtml b/www/kojiweb/taskinfo.chtml index d7dd98e8..88df94ee 100644 --- a/www/kojiweb/taskinfo.chtml +++ b/www/kojiweb/taskinfo.chtml @@ -53,9 +53,12 @@ #include "includes/header.chtml" -

Information for task $task.id

+

Information for task $koji.taskLabel($task)

+ + + From d1acf917410bc8079fac946382528e8ace5c3881 Mon Sep 17 00:00:00 2001 From: Mike Bonnet Date: Thu, 14 Aug 2008 17:05:52 -0400 Subject: [PATCH 10/56] clean up kojihub.conf option handling --- hub/kojixmlrpc.py | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/hub/kojixmlrpc.py b/hub/kojixmlrpc.py index 4aee2ec6..c9f0adf0 100644 --- a/hub/kojixmlrpc.py +++ b/hub/kojixmlrpc.py @@ -34,6 +34,20 @@ from kojihub import RootExports from kojihub import HostExports from koji.context import context +def _opt_bool(opts, name): + """Convert a string option into a boolean + True or False value. The following values + will be considered True (case-insensitive): + yes, on, true, 1 + Anything else will be considered False.""" + val = opts.get(name, 'no') + if val is None: + val = '' + if val.lower() in ('yes', 'on', 'true', '1'): + return True + else: + return False + class ModXMLRPCRequestHandler(object): """Simple XML-RPC handler for mod_python environment""" @@ -98,7 +112,7 @@ class ModXMLRPCRequestHandler(object): tb_type = context.opts.get('KojiTraceback',None) tb_str = ''.join(traceback.format_exception(*sys.exc_info())) if issubclass(e_class, koji.GenericError): - if context.opts.get('KojiDebug','off').lower() in ('yes', 'on', 'true', '1'): + if _opt_bool(context.opts, 'KojiDebug'): if tb_type == "extended": faultString = koji.format_exc_plus() else: @@ -116,7 +130,7 @@ class ModXMLRPCRequestHandler(object): sys.stderr.write('\n') response = dumps(Fault(faultCode, faultString)) - if context.opts.get('KojiDebug', 'no').lower() in ('yes', 'on', 'true', '1'): + if _opt_bool(context.opts, 'KojiDebug'): sys.stderr.write("Returning %d bytes after %f seconds\n" % (len(response),time.time() - start)) sys.stderr.flush() @@ -138,14 +152,14 @@ class ModXMLRPCRequestHandler(object): #might be ok, depending on method if method not in ('exclusiveSession','login', 'krbLogin', 'logout'): raise - if context.opts.get('LockOut', 'no').lower() in ('yes', 'on', 'true', '1') and \ + if _opt_bool(context.opts, 'LockOut') and \ method not in ('login', 'krbLogin', 'sslLogin', 'logout'): if not context.session.hasPerm('admin'): raise koji.GenericError, "Server disabled for maintenance" # handle named parameters params,opts = koji.decode_args(*params) - - if context.opts.get('KojiDebug', 'no').lower() in ('yes', 'on', 'true', '1'): + + if _opt_bool(context.opts, 'KojiDebug'): sys.stderr.write("Handling method %s for session %s (#%s)\n" \ % (method, context.session.id, context.session.callnum)) if method != 'uploadFile': @@ -155,7 +169,7 @@ class ModXMLRPCRequestHandler(object): ret = func(*params,**opts) - if context.opts.get('KojiDebug', 'no').lower() in ('yes', 'on', 'true', '1'): + if _opt_bool(context.opts, 'KojiDebug'): sys.stderr.write("Completed method %s for session %s (#%s): %f seconds\n" % (method, context.session.id, context.session.callnum, time.time()-start)) @@ -279,7 +293,7 @@ def handler(req, profiling=False): else: opts = req.get_options() try: - if opts.get("ServerOffline", False): + if _opt_bool(opts, 'ServerOffline'): offline_reply(req, msg=opts.get("OfflineMessage", None)) return apache.OK context._threadclear() @@ -290,7 +304,7 @@ def handler(req, profiling=False): user = opts["DBUser"], host = opts.get("DBhost",None)) try: - context.cnx = koji.db.connect(opts.get("KojiDebug",False)) + context.cnx = koji.db.connect(_opt_bool(opts, 'KojiDebug')) except Exception: offline_reply(req, msg="database outage") return apache.OK From a12b756e8906dd5680622b69ad7227c6990e17a4 Mon Sep 17 00:00:00 2001 From: Mike Bonnet Date: Thu, 14 Aug 2008 17:16:11 -0400 Subject: [PATCH 11/56] register system.multicall for compliance with the multicall spec --- hub/kojixmlrpc.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hub/kojixmlrpc.py b/hub/kojixmlrpc.py index c9f0adf0..dbe6903e 100644 --- a/hub/kojixmlrpc.py +++ b/hub/kojixmlrpc.py @@ -60,6 +60,8 @@ class ModXMLRPCRequestHandler(object): self.register_function(self.system_methodSignature, name="system.methodSignature") self.register_function(self.system_methodHelp, name="system.methodHelp") self.register_function(self.multiCall) + # Also register it as system.multicall for standards compliance + self.register_function(self.multiCall, name="system.multicall") def register_function(self, function, name = None): if name is None: From 1a362b791670ab68eb2ca70fecdeb5ce5415fb28 Mon Sep 17 00:00:00 2001 From: Dennis Gilmore Date: Mon, 18 Aug 2008 01:05:22 -0500 Subject: [PATCH 12/56] patch from Toshio adding support to use passwd auth for database connection. --- hub/httpd.conf | 1 + hub/kojixmlrpc.py | 1 + 2 files changed, 2 insertions(+) diff --git a/hub/httpd.conf b/hub/httpd.conf index e4acd641..ee9f6bec 100644 --- a/hub/httpd.conf +++ b/hub/httpd.conf @@ -10,6 +10,7 @@ Alias /kojihub "/usr/share/koji-hub/XMLRPC" PythonOption DBName koji PythonOption DBUser koji PythonOption DBHost db.example.com + PythonOption DBPass example_password PythonOption KojiDir /mnt/koji # Kerberos auth configuration diff --git a/hub/kojixmlrpc.py b/hub/kojixmlrpc.py index 4aee2ec6..ae3a02a9 100644 --- a/hub/kojixmlrpc.py +++ b/hub/kojixmlrpc.py @@ -288,6 +288,7 @@ def handler(req, profiling=False): context.req = req koji.db.provideDBopts(database = opts["DBName"], user = opts["DBUser"], + password = opts.get("DBPass",None), host = opts.get("DBhost",None)) try: context.cnx = koji.db.connect(opts.get("KojiDebug",False)) From aed32ed56fe399dc288c94f5e8212a1673bd3e92 Mon Sep 17 00:00:00 2001 From: Mike McLean Date: Fri, 22 Aug 2008 18:00:34 -0400 Subject: [PATCH 13/56] add missing init actions for kojid/kojira see: https://fedorahosted.org/koji/ticket/94 - Add missing actions to kojid.init (patch from wakko666) - Add missing actions to kojira.init (patch from wakko666) --- builder/kojid.init | 6 +++--- util/kojira.init | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/builder/kojid.init b/builder/kojid.init index 54872716..eea7b9a3 100755 --- a/builder/kojid.init +++ b/builder/kojid.init @@ -71,14 +71,14 @@ case "$1" in status) status $prog ;; - restart|reload) + restart|reload|force-reload) restart ;; - condrestart) + condrestart|try-restart) [ -f /var/lock/subsys/kojid ] && restart || : ;; *) - echo $"Usage: $0 {start|stop|status|restart|reload|condrestart}" + echo $"Usage: $0 {start|stop|status|restart|condrestart|try-restart|reload|force-reload}" exit 1 esac diff --git a/util/kojira.init b/util/kojira.init index 1beecfc2..4430a0db 100644 --- a/util/kojira.init +++ b/util/kojira.init @@ -71,14 +71,14 @@ case "$1" in status) status $prog ;; - restart|reload) + restart|reload|force-reload) restart ;; - condrestart) + condrestart|try-restart) [ -f /var/lock/subsys/kojira ] && restart || : ;; *) - echo $"Usage: $0 {start|stop|status|restart|reload|condrestart}" + echo $"Usage: $0 {start|stop|status|restart|condrestart|try-restart|reload|force-reload}" exit 1 esac From 8a731de32c17c80f16bde655f393a81f9c9ee7d1 Mon Sep 17 00:00:00 2001 From: Mike McLean Date: Mon, 25 Aug 2008 15:47:37 -0400 Subject: [PATCH 14/56] fix license, bump version, add changelog entry --- koji.spec | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/koji.spec b/koji.spec index 404274e1..8be686df 100644 --- a/koji.spec +++ b/koji.spec @@ -2,15 +2,15 @@ %define baserelease 1 #build with --define 'testbuild 1' to have a timestamp appended to release -%if x%{?testbuild} == x1 +%if "x%{?testbuild}" == "x1" %define release %{baserelease}.%(date +%%Y%%m%%d.%%H%%M.%%S) %else %define release %{baserelease} %endif Name: koji -Version: 1.2.5 +Version: 1.2.6 Release: %{release}%{?dist} -License: LGPL +License: LGPLv2 Summary: Build system tools Group: Applications/System URL: http://fedorahosted.org/koji @@ -160,6 +160,19 @@ if [ $1 = 0 ]; then fi %changelog +* Mon Aug 25 2008 Mike McLean 1.2.6-1 +- fix testbuild conditional [downstream] +- fix license tag [downstream] +- bump version +- more robust client sessions +- handle errors gracefully in web ui +- koji-gc added to utils subpackage +- skip sleep in kojid after taking a task +- new dir layout for task workdirs (avoids large directories) +- unified boolean option parsing in kojihub +- new ServerOffline exception +- other miscellaneous fixes + * Fri Jan 25 2008 jkeating 1.2.5-1 - Put createrepo arguments in correct order From 5f06bb6bf2d9fdaa989493fe8463a7fc475ae202 Mon Sep 17 00:00:00 2001 From: Mike Bonnet Date: Thu, 4 Sep 2008 18:00:33 -0400 Subject: [PATCH 15/56] make the "inherited" toggle on the builds page a drop-down instead of a link --- www/kojiweb/builds.chtml | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/www/kojiweb/builds.chtml b/www/kojiweb/builds.chtml index 05fff90b..87bd6a7a 100644 --- a/www/kojiweb/builds.chtml +++ b/www/kojiweb/builds.chtml @@ -14,19 +14,16 @@

#if $state != None then $util.stateName($state).capitalize() else ''# Builds#if $package then ' of %s' % ($package.id, $package.name) else ''##if $prefix then ' starting with "%s"' % $prefix else ''##if $user then ' by %s' % ($user.id, $user.name) else ''##if $tag then ' in tag %s' % ($tag.id, $tag.name) else ''#

ID$task.id
Method$task.method
- #if $tag - - - - #end if From 00044616bd05ef183ef7dfd0fc1d9a0f26821e02 Mon Sep 17 00:00:00 2001 From: Mike Bonnet Date: Mon, 8 Sep 2008 16:32:05 -0400 Subject: [PATCH 21/56] url quote the name in case it gets included in the url of the search results --- www/kojiweb/searchresults.chtml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/www/kojiweb/searchresults.chtml b/www/kojiweb/searchresults.chtml index 70842d62..df3b40c0 100644 --- a/www/kojiweb/searchresults.chtml +++ b/www/kojiweb/searchresults.chtml @@ -1,4 +1,5 @@ #from kojiweb import util +#import urllib #include "includes/header.chtml" @@ -36,7 +37,9 @@ #for $result in $results - + #set $quoted = $result.copy() + #silent $quoted['name'] = $urllib.quote($quoted['name']) + #end for #else From 117ac78c1ec88d24549326f6c75020d3a71b8d28 Mon Sep 17 00:00:00 2001 From: Mike McLean Date: Fri, 12 Sep 2008 15:21:18 -0400 Subject: [PATCH 22/56] choose a better arch for noarch builds When builder receives a noarch build, choose an arch that the host can handle AND that the build tag can support. --- builder/kojid | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/builder/kojid b/builder/kojid index 1a7afbf2..3a84d4ca 100755 --- a/builder/kojid +++ b/builder/kojid @@ -1697,11 +1697,27 @@ class BuildArchTask(BaseTaskHandler): #noarch is funny if arch == "noarch": - #use any arch this host can handle - host = session.host.getHost() - if host['arches'] is None: + #we need a buildroot arch. Pick one that: + # a) this host can handle + # b) the build tag can support + # c) is canonical + host_arches = session.host.getHost()['arches'] + if not host_arches: raise koji.BuildError, "No arch list for this host" - br_arch = host['arches'].split()[0] + tag_arches = session.getBuildConfig(root)['arches'] + if not tag_arches: + raise koji.BuildError, "No arch list for tag: %s" % build_tag + #index canonical host arches + host_arches = dict([(koji.canonArch(a),1) for a in host_arches.split()]) + #pick the first suitable match from tag's archlist + for br_arch in tag_arches.split(): + br_arch = koji.canonArch(br_arch) + if host_arches.has_key(br_arch): + #we're done + break + else: + #no overlap + raise koji.BuildError, "host does not match tag arches: %s (%s)" % (root, tag_arches) else: br_arch = arch From 7ade70b9977156eb9aa6e300e811358b7931cf47 Mon Sep 17 00:00:00 2001 From: Ricky Zhou Date: Sat, 13 Sep 2008 14:09:07 -0400 Subject: [PATCH 23/56] Use a python construct_url function (req.construct_url isn't available in mod_python mod_python-3.2.8) --- www/kojiweb/index.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/www/kojiweb/index.py b/www/kojiweb/index.py index 0a68d987..1ac7559a 100644 --- a/www/kojiweb/index.py +++ b/www/kojiweb/index.py @@ -111,10 +111,20 @@ def _getServer(req): req._session = session return session +def _construct_url(req, page): + port = req.server.port + url_scheme = 'http' + if env.get('HTTPS') == 'on': + url_scheme = 'https' + if (url_scheme == 'https' and port == 443) or \ + (url_scheme == 'http' and port == 80): + return "%s://%s%s" % (url_scheme, req.hostname, page) + return "%s://%s:%d%s" % (url_scheme, req.hostname, port, page) + def _getBaseURL(req): pieces = req.uri.split('/') base = '/'.join(pieces[:-1]) - return req.construct_url(base) + return _construct_url(req, base) def _redirectBack(req, page, forceSSL): if page: @@ -129,7 +139,7 @@ def _redirectBack(req, page, forceSSL): if page.startswith('http'): pass elif page.startswith('/'): - page = req.construct_url(page) + page = _construct_url(req, page) else: page = _getBaseURL(req) + '/' + page if forceSSL: From 4cf009fb7f5c864b2b99c302d6452c31bd0a6fe4 Mon Sep 17 00:00:00 2001 From: Mike Bonnet Date: Thu, 18 Sep 2008 15:00:39 -0400 Subject: [PATCH 24/56] implement passthrough_except(), which doesn't require a full list of vars for every invocation --- www/lib/kojiweb/util.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/www/lib/kojiweb/util.py b/www/lib/kojiweb/util.py index 61b8ab00..d63d1fa9 100644 --- a/www/lib/kojiweb/util.py +++ b/www/lib/kojiweb/util.py @@ -58,6 +58,22 @@ def passthrough(template, *vars): else: return '' +def passthrough_except(template, *exclude): + """ + Construct a string suitable for use as URL + parameters. The template calling this method must have + previously used + #attr _PASSTHROUGH = ... + to define the list of variable names to be passed-through. + Any variables names passed in will be excluded from the + list of variables in the output string. + """ + passvars = [] + for var in template._PASSTHROUGH: + if not var in exclude: + passvars.append(var) + return passthrough(template, *passvars) + def sortByKeyFunc(key, noneGreatest=False): """Return a function to sort a list of maps by the given key. If the key starts with '-', sort in reverse order. If noneGreatest From 1f325009bd52833866e4155a6deb88a4c76ce9ec Mon Sep 17 00:00:00 2001 From: Mike Bonnet Date: Fri, 19 Sep 2008 12:32:29 -0400 Subject: [PATCH 25/56] tasks page: add a View selector and move toplevel from State to Method --- www/kojiweb/index.py | 35 +++++++++++------- www/kojiweb/tasks.chtml | 79 +++++++++++++++++++++++++++-------------- www/static/koji.css | 4 +++ 3 files changed, 79 insertions(+), 39 deletions(-) diff --git a/www/kojiweb/index.py b/www/kojiweb/index.py index 1ac7559a..3c9a398a 100644 --- a/www/kojiweb/index.py +++ b/www/kojiweb/index.py @@ -343,7 +343,9 @@ def hello(req): def showSession(req): return _getServer(req).showSession() -def tasks(req, owner=None, state='active', method='all', hostID=None, start=None, order='-completion_time'): +_TOPLEVEL_TASKS = ['build', 'buildNotification', 'chainbuild', 'newRepo', 'tagBuild', 'tagNotification', 'waitrepo'] + +def tasks(req, owner=None, state='active', view='tree', method='all', hostID=None, start=None, order='-completion_time'): values = _initValues(req, 'Tasks', 'tasks') server = _getServer(req) @@ -361,32 +363,41 @@ def tasks(req, owner=None, state='active', method='all', hostID=None, start=None values['users'] = server.listUsers(queryOpts={'order': 'name'}) - if state in ('active', 'toplevel') and method == 'all' and not hostID: - # If we're only showing active or toplevel tasks, and not filtering by host or method, only query the top-level tasks as well, - # and then retrieve the task children so we can do the nice tree display. + treeEnabled = True + if hostID or (method not in ['all', 'toplevel'] + _TOPLEVEL_TASKS): + # force flat view if we're filtering by a hostID or non-toplevel task + view = 'flat' + # don't let them choose tree view either + treeEnabled = False + values['view'] = view + values['treeEnabled'] = treeEnabled + + if view == 'tree': treeDisplay = True else: treeDisplay = False values['treeDisplay'] = treeDisplay - if method != 'all': + if method == 'all': + # If we're showing all methods, and want it in tree view, we need to + # get only the top-level tasks. We'll retrieve their child tasks later. + if view == 'tree': + opts['parent'] = None + elif method == 'toplevel': + opts['parent'] = None + else: opts['method'] = method values['method'] = method if state == 'active': opts['state'] = [koji.TASK_STATES['FREE'], koji.TASK_STATES['OPEN'], koji.TASK_STATES['ASSIGNED']] - if treeDisplay: - opts['parent'] = None - elif state == 'toplevel': - # Show all top-level tasks, no tree display - opts['parent'] = None elif state == 'all': pass else: # Assume they've passed in a state name opts['state'] = [koji.TASK_STATES[state.upper()]] values['state'] = state - + if hostID: hostID = int(hostID) host = server.getHost(hostID, strict=True) @@ -405,7 +416,7 @@ def tasks(req, owner=None, state='active', method='all', hostID=None, start=None tasks = kojiweb.util.paginateMethod(server, values, 'listTasks', kw={'opts': opts}, start=start, dataName='tasks', prefix='task', order=order) - if treeDisplay: + if view == 'tree': server.multicall = True for task in tasks: server.getTaskDescendents(task['id'], request=True) diff --git a/www/kojiweb/tasks.chtml b/www/kojiweb/tasks.chtml index 412f66e6..0a6da03a 100644 --- a/www/kojiweb/tasks.chtml +++ b/www/kojiweb/tasks.chtml @@ -38,6 +38,8 @@ All #end if #end def +#attr _PASSTHROUGH = ['owner', 'state', 'view', 'method', 'hostID', 'order'] + #include "includes/header.chtml"

$headerPrefix($state) #if $method != 'all' then $method else ''# Tasks#if $ownerObj then ' owned by %s' % ($ownerObj.id, $ownerObj.name) else ''##if $host then ' on host %s' % ($host.id, $host.name) else ''#

@@ -50,22 +52,16 @@ All #if $state == 'active' active #else - active - #end if - | - #if $state == 'toplevel' - toplevel - #else - toplevel + active #end if | #if $state == 'all' all #else - all + all #end if | - @@ -74,10 +70,10 @@ All -
+
Owner: #if $ownerObj - everyone + everyone #else everyone #end if @@ -86,11 +82,11 @@ All #if $ownerObj and $ownerObj.id == $loggedInUser.id me #else - me + me #end if #end if | - #for $user in $users @@ -98,8 +94,20 @@ All
Method: - + @@ -114,6 +122,23 @@ All +
+ View: + #if $view == 'tree' + tree + #else + #if $treeEnabled + tree + #else + tree + #end if + #end if + | + #if $view == 'flat' + flat + #else + flat + #end if @@ -122,7 +147,7 @@ All #if $len($taskPages) > 1
Page: - #for $pageNum in $taskPages #end for @@ -130,23 +155,23 @@ All #end if #if $taskStart > 0 - <<< + <<< #end if #if $totalTasks != 0 Tasks #echo $taskStart + 1 # through #echo $taskStart + $taskCount # of $totalTasks #end if #if $taskStart + $taskCount < $totalTasks - >>> + >>> #end if
- - - - - - + + + + + + #if $len($tasks) > 0 #for $task in $tasks @@ -181,7 +206,7 @@ All #if $len($taskPages) > 1 Page: - #for $pageNum in $taskPages #end for @@ -189,13 +214,13 @@ All #end if #if $taskStart > 0 - <<< + <<< #end if #if $totalTasks != 0 Tasks #echo $taskStart + 1 # through #echo $taskStart + $taskCount # of $totalTasks #end if #if $taskStart + $taskCount < $totalTasks - >>> + >>> #end if diff --git a/www/static/koji.css b/www/static/koji.css index 64a291d2..122f81f9 100644 --- a/www/static/koji.css +++ b/www/static/koji.css @@ -83,6 +83,10 @@ p#footer a { display: none; } +.disabled { + color: #808080; +} + #mainNav { width: 100%; background-color: #009; From a50325b75d5bd33314a615ae50c1a7d5408ecf8d Mon Sep 17 00:00:00 2001 From: Mike Bonnet Date: Fri, 19 Sep 2008 13:22:27 -0400 Subject: [PATCH 26/56] tasks page: move toplevel into the View selector --- www/kojiweb/index.py | 33 +++++++++++++++++++++------------ www/kojiweb/tasks.chtml | 16 ++++++++++------ 2 files changed, 31 insertions(+), 18 deletions(-) diff --git a/www/kojiweb/index.py b/www/kojiweb/index.py index 3c9a398a..04ad6040 100644 --- a/www/kojiweb/index.py +++ b/www/kojiweb/index.py @@ -343,7 +343,10 @@ def hello(req): def showSession(req): return _getServer(req).showSession() +# Tasks that can exist without a parent _TOPLEVEL_TASKS = ['build', 'buildNotification', 'chainbuild', 'newRepo', 'tagBuild', 'tagNotification', 'waitrepo'] +# Tasks that can have children +_PARENT_TASKS = ['build', 'chainbuild', 'newRepo'] def tasks(req, owner=None, state='active', view='tree', method='all', hostID=None, start=None, order='-completion_time'): values = _initValues(req, 'Tasks', 'tasks') @@ -364,30 +367,36 @@ def tasks(req, owner=None, state='active', view='tree', method='all', hostID=Non values['users'] = server.listUsers(queryOpts={'order': 'name'}) treeEnabled = True - if hostID or (method not in ['all', 'toplevel'] + _TOPLEVEL_TASKS): - # force flat view if we're filtering by a hostID or non-toplevel task - view = 'flat' + if hostID or (method not in ['all'] + _PARENT_TASKS): + # force flat view if we're filtering by a hostID or a task that never has children + if view == 'tree': + view = 'flat' # don't let them choose tree view either treeEnabled = False - values['view'] = view values['treeEnabled'] = treeEnabled + toplevelEnabled = True + if method not in ['all'] + _TOPLEVEL_TASKS: + # force flat view if we're viewing a task that is never a top-level task + if view == 'toplevel': + view = 'flat' + toplevelEnabled = False + values['toplevelEnabled'] = toplevelEnabled + + values['view'] = view + if view == 'tree': treeDisplay = True else: treeDisplay = False values['treeDisplay'] = treeDisplay - if method == 'all': - # If we're showing all methods, and want it in tree view, we need to - # get only the top-level tasks. We'll retrieve their child tasks later. - if view == 'tree': - opts['parent'] = None - elif method == 'toplevel': - opts['parent'] = None - else: + if method != 'all': opts['method'] = method values['method'] = method + + if view in ('tree', 'toplevel'): + opts['parent'] = None if state == 'active': opts['state'] = [koji.TASK_STATES['FREE'], koji.TASK_STATES['OPEN'], koji.TASK_STATES['ASSIGNED']] diff --git a/www/kojiweb/tasks.chtml b/www/kojiweb/tasks.chtml index 0a6da03a..41ffaa0e 100644 --- a/www/kojiweb/tasks.chtml +++ b/www/kojiweb/tasks.chtml @@ -100,12 +100,6 @@ All all #end if | - #if $method == 'toplevel' - toplevel - #else - toplevel - #end if - | From 4aabd44a002fd8f8cd41e764623966ab66c4d1de Mon Sep 17 00:00:00 2001 From: Mike Bonnet Date: Fri, 19 Sep 2008 15:43:36 -0400 Subject: [PATCH 30/56] tasks.chtml: put the select lists into a 2x2 table --- www/kojiweb/tasks.chtml | 35 +++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/www/kojiweb/tasks.chtml b/www/kojiweb/tasks.chtml index 398bcf90..acc96000 100644 --- a/www/kojiweb/tasks.chtml +++ b/www/kojiweb/tasks.chtml @@ -29,8 +29,6 @@ #def headerPrefix($state) #if $state == 'active' Active - #elif $state == 'toplevel' -Top-level #elif $state == 'all' All #else @@ -42,13 +40,16 @@ All #include "includes/header.chtml" -

$headerPrefix($state) #if $method != 'all' then $method else ''# Tasks#if $ownerObj then ' owned by %s' % ($ownerObj.id, $ownerObj.name) else ''##if $host then ' on host %s' % ($host.id, $host.name) else ''#

+

$headerPrefix($state) #if $view == 'toplevel' then 'toplevel' else ''# #if $method != 'all' then $method else ''# Tasks#if $ownerObj then ' owned by %s' % ($ownerObj.id, $ownerObj.name) else ''##if $host then ' on host %s' % ($host.id, $host.name) else ''#

- #if $inherited - Hide inherited builds - #else - Show inherited builds - #end if -
+ #if $tag + Inherited: + +
+ #end if State:
Search - @@ -25,8 +25,9 @@
  - glob - regexp + glob + regexp + exact
$result.id$result.name$result.name
ID $util.sortImage($self, 'id')Type $util.sortImage($self, 'method')Owner $util.sortImage($self, 'owner')Arch $util.sortImage($self, 'arch')Finished $util.sortImage($self, 'completion_time')State $util.sortImage($self, 'state')ID $util.sortImage($self, 'id')Type $util.sortImage($self, 'method')Owner $util.sortImage($self, 'owner')Arch $util.sortImage($self, 'arch')Finished $util.sortImage($self, 'completion_time')State $util.sortImage($self, 'state')
State: - #if $state == 'active' - active - #else - active - #end if - | - #if $state == 'all' - all - #else - all - #end if - |
Owner: - #if $ownerObj - everyone - #else - everyone - #end if - #if $loggedInUser - | - #if $ownerObj and $ownerObj.id == $loggedInUser.id - me - #else - me - #end if - #end if - |
Method: - #if $method == 'all' - all - #else - all - #end if - |
View: - #if $view == 'tree' - tree - #else - #if $treeEnabled - tree - #else - tree - #end if - #end if - | - #if $view == 'toplevel' - toplevel - #else - #if $toplevelEnabled - toplevel - #else - toplevel - #end if - #end if - | - #if $view == 'flat' - flat - #else - flat - #end if +
From f2b833fb6d89324b7d7fae9a1fcb323ff20ebc2e Mon Sep 17 00:00:00 2001 From: Mike Bonnet Date: Mon, 22 Sep 2008 13:36:24 -0400 Subject: [PATCH 31/56] builds.chtml: pull all options in the select list for consistency with tasks.chtml --- www/kojiweb/builds.chtml | 34 +++++++++++++++++----------------- www/kojiweb/index.py | 6 +++--- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/www/kojiweb/builds.chtml b/www/kojiweb/builds.chtml index 87bd6a7a..f7983dbb 100644 --- a/www/kojiweb/builds.chtml +++ b/www/kojiweb/builds.chtml @@ -9,6 +9,8 @@ #end if #end def +#attr _PASSTHROUGH = ['userID', 'tagID', 'packageID', 'order', 'prefix', 'state', 'inherited'] + #include "includes/header.chtml"

#if $state != None then $util.stateName($state).capitalize() else ''# Builds#if $package then ' of %s' % ($package.id, $package.name) else ''##if $prefix then ' starting with "%s"' % $prefix else ''##if $user then ' by %s' % ($user.id, $user.name) else ''##if $tag then ' in tag %s' % ($tag.id, $tag.name) else ''#

@@ -16,43 +18,41 @@
+ + + +
State: + -
+
Owner: + -
+
Method: + -
- View: +
+ View: + + + + + +
diff --git a/www/kojiweb/index.py b/www/kojiweb/index.py index 11aba724..9b990224 100644 --- a/www/kojiweb/index.py +++ b/www/kojiweb/index.py @@ -917,7 +917,7 @@ def builds(req, userID=None, tagID=None, packageID=None, state=None, order='-com server = _getServer(req) user = None - if userID != None: + if userID: if userID.isdigit(): userID = int(userID) user = server.getUser(userID, strict=True) @@ -930,7 +930,7 @@ def builds(req, userID=None, tagID=None, packageID=None, state=None, order='-com values['users'] = server.listUsers(queryOpts={'order': 'name'}) tag = None - if tagID != None: + if tagID: if tagID.isdigit(): tagID = int(tagID) tag = server.getTag(tagID, strict=True) @@ -938,7 +938,7 @@ def builds(req, userID=None, tagID=None, packageID=None, state=None, order='-com values['tag'] = tag package = None - if packageID != None: + if packageID: if packageID.isdigit(): packageID = int(packageID) package = server.getPackage(packageID, strict=True) From daa685b80dfc5b0c419aed613979bca63cbaf4fe Mon Sep 17 00:00:00 2001 From: Mike Bonnet Date: Mon, 22 Sep 2008 13:48:14 -0400 Subject: [PATCH 32/56] builds.chtml: convert to using passthrough_except() --- www/kojiweb/builds.chtml | 56 +++++++++++++++++----------------------- 1 file changed, 24 insertions(+), 32 deletions(-) diff --git a/www/kojiweb/builds.chtml b/www/kojiweb/builds.chtml index f7983dbb..5b69eeba 100644 --- a/www/kojiweb/builds.chtml +++ b/www/kojiweb/builds.chtml @@ -1,14 +1,6 @@ #import koji #from kojiweb import util -#def toggleLink($comp, $label, $link) - #if $comp - $label#slurp - #else - $label#slurp - #end if -#end def - #attr _PASSTHROUGH = ['userID', 'tagID', 'packageID', 'order', 'prefix', 'state', 'inherited'] #include "includes/header.chtml" @@ -19,20 +11,20 @@
+ #if $tag + #end if + + +
Inherited: + -
+
State: + -
+
Built by: - #if $user - everyone - #else - everyone - #end if - #if $loggedInUser - | - #if $user and $user.id == $loggedInUser.id - me - #else - me - #end if - #end if - | + +
- #if $tag + #if $tag - #end if + + + #end if - - + + #if $tag - + #end if - - - + + + #if $len($builds) > 0 #for $build in $builds @@ -129,7 +121,7 @@ #if $len($buildPages) > 1 Page: - #for $pageNum in $buildPages #end for @@ -137,13 +129,13 @@ #end if #if $buildStart > 0 - <<< + <<< #end if #if $totalBuilds != 0 Builds #echo $buildStart + 1 # through #echo $buildStart + $buildCount # of $totalBuilds #end if #if $buildStart + $buildCount < $totalBuilds - >>> + >>> #end if From 5bb48bb2bb749582f7803bfb940d2654183426bd Mon Sep 17 00:00:00 2001 From: Mike Bonnet Date: Fri, 26 Sep 2008 12:42:13 -0400 Subject: [PATCH 33/56] update copyright to 2008 --- www/kojiweb/includes/footer.chtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/www/kojiweb/includes/footer.chtml b/www/kojiweb/includes/footer.chtml index f2686bd4..c09d2be1 100644 --- a/www/kojiweb/includes/footer.chtml +++ b/www/kojiweb/includes/footer.chtml @@ -1,7 +1,7 @@ From 68510f93feeebe51074455090975104fbd377db2 Mon Sep 17 00:00:00 2001 From: Mike Bonnet Date: Wed, 1 Oct 2008 18:00:22 -0400 Subject: [PATCH 34/56] - move the allowed_scms validation into the assert_allowed() method of the SCM class - make use_common an attribute of the SCM class instead of a method parameter --- builder/kojid | 56 ++++++++++++++++++++++++++++------------------ builder/kojid.conf | 2 +- 2 files changed, 35 insertions(+), 23 deletions(-) diff --git a/builder/kojid b/builder/kojid index 3a84d4ca..d0f2ece5 100755 --- a/builder/kojid +++ b/builder/kojid @@ -1842,23 +1842,7 @@ class BuildSRPMFromSCMTask(BaseTaskHandler): def handler(self,url): # will throw a BuildError if the url is invalid scm = SCM(url) - - use_common = True - - for allowed_scm in options.allowed_scms.split(): - scm_tuple = allowed_scm.split(':') - if len(scm_tuple) in (2, 3): - if fnmatch(scm.host, scm_tuple[0]) and fnmatch(scm.repository, scm_tuple[1]): - # SCM host:repository is in the allowed list - # check if we specify a value for use_common - if len(scm_tuple) == 3: - if scm_tuple[2].lower() in ('false', 'no', '0'): - use_common = False - break - else: - self.logger.warn('Ignoring incorrectly formatted SCM host:repository: %s' % allowed_scm) - else: - raise koji.BuildError, '%s:%s is not in the list of allowed SCMs' % (scm.host, scm.repository) + scm.assert_allowed(options.allowed_scms) # Setup files and directories for SRPM creation scmdir = self.workdir + '/scmroot' @@ -1868,7 +1852,7 @@ class BuildSRPMFromSCMTask(BaseTaskHandler): uploadpath = self.getUploadDir() # Check out spec file, etc. from SCM - sourcedir = scm.checkout(scmdir, uploadpath, logfile, use_common=use_common) + sourcedir = scm.checkout(scmdir, uploadpath, logfile) # Find and verify that there is only one spec file. spec_files = glob.glob("%s/*.spec" % sourcedir) @@ -2370,6 +2354,8 @@ class SCM(object): - repository - module - revision + - use_common (defaults to True, may be set by assert_allowed()) + - scmtype The exact format of each attribute is SCM-specific, but the structure of the url must conform to the template above, or an error will be raised. @@ -2386,6 +2372,7 @@ class SCM(object): self.repository = path self.module = query self.revision = fragment + self.use_common = True for scmtype, schemes in SCM.types.items(): if self.scheme in schemes: @@ -2437,15 +2424,40 @@ class SCM(object): # return parsed values return (scheme, user, netloc, path, query, fragment) - def checkout(self, scmdir, uploadpath, logfile, use_common=False): + def assert_allowed(self, allowed): + """ + Verify that the host and repository of this SCM is in the provided list of + allowed repositories. + + allowed is a space-separated list of host:repository[:use_common] tuples. Incorrectly-formatted + tuples will be ignored. + + If use_common is not present, kojid will attempt to checkout a common/ directory from the + repository. If use_common is set to no, off, false, or 0, it will not attempt to checkout a common/ + directory. + """ + for allowed_scm in options.allowed_scms.split(): + scm_tuple = allowed_scm.split(':') + if len(scm_tuple) in (2, 3): + if fnmatch(self.host, scm_tuple[0]) and fnmatch(self.repository, scm_tuple[1]): + # SCM host:repository is in the allowed list + # check if we specify a value for use_common + if len(scm_tuple) == 3: + if scm_tuple[2].lower() in ('no', 'off', 'false', '0'): + self.use_common = False + break + else: + self.logger.warn('Ignoring incorrectly formatted SCM host:repository: %s' % allowed_scm) + else: + raise koji.BuildError, '%s:%s is not in the list of allowed SCMs' % (self.host, self.repository) + + def checkout(self, scmdir, uploadpath, logfile): """ Checkout the module from SCM. Accepts the following parameters: - scmdir: the working directory - uploadpath: the path on the server the logfile should be uploaded to - logfile: the file used for logging command output - - use_common: if True, also checkout a "common" module from the top-level of the repository - and symlink it into the parent directory of the module checkout Returns the directory that the module was checked-out into (a subdirectory of scmdir) """ @@ -2546,7 +2558,7 @@ class SCM(object): raise koji.BuildError, 'Error running %s update command "%s", see %s for details' % \ (self.scmtype, ' '.join(update_checkout_cmd), os.path.basename(logfile)) - if use_common: + if self.use_common: if log_output(common_checkout_cmd[0], common_checkout_cmd, logfile, uploadpath, cwd=scmdir, logerror=1, append=1, env=env): raise koji.BuildError, 'Error running %s checkout command "%s", see %s for details' % \ (self.scmtype, ' '.join(common_checkout_cmd), os.path.basename(logfile)) diff --git a/builder/kojid.conf b/builder/kojid.conf index a0d6fbe1..af4b2633 100644 --- a/builder/kojid.conf +++ b/builder/kojid.conf @@ -37,7 +37,7 @@ pkgurl=http://hub.example.com/packages ; A space-separated list of hostname:repository[:use_common] tuples that kojid is authorized to checkout from (no quotes). ; Wildcards (as supported by fnmatch) are allowed. -; If use_common is specified and is one of "false", "no", or "0" (without quotes), then kojid will not attempt to checkout +; If use_common is specified and is one of "false", "no", "off", or "0" (without quotes), then kojid will not attempt to checkout ; a common/ dir when checking out sources from the source control system. Otherwise, it will attempt to checkout a common/ ; dir, and will raise an exception if it cannot. allowed_scms=scm.example.com:/cvs/example git.example.org:/example svn.example.org:/users/*:no From 80b845f8c6330813fbcebc4ca96fe729248dd5ca Mon Sep 17 00:00:00 2001 From: Mike Bonnet Date: Wed, 1 Oct 2008 18:20:36 -0400 Subject: [PATCH 35/56] use reset instead of checkout when updating a git checkout to the correct changeset --- builder/kojid | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/builder/kojid b/builder/kojid index d0f2ece5..4bcd5938 100755 --- a/builder/kojid +++ b/builder/kojid @@ -2502,7 +2502,7 @@ class SCM(object): module_checkout_cmd = ['git', 'clone', '-n', gitrepo, checkout_path] common_checkout_cmd = ['git', 'clone', commonrepo, 'common'] - update_checkout_cmd = ['git', 'checkout', self.revision] + update_checkout_cmd = ['git', 'reset', '--hard', self.revision] update_checkout_dir = '%s/%s' % (scmdir, checkout_path) sourcedir = '%s/%s' % (scmdir, checkout_path) @@ -2523,7 +2523,7 @@ class SCM(object): gitserver = 'ssh://%s@%s%s' % (self.user, self.host, self.repository) module_checkout_cmd = ['git', 'clone', '-n', '%s/%s' % (gitserver, self.module), self.module] common_checkout_cmd = ['git', 'clone', '%s/%s' % (gitserver, common_path)] - update_checkout_cmd = ['git', 'checkout', self.revision] + update_checkout_cmd = ['git', 'reset', '--hard', self.revision] elif self.scmtype == 'SVN': scheme = self.scheme From da4c99e3e5bf7bd631c8f8245c61eca2394e8c6d Mon Sep 17 00:00:00 2001 From: Mike Bonnet Date: Wed, 1 Oct 2008 18:46:43 -0400 Subject: [PATCH 36/56] handle ssh and non-ssh git checkouts the same --- builder/kojid | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/builder/kojid b/builder/kojid index 4bcd5938..dbe7a029 100755 --- a/builder/kojid +++ b/builder/kojid @@ -2514,16 +2514,30 @@ class SCM(object): elif self.scmtype == 'GIT+SSH': if not self.user: raise koji.BuildError, 'No user specified for repository access scheme: %s' % self.scheme - common_path = 'common' - if self.module.endswith('/.git'): + gitrepo = 'git+ssh://%s@%s%s' % (self.user, self.host, self.repository) + commonrepo = os.path.dirname(gitrepo) + '/common' + checkout_path = os.path.basename(self.repository) + if self.repository.endswith('/.git'): # If we're referring to the .git subdirectory of the main module, # assume we need to do the same for the common module - common_path = 'common/.git' + checkout_path = os.path.basename(self.repository[:-5]) + commonrepo = os.path.dirname(gitrepo[:-5]) + '/common/.git' + elif self.repository.endswith('.git'): + # If we're referring to a bare repository for the main module, + # assume we need to do the same for the common module + checkout_path = os.path.basename(self.repository[:-4]) + commonrepo = os.path.dirname(gitrepo[:-4]) + '/common.git' - gitserver = 'ssh://%s@%s%s' % (self.user, self.host, self.repository) - module_checkout_cmd = ['git', 'clone', '-n', '%s/%s' % (gitserver, self.module), self.module] - common_checkout_cmd = ['git', 'clone', '%s/%s' % (gitserver, common_path)] + module_checkout_cmd = ['git', 'clone', '-n', gitrepo, checkout_path] + common_checkout_cmd = ['git', 'clone', commonrepo, 'common'] update_checkout_cmd = ['git', 'reset', '--hard', self.revision] + update_checkout_dir = '%s/%s' % (scmdir, checkout_path) + + sourcedir = '%s/%s' % (scmdir, checkout_path) + # self.module may be empty, in which case the specfile should be in the top-level directory + if self.module: + # Treat the module as a directory inside the git repository + sourcedir = '%s/%s' % (sourcedir, self.module) elif self.scmtype == 'SVN': scheme = self.scheme From 05a1ec6e0c420284f953a1fcf1b2cd632da4c08a Mon Sep 17 00:00:00 2001 From: Mike Bonnet Date: Wed, 1 Oct 2008 18:54:16 -0400 Subject: [PATCH 37/56] typo --- builder/kojid | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builder/kojid b/builder/kojid index dbe7a029..8b566a3f 100755 --- a/builder/kojid +++ b/builder/kojid @@ -2436,7 +2436,7 @@ class SCM(object): repository. If use_common is set to no, off, false, or 0, it will not attempt to checkout a common/ directory. """ - for allowed_scm in options.allowed_scms.split(): + for allowed_scm in allowed.split(): scm_tuple = allowed_scm.split(':') if len(scm_tuple) in (2, 3): if fnmatch(self.host, scm_tuple[0]) and fnmatch(self.repository, scm_tuple[1]): From 58972ec76f943ca8f8ea0c3e8422ce4628df9d5b Mon Sep 17 00:00:00 2001 From: Mike Bonnet Date: Thu, 2 Oct 2008 01:44:22 -0400 Subject: [PATCH 38/56] builds.chtml: rearrange the select lists to save space --- www/kojiweb/builds.chtml | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/www/kojiweb/builds.chtml b/www/kojiweb/builds.chtml index 5b69eeba..5d7b1a6f 100644 --- a/www/kojiweb/builds.chtml +++ b/www/kojiweb/builds.chtml @@ -11,16 +11,6 @@ From 5c9f71997913545cc71ddd96a8e838ca6e2b9782 Mon Sep 17 00:00:00 2001 From: Mike Bonnet Date: Fri, 3 Oct 2008 10:56:52 -0400 Subject: [PATCH 39/56] update the web UI to conform to XHTML 1.0 Transitional (thanks W3C Validator!) --- www/kojiweb/buildrootinfo.chtml | 4 ++-- www/kojiweb/builds.chtml | 8 ++++---- www/kojiweb/buildsbystatus.chtml | 10 +++++----- www/kojiweb/buildsbytarget.chtml | 14 +++++++------- www/kojiweb/buildsbyuser.chtml | 10 +++++----- www/kojiweb/buildtargetedit.chtml | 7 ++++--- www/kojiweb/buildtargets.chtml | 8 ++++---- www/kojiweb/fileinfo.chtml | 6 +++--- www/kojiweb/hosts.chtml | 8 ++++---- www/kojiweb/includes/header.chtml | 24 ++++++++++++------------ www/kojiweb/index.chtml | 12 ++++++------ www/kojiweb/index.py | 13 ++++++++++++- www/kojiweb/notificationedit.chtml | 10 +++++----- www/kojiweb/packageinfo.chtml | 8 ++++---- www/kojiweb/packages.chtml | 8 ++++---- www/kojiweb/packagesbyuser.chtml | 10 +++++----- www/kojiweb/rpminfo.chtml | 14 +++++++------- www/kojiweb/rpmlist.chtml | 8 ++++---- www/kojiweb/rpmsbyhost.chtml | 10 +++++----- www/kojiweb/searchresults.chtml | 8 ++++---- www/kojiweb/tagedit.chtml | 4 ++-- www/kojiweb/tags.chtml | 8 ++++---- www/kojiweb/tasks.chtml | 8 ++++---- www/kojiweb/tasksbyhost.chtml | 10 +++++----- www/kojiweb/tasksbyuser.chtml | 10 +++++----- www/kojiweb/userinfo.chtml | 8 ++++---- www/kojiweb/users.chtml | 8 ++++---- www/lib/kojiweb/util.py | 4 ++-- www/static/koji.css | 26 ++++++++++++++------------ 29 files changed, 150 insertions(+), 136 deletions(-) diff --git a/www/kojiweb/buildrootinfo.chtml b/www/kojiweb/buildrootinfo.chtml index 4369ddc2..fc23f0b3 100644 --- a/www/kojiweb/buildrootinfo.chtml +++ b/www/kojiweb/buildrootinfo.chtml @@ -44,10 +44,10 @@ - + - +
- Inherited: + Inherited: - -
State: - #for $stateOpt in ['BUILDING', 'COMPLETE', 'FAILED', 'CANCELED'] @@ -42,7 +34,7 @@
Built by: - #if $loggedInUser @@ -61,12 +53,12 @@ #if $prefix == $char $char #else - $char + $char #end if | #end for #if $prefix - all + all #else all #end if @@ -77,7 +69,7 @@ #if $len($buildPages) > 1
Page: - #for $pageNum in $buildPages #end for @@ -85,25 +77,25 @@
#end if #if $buildStart > 0 - <<< + <<< #end if #if $totalBuilds != 0 Builds #echo $buildStart + 1 # through #echo $buildStart + $buildCount # of $totalBuilds #end if #if $buildStart + $buildCount < $totalBuilds - >>> + >>> #end if
ID $util.sortImage($self, 'build_id')NVR $util.sortImage($self, 'nvr')ID $util.sortImage($self, 'build_id')NVR $util.sortImage($self, 'nvr')Tag $util.sortImage($self, 'tag_name')Tag $util.sortImage($self, 'tag_name')Built by $util.sortImage($self, 'owner_name')Finished $util.sortImage($self, 'completion_time')State $util.sortImage($self, 'state')Built by $util.sortImage($self, 'owner_name')Finished $util.sortImage($self, 'completion_time')State $util.sortImage($self, 'state')
- #if $tag - - #end if - + #if $tag + + #end if
- Inherited: - - -
State: @@ -30,8 +20,7 @@ #end for -
+ Built by:
+ Inherited: + + +
Repo Created$util.formatTimeLong($buildroot.repo_create_event_time)
Component RPMsComponent RPMs
Built RPMsBuilt RPMs
diff --git a/www/kojiweb/builds.chtml b/www/kojiweb/builds.chtml index 5d7b1a6f..88fa1504 100644 --- a/www/kojiweb/builds.chtml +++ b/www/kojiweb/builds.chtml @@ -66,11 +66,11 @@
#if $len($buildPages) > 1 -
+ Page:
@@ -118,11 +118,11 @@
#if $len($buildPages) > 1 -
+ Page:
diff --git a/www/kojiweb/buildsbystatus.chtml b/www/kojiweb/buildsbystatus.chtml index a5a18f85..8a27dd01 100644 --- a/www/kojiweb/buildsbystatus.chtml +++ b/www/kojiweb/buildsbystatus.chtml @@ -4,7 +4,7 @@ #if not $label #set $label = $value #end if - + #end def #include "includes/header.chtml" @@ -13,7 +13,7 @@ - + - + - +
-
+ Show last
Succeededgraph row $numSucceeded
Failedgraph row $numFailed
Canceledgraph row $numCanceled
diff --git a/www/kojiweb/buildsbytarget.chtml b/www/kojiweb/buildsbytarget.chtml index 3605dc40..e68cb5bb 100644 --- a/www/kojiweb/buildsbytarget.chtml +++ b/www/kojiweb/buildsbytarget.chtml @@ -4,7 +4,7 @@ #if not $label #set $label = $value #end if - + #end def #include "includes/header.chtml" @@ -13,7 +13,7 @@ - + #end for @@ -74,11 +74,11 @@ - + #end for @@ -48,11 +48,11 @@ @@ -41,7 +41,7 @@ @@ -56,6 +56,7 @@ -
- + Show last
#if $len($targetPages) > 1 - + Page: @@ -62,7 +62,7 @@ #for $target in $targets
$target.namegraph row $target.builds
#if $len($targetPages) > 1 -
+ Page:
diff --git a/www/kojiweb/buildsbyuser.chtml b/www/kojiweb/buildsbyuser.chtml index f1394f9f..e3740fad 100644 --- a/www/kojiweb/buildsbyuser.chtml +++ b/www/kojiweb/buildsbyuser.chtml @@ -7,11 +7,11 @@
#if $len($userBuildPages) > 1 -
+ Page:
@@ -36,7 +36,7 @@ #for $userBuild in $userBuilds
$userBuild.namegraph row $userBuild.builds
#if $len($userBuildPages) > 1 -
+ Page:
diff --git a/www/kojiweb/buildtargetedit.chtml b/www/kojiweb/buildtargetedit.chtml index 9a62cf84..f550011c 100644 --- a/www/kojiweb/buildtargetedit.chtml +++ b/www/kojiweb/buildtargetedit.chtml @@ -30,7 +30,7 @@
+
+ #include "includes/footer.chtml" diff --git a/www/kojiweb/buildtargets.chtml b/www/kojiweb/buildtargets.chtml index cb4041bc..395ca413 100644 --- a/www/kojiweb/buildtargets.chtml +++ b/www/kojiweb/buildtargets.chtml @@ -7,11 +7,11 @@ #if $len($targetPages) > 1 -
+ Page:
@@ -46,11 +46,11 @@ #if $len($targetPages) > 1 -
+ Page:
diff --git a/www/kojiweb/fileinfo.chtml b/www/kojiweb/fileinfo.chtml index a7bb15bc..eadc570e 100644 --- a/www/kojiweb/fileinfo.chtml +++ b/www/kojiweb/fileinfo.chtml @@ -2,11 +2,11 @@ #import urllib #include "includes/header.chtml" -

Information for file $file.name

+

Information for file $file.name

- + @@ -24,7 +24,7 @@ #set $epoch = ($rpm.epoch != None and $str($rpm.epoch) + ':' or '') - +
Name$file.nameName$file.name
MD5 Sum$file.md5
RPM$rpm.name-$epoch$rpm.version-$rpm.release.${rpm.arch}.rpmRPM$rpm.name-$epoch$rpm.version-$rpm.release.${rpm.arch}.rpm
diff --git a/www/kojiweb/hosts.chtml b/www/kojiweb/hosts.chtml index 53bfc9d9..3466cd6b 100644 --- a/www/kojiweb/hosts.chtml +++ b/www/kojiweb/hosts.chtml @@ -7,11 +7,11 @@ #if $len($hostPages) > 1 -
+ Page:
@@ -54,11 +54,11 @@ #if $len($hostPages) > 1 -
+ Page:
diff --git a/www/kojiweb/includes/header.chtml b/www/kojiweb/includes/header.chtml index a0cada82..e000293a 100644 --- a/www/kojiweb/includes/header.chtml +++ b/www/kojiweb/includes/header.chtml @@ -1,7 +1,7 @@ #import koji #import random - + #def greeting() #set $greetings = ('hello', 'hi', 'yo', "what's up", "g'day", 'back to work', @@ -65,16 +65,16 @@ diff --git a/www/kojiweb/index.chtml b/www/kojiweb/index.chtml index 9d4f7f31..409af01a 100644 --- a/www/kojiweb/index.chtml +++ b/www/kojiweb/index.chtml @@ -10,11 +10,11 @@ #if $len($buildPages) > 1 -
+ Page:
@@ -57,11 +57,11 @@ #if $len($taskPages) > 1 -
+ Page:
@@ -109,11 +109,11 @@ #if $len($packagePages) > 1 -
+ Page:
diff --git a/www/kojiweb/index.py b/www/kojiweb/index.py index 9b990224..984d0ddd 100644 --- a/www/kojiweb/index.py +++ b/www/kojiweb/index.py @@ -4,6 +4,7 @@ import re import sys import mod_python import mod_python.Cookie +import Cheetah.Filters import Cheetah.Template import datetime import time @@ -85,6 +86,16 @@ def _initValues(req, title='Build System Info', pageID='summary'): return values +# Escape ampersands so the output can be valid XHTML +class XHTMLFilter(Cheetah.Filters.EncodeUnicode): + def filter(self, *args, **kw): + result = super(XHTMLFilter, self).filter(*args, **kw) + result = result.replace('&', '&') + result = result.replace('&nbsp;', ' ') + result = result.replace('&lt;', '<') + result = result.replace('&gt;', '>') + return result + def _genHTML(req, fileName): os.chdir(os.path.dirname(req.filename)) @@ -93,7 +104,7 @@ def _genHTML(req, fileName): else: req._values['currentUser'] = None - return Cheetah.Template.Template(file=fileName, searchList=[req._values], filter='EncodeUnicode').respond() + return Cheetah.Template.Template(file=fileName, searchList=[req._values], filter=XHTMLFilter).respond() def _getServer(req): serverURL = req.get_options().get('KojiHubURL', 'http://localhost/kojihub') diff --git a/www/kojiweb/notificationedit.chtml b/www/kojiweb/notificationedit.chtml index ac066e24..9907ea29 100644 --- a/www/kojiweb/notificationedit.chtml +++ b/www/kojiweb/notificationedit.chtml @@ -17,9 +17,9 @@ Package @@ -28,16 +28,16 @@ Tag Success Only? - + diff --git a/www/kojiweb/packageinfo.chtml b/www/kojiweb/packageinfo.chtml index 7ae06bd9..e0fcb716 100644 --- a/www/kojiweb/packageinfo.chtml +++ b/www/kojiweb/packageinfo.chtml @@ -20,11 +20,11 @@ #if $len($buildPages) > 1 -
+ Page:
@@ -68,11 +68,11 @@ #if $len($tagPages) > 1 -
+ Page:
diff --git a/www/kojiweb/packages.chtml b/www/kojiweb/packages.chtml index 9f79fee9..836cdca9 100644 --- a/www/kojiweb/packages.chtml +++ b/www/kojiweb/packages.chtml @@ -36,11 +36,11 @@ #if $len($packagePages) > 1 -
+ Page:
@@ -85,11 +85,11 @@ #if $len($packagePages) > 1 -
+ Page:
diff --git a/www/kojiweb/packagesbyuser.chtml b/www/kojiweb/packagesbyuser.chtml index ec6241d4..f32de512 100644 --- a/www/kojiweb/packagesbyuser.chtml +++ b/www/kojiweb/packagesbyuser.chtml @@ -7,11 +7,11 @@ #if $len($userPages) > 1 -
+ Page:
@@ -36,7 +36,7 @@ #for $user in $users $user.name - + graph row $user.packages #end for @@ -48,11 +48,11 @@ #if $len($userPages) > 1 -
+ Page:
diff --git a/www/kojiweb/rpminfo.chtml b/www/kojiweb/rpminfo.chtml index 896ccd61..7a3390b5 100644 --- a/www/kojiweb/rpminfo.chtml +++ b/www/kojiweb/rpminfo.chtml @@ -46,7 +46,7 @@ #for $dep in $provides - + #end for
$util.formatDep($dep.name, $dep.version, $dep.flags)$util.escapeHTML($util.formatDep($dep.name, $dep.version, $dep.flags))
@@ -62,7 +62,7 @@ #for $dep in $requires - + #end for
$util.formatDep($dep.name, $dep.version, $dep.flags)$util.escapeHTML($util.formatDep($dep.name, $dep.version, $dep.flags))
@@ -78,7 +78,7 @@ #for $dep in $obsoletes - + #end for
$util.formatDep($dep.name, $dep.version, $dep.flags)$util.escapeHTML($util.formatDep($dep.name, $dep.version, $dep.flags))
@@ -94,7 +94,7 @@ #for $dep in $conflicts - + #end for
$util.formatDep($dep.name, $dep.version, $dep.flags)$util.escapeHTML($util.formatDep($dep.name, $dep.version, $dep.flags))
@@ -111,11 +111,11 @@ #if $len($filePages) > 1 -
+ Page:
@@ -135,7 +135,7 @@ #for $file in $files - $file.name$file.size + $file.name$file.size #end for diff --git a/www/kojiweb/rpmlist.chtml b/www/kojiweb/rpmlist.chtml index 76493658..26b18e76 100644 --- a/www/kojiweb/rpmlist.chtml +++ b/www/kojiweb/rpmlist.chtml @@ -12,11 +12,11 @@ #if $len($rpmPages) > 1 -
+ Page:
@@ -57,11 +57,11 @@ #if $len($rpmPages) > 1 -
+ Page:
diff --git a/www/kojiweb/rpmsbyhost.chtml b/www/kojiweb/rpmsbyhost.chtml index c3018130..5503a826 100644 --- a/www/kojiweb/rpmsbyhost.chtml +++ b/www/kojiweb/rpmsbyhost.chtml @@ -39,11 +39,11 @@ #if $len($hostPages) > 1 -
+ Page:
@@ -68,7 +68,7 @@ #for $host in $hosts $host.name - + graph row $host.rpms #end for @@ -80,11 +80,11 @@ #if $len($hostPages) > 1 -
+ Page:
diff --git a/www/kojiweb/searchresults.chtml b/www/kojiweb/searchresults.chtml index df3b40c0..c0c21b5b 100644 --- a/www/kojiweb/searchresults.chtml +++ b/www/kojiweb/searchresults.chtml @@ -9,11 +9,11 @@ #if $len($resultPages) > 1 -
+ Page:
@@ -50,11 +50,11 @@ #if $len($resultPages) > 1 -
+ Page:
diff --git a/www/kojiweb/tagedit.chtml b/www/kojiweb/tagedit.chtml index e78bc0e6..242bdd6b 100644 --- a/www/kojiweb/tagedit.chtml +++ b/www/kojiweb/tagedit.chtml @@ -25,13 +25,13 @@ Locked - + Permission #for $pageNum in $tagPages - + #end for @@ -46,11 +46,11 @@ #if $len($tagPages) > 1 -
+ Page:
diff --git a/www/kojiweb/tasks.chtml b/www/kojiweb/tasks.chtml index acc96000..796002d8 100644 --- a/www/kojiweb/tasks.chtml +++ b/www/kojiweb/tasks.chtml @@ -108,11 +108,11 @@ All #if $len($taskPages) > 1 -
+ Page:
@@ -167,11 +167,11 @@ All #if $len($taskPages) > 1 -
+ Page:
diff --git a/www/kojiweb/tasksbyhost.chtml b/www/kojiweb/tasksbyhost.chtml index 5f2979b4..ba367565 100644 --- a/www/kojiweb/tasksbyhost.chtml +++ b/www/kojiweb/tasksbyhost.chtml @@ -23,11 +23,11 @@ #if $len($hostPages) > 1 -
+ Page:
@@ -52,7 +52,7 @@ #for $host in $hosts $host.name - + graph row $host.tasks #end for @@ -64,11 +64,11 @@ #if $len($hostPages) > 1 -
+ Page:
diff --git a/www/kojiweb/tasksbyuser.chtml b/www/kojiweb/tasksbyuser.chtml index 6c29ced5..1fe2704a 100644 --- a/www/kojiweb/tasksbyuser.chtml +++ b/www/kojiweb/tasksbyuser.chtml @@ -7,11 +7,11 @@ #if $len($userPages) > 1 -
+ Page:
@@ -36,7 +36,7 @@ #for $user in $users $user.name - + graph row $user.tasks #end for @@ -48,11 +48,11 @@ #if $len($userPages) > 1 -
+ Page:
diff --git a/www/kojiweb/userinfo.chtml b/www/kojiweb/userinfo.chtml index 42f40099..b5d980c5 100644 --- a/www/kojiweb/userinfo.chtml +++ b/www/kojiweb/userinfo.chtml @@ -22,11 +22,11 @@ #if $len($packagePages) > 1 -
+ Page:
@@ -66,11 +66,11 @@ #if $len($buildPages) > 1 -
+ Page:
diff --git a/www/kojiweb/users.chtml b/www/kojiweb/users.chtml index befd8be6..20dc2070 100644 --- a/www/kojiweb/users.chtml +++ b/www/kojiweb/users.chtml @@ -24,11 +24,11 @@ #if $len($userPages) > 1 -
+ Page:
@@ -69,11 +69,11 @@ #if $len($userPages) > 1 -
+ Page:
diff --git a/www/lib/kojiweb/util.py b/www/lib/kojiweb/util.py index 67a99233..451573bc 100644 --- a/www/lib/kojiweb/util.py +++ b/www/lib/kojiweb/util.py @@ -43,9 +43,9 @@ def sortImage(template, sortKey, orderVar='order'): """ orderVal = template.getVar(orderVar) if orderVal == sortKey: - return '' + return 'ascending sort' elif orderVal == '-' + sortKey: - return '' + return 'descending sort' else: return '' diff --git a/www/static/koji.css b/www/static/koji.css index 122f81f9..f9b97c0f 100644 --- a/www/static/koji.css +++ b/www/static/koji.css @@ -126,16 +126,16 @@ p#footer a { border-color: #ddd; } -body#summary #mainNav li#summary a, -body#tasks #mainNav li#tasks a, -body#tags #mainNav li#tags a, -body#builds #mainNav li#builds a, -body#packages #mainNav li#packages a, -body#users #mainNav li#users a, -body#hosts #mainNav li#hosts a, -body#buildtargets #mainNav li#buildtargets a, -body#reports #mainNav li#reports a, -body#search #mainNav li#search a { +body#summary #mainNav li#summaryTab a, +body#tasks #mainNav li#tasksTab a, +body#tags #mainNav li#tagsTab a, +body#builds #mainNav li#buildsTab a, +body#packages #mainNav li#packagesTab a, +body#users #mainNav li#usersTab a, +body#hosts #mainNav li#hostsTab a, +body#buildtargets #mainNav li#buildtargetsTab a, +body#reports #mainNav li#reportsTab a, +body#search #mainNav li#searchTab a { background-color: #eee; color: #000; } @@ -325,11 +325,13 @@ tr.list-header th { } tr.list-header th:first-child { - -moz-border-radius-topleft: 15px; + -moz-border-radius-topleft: 1em; + -webkit-border-top-left-radius: 1em; } tr.list-header th:last-child { - -moz-border-radius-topright: 15px; + -moz-border-radius-topright: 1em; + -webkit-border-top-right-radius: 1em; } tr.row-odd { From b7c054204e1041899a1c7c6c9e43c489ccd13c35 Mon Sep 17 00:00:00 2001 From: Mike Bonnet Date: Fri, 3 Oct 2008 11:37:48 -0400 Subject: [PATCH 40/56] missed a few XHTML problems --- www/kojiweb/notificationedit.chtml | 2 +- www/kojiweb/tagedit.chtml | 2 +- www/kojiweb/tagparent.chtml | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/www/kojiweb/notificationedit.chtml b/www/kojiweb/notificationedit.chtml index 9907ea29..1f2f5488 100644 --- a/www/kojiweb/notificationedit.chtml +++ b/www/kojiweb/notificationedit.chtml @@ -37,7 +37,7 @@ Success Only? - + diff --git a/www/kojiweb/tagedit.chtml b/www/kojiweb/tagedit.chtml index 242bdd6b..1b8a6deb 100644 --- a/www/kojiweb/tagedit.chtml +++ b/www/kojiweb/tagedit.chtml @@ -25,7 +25,7 @@ Locked - + Permission diff --git a/www/kojiweb/tagparent.chtml b/www/kojiweb/tagparent.chtml index a7df1df2..4cb9052c 100644 --- a/www/kojiweb/tagparent.chtml +++ b/www/kojiweb/tagparent.chtml @@ -40,13 +40,13 @@ Intransitive - + Packages Only - + From f0b8fa751ccccd90000bdada3fa3baaa7c1b2d01 Mon Sep 17 00:00:00 2001 From: Mike Bonnet Date: Fri, 3 Oct 2008 11:56:13 -0400 Subject: [PATCH 41/56] missed one short attribute --- www/kojiweb/tagedit.chtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/www/kojiweb/tagedit.chtml b/www/kojiweb/tagedit.chtml index 1b8a6deb..8257c42f 100644 --- a/www/kojiweb/tagedit.chtml +++ b/www/kojiweb/tagedit.chtml @@ -33,7 +33,7 @@ From a99f5dd20ea0165cb08f6220c249f31c77faa728 Mon Sep 17 00:00:00 2001 From: Mike Bonnet Date: Fri, 3 Oct 2008 12:51:54 -0400 Subject: [PATCH 42/56] index.py: cache compiled Cheetah templates --- www/kojiweb/index.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/www/kojiweb/index.py b/www/kojiweb/index.py index 984d0ddd..ab831a60 100644 --- a/www/kojiweb/index.py +++ b/www/kojiweb/index.py @@ -96,15 +96,25 @@ class XHTMLFilter(Cheetah.Filters.EncodeUnicode): result = result.replace('&gt;', '>') return result +TEMPLATES = {} + def _genHTML(req, fileName): - os.chdir(os.path.dirname(req.filename)) + reqdir = os.path.dirname(req.filename) + if os.getcwd() != reqdir: + os.chdir(reqdir) if hasattr(req, 'currentUser'): req._values['currentUser'] = req.currentUser else: req._values['currentUser'] = None - return Cheetah.Template.Template(file=fileName, searchList=[req._values], filter=XHTMLFilter).respond() + tmpl_class = TEMPLATES.get(fileName) + if not tmpl_class: + tmpl_class = Cheetah.Template.Template.compile(file=fileName) + TEMPLATES[fileName] = tmpl_class + mod_python.apache.log_error('Compiled template for ' + fileName) + tmpl_inst = tmpl_class(namespaces=[req._values], filter=XHTMLFilter) + return str(tmpl_inst) def _getServer(req): serverURL = req.get_options().get('KojiHubURL', 'http://localhost/kojihub') From a5125512a2eb6e4a7d41db5a8ee277457e9e0457 Mon Sep 17 00:00:00 2001 From: Mike Bonnet Date: Fri, 3 Oct 2008 12:54:03 -0400 Subject: [PATCH 43/56] remove some debug logging --- www/kojiweb/index.py | 1 - 1 file changed, 1 deletion(-) diff --git a/www/kojiweb/index.py b/www/kojiweb/index.py index ab831a60..2027e52f 100644 --- a/www/kojiweb/index.py +++ b/www/kojiweb/index.py @@ -112,7 +112,6 @@ def _genHTML(req, fileName): if not tmpl_class: tmpl_class = Cheetah.Template.Template.compile(file=fileName) TEMPLATES[fileName] = tmpl_class - mod_python.apache.log_error('Compiled template for ' + fileName) tmpl_inst = tmpl_class(namespaces=[req._values], filter=XHTMLFilter) return str(tmpl_inst) From 60d66b010abfc65e7a1eb4f58cbf1f05fce6bc02 Mon Sep 17 00:00:00 2001 From: Mike McLean Date: Fri, 3 Oct 2008 16:51:13 -0400 Subject: [PATCH 44/56] a couple changes to assist in theming move explicit image sizes to css make welcome message customizable --- www/kojiweb/includes/header.chtml | 2 +- www/kojiweb/index.chtml | 2 +- www/kojiweb/index.py | 1 + www/lib/kojiweb/util.py | 4 ++-- www/static/errors/unauthorized.html | 2 +- www/static/koji.css | 9 ++++++++- 6 files changed, 14 insertions(+), 6 deletions(-) diff --git a/www/kojiweb/includes/header.chtml b/www/kojiweb/includes/header.chtml index e000293a..a52268ef 100644 --- a/www/kojiweb/includes/header.chtml +++ b/www/kojiweb/includes/header.chtml @@ -43,7 +43,7 @@