diff --git a/cli/koji b/cli/koji index 9487aec7..6bac23af 100755 --- a/cli/koji +++ b/cli/koji @@ -1071,6 +1071,11 @@ def handle_maven_build(options, session, args): parser.add_option("-M", "--maven-option", action="append", dest="maven_options", metavar="OPTION", default=[], help=_("Pass a command-line option to Maven")) + parser.add_option("--ini", action="append", + dest="inis", metavar="CONFIG", default=[], + help=_("Pass build parameters via a .ini file")) + parser.add_option("-s", "--section", + help=_("Get build parameters from this section of the .ini")) parser.add_option("--debug", action="store_true", help=_("Run Maven build in debug mode")) parser.add_option("--specfile", action="store", metavar="URL", @@ -1086,9 +1091,12 @@ def handle_maven_build(options, session, args): 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) != 2: - parser.error(_("Exactly two arguments (a build target and a SCM URL) are required")) - assert False + if build_opts.inis: + if len(args)!= 1: + parser.error(_("Exactly one argument (a build target) is required")) + else: + if len(args) != 2: + parser.error(_("Exactly two arguments (a build target and a SCM URL) are required")) activate_session(session) target = args[0] build_target = session.getBuildTarget(target) @@ -1099,34 +1107,25 @@ def handle_maven_build(options, session, args): parser.error(_("Unknown destination tag: %s" % build_target['dest_tag_name'])) if dest_tag['locked'] and not build_opts.scratch: parser.error(_("Destination tag %s is locked" % dest_tag['name'])) - source = args[1] - if '://' not in source: - parser.error(_("Invalid SCM URL: %s" % source)) - assert False - opts = {} + if build_opts.inis: + try: + params = koji.util.parse_maven_param(build_opts.inis, scratch=build_opts.scratch, + section=build_opts.section) + except ValueError, e: + parser.error(e.args[0]) + opts = params.values()[0] + if opts.pop('type', 'maven') != 'maven': + parser.error(_("Section %s does not contain a maven-build config") % params.keys()[0]) + source = opts.pop('scmurl') + else: + source = args[1] + if '://' not in source: + parser.error(_("Invalid SCM URL: %s" % source)) + opts = koji.util.maven_opts(build_opts, scratch=build_opts.scratch) if build_opts.debug: - build_opts.maven_options.append('--debug') - for key in ('skip_tag', 'scratch', 'specfile', 'patches', 'goals', 'profiles', 'packages', 'jvm_options', 'maven_options'): - val = getattr(build_opts, key) - if val: - opts[key] = val - props = {} - for prop in build_opts.properties: - fields = prop.split('=', 1) - if len(fields) != 2: - fields.append(None) - props[fields[0]] = fields[1] - if props: - opts['properties'] = props - envs = {} - for env in build_opts.envs: - fields = env.split('=', 1) - if len(fields) != 2: - parser.error(_("Environment variables must be in NAME=VALUE format")) - assert False - envs[fields[0]] = fields[1] - if envs: - opts['envs'] = envs + opts.setdefault('maven_options', []).append('--debug') + if build_opts.skip_tag: + opts['skip_tag'] = True priority = None if build_opts.background: #relative to koji.PRIO_DEFAULT @@ -1146,22 +1145,47 @@ def handle_wrapper_rpm(options, session, args): usage += _("\n(Specify the --help global option for a list of other help options)") parser = OptionParser(usage=usage) parser.add_option("--create-build", action="store_true", help=_("Create a new build to contain wrapper rpms")) + parser.add_option("--ini", action="append", + dest="inis", metavar="CONFIG", default=[], + help=_("Pass build parameters via a .ini file")) + parser.add_option("-s", "--section", + help=_("Get build parameters from this section of the .ini")) parser.add_option("--skip-tag", action="store_true", help=_("If creating a new build, don't tag it")) parser.add_option("--scratch", action="store_true", help=_("Perform a scratch build")) 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 + if build_opts.inis: + if len(args)!= 1: + parser.error(_("Exactly one argument (a build target) is required")) + else: + 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")) activate_session(session) target = args[0] - build_id = args[1] - if build_id.isdigit(): - build_id = int(build_id) - url = args[2] + if build_opts.inis: + try: + params = koji.util.parse_maven_param(build_opts.inis, scratch=build_opts.scratch, + section=build_opts.section) + except ValueError, e: + parser.error(e.args[0]) + opts = params.values()[0] + if opts.get('type') != 'wrapper': + parser.error(_("Section %s does not contain a wrapper-rpm config") % params.keys()[0]) + url = opts['scmurl'] + package = opts['buildrequires'][0] + target_info = session.getBuildTarget(target, strict=True) + latest_builds = session.getLatestBuilds(target_info['dest_tag'], package=package) + if not latest_builds: + parser.error(_("No build of %s in %s") % (package, target_info['dest_tag_name'])) + build_id = latest_builds[0]['nvr'] + else: + build_id = args[1] + if build_id.isdigit(): + build_id = int(build_id) + url = args[2] priority = None if build_opts.background: priority = 5 diff --git a/koji/util.py b/koji/util.py index 569eb05c..648c2935 100644 --- a/koji/util.py +++ b/koji/util.py @@ -460,65 +460,81 @@ def tsort(parts): raise ValueError, 'total ordering not possible' return result -def _maven_params(config, package, scratch=False): - params = {} - for name, value in config.items(package): - value = value.strip() - if not value: - continue - if name in ['scmurl', 'patches', 'specfile']: - # single-valued options - pass - elif name in ['buildrequires', 'goals', 'profiles', 'packages', - 'jvm_options', 'maven_options']: - # multi-valued options - value = value.split() - elif name == 'properties': - props = {} - for prop in value.splitlines(): - fields = prop.split('=', 1) - if len(fields) != 2: - fields.append(None) - props[fields[0]] = fields[1] - value = props - elif name == 'envs': - envs = {} - for env in value.splitlines(): - fields = env.split('=', 1) - if len(fields) != 2: - raise ValueError, "Environment variables must be in NAME=VALUE format" - envs[fields[0]] = fields[1] - value = envs - elif name == 'type': - continue - else: - raise ValueError, "Unknown option: %s" % name - params[name] = value - return params +class MavenConfigOptAdapter(object): + """ + Wrap a ConfigParser so it looks like a optparse.Values instance + used by maven-build. + """ + MULTILINE = ['properties', 'envs'] + MULTIVALUE = ['goals', 'profiles', 'packages', + 'jvm_options', 'maven_options', 'buildrequires'] -def _wrapper_params(config, package, scratch=False): + def __init__(self, conf, section): + self._conf = conf + self._section = section + + def __getattr__(self, name): + if self._conf.has_option(self._section, name): + value = self._conf.get(self._section, name) + if name in self.MULTIVALUE: + value = value.split() + elif name in self.MULTILINE: + value = value.splitlines() + return value + raise AttributeError, name + +def maven_opts(values, chain=False, scratch=False): + """ + Convert the argument (an optparse.Values object) to a dict of build options + suitable for passing to maven-build or maven-chain. + """ + opts = {} + for key in ('scmurl', 'patches', 'specfile', 'goals', 'profiles', 'packages', + 'jvm_options', 'maven_options'): + val = getattr(values, key, None) + if val: + opts[key] = val + props = {} + for prop in getattr(values, 'properties', []): + fields = prop.split('=', 1) + if len(fields) != 2: + fields.append(None) + props[fields[0]] = fields[1] + if props: + opts['properties'] = props + envs = {} + for env in getattr(values, 'envs', []): + fields = env.split('=', 1) + if len(fields) != 2: + raise ValueError, "Environment variables must be in NAME=VALUE format" + envs[fields[0]] = fields[1] + if envs: + opts['envs'] = envs + if chain: + val = getattr(values, 'buildrequires', []) + if val: + opts['buildrequires'] = val + if scratch and not chain: + opts['scratch'] = True + return opts + +def maven_params(config, package, chain=False, scratch=False): + values = MavenConfigOptAdapter(config, package) + return maven_opts(values, chain=chain, scratch=scratch) + +def wrapper_params(config, package, chain=False, scratch=False): params = {} - for name, value in config.items(package): - if name == 'scmurl': - pass - elif name == 'buildrequires': - value = value.split() - if len(value) != 1: - raise ValueError, "A wrapper-rpm must depend on exactly one package" - elif name == 'type': - pass - else: - raise ValueError, "Unknown option: %s" % name - params[name] = value + values = MavenConfigOptAdapter(config, package) + params['type'] = getattr(values, 'type', None) + params['scmurl'] = getattr(values, 'scmurl', None) + params['buildrequires'] = getattr(values, 'buildrequires', []) if not scratch: params['create_build'] = True return params -def parse_maven_chain(confs, scratch=False): +def parse_maven_params(confs, chain=False, scratch=False): """ - Parse maven-chain config. - - confs is a list of paths to config files. + Parse .ini files that contain parameters to launch a Maven build. Return a map whose keys are package names and values are config parameters. """ @@ -536,14 +552,49 @@ def parse_maven_chain(confs, scratch=False): if config.has_option(package, 'type'): buildtype = config.get(package, 'type') if buildtype == 'maven': - params = _maven_params(config, package, scratch) + params = maven_params(config, package, chain=chain, scratch=scratch) elif buildtype == 'wrapper': - params = _wrapper_params(config, package, scratch) + params = wrapper_params(config, package, chain=chain, scratch=scratch) + if len(params.get('buildrequires')) != 1: + raise ValueError, "A wrapper-rpm must depend on exactly one package" else: raise ValueError, "Unsupported build type: %s" % buildtype if not 'scmurl' in params: raise ValueError, "%s is missing the scmurl parameter" % package builds[package] = params + if not builds: + raise ValueError, "No sections found in: %s" % ', '.join(confs) + return builds + +def parse_maven_param(confs, chain=False, scratch=False, section=None): + """ + Parse .ini files that contain parameters to launch a Maven build. + + Return a map that contains a single entry corresponding to the given + section of the .ini file. If the config file only contains a single + section, section does not need to be specified. + """ + if not isinstance(confs, (list, tuple)): + confs = [confs] + builds = parse_maven_params(confs, chain=chain, scratch=scratch) + if section: + if section in builds: + builds = {section: builds[section]} + else: + raise ValueError, "Section %s does not exist in: %s" % (section, ', '.join(confs)) + elif len(builds) > 1: + raise ValueError, "Multiple sections in: %s, you must specify the section" % ', '.join(confs) + return builds + +def parse_maven_chain(confs, scratch=False): + """ + Parse maven-chain config. + + confs is a path to a config file or a list of paths to config files. + + Return a map whose keys are package names and values are config parameters. + """ + builds = parse_maven_params(confs, chain=True, scratch=scratch) depmap = {} for package, params in builds.items(): depmap[package] = set(params.get('buildrequires', []))