Allow wrapperRPM to be called as a subtask of a maven build of as a standalone top-level task (by an admin). Subsequent wrapperRPM calls will replace the set of rpms associated with an existing build.

This commit is contained in:
Mike Bonnet 2008-05-05 23:08:57 -04:00
parent 6fc2cca9ff
commit e7ce66059b
5 changed files with 239 additions and 87 deletions

View file

@ -2126,6 +2126,11 @@ class BuildMavenTask(BaseTaskHandler):
'jars': jars}
class WrapperRPMTask(BaseTaskHandler):
"""Build a wrapper rpm around jars output from a Maven build.
Can either be called as a subtask of a maven task or as a separate
top-level task. In the latter case it will permanently delete any
existing rpms associated with the build and replace them with the
newly-built rpms."""
Methods = ['wrapperRPM']
@ -2147,12 +2152,32 @@ class WrapperRPMTask(BaseTaskHandler):
def handler(self, spec_url, build_tag, build, task):
values = {}
jarnames = [filename for filename in session.listTaskOutput(task['id']) if \
filename.endswith('.jar')]
jars = []
for jarname in jarnames:
jars.append(os.path.join(koji.pathinfo.task(task['id']), jarname))
jars.sort()
if task:
# called as a subtask of a maven build
jarnames = [filename for filename in session.listTaskOutput(task['id']) if \
filename.endswith('.jar')]
jars = []
for jarname in jarnames:
jars.append(os.path.join(koji.pathinfo.task(task['id']), jarname))
jars.sort()
else:
# called as a top-level task to create or replace wrapper rpms on an existing build
# verify that the build is complete
if not build['state'] == koji.BUILD_STATES['COMPLETE']:
raise koji.BuildError, 'cannot call wrapperRPM on a build that did not complete successfully'
# get the list of jars from the build instead of the task, because the task output directory may
# have already been cleaned up
jarinfos = [archive for archive in session.listArchives(buildID=build['id'], type='maven') if \
archive['filename'].endswith('.jar')]
jars = []
for jarinfo in jarinfos:
jars.append(os.path.join(koji.pathinfo.mavenbuild(build, jarinfo), jarinfo['filename']))
jars.sort()
if not jars:
raise koji.BuildError, 'no jars found for %s' % (task and koji.taskLabel(task) or koji.buildLabel(build))
values['jars'] = [os.path.basename(jarpath) for jarpath in jars]
if build:
@ -2277,15 +2302,22 @@ class WrapperRPMTask(BaseTaskHandler):
for rpm in [srpm] + rpms:
self.uploadFile(os.path.join(resultdir, rpm))
results = {'buildroot_id': buildroot.id,
'srpm': srpm,
'rpms': rpms,
'logs': logs}
if not task:
# Called as a standalone top-level task, so import the rpms now.
# Otherwise we let the parent task handle it.
session.host.importWrapperRPMs(self.id, build['id'], results)
# no need to upload logs, they've already been streamed to the hub
# during the build process
buildroot.expire()
return {'buildroot_id': buildroot.id,
'srpm': srpm,
'rpms': rpms,
'logs': logs}
return results
class TagBuildTask(BaseTaskHandler):

View file

@ -846,6 +846,40 @@ def handle_maven_build(options, session, args):
session.logout()
return watch_tasks(session,[task_id])
def handle_wrapper_rpm(options, session, args):
"""[admin] Build wrapper rpms for any jars associated with the build. Any existing rpms will be deleted from the database and the filesystem."""
usage = _("usage: %prog wrapper-rpm [options] target build-id|n-v-r URL")
usage += _("\n(Specify the --help global option for a list of other help options)")
parser = OptionParser(usage=usage)
parser.add_option("--nowait", action="store_true", help=_("Don't wait on build"))
parser.add_option("--background", action="store_true", help=_("Run the build at a lower priority"))
(build_opts, args) = parser.parse_args(args)
if len(args) < 3:
parser.error(_("You must provide a build target, a build ID or NVR, and a SCM URL to a specfile fragment"))
assert False
activate_session(session)
if not session.hasPerm('admin'):
print "This action requires admin privileges"
return 1
target = args[0]
build_id = args[1]
if build_id.isdigit():
build_id = int(build_id)
url = args[2]
priority = None
if build_opts.background:
priority = 5
task_id = session.wrapperRPM(build_id, url, target, priority)
print "Created task:", task_id
print "Task info: %s/taskinfo?taskID=%s" % (options.weburl, task_id)
if _running_in_bg() or build_opts.nowait:
return
else:
session.logout()
return watch_tasks(session,[task_id])
def handle_resubmit(options, session, args):
"""Retry a canceled or failed task, using the same parameter as the original task."""
usage = _("usage: %prog resubmit [options] taskID")

View file

@ -2629,6 +2629,69 @@ def get_rpm(rpminfo,strict=False):
return None
return dict(zip(fields,row))
def list_rpms(buildID=None, buildrootID=None, componentBuildrootID=None, hostID=None, arches=None, queryOpts=None):
"""List RPMS. If buildID and/or buildrootID are specified,
restrict the list of RPMs to only those RPMs that are part of that
build, or were built in that buildroot. If componentBuildrootID is specified,
restrict the list to only those RPMs that will get pulled into that buildroot
when it is used to build another package. A list of maps is returned, each map
containing the following keys:
- id
- name
- version
- release
- nvr (synthesized for sorting purposes)
- arch
- epoch
- payloadhash
- size
- buildtime
- build_id
- buildroot_id
If componentBuildrootID is specified, two additional keys will be included:
- component_buildroot_id
- is_update
If no build has the given ID, or the build generated no RPMs,
an empty list is returned."""
fields = [('rpminfo.id', 'id'), ('rpminfo.name', 'name'), ('rpminfo.version', 'version'),
('rpminfo.release', 'release'),
("rpminfo.name || '-' || rpminfo.version || '-' || rpminfo.release", 'nvr'),
('rpminfo.arch', 'arch'),
('rpminfo.epoch', 'epoch'), ('rpminfo.payloadhash', 'payloadhash'),
('rpminfo.size', 'size'), ('rpminfo.buildtime', 'buildtime'),
('rpminfo.build_id', 'build_id'), ('rpminfo.buildroot_id', 'buildroot_id')]
joins = []
clauses = []
if buildID != None:
clauses.append('rpminfo.build_id = %(buildID)i')
if buildrootID != None:
clauses.append('rpminfo.buildroot_id = %(buildrootID)i')
if componentBuildrootID != None:
fields.append(('buildroot_listing.buildroot_id as component_buildroot_id',
'component_buildroot_id'))
fields.append(('buildroot_listing.is_update', 'is_update'))
joins.append('buildroot_listing ON rpminfo.id = buildroot_listing.rpm_id')
clauses.append('buildroot_listing.buildroot_id = %(componentBuildrootID)i')
if hostID != None:
joins.append('buildroot ON rpminfo.buildroot_id = buildroot.id')
clauses.append('buildroot.host_id = %(hostID)i')
if arches != None:
if isinstance(arches, list) or isinstance(arches, tuple):
clauses.append('rpminfo.arch IN %(arches)s')
elif isinstance(arches, str):
clauses.append('rpminfo.arch = %(arches)s')
else:
raise koji.GenericError, 'invalid type for "arches" parameter: %s' % type(arches)
query = QueryProcessor(columns=[f[0] for f in fields], aliases=[f[1] for f in fields],
tables=['rpminfo'], joins=joins, clauses=clauses,
values=locals(), opts=queryOpts)
return query.execute()
def get_maven_build(buildInfo=None, mavenInfo=None, strict=False):
"""
Retrieve Maven-specific information about a build.
@ -3464,6 +3527,22 @@ def import_build_in_place(build):
_dml(update,locals())
return build_id
def _import_wrapper(task_id, build_info, rpm_results):
"""Helper function to import wrapper rpms for a Maven build"""
rpm_buildroot_id = rpm_results['buildroot_id']
rpm_task_dir = koji.pathinfo.task(task_id)
for rpm_path in [rpm_results['srpm']] + rpm_results['rpms']:
rpm_path = os.path.join(rpm_task_dir, rpm_path)
rpm_info = import_rpm(rpm_path, build_info, rpm_buildroot_id)
import_rpm_file(rpm_path, build_info, rpm_info)
add_rpm_sig(rpm_info['id'], koji.rip_rpm_sighdr(rpm_path))
for log in rpm_results['logs']:
# assume we're only importing noarch packages
import_build_log(os.path.join(rpm_task_dir, log),
build_info, subdir='noarch')
def _get_archive_type_by_name(name, strict=True):
select = """SELECT id, name, description, extensions FROM archivetypes
WHERE name = %(name)s"""
@ -4587,6 +4666,36 @@ class RootExports(object):
return make_task('maven', [url, target, opts], **taskOpts)
def wrapperRPM(self, build, url, target, priority=None, channel='maven'):
"""Create a top-level wrapperRPM task
build: The build to generate wrapper rpms for. Must be in the COMPLETE state, and have
associated Maven jars.
url: SCM URL to a specfile fragment
target: The build target to use when building the wrapper rpm. The build_tag of the target will
be used to populate the buildroot in which the rpms are built.
priority: the amount to increase (or decrease) the task priority, relative
to the default priority; higher values mean lower priority; only
admins have the right to specify a negative priority here
channel: the channel to allocate the task to (defaults to the "maven" channel)
returns the task ID
"""
context.session.assertPerm('admin')
build = self.getBuild(build, strict=True)
build_target = self.getBuildTarget(target)
if not build_target:
raise koji.PreBuildError, 'no such build target: %s' % target
build_tag = self.getTag(build_target['build_tag'], strict=True)
taskOpts = {}
if priority:
taskOpts['priority'] = koji.PRIO_DEFAULT + priority
taskOpts['channel'] = channel
return make_task('wrapperRPM', [url, build_tag, build, None], **taskOpts)
def hello(self,*args):
return "Hello World"
@ -5296,68 +5405,7 @@ class RootExports(object):
mapping[int(key)] = mapping[key]
return readFullInheritance(tag,event,reverse,stops,jumps)
def listRPMs(self, buildID=None, buildrootID=None, componentBuildrootID=None, hostID=None, arches=None, queryOpts=None):
"""List RPMS. If buildID and/or buildrootID are specified,
restrict the list of RPMs to only those RPMs that are part of that
build, or were built in that buildroot. If componentBuildrootID is specified,
restrict the list to only those RPMs that will get pulled into that buildroot
when it is used to build another package. A list of maps is returned, each map
containing the following keys:
- id
- name
- version
- release
- nvr (synthesized for sorting purposes)
- arch
- epoch
- payloadhash
- size
- buildtime
- build_id
- buildroot_id
If componentBuildrootID is specified, two additional keys will be included:
- component_buildroot_id
- is_update
If no build has the given ID, or the build generated no RPMs,
an empty list is returned."""
fields = [('rpminfo.id', 'id'), ('rpminfo.name', 'name'), ('rpminfo.version', 'version'),
('rpminfo.release', 'release'),
("rpminfo.name || '-' || rpminfo.version || '-' || rpminfo.release", 'nvr'),
('rpminfo.arch', 'arch'),
('rpminfo.epoch', 'epoch'), ('rpminfo.payloadhash', 'payloadhash'),
('rpminfo.size', 'size'), ('rpminfo.buildtime', 'buildtime'),
('rpminfo.build_id', 'build_id'), ('rpminfo.buildroot_id', 'buildroot_id')]
joins = []
clauses = []
if buildID != None:
clauses.append('rpminfo.build_id = %(buildID)i')
if buildrootID != None:
clauses.append('rpminfo.buildroot_id = %(buildrootID)i')
if componentBuildrootID != None:
fields.append(('buildroot_listing.buildroot_id as component_buildroot_id',
'component_buildroot_id'))
fields.append(('buildroot_listing.is_update', 'is_update'))
joins.append('buildroot_listing ON rpminfo.id = buildroot_listing.rpm_id')
clauses.append('buildroot_listing.buildroot_id = %(componentBuildrootID)i')
if hostID != None:
joins.append('buildroot ON rpminfo.buildroot_id = buildroot.id')
clauses.append('buildroot.host_id = %(hostID)i')
if arches != None:
if isinstance(arches, list) or isinstance(arches, tuple):
clauses.append('rpminfo.arch IN %(arches)s')
elif isinstance(arches, str):
clauses.append('rpminfo.arch = %(arches)s')
else:
raise koji.GenericError, 'invalid type for "arches" parameter: %s' % type(arches)
query = QueryProcessor(columns=[f[0] for f in fields], aliases=[f[1] for f in fields],
tables=['rpminfo'], joins=joins, clauses=clauses,
values=locals(), opts=queryOpts)
return query.execute()
listRPMs = staticmethod(list_rpms)
def listBuildRPMs(self,build):
"""Get information about all the RPMs generated by the build with the given
@ -6841,20 +6889,7 @@ class HostExports(object):
build_info, subdir='maven2')
if rpm_results:
rpm_task_id = rpm_results['task_id']
rpm_buildroot_id = rpm_results['buildroot_id']
rpm_task_dir = koji.pathinfo.task(rpm_task_id)
for rpm_path in [rpm_results['srpm']] + rpm_results['rpms']:
rpm_path = os.path.join(rpm_task_dir, rpm_path)
rpm_info = import_rpm(rpm_path, build_info, rpm_buildroot_id)
import_rpm_file(rpm_path, build_info, rpm_info)
add_rpm_sig(rpm_info['id'], koji.rip_rpm_sighdr(rpm_path))
for log in rpm_results['logs']:
# assume we're only importing noarch packages
import_build_log(os.path.join(rpm_task_dir, log),
build_info, subdir='noarch')
_import_wrapper(rpm_results['task_id'], build_info, rpm_results)
# update build state
st_complete = koji.BUILD_STATES['COMPLETE']
@ -6865,6 +6900,51 @@ class HostExports(object):
# send email
build_notification(task_id, build_id)
def importWrapperRPMs(self, task_id, build_id, rpm_results):
"""Import the wrapper rpms and associate them with the given build. Any existing
rpms are deleted before import."""
host = Host()
host.verify()
task = Task(task_id)
task.assertHost(host.id)
build_info = get_build(build_id, strict=True)
if build_info['state'] != koji.BUILD_STATES['COMPLETE']:
raise koji.GenericError, 'cannot import wrapper rpms for %s: build state is %s, not complete' % \
(koji.buildLabel(build_info), koji.BUILD_STATES[build_info['state']].lower())
rpms = list_rpms(buildID=build_info['id'])
rpmfiles = []
for rpm in rpms:
# delete rpminfo from the database
# this will fail if the rpm has ever been used in a buildroot, which is what we want
delete = """DELETE FROM rpmdeps WHERE rpm_id=%(id)i"""
_dml(delete, rpm)
delete = """DELETE FROM rpmfiles WHERE rpm_id=%(id)i"""
_dml(delete, rpm)
delete = """DELETE FROM rpmsigs WHERE rpm_id=%(id)i"""
_dml(delete, rpm)
delete = """DELETE FROM rpminfo WHERE id=%(id)i"""
_dml(delete, rpm)
rpm_path = os.path.join(koji.pathinfo.build(build_info), koji.pathinfo.rpm(rpm))
rpmfiles.append(rpm_path)
for rpm_path in rpmfiles:
os.remove(rpm_path)
if not os.listdir(os.path.dirname(rpm_path)):
# also remove empty directories
os.removedirs(os.path.dirname(rpm_path))
for log in rpm_results['logs']:
# remove previously imported build logs too
log_path = os.path.join(koji.pathinfo.build_logs(build_info), 'noarch',
os.path.basename(log))
if os.path.exists(log_path):
os.remove(log_path)
_import_wrapper(task.id, build_info, rpm_results)
def failBuild(self, task_id, build_id):
"""Mark the build as failed. If the current state is not
'BUILDING', or the current competion_time is not null, a

View file

@ -1733,7 +1733,11 @@ def taskLabel(taskInfo):
elif method == 'wrapperRPM':
if taskInfo.has_key('request'):
build_tag = taskInfo['request'][1]
extra = build_tag['name']
build = taskInfo['request'][2]
if build:
extra = '%s, %s' % (build_tag['name'], buildLabel(build))
else:
extra = build_tag['name']
elif method == 'buildNotification':
if taskInfo.has_key('request'):
build = taskInfo['request'][1]

View file

@ -123,7 +123,9 @@ $value
#if $params[2]
<strong>Build:</strong> <a href="buildinfo?buildID=$params[2].id">$koji.buildLabel($params[2])</a><br/>
#end if
#if $params[3]
<strong>Task:</strong> <a href="taskinfo?taskID=$wrapTask.id">$koji.taskLabel($wrapTask)</a>
#end if
#elif $task.method == 'newRepo'
<strong>Tag:</strong> <a href="taginfo?tagID=$tag.id">$tag.name</a>
#elif $task.method == 'prepRepo'