Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package product-composer for openSUSE:Factory checked in at 2024-03-08 18:10:23 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/product-composer (Old) and /work/SRC/openSUSE:Factory/.product-composer.new.1770 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "product-composer" Fri Mar 8 18:10:23 2024 rev:8 rq:1156330 version:0.4.2 Changes: -------- --- /work/SRC/openSUSE:Factory/product-composer/product-composer.changes 2024-03-07 18:32:55.632786828 +0100 +++ /work/SRC/openSUSE:Factory/.product-composer.new.1770/product-composer.changes 2024-03-08 18:10:32.249134803 +0100 @@ -1,0 +2,8 @@ +Fri Mar 8 10:40:48 UTC 2024 - Adrian Schröter <adr...@suse.de> + +- update to version 0.4.2 + * generate also diskusage data now (done by mls) + * code cleanup +- add missing dependency to python-zstandard + +------------------------------------------------------------------- Old: ---- product-composer-0.4.1.obscpio New: ---- product-composer-0.4.2.obscpio ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ product-composer.spec ++++++ --- /var/tmp/diff_new_pack.DsDBeS/_old 2024-03-08 18:10:33.065164791 +0100 +++ /var/tmp/diff_new_pack.DsDBeS/_new 2024-03-08 18:10:33.069164937 +0100 @@ -17,7 +17,7 @@ Name: product-composer -Version: 0.4.1 +Version: 0.4.2 Release: 0 Summary: Product Composer License: GPL-2.0-or-later @@ -31,6 +31,7 @@ Requires: python3-PyYAML Requires: python3-pydantic Requires: python3-rpm +Requires: python3-zstandard # build for signdummy Requires: build Requires: createrepo ++++++ _service ++++++ --- /var/tmp/diff_new_pack.DsDBeS/_old 2024-03-08 18:10:33.097165966 +0100 +++ /var/tmp/diff_new_pack.DsDBeS/_new 2024-03-08 18:10:33.101166114 +0100 @@ -2,8 +2,8 @@ <service name="obs_scm" mode="manual"> <param name="url">https://github.com/openSUSE/product-composer</param> <param name="scm">git</param> - <param name="version">0.4.1</param> - <param name="revision">0.4.1</param> + <param name="version">0.4.2</param> + <param name="revision">0.4.2</param> </service> <service name="set_version" mode="manual" /> <service name="tar" mode="buildtime" /> ++++++ product-composer-0.4.1.obscpio -> product-composer-0.4.2.obscpio ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/product-composer-0.4.1/src/productcomposer/__main__.py new/product-composer-0.4.2/src/productcomposer/__main__.py --- old/product-composer-0.4.1/src/productcomposer/__main__.py 2024-03-07 16:46:38.000000000 +0100 +++ new/product-composer-0.4.2/src/productcomposer/__main__.py 2024-03-08 11:50:36.000000000 +0100 @@ -3,6 +3,8 @@ python -m productcomposer ... """ + + def main(): """ Execute the application. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/product-composer-0.4.1/src/productcomposer/__version__.py new/product-composer-0.4.2/src/productcomposer/__version__.py --- old/product-composer-0.4.1/src/productcomposer/__version__.py 2024-03-07 16:46:38.000000000 +0100 +++ new/product-composer-0.4.2/src/productcomposer/__version__.py 2024-03-08 11:50:36.000000000 +0100 @@ -5,7 +5,7 @@ <https://semver.org/> <https://www.python.org/dev/peps/pep-0440> -Major versions introduce significant changes to the API, and backwards +Major versions introduce significant changes to the API, and backwards compatibility is not guaranteed. Minor versions are for new features and other backwards-compatible changes to the API. Patch versions are for bug fixes and internal code changes that do not affect the API. Development versions are diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/product-composer-0.4.1/src/productcomposer/api/parse.py new/product-composer-0.4.2/src/productcomposer/api/parse.py --- old/product-composer-0.4.1/src/productcomposer/api/parse.py 2024-03-07 16:46:38.000000000 +0100 +++ new/product-composer-0.4.2/src/productcomposer/api/parse.py 2024-03-08 11:50:36.000000000 +0100 @@ -6,7 +6,7 @@ def main(name="World") -> str: """ Execute the command. - + :param name: name to use in greeting """ logger.debug("executing hello command") diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/product-composer-0.4.1/src/productcomposer/cli.py new/product-composer-0.4.2/src/productcomposer/cli.py --- old/product-composer-0.4.1/src/productcomposer/cli.py 2024-03-07 16:46:38.000000000 +0100 +++ new/product-composer-0.4.2/src/productcomposer/cli.py 2024-03-08 11:50:36.000000000 +0100 @@ -9,10 +9,8 @@ from argparse import ArgumentParser from xml.etree import ElementTree as ET -import rpm import yaml -from . import __version__ from .core.logger import logger from .core.PkgSet import PkgSet from .core.Package import Package @@ -37,6 +35,7 @@ # per package override via supportstatus.txt file supportstatus_override = {} + def main(argv=None) -> int: """ Execute the application CLI. @@ -85,6 +84,7 @@ args.func(args) return 0 + def die(msg, details=None): if msg: print("ERROR: " + msg) @@ -92,11 +92,13 @@ print(details) raise SystemExit(1) + def warn(msg, details=None): print("WARNING: " + msg) if details: print(details) + def note(msg): print(msg) @@ -121,7 +123,7 @@ directory = os.path.dirname(args.filename) reposdir = args.reposdir if args.reposdir else directory + "/repos" - supportstatus_fn = os.path.join(directory,'supportstatus.txt') + supportstatus_fn = os.path.join(directory, 'supportstatus.txt') if os.path.isfile(supportstatus_fn): parse_supportstatus(supportstatus_fn) @@ -140,6 +142,7 @@ def verify(args): parse_yaml(args.filename, args.flavor) + def parse_yaml(filename, flavor): with open(filename, 'r') as file: @@ -175,12 +178,14 @@ return yml + def parse_supportstatus(filename): with open(filename, 'r') as file: for line in file.readlines(): a = line.strip().split(' ') supportstatus_override[a[0]] = a[1] + def get_product_dir(yml, flavor, release): name = yml['name'] + "-" + str(yml['version']) if 'product_directory_name' in yml: @@ -202,9 +207,9 @@ def run_helper(args, cwd=None, stdout=None, stdin=None, failmsg=None): if stdout is None: - stdout=subprocess.PIPE + stdout = subprocess.PIPE if stdin is None: - stdin=subprocess.PIPE + stdin = subprocess.PIPE popen = subprocess.Popen(args, stdout=stdout, stdin=stdin, cwd=cwd) if popen.wait(): output = popen.stdout.read() @@ -214,12 +219,13 @@ die("Failed to run" + args[0], details=output) return popen.stdout.read() if stdout == subprocess.PIPE else '' + def create_tree(outdir, product_base_dir, yml, pool, flavor, vcs=None, disturl=None): if not os.path.exists(outdir): os.mkdir(outdir) maindir = outdir + '/' + product_base_dir - rpmdir = maindir # we may offer to set it up in sub directories + rpmdir = maindir # we may offer to set it up in sub directories if not os.path.exists(rpmdir): os.mkdir(rpmdir) @@ -238,7 +244,7 @@ debugdir = outdir + '/' + product_base_dir + '-Debug' os.mkdir(debugdir) elif yml['debug'] == 'drop': - debugdir = None + debugdir = None elif yml['debug'] != 'include': die("Bad debug option, must be either 'include', 'split' or 'drop'") @@ -246,9 +252,9 @@ link_rpms_to_tree(rpmdir, yml, pool, arch, flavor, debugdir, sourcedir) for arch in yml['architectures']: - unpack_meta_rpms(rpmdir, yml, pool, arch, flavor, medium=1) # only for first medium am + unpack_meta_rpms(rpmdir, yml, pool, arch, flavor, medium=1) # only for first medium am - repos=[] + repos = [] if disturl: match = re.match("^obs://([^/]*)/([^/]*)/.*", disturl) if match: @@ -258,13 +264,13 @@ repos = [repo] if vcs: repos.append(vcs) - - default_content=["pool"] + + default_content = ["pool"] for file in os.listdir(rpmdir): if not file.startswith('gpg-pubkey-'): continue - args=['gpg', '--no-keyring', '--no-default-keyring', '--with-colons', + args = ['gpg', '--no-keyring', '--no-default-keyring', '--with-colons', '--import-options', 'show-only', '--import', '--fingerprint'] out = run_helper(args, stdin=open(f'{rpmdir}/{file}', 'rb'), failmsg="Finger printing of gpg file") @@ -293,38 +299,23 @@ # the tools read the subdirectory of the rpmdir from environment variable os.environ['ROOT_ON_CD'] = '.' if os.path.exists("/usr/bin/mk_changelog"): - args = [ "/usr/bin/mk_changelog", rpmdir ] + args = ["/usr/bin/mk_changelog", rpmdir] run_helper(args) # ARCHIVES.gz if os.path.exists("/usr/bin/mk_listings"): - args = [ "/usr/bin/mk_listings", rpmdir ] + args = ["/usr/bin/mk_listings", rpmdir] run_helper(args) # media.X structures FIXME mediavendor = yml['vendor'] + ' - ' + product_base_dir mediaident = product_base_dir # FIXME: calculate from product provides - mediaproducts = [ yml['vendor'] + '-' + yml['name'] + ' ' + str(yml['version']) + '-1' ] + mediaproducts = [yml['vendor'] + '-' + yml['name'] + ' ' + str(yml['version']) + '-1'] create_media_dir(maindir, mediavendor, mediaident, mediaproducts) - # CHECKSUMS file create_checksums_file(maindir) - # repodata/appdata - # currently not supported in ALP? -# if os.path.exists("/usr/bin/openSUSE-appstream-process"): -# args = [ "/usr/bin/openSUSE-appstream-process", -# rpmdir, rpmdir + "/repodata" ] -# run_helper(args) - - # repodata/*susedata* -# if os.path.exists("/usr/bin/add_product_susedata"): -# args = [ "/usr/bin/add_product_susedata", -# '-p', # diskusage data -# '-d', rpmdir ] -# run_helper(args) - create_susedata_xml(rpmdir, yml) process_updateinfos(rpmdir, yml, pool, flavor, debugdir, sourcedir) @@ -340,7 +331,7 @@ licensedir = rpmdir + ".license" if not os.path.exists(licensedir): os.mkdir(licensedir) - args = [ 'tar', 'xf', rpmdir + licensefilename, '-C', licensedir ] + args = ['tar', 'xf', rpmdir + licensefilename, '-C', licensedir] output = run_helper(args, failmsg="extract license tar ball") if not os.path.exists(licensedir + "/license.txt"): die("No license.txt extracted", details=output) @@ -358,38 +349,38 @@ os.unlink(rpmdir + '/license.tar.gz') # detached signature - args = [ '/usr/lib/build/signdummy', '-d', rpmdir + "/repodata/repomd.xml" ] + args = ['/usr/lib/build/signdummy', '-d', rpmdir + "/repodata/repomd.xml"] run_helper(args, failmsg="create detached signature") - args = [ '/usr/lib/build/signdummy', '-d', maindir + '/CHECKSUMS' ] + args = ['/usr/lib/build/signdummy', '-d', maindir + '/CHECKSUMS'] run_helper(args, failmsg="create detached signature for CHECKSUMS") # pubkey with open(rpmdir + "/repodata/repomd.xml.key", 'w') as pubkey_file: - args = [ '/usr/lib/build/signdummy', '-p' ] + args = ['/usr/lib/build/signdummy', '-p'] run_helper(args, stdout=pubkey_file, failmsg="write signature public key") # do we need an ISO file? if 'iso' in yml: for workdir in [maindir, sourcedir, debugdir]: application_id = re.sub(r'^.*/', '', maindir) - args = [ '/usr/bin/mkisofs', '-quiet', '-p', 'Product Composer - http://www.github.com/openSUSE/product-composer' ] - args += [ '-r', '-pad', '-f', '-J', '-joliet-long' ] + args = ['/usr/bin/mkisofs', '-quiet', '-p', 'Product Composer - http://www.github.com/openSUSE/product-composer'] + args += ['-r', '-pad', '-f', '-J', '-joliet-long'] # FIXME: do proper multi arch handling isolinux = 'boot/' + yml['architectures'][0] + '/loader/isolinux.bin' if os.path.isfile(workdir + '/' + isolinux): - args += [ '-no-emul-boot', '-boot-load-size', '4', '-boot-info-table' ] - args += [ '-hide', 'glump', '-hide-joliet', 'glump' ] - args += [ '-eltorito-alt-boot', '-eltorito-platform', 'efi' ] - args += [ '-no-emul-boot' ] - #args += [ '-sort', $sort_file ] - #args += [ '-boot-load-size', block_size("boot/"+arch+"/loader") ] - args += [ '-b', isolinux] + args += ['-no-emul-boot', '-boot-load-size', '4', '-boot-info-table'] + args += ['-hide', 'glump', '-hide-joliet', 'glump'] + args += ['-eltorito-alt-boot', '-eltorito-platform', 'efi'] + args += ['-no-emul-boot'] + # args += [ '-sort', $sort_file ] + # args += [ '-boot-load-size', block_size("boot/"+arch+"/loader") ] + args += ['-b', isolinux] if 'publisher' in yml['iso']: - args += [ '-publisher', yml['iso']['publisher'] ] + args += ['-publisher', yml['iso']['publisher']] if 'volume_id' in yml['iso']: - args += [ '-V', yml['iso']['volume_id'] ] - args += [ '-A', application_id ] - args += [ '-o', workdir + '.iso', workdir ] + args += ['-V', yml['iso']['volume_id']] + args += ['-A', application_id] + args += ['-o', workdir + '.iso', workdir] run_helper(args, cwd=maindir, failmsg="create iso file") # create SBOM data @@ -397,37 +388,40 @@ spdx_distro = "ALP" spdx_distro += "-" + str(yml['version']) # SPDX - args = [ "/usr/lib/build/generate_sbom", + args = ["/usr/lib/build/generate_sbom", "--format", 'spdx', "--distro", spdx_distro, - "--product", rpmdir + "--product", rpmdir ] with open(rpmdir + ".spdx.json", 'w') as sbom_file: run_helper(args, stdout=sbom_file, failmsg="run generate_sbom for SPDX") # CycloneDX - args = [ "/usr/lib/build/generate_sbom", + args = ["/usr/lib/build/generate_sbom", "--format", 'cyclonedx', "--distro", spdx_distro, - "--product", rpmdir + "--product", rpmdir ] with open(rpmdir + ".cdx.json", 'w') as sbom_file: run_helper(args, stdout=sbom_file, failmsg="run generate_sbom for CycloneDX") # create media info files + + def create_media_dir(maindir, vendorstr, identstr, products): media1dir = maindir + '/' + 'media.1' if not os.path.isdir(media1dir): - os.mkdir(media1dir) # we do only support seperate media atm + os.mkdir(media1dir) # we do only support seperate media atm with open(media1dir + '/media', 'w') as media_file: media_file.write(vendorstr + "\n") media_file.write(identstr + "\n") media_file.write("1\n") if products: with open(media1dir + '/products', 'w') as products_file: - for productname in products: + for productname in products: products_file.write('/ ' + productname + "\n") + def create_checksums_file(maindir): with open(maindir + '/CHECKSUMS', 'a') as chksums_file: for subdir in ('boot', 'EFI', 'docu', 'media.1'): @@ -439,13 +433,55 @@ run_helper([chksums_tool, relname], cwd=maindir, stdout=chksums_file) # create a fake package entry from an updateinfo package spec + + def create_updateinfo_package(pkgentry): entry = Package() for tag in 'name', 'epoch', 'version', 'release', 'arch': setattr(entry, tag, pkgentry.get(tag)) return entry +def generate_du_data(pkg, maxdepth): + dirs = pkg.get_directories() + seen = set() + dudata_size = {} + dudata_count = {} + for dir, filedatas in pkg.get_directories().items(): + if dir == '': + dir = '/usr/src/packages/' + size = 0 + count = 0 + for filedata in filedatas: + (basename, filesize, cookie) = filedata + if cookie: + if cookie in seen: + next + seen.add(cookie) + size += filesize + count += 1 + dir = '/' + dir.strip('/') + subdir = '' + depth = 0 + for comp in dir.split('/'): + if comp == '' and subdir != '': + next + subdir += comp + '/' + if subdir not in dudata_size: + dudata_size[subdir] = 0 + dudata_count[subdir] = 0 + dudata_size[subdir] += size + dudata_count[subdir] += count + depth += 1 + if depth > maxdepth: + break + dudata = [] + for dir, size in sorted(dudata_size.items()): + dudata.append((dir, size, dudata_count[dir])) + return dudata + # Create the main susedata.xml with support and disk usage informations + + def create_susedata_xml(rpmdir, yml): # get supported translations based on local packages i18ndir = '/usr/share/locale/en_US/LC_MESSAGES/' @@ -464,9 +500,9 @@ languages=['en_US']) # read repomd.xml - ns='{http://linux.duke.edu/metadata/repo}' + ns = '{http://linux.duke.edu/metadata/repo}' tree = ET.parse(rpmdir + '/repodata/repomd.xml') - primary_fn = tree.find(f".//{ns}data[@type='primary']/{ns}location").attrib['href'] + primary_fn = tree.find(f".//{ns}data[@type='primary']/{ns}location").get('href') # read compressed primary.xml openfunction = None @@ -479,7 +515,7 @@ else: die(f"unsupported primary compression type ({primary_fm})") tree = ET.parse(openfunction(rpmdir + '/' + primary_fn, 'rb')) - ns='{http://linux.duke.edu/metadata/common}' + ns = '{http://linux.duke.edu/metadata/common}' # Create susedata structure susedata = ET.Element('susedata') @@ -487,9 +523,9 @@ # go for every rpm file of the repo via the primary count = 0 for pkg in tree.findall(f".//{ns}package[@type='rpm']"): - name = pkg.find(f'{ns}name').text - pkgid = pkg.find(f'{ns}checksum').text - arch = pkg.find(f'{ns}arch').text + name = pkg.find(f'{ns}name').text + pkgid = pkg.find(f'{ns}checksum').text + arch = pkg.find(f'{ns}arch').text version = pkg.find(f'{ns}version').attrib package = ET.SubElement(susedata, 'package') @@ -499,6 +535,16 @@ ET.SubElement(package, 'version', version) if name in supportstatus and supportstatus[name] is not None: ET.SubElement(package, 'keyword').text = f'support_{supportstatus[name]}' + location = pkg.find(f'{ns}location').get('href') + if os.path.exists(rpmdir + '/' + location): + p = Package() + p.location = rpmdir + '/' + location + dudata = generate_du_data(p, 3) + if dudata: + duelement = ET.SubElement(package, 'diskusage') + dirselement = ET.SubElement(duelement, 'dirs') + for duitem in dudata: + ET.SubElement(dirselement, 'dir', { 'name': duitem[0], 'size': str(duitem[1]), 'count': str(duitem[2]) }) count += 1 # look for pattern category @@ -513,7 +559,7 @@ isummary = i18ntrans[lang].gettext(summary) idescription = i18ntrans[lang].gettext(description) icategory = i18ntrans[lang].gettext(category) if category else None - if isummary == summary and idescription == description: + if isummary == summary and idescription == description and icategory == category: continue if lang not in i18ndata: i18ndata[lang] = ET.Element('susedata') @@ -546,7 +592,7 @@ for lang in i18ndata: i18ndata[lang].set('packages', str(i18ndata_count[lang])) - susedata_fn=rpmdir + f'/susedata.{lang}.xml' + susedata_fn = rpmdir + f'/susedata.{lang}.xml' ET.indent(i18ndata[lang], space=" ", level=0) with open(susedata_fn, 'x') as sd_file: @@ -656,6 +702,7 @@ cr.excludes = ["boot"] cr.run_cmd(cwd=rpmdir, stdout=subprocess.PIPE) + def unpack_one_meta_rpm(rpmdir, rpm, medium): tempdir = rpmdir + "/temp" os.mkdir(tempdir) @@ -666,9 +713,10 @@ shutil.copytree(skel_dir, rpmdir, dirs_exist_ok=True) shutil.rmtree(tempdir) + def unpack_meta_rpms(rpmdir, yml, pool, arch, flavor, medium): missing_package = False - for unpack_pkgset_name in yml.get('unpack', [ 'unpack' ]): + for unpack_pkgset_name in yml.get('unpack', ['unpack']): unpack_pkgset = create_package_set(yml, arch, flavor, unpack_pkgset_name) for sel in unpack_pkgset: rpm = pool.lookup_rpm(arch, sel.name, sel.op, sel.epoch, sel.version, sel.release) @@ -681,11 +729,12 @@ if missing_package and not 'ignore_missing_packages' in yml['build_options']: die('Abort due to missing packages') + def create_package_set_compat(yml, arch, flavor, setname): if setname == 'main': - oldname = 'packages' + oldname = 'packages' elif setname == 'unpack': - oldname = 'unpack_packages' + oldname = 'unpack_packages' else: return None if oldname not in yml: @@ -701,9 +750,10 @@ continue pkgset.add_specs(entry['packages']) else: - pkgset.add_specs([ str(entry) ]) + pkgset.add_specs([str(entry)]) return pkgset + def create_package_set(yml, arch, flavor, setname): if 'packagesets' not in yml: pkgset = create_package_set_compat(yml, arch, flavor, setname) @@ -752,6 +802,7 @@ pkgsets[setname] = PkgSet(setname) # instantiate return pkgsets[setname] + def link_rpms_to_tree(rpmdir, yml, pool, arch, flavor, debugdir=None, sourcedir=None): singlemode = True if 'take_all_available_versions' in yml['build_options']: @@ -790,7 +841,7 @@ if srpm: link_entry_into_dir(srpm, sourcedir) else: - details=f" required by {rpm}" + details = f" required by {rpm}" warn(f"source rpm package {srcrpm} not found", details=details) missing_package = True @@ -806,6 +857,7 @@ if missing_package and not 'ignore_missing_packages' in yml['build_options']: die('Abort due to missing packages') + def link_file_into_dir(filename, directory): if not os.path.exists(directory): os.mkdir(directory) @@ -823,12 +875,14 @@ link_file_into_dir(entry.location, directory + '/' + entry.arch) add_entry_to_report(entry, directory) + def add_entry_to_report(entry, directory): outname = directory + '/' + entry.arch + '/' + os.path.basename(entry.location) # first one wins, see link_file_into_dir if outname not in tree_report: tree_report[outname] = entry + def write_report_file(directory, outfile): root = ET.Element('report') if not directory.endswith('/'): @@ -855,6 +909,7 @@ tree = ET.ElementTree(root) tree.write(outfile) + if __name__ == "__main__": try: status = main() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/product-composer-0.4.1/src/productcomposer/core/Package.py new/product-composer-0.4.2/src/productcomposer/core/Package.py --- old/product-composer-0.4.1/src/productcomposer/core/Package.py 2024-03-07 16:46:38.000000000 +0100 +++ new/product-composer-0.4.2/src/productcomposer/core/Package.py 2024-03-08 11:50:36.000000000 +0100 @@ -6,6 +6,7 @@ import rpm import functools + @functools.total_ordering class Package: def __init__(self, location=None, rpm_ts=None): @@ -49,7 +50,7 @@ h = self._read_rpm_header() if h is None: return None - return [ dep.DNEVR()[2:] for dep in rpm.ds(h, 'provides') ] + return [dep.DNEVR()[2:] for dep in rpm.ds(h, 'provides')] def _read_rpm_header(self, rpm_ts=None): if self.location is None: @@ -113,4 +114,36 @@ return '<' in op return '=' in op + def get_directories(self): + h = self._read_rpm_header() + if h is None: + return None + dirs = {} + filedevs = h['filedevices'] + fileinos= h['fileinodes'] + filesizes = h['filesizes'] + filemodes = h['filemodes'] + dirnames = h['dirnames'] + dirindexes = h['dirindexes'] + basenames = h['basenames'] + if not basenames: + return dirs + for basename, dirindex, filesize, filemode, filedev, fileino in zip(basenames, dirindexes, filesizes, filemodes, filedevs, fileinos): + dirname = dirnames[dirindex] + if isinstance(basename, bytes): + basename = basename.decode('utf-8') + if isinstance(dirname, bytes): + dirname = dirname.decode('utf-8') + if dirname != '' and not dirname.endswith('/'): + dirname += '/' + if not dirname in dirs: + dirs[dirname] = [] + cookie = f"{filedev}/{fileino}" + if (filemode & 0o170000) != 0o100000: + filesize = 0 + dirs[dirname].append((basename, filesize, cookie)) + return dirs + + + # vim: sw=4 et diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/product-composer-0.4.1/src/productcomposer/core/PkgSelect.py new/product-composer-0.4.2/src/productcomposer/core/PkgSelect.py --- old/product-composer-0.4.1/src/productcomposer/core/PkgSelect.py 2024-03-07 16:46:38.000000000 +0100 +++ new/product-composer-0.4.2/src/productcomposer/core/PkgSelect.py 2024-03-08 11:50:36.000000000 +0100 @@ -5,21 +5,22 @@ import re import rpm + class PkgSelect: - def __init__(self, spec, supportstatus = None): + def __init__(self, spec, supportstatus=None): self.supportstatus = supportstatus match = re.match(r'([^><=]*)([><=]=?)(.*)', spec.replace(' ', '')) if match: - self.name = match.group(1) - self.op = match.group(2) - epoch = '0' - version = match.group(3) - release = None + self.name = match.group(1) + self.op = match.group(2) + epoch = '0' + version = match.group(3) + release = None if ':' in version: (epoch, version) = version.split(':', 2) if '-' in version: (version, release) = version.rsplit('-', 2) - self.epoch = epoch + self.epoch = epoch self.version = version self.release = release else: @@ -31,7 +32,7 @@ def matchespkg(self, arch, pkg): return pkg.matches(arch, self.name, self.op, self.epoch, self.version, self.release) - + @staticmethod def _sub_ops(op1, op2): if '>' in op2: @@ -113,7 +114,7 @@ if '>' in self.op and '<' not in other.op: return other if '<' in other.op and '>' not in self.op: - return self + return self if '<' not in other.op and '>' not in self.op: return None elif cmp > 0: @@ -143,7 +144,7 @@ if self.epoch and self.epoch != '0': evr = self.epoch + ':' + evr return self.name + ' ' + self.op + ' ' + evr - + def __hash__(self): if self.op: return hash((self.name, self.op, self.epoch, self.version, self.release)) @@ -154,5 +155,5 @@ if self.name != other.name: return False return str(self) == str(other) - + # vim: sw=4 et diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/product-composer-0.4.1/src/productcomposer/core/PkgSet.py new/product-composer-0.4.2/src/productcomposer/core/PkgSet.py --- old/product-composer-0.4.1/src/productcomposer/core/PkgSet.py 2024-03-07 16:46:38.000000000 +0100 +++ new/product-composer-0.4.2/src/productcomposer/core/PkgSet.py 2024-03-08 11:50:36.000000000 +0100 @@ -4,6 +4,7 @@ from .PkgSelect import PkgSelect + class PkgSet: def __init__(self, name): self.name = name @@ -25,7 +26,7 @@ sel = PkgSelect(spec, supportstatus=self.supportstatus) self.pkgs.append(sel) self.byname = None - + def add(self, other): s1 = set(self) for sel in other.pkgs: @@ -90,5 +91,5 @@ def __iter__(self): return iter(self.pkgs) - + # vim: sw=4 et diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/product-composer-0.4.1/src/productcomposer/core/Pool.py new/product-composer-0.4.2/src/productcomposer/core/Pool.py --- old/product-composer-0.4.1/src/productcomposer/core/Pool.py 2024-03-07 16:46:38.000000000 +0100 +++ new/product-composer-0.4.2/src/productcomposer/core/Pool.py 2024-03-08 11:50:36.000000000 +0100 @@ -8,6 +8,7 @@ from .Package import Package from .Updateinfo import Updateinfo + class Pool: def __init__(self): self.rpms = {} @@ -44,11 +45,11 @@ elif filename.endswith('.rpm'): pkg = self.make_rpm(fname, rpm_ts=ts) self.add_rpm(pkg, os.path.join(reldirpath, filename)) - + def lookup_all_rpms(self, arch, name, op=None, epoch=None, version=None, release=None): if name not in self.rpms: return [] - return [ rpm for rpm in self.rpms[name] if rpm.matches(arch, name, op, epoch, version, release) ] + return [rpm for rpm in self.rpms[name] if rpm.matches(arch, name, op, epoch, version, release)] def lookup_rpm(self, arch, name, op=None, epoch=None, version=None, release=None): return max(self.lookup_all_rpms(arch, name, op, epoch, version, release), default=None) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/product-composer-0.4.1/src/productcomposer/core/Updateinfo.py new/product-composer-0.4.2/src/productcomposer/core/Updateinfo.py --- old/product-composer-0.4.1/src/productcomposer/core/Updateinfo.py 2024-03-07 16:46:38.000000000 +0100 +++ new/product-composer-0.4.2/src/productcomposer/core/Updateinfo.py 2024-03-08 11:50:36.000000000 +0100 @@ -5,6 +5,7 @@ from xml.etree import ElementTree as ET + @functools.total_ordering class Updateinfo: def __init__(self, location=None): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/product-composer-0.4.1/src/productcomposer/core/logger.py new/product-composer-0.4.2/src/productcomposer/core/logger.py --- old/product-composer-0.4.1/src/productcomposer/core/logger.py 2024-03-07 16:46:38.000000000 +0100 +++ new/product-composer-0.4.2/src/productcomposer/core/logger.py 2024-03-08 11:50:36.000000000 +0100 @@ -20,8 +20,8 @@ def __init__(self, name=None): """ Initialize this logger. - Loggers with the same name refer to the same underlying object. - Names are hierarchical, e.g. 'parent.child' defines a logger that is a + Loggers with the same name refer to the same underlying object. + Names are hierarchical, e.g. 'parent.child' defines a logger that is a descendant of 'parent'. :param name: logger name (application name by default) @@ -38,16 +38,16 @@ """ Start logging to a stream. Until the logger is started, no messages will be emitted. This applies - to all loggers with the same name and any child loggers. + to all loggers with the same name and any child loggers. Multiple streams can be logged to by calling start() for each one. Calling start() more than once for the same stream will result in duplicate records to that stream. Messages less than the given priority level will be ignored. The - default level conforms to the *nix convention that a successful run - should produce no diagnostic output. Call setLevel() to change the - logger's priority level after it has been stared. Available levels and + default level conforms to the *nix convention that a successful run + should produce no diagnostic output. Call setLevel() to change the + logger's priority level after it has been stared. Available levels and their suggested meanings: DEBUG - output useful for developers diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/product-composer-0.4.1/src/productcomposer/wrappers/common.py new/product-composer-0.4.2/src/productcomposer/wrappers/common.py --- old/product-composer-0.4.1/src/productcomposer/wrappers/common.py 2024-03-07 16:46:38.000000000 +0100 +++ new/product-composer-0.4.2/src/productcomposer/wrappers/common.py 2024-03-08 11:50:36.000000000 +0100 @@ -6,7 +6,6 @@ import os import subprocess -from abc import ABC from abc import abstractmethod from pydantic import BaseModel ++++++ product-composer.obsinfo ++++++ --- /var/tmp/diff_new_pack.DsDBeS/_old 2024-03-08 18:10:33.225170670 +0100 +++ /var/tmp/diff_new_pack.DsDBeS/_new 2024-03-08 18:10:33.229170817 +0100 @@ -1,5 +1,5 @@ name: product-composer -version: 0.4.1 -mtime: 1709826398 -commit: d4343d718c9ebcc9bcdce68b133b0c84979c870e +version: 0.4.2 +mtime: 1709895036 +commit: 2e1933d5752f59d07883338de554c829ba7daeb2