draft builds
This commit is contained in:
parent
124450cec0
commit
87409499a3
30 changed files with 1763 additions and 186 deletions
|
|
@ -580,6 +580,8 @@ def handle_build(options, session, args):
|
|||
parser.add_option("--custom-user-metadata", type="str",
|
||||
help="Provide a JSON string of custom metadata to be deserialized and "
|
||||
"stored under the build's extra.custom_user_metadata field")
|
||||
parser.add_option("--draft", action="store_true",
|
||||
help="Build draft build instead")
|
||||
(build_opts, args) = parser.parse_args(args)
|
||||
if len(args) != 2:
|
||||
parser.error("Exactly two arguments (a build target and a SCM URL or srpm file) are "
|
||||
|
|
@ -588,6 +590,8 @@ def handle_build(options, session, args):
|
|||
parser.error("--no-/rebuild-srpm is only allowed for --scratch builds")
|
||||
if build_opts.arch_override and not build_opts.scratch:
|
||||
parser.error("--arch_override is only allowed for --scratch builds")
|
||||
if build_opts.scratch and build_opts.draft:
|
||||
parser.error("--scratch and --draft cannot be both specfied")
|
||||
custom_user_metadata = {}
|
||||
if build_opts.custom_user_metadata:
|
||||
try:
|
||||
|
|
@ -618,7 +622,7 @@ def handle_build(options, session, args):
|
|||
if build_opts.arch_override:
|
||||
opts['arch_override'] = koji.parse_arches(build_opts.arch_override)
|
||||
for key in ('skip_tag', 'scratch', 'repo_id', 'fail_fast', 'wait_repo', 'wait_builds',
|
||||
'rebuild_srpm'):
|
||||
'rebuild_srpm', 'draft'):
|
||||
val = getattr(build_opts, key)
|
||||
if val is not None:
|
||||
opts[key] = val
|
||||
|
|
@ -830,6 +834,8 @@ def handle_wrapper_rpm(options, session, args):
|
|||
parser.add_option("--nowait", action="store_false", dest="wait", help="Don't wait on build")
|
||||
parser.add_option("--background", action="store_true",
|
||||
help="Run the build at a lower priority")
|
||||
parser.add_option("--draft", action="store_true",
|
||||
help="Build draft build instead")
|
||||
|
||||
(build_opts, args) = parser.parse_args(args)
|
||||
if build_opts.inis:
|
||||
|
|
@ -839,6 +845,8 @@ def handle_wrapper_rpm(options, session, 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")
|
||||
if build_opts.scratch and build_opts.draft:
|
||||
parser.error("--scratch and --draft cannot be both specfied")
|
||||
activate_session(session, options)
|
||||
|
||||
target = args[0]
|
||||
|
|
@ -874,6 +882,8 @@ def handle_wrapper_rpm(options, session, args):
|
|||
opts['skip_tag'] = True
|
||||
if build_opts.scratch:
|
||||
opts['scratch'] = True
|
||||
if build_opts.draft:
|
||||
opts['draft'] = True
|
||||
task_id = session.wrapperRPM(build_id, url, target, priority, opts=opts)
|
||||
print("Created task: %d" % task_id)
|
||||
print("Task info: %s/taskinfo?taskID=%s" % (options.weburl, task_id))
|
||||
|
|
@ -1314,6 +1324,10 @@ def handle_import(goptions, session, args):
|
|||
parser.add_option("--test", action="store_true", help="Don't actually import")
|
||||
parser.add_option("--create-build", action="store_true", help="Auto-create builds as needed")
|
||||
parser.add_option("--src-epoch", help="When auto-creating builds, use this epoch")
|
||||
parser.add_option("--create-draft", action="store_true",
|
||||
help="Auto-create draft builds instead as needed")
|
||||
parser.add_option("--draft-build", metavar='NVR|ID',
|
||||
help="The target draft build to import to")
|
||||
(options, args) = parser.parse_args(args)
|
||||
if len(args) < 1:
|
||||
parser.error("At least one package must be specified")
|
||||
|
|
@ -1324,6 +1338,35 @@ def handle_import(goptions, session, args):
|
|||
options.src_epoch = int(options.src_epoch)
|
||||
except (ValueError, TypeError):
|
||||
parser.error("Invalid value for epoch: %s" % options.src_epoch)
|
||||
if options.create_draft:
|
||||
print("Will create draft build instead if desired nvr doesn't exist")
|
||||
options.create_build = True
|
||||
draft_build = None
|
||||
draft_target_nvr = None
|
||||
if options.draft_build:
|
||||
if options.create_build:
|
||||
parser.error(
|
||||
"To ensure no misuse, don't specify --draft-build while auto-creating."
|
||||
)
|
||||
try:
|
||||
draft_build = int(options.draft_build)
|
||||
except ValueError:
|
||||
draft_build = options.draft_build
|
||||
draft_build = session.getBuild(draft_build)
|
||||
if not draft_build:
|
||||
error("No such build: %s" % options.draft_build)
|
||||
if not draft_build.get('draft'):
|
||||
error("%s is not a draft build" % draft_build['nvr'])
|
||||
b_state = koji.BUILD_STATES[draft_build['state']]
|
||||
if b_state != 'COMPLETE':
|
||||
error("draft build %s is expected as COMPLETE, got %s" % (draft_build['nvr'], b_state))
|
||||
target_release = draft_build.get('extra', {}).get('draft', {}).get('target_release')
|
||||
if not target_release:
|
||||
error("Invalid draft build: %s,"
|
||||
" no draft.target_release found in extra" % draft_build['nvr'])
|
||||
draft_target_nvr = "%s-%s-%s" % (
|
||||
draft_build['name'], draft_build['version'], target_release
|
||||
)
|
||||
activate_session(session, goptions)
|
||||
to_import = {}
|
||||
for path in args:
|
||||
|
|
@ -1335,6 +1378,7 @@ def handle_import(goptions, session, args):
|
|||
else:
|
||||
nvr = "%(name)s-%(version)s-%(release)s" % koji.parse_NVRA(data['sourcerpm'])
|
||||
to_import.setdefault(nvr, []).append((path, data))
|
||||
|
||||
builds_missing = False
|
||||
nvrs = sorted(to_list(to_import.keys()))
|
||||
for nvr in nvrs:
|
||||
|
|
@ -1343,29 +1387,41 @@ def handle_import(goptions, session, args):
|
|||
if data['sourcepackage']:
|
||||
break
|
||||
else:
|
||||
if nvr == draft_target_nvr:
|
||||
continue
|
||||
# when no srpm and create_draft
|
||||
elif options.create_draft:
|
||||
print("Missing srpm for draft build creating with target nvr: %s" % nvr)
|
||||
builds_missing = True
|
||||
continue
|
||||
# no srpm included, check for build
|
||||
binfo = session.getBuild(nvr)
|
||||
if not binfo:
|
||||
print("Missing build or srpm: %s" % nvr)
|
||||
builds_missing = True
|
||||
if builds_missing and not options.create_build:
|
||||
if builds_missing and (not options.create_build or options.create_draft):
|
||||
print("Aborting import")
|
||||
return
|
||||
|
||||
# local function to help us out below
|
||||
def do_import(path, data):
|
||||
def do_import(path, data, draft_build=None):
|
||||
draft = bool(draft_build) or options.create_draft
|
||||
rinfo = dict([(k, data[k]) for k in ('name', 'version', 'release', 'arch')])
|
||||
prev = session.getRPM(rinfo)
|
||||
if prev and not prev.get('external_repo_id', 0):
|
||||
if prev['payloadhash'] == koji.hex_string(data['sigmd5']):
|
||||
print("RPM already imported: %s" % path)
|
||||
else:
|
||||
warn("md5sum mismatch for %s" % path)
|
||||
warn(" A different rpm with the same name has already been imported")
|
||||
warn(" Existing sigmd5 is %r, your import has %r" % (
|
||||
prev['payloadhash'], koji.hex_string(data['sigmd5'])))
|
||||
print("Skipping import")
|
||||
return
|
||||
if draft_build or not options.create_draft:
|
||||
opts = {}
|
||||
if draft_build:
|
||||
opts['build'] = draft_build
|
||||
prev = session.getRPM(rinfo, **opts)
|
||||
if prev and not prev.get('external_repo_id', 0):
|
||||
if prev['payloadhash'] == koji.hex_string(data['sigmd5']):
|
||||
print("RPM already imported: %s" % path)
|
||||
else:
|
||||
warn("md5sum mismatch for %s" % path)
|
||||
warn(" A different rpm with the same name has already been imported")
|
||||
warn(" Existing sigmd5 is %r, your import has %r" % (
|
||||
prev['payloadhash'], koji.hex_string(data['sigmd5'])))
|
||||
print("Skipping import")
|
||||
return
|
||||
if options.test:
|
||||
print("Test mode -- skipping import for %s" % path)
|
||||
return
|
||||
|
|
@ -1381,41 +1437,69 @@ def handle_import(goptions, session, args):
|
|||
sys.stdout.write("importing %s... " % path)
|
||||
sys.stdout.flush()
|
||||
try:
|
||||
session.importRPM(serverdir, os.path.basename(path))
|
||||
opts = {}
|
||||
if draft_build:
|
||||
opts['build'] = draft_build
|
||||
if draft:
|
||||
opts['draft'] = draft
|
||||
rpminfo = session.importRPM(serverdir, os.path.basename(path), **opts)
|
||||
except koji.GenericError as e:
|
||||
rpminfo = None
|
||||
print("\nError importing: %s" % str(e).splitlines()[-1])
|
||||
sys.stdout.flush()
|
||||
else:
|
||||
print("done")
|
||||
sys.stdout.flush()
|
||||
return rpminfo
|
||||
|
||||
for nvr in nvrs:
|
||||
# check for existing build
|
||||
need_build = True
|
||||
binfo = session.getBuild(nvr)
|
||||
if binfo:
|
||||
b_state = koji.BUILD_STATES[binfo['state']]
|
||||
if b_state == 'COMPLETE':
|
||||
need_build = False
|
||||
elif b_state in ['FAILED', 'CANCELED']:
|
||||
if not options.create_build:
|
||||
print("Build %s state is %s. Skipping import" % (nvr, b_state))
|
||||
is_draft = True
|
||||
if nvr == draft_target_nvr:
|
||||
binfo = draft_build
|
||||
need_build = False
|
||||
elif options.create_draft:
|
||||
binfo = None
|
||||
need_build = True
|
||||
else:
|
||||
is_draft = False
|
||||
binfo = session.getBuild(nvr)
|
||||
if binfo:
|
||||
b_state = koji.BUILD_STATES[binfo['state']]
|
||||
if b_state == 'COMPLETE':
|
||||
need_build = False
|
||||
elif b_state in ['FAILED', 'CANCELED']:
|
||||
if not options.create_build:
|
||||
print("Build %s state is %s. Skipping import" % (nvr, b_state))
|
||||
continue
|
||||
else:
|
||||
print("Build %s exists with state=%s. Skipping import" % (nvr, b_state))
|
||||
continue
|
||||
else:
|
||||
print("Build %s exists with state=%s. Skipping import" % (nvr, b_state))
|
||||
continue
|
||||
|
||||
# import srpms first, if any
|
||||
for path, data in to_import[nvr]:
|
||||
if data['sourcepackage']:
|
||||
if binfo and b_state != 'COMPLETE':
|
||||
# we can not fix state for draft build by createEmptyBuild
|
||||
if not is_draft and binfo and b_state != 'COMPLETE':
|
||||
# need to fix the state
|
||||
print("Creating empty build: %s" % nvr)
|
||||
b_data = koji.util.dslice(binfo, ['name', 'version', 'release'])
|
||||
b_data['epoch'] = data['epoch']
|
||||
session.createEmptyBuild(**b_data)
|
||||
binfo = session.getBuild(nvr)
|
||||
do_import(path, data)
|
||||
dbld = binfo if is_draft else None
|
||||
will_create = False
|
||||
if options.create_draft and not dbld:
|
||||
will_create = True
|
||||
print("Will create draft build with target nvr: %s while importing" % nvr)
|
||||
rpminfo = do_import(path, data, draft_build=dbld)
|
||||
# only needed for draft build, but
|
||||
# TODO: should be able to apply to regular import
|
||||
if rpminfo and rpminfo.get('build', {}).get('draft'):
|
||||
binfo = rpminfo['build']
|
||||
if will_create:
|
||||
print("Draft build: %s created" % binfo['nvr'])
|
||||
need_build = False
|
||||
|
||||
if need_build:
|
||||
|
|
@ -1424,11 +1508,11 @@ def handle_import(goptions, session, args):
|
|||
if binfo:
|
||||
# should have caught this earlier, but just in case...
|
||||
b_state = koji.BUILD_STATES[binfo['state']]
|
||||
print("Build %s state is %s. Skipping import" % (nvr, b_state))
|
||||
print("Build %s state is %s. Skipping import" % (binfo['nvr'], b_state))
|
||||
continue
|
||||
else:
|
||||
print("No such build: %s (include matching srpm or use "
|
||||
"--create-build option to add it)" % nvr)
|
||||
"--create-build/--create-draft option to add it)" % nvr)
|
||||
continue
|
||||
else:
|
||||
# let's make a new build
|
||||
|
|
@ -1439,17 +1523,22 @@ def handle_import(goptions, session, args):
|
|||
# pull epoch from first rpm
|
||||
data = to_import[nvr][0][1]
|
||||
b_data['epoch'] = data['epoch']
|
||||
if options.test:
|
||||
print("Test mode -- would have created empty build: %s" % nvr)
|
||||
if options.create_draft:
|
||||
b_data['draft'] = True
|
||||
b_display = "empty draft build with target nvr: %s" % nvr
|
||||
else:
|
||||
print("Creating empty build: %s" % nvr)
|
||||
session.createEmptyBuild(**b_data)
|
||||
binfo = session.getBuild(nvr)
|
||||
b_display = "empty build: %s" % nvr
|
||||
if options.test:
|
||||
print("Test mode -- would have created %s" % b_display)
|
||||
else:
|
||||
print("Creating %s" % b_display)
|
||||
buildid = session.createEmptyBuild(**b_data)
|
||||
binfo = session.getBuild(buildid)
|
||||
|
||||
for path, data in to_import[nvr]:
|
||||
if data['sourcepackage']:
|
||||
continue
|
||||
do_import(path, data)
|
||||
do_import(path, data, draft_build=binfo if is_draft else None)
|
||||
|
||||
|
||||
def handle_import_cg(goptions, session, args):
|
||||
|
|
@ -2727,11 +2816,15 @@ def anon_handle_list_tagged(goptions, session, args):
|
|||
parser.add_option("--ts", type='int', metavar="TIMESTAMP",
|
||||
help="query at last event before timestamp")
|
||||
parser.add_option("--repo", type='int', metavar="REPO#", help="query at event for a repo")
|
||||
parser.add_option("--draft-only", action="store_true", help="Only list draft builds/rpms")
|
||||
parser.add_option("--no-draft", action="store_true", help="Only list regular builds/rpms")
|
||||
(options, args) = parser.parse_args(args)
|
||||
if len(args) == 0:
|
||||
parser.error("A tag name must be specified")
|
||||
elif len(args) > 2:
|
||||
parser.error("Only one package name may be specified")
|
||||
if options.no_draft and options.draft_only:
|
||||
parser.error("--draft-only conflicts with --no-draft")
|
||||
ensure_connection(session, goptions)
|
||||
pathinfo = koji.PathInfo()
|
||||
package = None
|
||||
|
|
@ -2753,6 +2846,10 @@ def anon_handle_list_tagged(goptions, session, args):
|
|||
options.rpms = True
|
||||
if options.type:
|
||||
opts['type'] = options.type
|
||||
elif options.no_draft:
|
||||
opts['draft'] = koji.FLAG_REGULAR_BUILD
|
||||
elif options.draft_only:
|
||||
opts['draft'] = koji.FLAG_DRAFT_BUILD
|
||||
event = koji.util.eventFromOpts(session, options)
|
||||
event_id = None
|
||||
if event:
|
||||
|
|
@ -2798,7 +2895,9 @@ def anon_handle_list_tagged(goptions, session, args):
|
|||
fmt = "%(path)s"
|
||||
data = [x for x in data if 'path' in x]
|
||||
else:
|
||||
fmt = "%(name)s-%(version)s-%(release)s.%(arch)s"
|
||||
fmt = "%(name)s-%(version)s-%(release)s.%(arch)s%(draft_suffix)s"
|
||||
for x in data:
|
||||
x['draft_suffix'] = (' (#draft_%s)' % x['build_id']) if x.get('draft') else ''
|
||||
if options.sigs:
|
||||
fmt = "%(sigkey)s " + fmt
|
||||
else:
|
||||
|
|
@ -2861,10 +2960,13 @@ def anon_handle_list_buildroot(goptions, session, args):
|
|||
fmt = "%(nvr)s.%(arch)s"
|
||||
order = sorted([(fmt % x, x) for x in list_rpms])
|
||||
for nvra, rinfo in order:
|
||||
if options.verbose and rinfo.get('is_update'):
|
||||
print("%s [update]" % nvra)
|
||||
else:
|
||||
print(nvra)
|
||||
line = nvra
|
||||
if options.verbose:
|
||||
if rinfo.get('draft'):
|
||||
line += " (#draft_%s)" % rinfo['build_id']
|
||||
if rinfo.get('is_update'):
|
||||
line += " [update]"
|
||||
print(line)
|
||||
|
||||
list_archives = session.listArchives(**opts)
|
||||
if list_archives:
|
||||
|
|
@ -3403,6 +3505,8 @@ def anon_handle_list_builds(goptions, session, args):
|
|||
parser.add_option("--source", help="Only builds where the source field matches (glob pattern)")
|
||||
parser.add_option("--owner", help="List builds built by this owner")
|
||||
parser.add_option("--volume", help="List builds by volume ID")
|
||||
parser.add_option("--draft-only", action="store_true", help="Only list draft builds")
|
||||
parser.add_option("--no-draft", action="store_true", help="Only list regular builds")
|
||||
parser.add_option("-k", "--sort-key", action="append", metavar='FIELD',
|
||||
default=[], help="Sort the list by the named field. Allowed sort keys: "
|
||||
"build_id, owner_name, state")
|
||||
|
|
@ -3419,6 +3523,12 @@ def anon_handle_list_builds(goptions, session, args):
|
|||
value = getattr(options, key)
|
||||
if value is not None:
|
||||
opts[key] = value
|
||||
if options.no_draft and options.draft_only:
|
||||
parser.error("--draft-only conflits with --no-draft")
|
||||
elif options.no_draft:
|
||||
opts['draft'] = koji.FLAG_REGULAR_BUILD
|
||||
elif options.draft_only:
|
||||
opts['draft'] = koji.FLAG_DRAFT_BUILD
|
||||
if options.cg:
|
||||
opts['cgID'] = options.cg
|
||||
if options.package:
|
||||
|
|
@ -3520,13 +3630,24 @@ def anon_handle_rpminfo(goptions, session, args):
|
|||
parser = OptionParser(usage=get_usage_str(usage))
|
||||
parser.add_option("--buildroots", action="store_true",
|
||||
help="show buildroots the rpm was used in")
|
||||
parser.add_option("--build", metavar="NVR|ID",
|
||||
help="show the rpm(s) in the build")
|
||||
|
||||
(options, args) = parser.parse_args(args)
|
||||
if len(args) < 1:
|
||||
parser.error("Please specify an RPM")
|
||||
opts = {}
|
||||
build = options.build
|
||||
if options.build:
|
||||
try:
|
||||
build = int(build)
|
||||
except ValueError:
|
||||
pass
|
||||
opts['build'] = build
|
||||
ensure_connection(session, goptions)
|
||||
error_hit = False
|
||||
for rpm in args:
|
||||
info = session.getRPM(rpm)
|
||||
info = session.getRPM(rpm, **opts)
|
||||
if info is None:
|
||||
warn("No such rpm: %s\n" % rpm)
|
||||
error_hit = True
|
||||
|
|
@ -3536,24 +3657,29 @@ def anon_handle_rpminfo(goptions, session, args):
|
|||
else:
|
||||
info['epoch'] = str(info['epoch']) + ":"
|
||||
if not info.get('external_repo_id', 0):
|
||||
buildinfo = session.getBuild(info['build_id'])
|
||||
buildinfo['name'] = buildinfo['package_name']
|
||||
buildinfo['arch'] = 'src'
|
||||
if buildinfo['epoch'] is None:
|
||||
buildinfo['epoch'] = ""
|
||||
if info['arch'] == 'src':
|
||||
srpminfo = info.copy()
|
||||
else:
|
||||
buildinfo['epoch'] = str(buildinfo['epoch']) + ":"
|
||||
srpminfo = session.listRPMs(buildID=info['build_id'], arches='src')[0]
|
||||
if srpminfo['epoch'] is None:
|
||||
srpminfo['epoch'] = ""
|
||||
else:
|
||||
srpminfo['epoch'] = str(srpminfo['epoch']) + ":"
|
||||
buildinfo = session.getBuild(info['build_id'])
|
||||
print("RPM: %(epoch)s%(name)s-%(version)s-%(release)s.%(arch)s [%(id)d]" % info)
|
||||
if info.get('draft'):
|
||||
print("Draft: YES")
|
||||
if info.get('external_repo_id'):
|
||||
repo = session.getExternalRepo(info['external_repo_id'])
|
||||
print("External Repository: %(name)s [%(id)i]" % repo)
|
||||
print("External Repository url: %(url)s" % repo)
|
||||
else:
|
||||
print("Build: %(nvr)s [%(id)d]" % buildinfo)
|
||||
print("RPM Path: %s" %
|
||||
os.path.join(koji.pathinfo.build(buildinfo), koji.pathinfo.rpm(info)))
|
||||
print("SRPM: %(epoch)s%(name)s-%(version)s-%(release)s [%(id)d]" % buildinfo)
|
||||
print("SRPM: %(epoch)s%(name)s-%(version)s-%(release)s [%(id)d]" % srpminfo)
|
||||
print("SRPM Path: %s" %
|
||||
os.path.join(koji.pathinfo.build(buildinfo), koji.pathinfo.rpm(buildinfo)))
|
||||
os.path.join(koji.pathinfo.build(buildinfo), koji.pathinfo.rpm(srpminfo)))
|
||||
print("Built: %s" % time.strftime('%a, %d %b %Y %H:%M:%S %Z',
|
||||
time.localtime(info['buildtime'])))
|
||||
print("SIGMD5: %(payloadhash)s" % info)
|
||||
|
|
@ -3563,6 +3689,7 @@ def anon_handle_rpminfo(goptions, session, args):
|
|||
headers=["license"])
|
||||
if 'license' in headers:
|
||||
print("License: %(license)s" % headers)
|
||||
# kept for backward compatibility
|
||||
print("Build ID: %(build_id)s" % info)
|
||||
if info['buildroot_id'] is None:
|
||||
print("No buildroot data available")
|
||||
|
|
@ -3619,6 +3746,8 @@ def anon_handle_buildinfo(goptions, session, args):
|
|||
info['arch'] = 'src'
|
||||
info['state'] = koji.BUILD_STATES[info['state']]
|
||||
print("BUILD: %(name)s-%(version)s-%(release)s [%(id)d]" % info)
|
||||
if info.get('draft'):
|
||||
print("Draft: YES")
|
||||
print("State: %(state)s" % info)
|
||||
if info['state'] == 'BUILDING':
|
||||
print("Reserved by: %(cg_name)s" % info)
|
||||
|
|
|
|||
|
|
@ -925,3 +925,10 @@ class DatetimeJSONEncoder(json.JSONEncoder):
|
|||
if isinstance(o, xmlrpc_client.DateTime):
|
||||
return str(o)
|
||||
return json.JSONEncoder.default(self, o)
|
||||
|
||||
|
||||
def yesno(x):
|
||||
if x:
|
||||
return 'Y'
|
||||
else:
|
||||
return 'N'
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue