#3069 basic kiwi support
Merged 2 years ago by tkopecek. Opened 2 years ago by tkopecek.
tkopecek/koji kiwi  into  master

file modified
+47
@@ -226,3 +226,50 @@ 

  Admins should consider the balance between the ``batch_size`` and

  ``extra_limit`` options, as both can affect the total amount of data that the

  plugin could attempt to send during a single call.

+ 

+ 

+ Image builds using Kiwi

+ =======================

+ 

+ **This is just a tech-preview. API/usage can drastically change in upcoming

+ releases**

+ 

+ Plugin for creating images via `kiwi <http://osinside.github.io/kiwi/>`_

+ project. Minimal supported version of kiwi is ``kiwi-9.24.2``.

+ 

+ All three parts (cli/hub/builder) needs to be installed. There is currently no

+ configuration except allowing the plugins (name is 'kiwi' for all components).

+ 

+ Builders have to be part of ``image`` channel and don't need to have any

+ specific library installed (kiwi invocation/usage is only in buildroots not on

+ builder itself). (Temporarily ``python3-kiwi`` needs to be installed on builder

+ for kojid to be able to parse kiwi output. It will be changed to json in next

+ version and this requirement will be dropped.)

+ 

+ Buildtag needs to be configured by adding special group ``kiwi`` which should

+ contain at least ``kiwi-cli``, potentially ``jing`` for better description files

+ validation and any ``kiwi-systemdeps-*`` packages for requested image types. So,

+ most simple configuration will look like:

+ 

+ .. code-block:: shell

+ 

+    $ koji add-group kiwi-build-tag kiwi

+    $ koji add-group-pkg kiwi-build-tag kiwi kiwi-cli

+ 

+ Another thing we need to ensure is that we're building in chroot and not in

+ container.

+ 

+ .. code-block:: shell

+ 

+    $ koji edit-tag kiwi-build-tag -x mock.new_chroot=False

+ 

+ Calling the build itself is a matter of simple CLI call:

+ 

+ .. code-block: shell

+ 

+    $ koji kiwi-build kiwi-target git+https://my.git/image-descriptions#master my_image_path

+ 

+ Selecting other than default kiwi profile can be done by ``--kiwi-profile``

+ option. Similarly to other image tasks, alternative architecture failures can be

+ ignored for successful build by ``--can-fail`` option. ``--arch`` can be used to

+ limit build tag architectures.

@@ -0,0 +1,424 @@ 

+ import glob

+ # import json

+ import os

+ import xml.dom.minidom

+ from fnmatch import fnmatch

+ 

+ import koji

+ from koji.util import joinpath, to_list

+ from koji.tasks import ServerExit

+ from __main__ import BaseBuildTask, BuildImageTask, BuildRoot, SCM

+ 

+ 

+ class KiwiBuildTask(BuildImageTask):

+     Methods = ['kiwiBuild']

+     _taskWeight = 4.0

+ 

+     def get_nvrp(self, desc_path):

+         # TODO: update release in desc

+         kiwi_files = glob.glob('%s/*.kiwi' % desc_path)

+         if len(kiwi_files) != 1:

+             raise koji.GenericError("Repo must contain only one .kiwi file.")

+ 

+         cfg = kiwi_files[0]

+ 

+         newxml = xml.dom.minidom.parse(cfg)  # nosec

+         image = newxml.getElementsByTagName('image')[0]

+ 

+         name = image.getAttribute('name')

+         version = None

+         release = None

+         for preferences in image.getElementsByTagName('preferences'):

+             try:

+                 version = preferences.getElementsByTagName('version')[0].childNodes[0].data

+             except Exception:

+                 pass

+             try:

+                 release = preferences.getElementsByTagName('release')[0].childNodes[0].data

+             except Exception:

+                 release = None

+         profile = None

+         try:

+             for p in image.getElementsByTagName('profiles')[0].getElementsByTagName('profile'):

+                 if p.getAttribute('image') == 'true':

+                     profile = p.getAttribute('name')

+         except IndexError:

+             # missing profiles section

+             pass

+         if not version:

+             raise koji.BuildError("Description file doesn't contain preferences/version")

+         return name, version, release, profile

+ 

+     def handler(self, target, arches, desc_url, desc_path, opts=None):

+         target_info = self.session.getBuildTarget(target, strict=True)

+         build_tag = target_info['build_tag']

+         repo_info = self.getRepo(build_tag)

+         # check requested arches against build tag

+         buildconfig = self.session.getBuildConfig(build_tag)

+         if not buildconfig['arches']:

+             raise koji.BuildError("No arches for tag %(name)s [%(id)s]" % buildconfig)

+         tag_archlist = [koji.canonArch(a) for a in buildconfig['arches'].split()]

+         if arches:

+             for arch in arches:

+                 if koji.canonArch(arch) not in tag_archlist:

+                     raise koji.BuildError("Invalid arch for build tag: %s" % arch)

+         else:

+             arches = tag_archlist

+ 

+         if not opts:

+             opts = {}

+         if not opts.get('scratch'):

+             opts['scratch'] = False

+         if not opts.get('optional_arches'):

+             opts['optional_arches'] = []

+         self.opts = opts

+ 

+         # get configuration

+         scm = SCM(desc_url)

+         scm.assert_allowed(allowed=self.options.allowed_scms,

+                            session=self.session,

+                            by_config=self.options.allowed_scms_use_config,

+                            by_policy=self.options.allowed_scms_use_policy,

+                            policy_data={

+                                'user_id': self.taskinfo['owner'],

+                                'channel': self.session.getChannel(self.taskinfo['channel_id'],

+                                                                   strict=True)['name'],

+                                'scratch': opts['scratch'],

+                            })

+         logfile = os.path.join(self.workdir, 'checkout.log')

+         self.run_callbacks('preSCMCheckout', scminfo=scm.get_info(),

+                            build_tag=build_tag, scratch=opts['scratch'])

+         scmdir = self.workdir

+         koji.ensuredir(scmdir)

+         scmsrcdir = scm.checkout(scmdir, self.session,

+                                  self.getUploadDir(), logfile)

+         self.run_callbacks("postSCMCheckout",

+                            scminfo=scm.get_info(),

+                            build_tag=build_tag,

+                            scratch=opts['scratch'],

+                            srcdir=scmsrcdir)

+ 

+         path = os.path.join(scmsrcdir, desc_path)

+ 

+         name, version, release, default_profile = self.get_nvrp(path)

+         if opts.get('profile') or default_profile:

+             # package name is a combination of name + profile

+             # in case profiles are not used, let's use the standalone name

+             name = "%s-%s" % (name, opts.get('profile', default_profile))

+ 

+         bld_info = {}

+         if not opts['scratch']:

+             bld_info = self.initImageBuild(name, version, release, target_info, opts)

+             release = bld_info['release']

+         elif not release:

+             release = self.session.getNextRelease({'name': name, 'version': version})

+ 

+         try:

+             subtasks = {}

+             canfail = []

+             self.logger.debug("Spawning jobs for image arches: %r" % (arches))

+             for arch in arches:

+                 subtasks[arch] = self.session.host.subtask(

+                     method='createKiwiImage',

+                     arglist=[name, version, release, arch,

+                              target_info, build_tag, repo_info,

+                              desc_url, desc_path, opts],

+                     label=arch, parent=self.id, arch=arch)

+                 if arch in self.opts['optional_arches']:

+                     canfail.append(subtasks[arch])

+             self.logger.debug("Got image subtasks: %r" % (subtasks))

+             self.logger.debug("Waiting on image subtasks (%s can fail)..." % canfail)

+             results = self.wait(to_list(subtasks.values()), all=True,

+                                 failany=True, canfail=canfail)

+ 

+             # if everything failed, fail even if all subtasks are in canfail

+             self.logger.debug('subtask results: %r', results)

+             all_failed = True

+             for result in results.values():

+                 if not isinstance(result, dict) or 'faultCode' not in result:

+                     all_failed = False

+                     break

+             if all_failed:

+                 raise koji.GenericError("all subtasks failed")

+ 

+             # determine ignored arch failures

+             ignored_arches = set()

+             for arch in arches:

+                 if arch in self.opts['optional_arches']:

+                     task_id = subtasks[arch]

+                     result = results[task_id]

+                     if isinstance(result, dict) and 'faultCode' in result:

+                         ignored_arches.add(arch)

+ 

+             self.logger.debug('Image Results for hub: %s' % results)

+             results = {str(k): v for k, v in results.items()}

+             if opts['scratch']:

+                 self.session.host.moveImageBuildToScratch(self.id, results)

+             else:

+                 self.session.host.completeImageBuild(self.id, bld_info['id'], results)

+         except (SystemExit, ServerExit, KeyboardInterrupt):

+             # we do not trap these

+             raise

+         except Exception:

+             if not opts['scratch']:

+                 if bld_info:

+                     self.session.host.failBuild(self.id, bld_info['id'])

+             raise

+ 

+         # tag it

+         if not opts['scratch'] and not opts.get('skip_tag'):

+             tag_task_id = self.session.host.subtask(method='tagBuild',

+                                                     arglist=[target_info['dest_tag'],

+                                                              bld_info['id'], False, None, True],

+                                                     label='tag', parent=self.id, arch='noarch')

+             self.wait(tag_task_id)

+ 

+         # report results

+         report = ''

+         if opts['scratch']:

+             respath = ', '.join(

+                 [os.path.join(koji.pathinfo.work(),

+                               koji.pathinfo.taskrelpath(tid)) for tid in subtasks.values()])

+             report += 'Scratch '

+         else:

+             respath = koji.pathinfo.imagebuild(bld_info)

+         report += 'image build results in: %s' % respath

+         return report

+ 

+ 

+ class KiwiCreateImageTask(BaseBuildTask):

+     Methods = ['createKiwiImage']

+     _taskWeight = 2.0

+ 

+     def prepareDescription(self, desc_path, name, release, repos):

+         # TODO: update release in desc

+         kiwi_files = glob.glob('%s/*.kiwi' % desc_path)

+         if len(kiwi_files) != 1:

+             raise koji.GenericError("Repo must contain only one .kiwi file.")

+ 

+         cfg = kiwi_files[0]

+ 

+         newxml = xml.dom.minidom.parse(cfg)  # nosec

+         image = newxml.getElementsByTagName('image')[0]

+ 

+         # apply includes - kiwi can include only top-level nodes, so we can simply

+         # go through "include" elements and replace them with referred content (without

+         # doing it recursively)

+         for inc_node in image.getElementsByTagName('include'):

+             path = inc_node.getAttribute('from')

+             inc = xml.dom.minidom.parse(path)  # nosec

+             # every included xml has image root element again

+             for node in inc.getElementsByTagName('image').childNodes:

+                 if node.nodeName != 'repository':

+                     image.appendChild(node)

+ 

+         # remove remaining old repos

+         for old_repo in image.getElementsByTagName('repository'):

+             image.removeChild(old_repo)

+ 

+         # add koji ones

+         for repo in sorted(set(repos)):

+             repo_node = newxml.createElement('repository')

+             repo_node.setAttribute('type', 'rpm-md')

+             source = newxml.createElement('source')

+             source.setAttribute('path', repo)

+             repo_node.appendChild(source)

+             image.appendChild(repo_node)

+ 

+         image.setAttribute('name', name)

+         # TODO: release is part of version (major.minor.release)

+         # preferences = image.getElementsByTagName('preferences')[0]

+         # try:

+         #    preferences.getElementsByTagName('release')[0].childNodes[0].data = release

+         # except Exception:

+         #    rel_node = newxml.createElement('release')

+         #    rel_node.data = release

+         #    preferences.appendChild(rel_node)

+ 

+         types = []

+         for pref in image.getElementsByTagName('preferences'):

+             for type in pref.getElementsByTagName('type'):

+                 # TODO: if type.getAttribute('primary') == 'true':

+                 types.append(type.getAttribute('image'))

+ 

+         # write file back

+         with open(cfg, 'wt') as f:

+             s = newxml.toprettyxml()

+             # toprettyxml adds too many whitespaces/newlines

+             s = '\n'.join([x for x in s.splitlines() if x.strip()])

+             f.write(s)

+ 

+         return cfg, types

+ 

+     def getImagePackagesFromCache(self, cachepath):

+         """

+         Read RPM header information from the yum cache available in the

+         given path. Returns a list of dictionaries for each RPM included.

+         """

+         found = False

+         hdrlist = {}

+         fields = ['name', 'version', 'release', 'epoch', 'arch',

+                   'buildtime', 'sigmd5']

+         for root, dirs, files in os.walk(cachepath):

+             for f in files:

+                 if fnmatch(f, '*.rpm'):

+                     pkgfile = os.path.join(root, f)

+                     hdr = koji.get_header_fields(pkgfile, fields)

+                     hdr['size'] = os.path.getsize(pkgfile)

+                     hdr['payloadhash'] = koji.hex_string(hdr['sigmd5'])

+                     del hdr['sigmd5']

+                     hdrlist[os.path.basename(pkgfile)] = hdr

+                     found = True

+         if not found:

+             raise koji.LiveCDError('No repos found in yum cache!')

+         return list(hdrlist.values())

+ 

+     def getImagePackages(self, result):

+         """Proper handler for getting rpminfo from result list,

+         it need result list to contain payloadhash, etc. to work correctly"""

+         hdrlist = []

+         for line in open(result, 'rt'):

+             line = line.strip()

+             name, epoch, version, release, arch, disturl, license = line.split('|')

+             if epoch == '(none)':

+                 epoch = None

+             else:

+                 epoch = int(epoch)

+             hdrlist.append({

+                 'name': name,

+                 'epoch': epoch,

+                 'version': version,

+                 'release': release,

+                 'arch': arch,

+                 'payloadhash': '',

+                 'size': 0,

+                 'buildtime': 0,

+             })

+ 

+         return hdrlist

+ 

+     def handler(self, name, version, release, arch,

+                 target_info, build_tag, repo_info,

+                 desc_url, desc_path, opts=None):

+         self.opts = opts

+         build_tag = target_info['build_tag']

+         broot = BuildRoot(self.session, self.options,

+                           tag=build_tag,

+                           arch=arch,

+                           task_id=self.id,

+                           repo_id=repo_info['id'],

+                           install_group='kiwi',

+                           setup_dns=True,

+                           bind_opts={'dirs': {'/dev': '/dev', }})

+         broot.workdir = self.workdir

+ 

+         # create the mock chroot

+         self.logger.debug("Initializing kiwi buildroot")

+         broot.init()

+         self.logger.debug("Kiwi buildroot ready: " + broot.rootdir())

+ 

+         # get configuration

+         scm = SCM(desc_url)

+         scm.assert_allowed(allowed=self.options.allowed_scms,

+                            session=self.session,

+                            by_config=self.options.allowed_scms_use_config,

+                            by_policy=self.options.allowed_scms_use_policy,

+                            policy_data={

+                                'user_id': self.taskinfo['owner'],

+                                'channel': self.session.getChannel(self.taskinfo['channel_id'],

+                                                                   strict=True)['name'],

+                                'scratch': self.opts.get('scratch')

+                            })

+         logfile = os.path.join(self.workdir, 'checkout-%s.log' % arch)

+         self.run_callbacks('preSCMCheckout', scminfo=scm.get_info(),

+                            build_tag=build_tag, scratch=self.opts.get('scratch'))

+         scmdir = broot.tmpdir()

+         koji.ensuredir(scmdir)

+         scmsrcdir = scm.checkout(scmdir, self.session,

+                                  self.getUploadDir(), logfile)

+         self.run_callbacks("postSCMCheckout",

+                            scminfo=scm.get_info(),

+                            build_tag=build_tag,

+                            scratch=self.opts.get('scratch'),

+                            srcdir=scmsrcdir)

+ 

+         # user repos

+         repos = self.opts.get('repos', [])

+         # buildroot repo

+         path_info = koji.PathInfo(topdir=self.options.topurl)

+         repopath = path_info.repo(repo_info['id'], target_info['build_tag_name'])

+         baseurl = '%s/%s' % (repopath, arch)

+         self.logger.debug('BASEURL: %s' % baseurl)

+         repos.append(baseurl)

+ 

+         path = os.path.join(scmsrcdir, desc_path)

+         desc, types = self.prepareDescription(path, name, release, repos)

+         self.uploadFile(desc)

+ 

+         cmd = ['kiwi-ng']

+         if self.opts.get('profile'):

+             cmd.extend(['--profile', self.opts['profile']])

+         target_dir = '/builddir/result/image'

+         cmd.extend([

+             'system', 'build',

+             '--description', os.path.join(os.path.basename(scmsrcdir), desc_path),

+             '--target-dir', target_dir,

+         ])

+         rv = broot.mock(['--cwd', broot.tmpdir(within=True), '--chroot', '--'] + cmd)

+         if rv:

+             raise koji.GenericError("Kiwi failed")

+ 

+         # result = json.load(open(joinpath(broot.rootdir(), target_dir[1:], 'kiwi.result'), 'rb'))

+         # nosec comment - we will replace it with json ASAP

+         import pickle

+         result = pickle.load(open(joinpath(broot.rootdir(), target_dir[1:],  # nosec

+                                            'kiwi.result'), 'rb'))

+ 

+         imgdata = {

+             'arch': arch,

+             'task_id': self.id,

+             'logs': [

+                 os.path.basename(desc),

+             ],

+             'name': name,

+             'version': version,

+             'release': release,

+             'rpmlist': [],

+             'files': [],

+         }

+ 

+         # TODO: upload detailed log?

+         # build/image-root.log

+         root_log_path = os.path.join(broot.tmpdir(), target_dir[1:], "build/image-root.log")

+         if os.path.exists(root_log_path):

+             self.uploadFile(root_log_path, remoteName="image-root.log")

+ 

+         # for type in types:

+         #     img_file = '%s.%s-%s.%s' % (name, version, arch, type)

+         #     self.uploadFile(os.path.join(broot.rootdir()), remoteName=img_file)

+         #     imgdata['files'].append(img_file)

+         for ftype in ('disk_format_image', 'installation_image'):

+             fdata = result.result_files.get(ftype)

+             if not fdata:

+                 continue

+             fpath = os.path.join(broot.rootdir(), fdata.filename[1:])

+             img_file = os.path.basename(fpath)

+             self.uploadFile(fpath, remoteName=os.path.basename(img_file))

+             imgdata['files'].append(img_file)

+ 

+         if not self.opts.get('scratch'):

+             if False:

+                 # should be used after kiwi update

+                 fpath = os.path.join(broot.rootdir(),

+                                      result['result_files']['image_packages'].filename[1:])

+                 hdrlist = self.getImagePackages(fpath)

+             else:

+                 cachepath = os.path.join(broot.rootdir(), 'var/cache/kiwi/dnf')

+                 hdrlist = self.getImagePackagesFromCache(cachepath)

+             broot.markExternalRPMs(hdrlist)

+             imgdata['rpmlist'] = hdrlist

+ 

+         broot.expire()

+ 

+         self.logger.error("Uploading image data: %s", imgdata)

+         return imgdata

file added
+72
@@ -0,0 +1,72 @@ 

+ from optparse import OptionParser

I think in 2021 it is better to use argparse :) Curious when optparse will be removed from stdlib…

+ 

+ from koji import canonArch

+ 

+ from koji.plugin import export_cli

+ from koji_cli.lib import (

+     _running_in_bg,

+     activate_session,

+     watch_tasks,

+ )

+ 

+ 

+ @export_cli

+ def handle_kiwi_build(goptions, session, args):

+     "[build] Run a command in a buildroot"

+     usage = "usage: %prog kiwi-build [options] <target> <description_scm> <description_path>"

+     usage += "\n(Specify the --help global option for a list of other help options)"

+     parser = OptionParser(usage=usage)

+     parser.add_option("--scratch", action="store_true", default=False,

+                       help="Perform a scratch build")

+     parser.add_option("--repo", action="append",

+                       help="Specify a repo that will override the repo used to install "

+                            "RPMs in the image. May be used multiple times. The "

+                            "build tag repo associated with the target is the default.")

+     parser.add_option("--noprogress", action="store_true",

+                       help="Do not display progress of the upload")

+     parser.add_option("--kiwi-profile", action="store", default=None,

+                       help="Select profile from description file")

+     parser.add_option("--can-fail", action="store", dest="optional_arches",

+                       metavar="ARCH1,ARCH2,...", default="",

+                       help="List of archs which are not blocking for build "

+                            "(separated by commas.")

+     parser.add_option("--arch", action="append", dest="arches", default=[],

+                       help="Limit arches to this subset")

+     parser.add_option("--nowait", action="store_false", dest="wait", default=True)

+     parser.add_option("--wait", action="store_true",

+                       help="Wait on the image creation, even if running in the background")

+     (options, args) = parser.parse_args(args)

+ 

+     if len(args) != 3:

+         parser.error("Incorrect number of arguments")

+         assert False  # pragma: no cover

+     target, scm, path = args

+ 

+     activate_session(session, goptions)

+ 

+     kwargs = {

+         'scratch': options.scratch,

+         'optional_arches': [canonArch(arch)

+                             for arch in options.optional_arches.split(',')

+                             if arch],

+         'profile': options.kiwi_profile,

+     }

+ 

+     arches = []

+     if options.arches:

+         arches = [canonArch(arch) for arch in options.arches]

+ 

+     task_id = session.kiwiBuild(

+         target=target,

+         arches=arches,

+         desc_url=scm,

+         desc_path=path,

+         **kwargs)

+ 

+     if not goptions.quiet:

+         print("Created task: %d" % task_id)

+         print("Task info: %s/taskinfo?taskID=%s" % (goptions.weburl, task_id))

+     if options.wait or (options.wait is None and not _running_in_bg()):

+         session.logout()

+         return watch_tasks(session, [task_id], quiet=goptions.quiet,

+                            poll_interval=goptions.poll_interval, topurl=goptions.topurl)

file added
+38
@@ -0,0 +1,38 @@ 

+ import koji

+ import koji.tasks

+ import kojihub

+ 

+ from koji.context import context

+ from koji.plugin import export

+ 

+ koji.tasks.LEGACY_SIGNATURES['kiwiBuild'] = [

+     [['target', 'arches', 'desc_url', 'desc_path', 'opts'],

+      None, None, (None,)]]

+ koji.tasks.LEGACY_SIGNATURES['createKiwiImage'] = [

+     [['name', 'version', 'release', 'arch',

+       'target_info', 'build_tag', 'repo_info', 'desc_url', 'desc_path', 'opts'],

+      None, None, (None,)]]

+ 

+ 

+ @export

+ def kiwiBuild(target, arches, desc_url, desc_path, optional_arches=None, profile=None,

+               scratch=False, priority=None):

+     context.session.assertPerm('image')

+     taskOpts = {

+         'channel': 'image',

+     }

+     if priority:

+         if priority < 0:

+             if not context.session.hasPerm('admin'):

+                 raise koji.ActionNotAllowed(

+                     'only admins may create high-priority tasks')

+         taskOpts['priority'] = koji.PRIO_DEFAULT + priority

+ 

+     opts = {

+         'optional_arches': optional_arches,

+         'profile': profile,

+         'scratch': scratch,

+     }

+     return kojihub.make_task('kiwiBuild',

+                              [target, arches, desc_url, desc_path, opts],

+                              **taskOpts)

%SUBJ% - work in progress

We should probably capture all the logs, like we do for other image build tasks...

Is it possible to make it so that Koji would tail the log files while the build is running? That way it's possible to see the detailed execution while it's happening...

Is it possible to make it so that Koji would tail the log files while the build is running? That way it's possible to see the detailed execution while it's happening...

yes, it is being uploaded as for other tasks (broot does that automatically on background)

1 new commit added

  • json results
2 years ago

Metadata Update from @julian8628:
- Pull-request tagged with: testing-ready

2 years ago

2 new commits added

  • kiwi: check include directive
  • basic kiwi docs
2 years ago

This should probably be called "Image builds using kiwi"

Would it make sense documenting a minimum version for this? kiwi-9.24.2 will have the JSON results feature, and possibly also the --releasever support...

1 new commit added

  • update docs
2 years ago

rebased onto aba798cb7b14ba9a67274282010518281256d7e6

2 years ago

1 new commit added

  • use json results
2 years ago

can we actually do it (possibly optionally?) other way around? generate version in koji using some kind of getNextRelease() like we do for some other types and then pass it to xml?

results = {str(k): v for k, v in results.items()}

I did not have chance to test this PR, but from code it seems that the "NVR" includes only name, version and release but not profile which would mean that you essentially can't build/tag same image with different profiles. Is my understanding correct?

Yeah, the "name" recorded by Koji should probably include the profile too...

instead of sorting those repos, would not it make sense to mirror koji's repository inheritance (with weight)?
From kiwi documentation it supports repository priorities:

priority: An integer priority for all packages in this repository. If the same package is available in more than one repository, then the one with the highest priority is used.

wouldn't it be better to just compare it with (none) and otherwise just convert it to int()?

but not all of them are external? I mean, if some "external" repos are used in build tag, some of them would be... but otherwise some RPMs are directly taken from koji. Or is my assumption wrong?

I think in 2021 it is better to use argparse :) Curious when optparse will be removed from stdlib…

can we actually do it (possibly optionally?) other way around? generate version in koji using some kind of getNextRelease() like we do for some other types and then pass it to xml?

I understand kiwi's file more like a spec. If nvr is already there, maintainer should bump it in description file if he wants higher release. It seems more right to me than have some confusing commit with nvrA and resulting build with nvrB.

can we actually do it (possibly optionally?) other way around? generate version in koji using some kind of getNextRelease() like we do for some other types and then pass it to xml?

I understand kiwi's file more like a spec. If nvr is already there, maintainer should bump it in description file if he wants higher release. It seems more right to me than have some confusing commit with nvrA and resulting build with nvrB.

Fair point, but being able to rewrite that for snapshot releases is probably useful.

I think in 2021 it is better to use argparse :) Curious when optparse will be removed from stdlib…

I would stay with optparse for now. We will have to make a leap with whole codebase to argparse or something else (migrate CLI commands to click (or whatever else)). Until that it is better to have same lib everywhere.

but not all of them are external? I mean, if some "external" repos are used in build tag, some of them would be... but otherwise some RPMs are directly taken from koji. Or is my assumption wrong?

markExternalRPMS is a helper function which will find external rpms in the list and alter their buildinfos with link to external repo, etc. It is used in the same way for all archive/image builds. So, yes, most rpms are expected to be from koji directly.

instead of sorting those repos, would not it make sense to mirror koji's repository inheritance (with weight)?
From kiwi documentation it supports repository priorities:

priority: An integer priority for all packages in this repository. If the same package is available in more than one repository, then the one with the highest priority is used.

Sorting there is just for human-reading convenience. It doesn't have any impact. We're not using priorities anywhere now. But I'm not against adding it here. Question would be - what is the priority of buildroot repo vs. repos which are supplied by the user? I was thinking if even --repo options should exist here if we shouldn't limit it to buildroot repo itself. But as for other image types it is probably useful to have it configurable. But it similarly to those open door to "untracked" content not present in koji.

Yeah, the "name" recorded by Koji should probably include the profile too...

I'm hesitating here. I was thinking that maybe we want one build with more profiles? So, something like koji kiwi-build --profile=Live,Virtual,Disk .... and have one NVR with multiple attached images. Second option is to link name somehow with profile. name = f"{name}-{profile" and third (I don't like this one) to allow user to override the name from CLI. Do you think that first variant is not practical?

1 new commit added

  • code simplification
2 years ago

can we actually do it (possibly optionally?) other way around? generate version in koji using some kind of getNextRelease() like we do for some other types and then pass it to xml?

I understand kiwi's file more like a spec. If nvr is already there, maintainer should bump it in description file if he wants higher release. It seems more right to me than have some confusing commit with nvrA and resulting build with nvrB.

Fair point, but being able to rewrite that for snapshot releases is probably useful.

I'm looking again into http://osinside.github.io/kiwi/image_description/elements.html#preferences-version - It seems that we can treat it as "version" (of NVR) only. In such case release for us could be a different field handled by --release or getNextRelease and we don't need to touch xml in such case. Does it make sense?

Yeah, the "name" recorded by Koji should probably include the profile too...

I'm hesitating here. I was thinking that maybe we want one build with more profiles? So, something like koji kiwi-build --profile=Live,Virtual,Disk .... and have one NVR with multiple attached images. Second option is to link name somehow with profile. name = f"{name}-{profile" and third (I don't like this one) to allow user to override the name from CLI. Do you think that first variant is not practical?

I suppose the first variant is practical. That would allow them to be built in parallel too, right?

rebased onto 3f51428c98f14634232e48b2848bc9a92cd0b181

2 years ago

2 new commits added

  • alter name by selected profile
  • fix signature
2 years ago

pretty please pagure-ci rebuild

2 years ago

rebased onto 71e1dc13b998eabcecd97529d50fa7f3b1cd8403

2 years ago

1 new commit added

  • fix bandit
2 years ago

1 new commit added

  • expand docs
2 years ago

1 new commit added

  • fix flake8
2 years ago

1 new commit added

  • fix docs
2 years ago

rebased onto d31306e

2 years ago

Commit 101433a fixes this pull-request

Pull-Request has been merged by tkopecek

2 years ago