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 2023-12-06 23:47:25
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/product-composer (Old)
and /work/SRC/openSUSE:Factory/.product-composer.new.25432 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "product-composer"
Wed Dec 6 23:47:25 2023 rev:2 rq:1130998 version:0.2
Changes:
--------
--- /work/SRC/openSUSE:Factory/product-composer/product-composer.changes
2023-12-04 23:02:50.206543986 +0100
+++
/work/SRC/openSUSE:Factory/.product-composer.new.25432/product-composer.changes
2023-12-06 23:47:28.281591815 +0100
@@ -1,0 +2,6 @@
+Tue Dec 5 14:46:55 UTC 2023 - Adrian Schröter <[email protected]>
+
+- update to version 0.2
+ * Support .report file generation
+
+-------------------------------------------------------------------
Old:
----
product-composer-0.1.obscpio
New:
----
product-composer-0.2.obscpio
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ product-composer.spec ++++++
--- /var/tmp/diff_new_pack.ucyY1r/_old 2023-12-06 23:47:29.065620719 +0100
+++ /var/tmp/diff_new_pack.ucyY1r/_new 2023-12-06 23:47:29.065620719 +0100
@@ -17,7 +17,7 @@
Name: product-composer
-Version: 0.1
+Version: 0.2
Release: 0
Summary: Product Composer
License: GPL-2.0-or-later
@@ -53,7 +53,7 @@
mv %buildroot/usr/bin/productcomposer %buildroot%_bindir/product-composer
%files
-%doc README.rst docs
+%doc README.rst docs examples
%_bindir/product-composer
%{python3_sitelib}/*
++++++ _service ++++++
--- /var/tmp/diff_new_pack.ucyY1r/_old 2023-12-06 23:47:29.089621603 +0100
+++ /var/tmp/diff_new_pack.ucyY1r/_new 2023-12-06 23:47:29.093621752 +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.1</param>
- <param name="revision">0.1</param>
+ <param name="version">0.2</param>
+ <param name="revision">0.2</param>
</service>
<service name="tar" mode="buildtime" />
<service name="recompress" mode="buildtime">
++++++ product-composer-0.1.obscpio -> product-composer-0.2.obscpio ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/product-composer-0.1/docs/build_description.adoc
new/product-composer-0.2/docs/build_description.adoc
--- old/product-composer-0.1/docs/build_description.adoc 2023-12-04
15:22:14.000000000 +0100
+++ new/product-composer-0.2/docs/build_description.adoc 2023-12-05
15:36:06.000000000 +0100
@@ -52,7 +52,7 @@
There is usually one master list and in addition there
can be addional optional lists.
-The additional list can be filter by flavors and/or
+The additional lists can be filtered by flavors and/or
architectures.
==== unpack_packages
@@ -116,6 +116,13 @@
The package list can be valid global or for specific flavors
or architectures only.
+The syntax is rpm like
+
+ [EPOCH:]VERSION[-RELEASE]
+
+So an epoch or a release can be specified optional. Otherwise
+any epoch or release is qualified.
+
==== product_compose_schema
Defines the level of the yaml syntax. We are currently at level 0.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/product-composer-0.1/docs/productcomposer.adoc
new/product-composer-0.2/docs/productcomposer.adoc
--- old/product-composer-0.1/docs/productcomposer.adoc 2023-12-04
15:22:14.000000000 +0100
+++ new/product-composer-0.2/docs/productcomposer.adoc 2023-12-05
15:36:06.000000000 +0100
@@ -11,19 +11,19 @@
It is used to generate product rpm repositories out of a pool of rpms.
Unlike product builder, these can also be used to ship maintenance updates.
-Currently it supports:
- - processing based on a list of rpm package names.
- product compose is not take care of dependencies atm.
- - providing matching source and/or debug packages for picked rpm packages.
- These can be either included into main repository or prepared via
- extra repositories
- - optional filters for architectures, versions and flavors can be defined
- - it can provide either just a single rpm of a given name or all of them
- - it can post process updateinfo data
- - post processing to provide various rpm meta data generation
+.Currently it supports:
+- processing based on a list of rpm package names.
+ product compose is not take care of dependencies atm.
+- providing matching source and/or debug packages for picked rpm packages.
+ These can be either included into main repository or prepared via
+ extra repositories
+- optional filters for architectures, versions and flavors can be defined
+- it can provide either just a single rpm of a given name or all of them
+- it can post process updateinfo data
+- post processing to provide various rpm meta data generation
Not yet implemented:
- - create bootable iso files
+- create bootable iso files
== Design
@@ -35,10 +35,10 @@
You will require OBS 2.11 or later.
-Create a new repository with any name. Either in a new or existing project.
- - The product-composer package must be available in any repository
- listed in the path elements.
- - All scheduler architectures where packages are taken from must be listed.
+.Create a new repository with any name. Either in a new or existing project.
+- The product-composer package must be available in any repository
+ listed in the path elements.
+- All scheduler architectures where packages are taken from must be listed.
Your build description file may have any name, but must have a .productcompose
suffix.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/product-composer-0.1/docs/productcomposer.html
new/product-composer-0.2/docs/productcomposer.html
--- old/product-composer-0.1/docs/productcomposer.html 2023-12-04
15:22:14.000000000 +0100
+++ new/product-composer-0.2/docs/productcomposer.html 2023-12-05
15:36:06.000000000 +0100
@@ -748,17 +748,17 @@
<div class="paragraph"><p>It is used to generate product rpm repositories out
of a pool of rpms.
Unlike product builder, these can also be used to ship maintenance
updates.</p></div>
<div class="paragraph"><p>Currently it supports:
- - processing based on a list of rpm package names.
- product compose is not take care of dependencies atm.
- - providing matching source and/or debug packages for picked rpm packages.
- These can be either included into main repository or prepared via
- extra repositories
- - optional filters for architectures, versions and flavors can be defined
- - it can provide either just a single rpm of a given name or all of them
- - it can post process updateinfo data
- - post processing to provide various rpm meta data generation</p></div>
+- processing based on a list of rpm package names.
+ product compose is not take care of dependencies atm.
+- providing matching source and/or debug packages for picked rpm packages.
+ These can be either included into main repository or prepared via
+ extra repositories
+- optional filters for architectures, versions and flavors can be defined
+- it can provide either just a single rpm of a given name or all of them
+- it can post process updateinfo data
+- post processing to provide various rpm meta data generation</p></div>
<div class="paragraph"><p>Not yet implemented:
- - create bootable iso files</p></div>
+- create bootable iso files</p></div>
</div>
</div>
<div class="sect1">
@@ -774,9 +774,9 @@
<div class="sectionbody">
<div class="paragraph"><p>You will require OBS 2.11 or later.</p></div>
<div class="paragraph"><p>Create a new repository with any name. Either in a
new or existing project.
- - The product-composer package must be available in any repository
- listed in the path elements.
- - All scheduler architectures where packages are taken from must be
listed.</p></div>
+- The product-composer package must be available in any repository
+ listed in the path elements.
+- All scheduler architectures where packages are taken from must be
listed.</p></div>
<div class="paragraph"><p>Your build description file may have any name, but
must have a .productcompose
suffix.</p></div>
<div class="paragraph"><p>The build type for the repository must be set
to</p></div>
@@ -833,7 +833,14 @@
still listed in the build log.</p></div>
</div>
<div class="sect3">
-<h4 id="_flavors">5.2.3. flavors</h4>
+<h4 id="_hide_flavor_in_product_directory_name">5.2.3.
hide_flavor_in_product_directory_name</h4>
+<div class="paragraph"><p>The flavor name is by default part of the directory
+name of the build result. This can be disabled,
+when each flavor has a different arch list. Otherwise
+conflicts can happen.</p></div>
+</div>
+<div class="sect3">
+<h4 id="_flavors">5.2.4. flavors</h4>
<div class="paragraph"><p>Flavors can be defined with any name. These can be
used to build multiple media from one build description.</p></div>
<div class="paragraph"><p>Each flavor may define an own architecture
list.</p></div>
@@ -842,15 +849,15 @@
to enable the build.</p></div>
</div>
<div class="sect3">
-<h4 id="_packages">5.2.4. packages</h4>
+<h4 id="_packages">5.2.5. packages</h4>
<div class="paragraph"><p>The packages list lists rpm names to be put on the
medium.</p></div>
<div class="paragraph"><p>There is usually one master list and in addition
there
can be addional optional lists.</p></div>
-<div class="paragraph"><p>The additional list can be filter by flavors and/or
+<div class="paragraph"><p>The additional lists can be filtered by flavors
and/or
architectures.</p></div>
</div>
<div class="sect3">
-<h4 id="_unpack_packages">5.2.5. unpack_packages</h4>
+<h4 id="_unpack_packages">5.2.6. unpack_packages</h4>
<div class="paragraph"><p>The unpack_packages section can be used in the same
way
as the packages section.</p></div>
<div class="paragraph"><p>The difference is that not the rpm itself is put
@@ -906,6 +913,13 @@
specific versions.</p></div>
<div class="paragraph"><p>The package list can be valid global or for specific
flavors
or architectures only.</p></div>
+<div class="paragraph"><p>The syntax is rpm like</p></div>
+<div class="literalblock">
+<div class="content">
+<pre><code>[EPOCH:]VERSION[-RELEASE]</code></pre>
+</div></div>
+<div class="paragraph"><p>So an epoch or a release can be specified optional.
Otherwise
+any epoch or release is qualified.</p></div>
</div>
<div class="sect3">
<h4 id="_product_compose_schema">5.3.7. product_compose_schema</h4>
@@ -951,7 +965,7 @@
<div id="footer">
<div id="footer-text">
Last updated
- 2023-11-30 15:49:31 CET
+ 2023-12-04 16:19:48 CET
</div>
</div>
</body>
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/product-composer-0.1/examples/ftp.obsproduct
new/product-composer-0.2/examples/ftp.obsproduct
--- old/product-composer-0.1/examples/ftp.obsproduct 2023-12-04
15:22:14.000000000 +0100
+++ new/product-composer-0.2/examples/ftp.obsproduct 1970-01-01
01:00:00.000000000 +0100
@@ -1,67 +0,0 @@
-# Our initial schema version. Be prepared that it breaks until we are
-# in full production mode
-product_compose_schema: 0
-
-vendor: openSUSE
-name: Tumbleweed
-version: 1.0
-
-summary: openSUSE Tumbleweed
-description: >
- openSUSE Tumbleweed is the rolling distribution by the
- openSUSE.org project.
-
-
-build_options:
-### For maintenance, otherwise only "the best" version of each package is
picked:
- - take_all_available_versions
-### do not abort on missing packages
- - ignore_missing_packages
-# - hide_flavor_in_product_directory_name
-
-# Enable collection of source and debug packages. Either "include" it
-# on main medium or "split" it away on extra medium.
-source: include
-debug: split
-
-# The default architecture list. Each of these will be put on the medium.
-# It is optional to have a default list, when each flavor defines an
-# architecture list. The main package won't be build in that case.
-architectures: [ x86_64 ]
-
-# A flavor list, each flavor may change the architecture list
-flavors:
-- small
-- large_arm:
- architectures: [ armv7l, aarch64 ]
-
-# packages to be extracted
-unpack_packages:
- - flavors:
- - DVD medium
- architectures:
- - ppc64le
- packages:
- - Super-Special-Slideshow-for-DVD_medium-on-ppc64le
- - skelcd-openSUSE
- - skelcd-openSUSE-installer
-
-# packages to be put on the medium
-packages:
- - flavors:
- - DVD medium
- packages:
- - DVD-player
-
- - architectures:
- - i586
- - i686
- packages:
- - kernel-default-pae
-
- - kernel-default
-# take only glibc packages newer than 2.38-9
-# note: this works like a rpm dependency, i.e. the release part is optional
-# and epochs can be specified with EPOCH: prefix
- - glibc > 2.38-9
-
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/product-composer-0.1/examples/ftp.productcompose
new/product-composer-0.2/examples/ftp.productcompose
--- old/product-composer-0.1/examples/ftp.productcompose 1970-01-01
01:00:00.000000000 +0100
+++ new/product-composer-0.2/examples/ftp.productcompose 2023-12-05
15:36:06.000000000 +0100
@@ -0,0 +1,67 @@
+# Our initial schema version. Be prepared that it breaks until we are
+# in full production mode
+product_compose_schema: 0
+
+vendor: openSUSE
+name: Tumbleweed
+version: 1.0
+
+summary: openSUSE Tumbleweed
+description: >
+ openSUSE Tumbleweed is the rolling distribution by the
+ openSUSE.org project.
+
+
+build_options:
+### For maintenance, otherwise only "the best" version of each package is
picked:
+ - take_all_available_versions
+### do not abort on missing packages
+ - ignore_missing_packages
+# - hide_flavor_in_product_directory_name
+
+# Enable collection of source and debug packages. Either "include" it
+# on main medium or "split" it away on extra medium.
+source: include
+debug: split
+
+# The default architecture list. Each of these will be put on the medium.
+# It is optional to have a default list, when each flavor defines an
+# architecture list. The main package won't be build in that case.
+architectures: [ x86_64 ]
+
+# A flavor list, each flavor may change the architecture list
+flavors:
+- small
+- large_arm:
+ architectures: [ armv7l, aarch64 ]
+
+# packages to be extracted
+unpack_packages:
+ - flavors:
+ - DVD medium
+ architectures:
+ - ppc64le
+ packages:
+ - Super-Special-Slideshow-for-DVD_medium-on-ppc64le
+ - skelcd-openSUSE
+ - skelcd-openSUSE-installer
+
+# packages to be put on the medium
+packages:
+ - flavors:
+ - DVD medium
+ packages:
+ - DVD-player
+
+ - architectures:
+ - i586
+ - i686
+ packages:
+ - kernel-default-pae
+
+ - kernel-default
+# take only glibc packages newer than 2.38-9
+# note: this works like a rpm dependency, i.e. the release part is optional
+# and epochs can be specified with EPOCH: prefix
+ - glibc > 2.38-9
+
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/product-composer-0.1/src/productcomposer/cli.py
new/product-composer-0.2/src/productcomposer/cli.py
--- old/product-composer-0.1/src/productcomposer/cli.py 2023-12-04
15:22:14.000000000 +0100
+++ new/product-composer-0.2/src/productcomposer/cli.py 2023-12-05
15:36:06.000000000 +0100
@@ -25,6 +25,8 @@
local_rpms = {} # hased via name
local_updateinfos = {} # sorted by updateinfo_id
+tree_report = {} # hashed via file name
+
# hardcoded defaults for now
chksums_tool = 'sha512sum'
repomd_checksum_parameter = '--checksum=sha512'
@@ -50,8 +52,9 @@
# Generic options
for cmd_parser in [verify_parser, build_parser]:
- cmd_parser.add_argument('-f', '--flavor', default='.x86_64',
help='Build a given flavor')
+ cmd_parser.add_argument('-f', '--flavor', help='Build a given flavor')
cmd_parser.add_argument('-v', '--verbose', action='store_true',
help='Enable verbose output')
+ cmd_parser.add_argument('--reposdir', action='store', help='Take
packages from this directory')
cmd_parser.add_argument('filename', default='default.productcompose',
help='Filename of product YAML spec')
# build command options
@@ -87,7 +90,7 @@
raise SystemExit(1)
def warn(msg, details=None):
- print("WARNING " + msg)
+ print("WARNING: " + msg)
if details:
print(details)
@@ -96,16 +99,18 @@
def build(args):
- f = args.flavor.split('.')
flavor = None
- if f[0] != '':
- flavor = f[0]
+ if args.flavor:
+ f = args.flavor.split('.')
+ if f[0] != '':
+ flavor = f[0]
yml, archlist = parse_yaml(args.filename, flavor)
directory = os.getcwd()
if args.filename.startswith('/'):
directory = os.path.dirname(args.filename)
- scan_rpms(directory + "/repos", yml)
+ reposdir = args.reposdir if args.reposdir else directory + "/repos"
+ scan_rpms(reposdir, yml)
if args.clean and os.path.exists(args.out):
shutil.rmtree(args.out)
@@ -214,6 +219,12 @@
if not os.path.exists(rpmdir + '/repodata'):
return
+ write_report_file(maindir, maindir + '.report')
+ if sourcedir and maindir != sourcedir:
+ write_report_file(sourcedir, sourcedir + '.report')
+ if debugdir and maindir != debugdir:
+ write_report_file(debugdir, debugdir + '.report')
+
# CHANGELOG file
# the tools read the subdirectory of the rpmdir from environment variable
os.environ['ROOT_ON_CD'] = '.'
@@ -341,13 +352,13 @@
def create_checksums_file(maindir):
with open(maindir + '/CHECKSUMS', 'a') as chksums_file:
- for subdir in ('boot', 'EFI', 'docu', 'media.1'):
- if not os.path.exists(maindir + '/' + subdir):
- continue
- for root, dirnames, filenames in os.walk(maindir + '/' + subdir):
- for name in filenames:
- relname = os.path.relpath(root + '/' + name, maindir)
- run_helper([chksums_tool, relname], cwd=maindir,
stdout=chksums_file)
+ for subdir in ('boot', 'EFI', 'docu', 'media.1'):
+ if not os.path.exists(maindir + '/' + subdir):
+ continue
+ for root, dirnames, filenames in os.walk(maindir + '/' + subdir):
+ for name in filenames:
+ relname = os.path.relpath(root + '/' + name, maindir)
+ run_helper([chksums_tool, relname], cwd=maindir,
stdout=chksums_file)
# create a fake entry from an updateinfo package spec
def create_updateinfo_entry(pkgentry):
@@ -465,7 +476,8 @@
missing_package = False
for package in create_package_list(yml['unpack_packages'], arch, flavor):
- rpm = lookup_rpm(arch, package)
+ name, op, epoch, version, release = split_package_spec(package)
+ rpm = lookup_rpm(arch, name, op, epoch, version, release)
if not rpm:
warn("package " + package + " not found")
missing_package = True
@@ -525,12 +537,6 @@
missing_package = None
for package in create_package_list(yml['packages'], arch, flavor):
name, op, epoch, version, release = split_package_spec(package)
- if name not in local_rpms:
- warn("package " + package + " not found")
- missing_package = True
- continue
-
- # We may want to put multiple candidates on the medium
if singlemode:
rpm = lookup_rpm(arch, name, op, epoch, version, release)
rpms = [rpm] if rpm else []
@@ -547,7 +553,7 @@
match = re.match(r'^(.*)-([^-]*)-([^-]*)\.([^\.]*)\.rpm$',
rpm['tags']['sourcerpm'])
if not match:
- warn("rpm package " + entry_nvra(rpm) + " does not have a
source rpm")
+ warn("package " + entry_nvra(rpm) + " does not have a source
rpm")
continue
source_package_name = match.group(1)
@@ -580,13 +586,46 @@
def link_file_into_dir(filename, directory):
if not os.path.exists(directory):
os.mkdir(directory)
- outname = directory + '/' + re.sub(r'.*/', '', filename)
+ outname = directory + '/' + os.path.basename(filename)
if not os.path.exists(outname):
os.link(filename, outname)
def link_entry_into_dir(entry, directory):
link_file_into_dir(entry['filename'], directory + '/' +
entry['tags']['arch'])
+ add_entry_to_report(entry, directory)
+
+def add_entry_to_report(entry, directory):
+ outname = directory + '/' + entry['tags']['arch'] + '/' +
os.path.basename(entry['filename'])
+ # 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('/'):
+ directory += '/'
+ for fn, entry in sorted(tree_report.items()):
+ if not fn.startswith(directory):
+ continue
+ binary = ET.SubElement(root, 'binary')
+ binary.text = 'obs://' + entry['origin']
+ tags = entry['tags']
+ for tag in 'name', 'epoch', 'version', 'release', 'arch', 'buildtime',
'disturl', 'license':
+ if tags[tag] is None or tags[tag] == '':
+ continue
+ if tag == 'epoch' and tags[tag] == 0:
+ continue
+ if tag == 'arch':
+ binary.set('binaryarch', str(tags[tag]))
+ else:
+ binary.set(tag, str(tags[tag]))
+ if tags['name'].endswith('-release'):
+ cpeid = read_cpeid(entry['filename'])
+ if cpeid:
+ binary.set('cpeid', cpeid)
+ tree = ET.ElementTree(root)
+ tree.write(outfile)
def entry_qualifies(entry, arch, name, op, epoch, version, release):
tags = entry['tags']
@@ -651,17 +690,12 @@
ts.setVSFlags(rpm._RPMVSF_NOSIGNATURES)
for dirpath, dirs, files in os.walk(directory):
- subdirs = dirpath.replace(directory,'').split('/')
- if len(subdirs) < 4:
+ reldirpath = os.path.relpath(dirpath, directory)
+ subdirs = reldirpath.split('/')
+ if len(subdirs) < 1:
continue
- project = subdirs[-3]
- repository = subdirs[-2]
arch = subdirs[-1]
- # we try to avoid second path config for now
- # if not project+"/"+repository in yml['repositories']:
- # print("Warning: local repo not listed in yml file: " + project +
"/" + repository)
- # continue
- note("scanning: " + project + "/" + repository)
+ note("scanning: " + reldirpath)
for filename in files:
fname = os.path.join(dirpath, filename)
if arch == 'updateinfo':
@@ -672,13 +706,13 @@
h = ts.hdrFromFdno(fd)
os.close(fd)
tags = {}
- for tag in 'name', 'epoch', 'version', 'release', 'arch',
'sourcerpm', 'nosource', 'nopatch':
+ for tag in 'name', 'epoch', 'version', 'release', 'arch',
'sourcerpm', 'nosource', 'nopatch', 'buildtime', 'disturl', 'license':
tags[tag] = h[tag]
if not tags['sourcerpm']:
tags['arch'] = 'nosrc' if tags['nosource'] or
tags['nopatch'] else 'src'
- item = { 'filename': fname, 'tags': tags }
+ item = { 'filename': fname, 'origin':os.path.join(reldirpath,
filename), 'tags': tags }
# add entry to local_rpms hash
name = tags['name']
@@ -686,6 +720,35 @@
local_rpms[name] = []
local_rpms[name].append(item)
+def cpeid_hexdecode(p):
+ pout = ''
+ while True:
+ match = re.match(r'^(.*?)%([0-9a-fA-F][0-9a-fA-F])(.*)', p)
+ if not match:
+ return pout + p
+ pout = pout + match.group(1) + chr(int(match.group(2), 16))
+ p = match.group(3)
+
+def read_cpeid(fname):
+ ts = rpm.TransactionSet()
+ ts.setVSFlags(rpm._RPMVSF_NOSIGNATURES)
+ fd = os.open(fname, os.O_RDONLY)
+ h = ts.hdrFromFdno(fd)
+ os.close(fd)
+ pn = h['providename']
+ pf = h['provideflags']
+ pv = h['provideversion']
+ if pn and pf and pv:
+ idx = 0
+ for p in h['providename']:
+ if p == 'product-cpeid()':
+ f = pf[idx]
+ v = pv[idx]
+ if v and f and (f & 14) == 8:
+ return cpeid_hexdecode(v)
+ idx = idx + 1
+ return None
+
if __name__ == "__main__":
try:
status = main()
++++++ product-composer.obsinfo ++++++
--- /var/tmp/diff_new_pack.ucyY1r/_old 2023-12-06 23:47:29.189625291 +0100
+++ /var/tmp/diff_new_pack.ucyY1r/_new 2023-12-06 23:47:29.189625291 +0100
@@ -1,5 +1,5 @@
name: product-composer
-version: 0.1
-mtime: 1701699734
-commit: 911f9cad2d0557836155b12b68df6c78b1c489bd
+version: 0.2
+mtime: 1701786966
+commit: 6657c85d63ff68142000d775a5bf65bc2bc32bda