Skip to content

CLOS-7051: CloudLinux 9 to CloudLinux 10 upgrade data - #28

Open
prilr wants to merge 19 commits into
cloudlinux:cloudlinuxfrom
prilr:CLOS-7051-cl9-to-cl10-upgrade-data
Open

prilr wants to merge 19 commits into
cloudlinux:cloudlinuxfrom
prilr:CLOS-7051-cl9-to-cl10-upgrade-data

Conversation

@prilr

@prilr prilr commented Sep 23, 2026

Copy link
Copy Markdown
Collaborator

The data side of the CloudLinux 9 to CloudLinux 10 upgrade support.

PES data is now two composed layers

The shipped pes-events.json used to be a single source file - AlmaLinux's data with our events mixed in, nothing marking which was which.

Rebasing meant comparing event content across two 20 MB files, and most apparent differences were not differences: upstream reshapes repository names and minor versions freely, so most of "our" events were actually upstream's.

The source is now split, and the final file is generated from a composition:

file owner
pes-events-upstream.json AlmaLinux, not hand-edited
pes-events-cloudlinux.json ours, applied on top
pes-events.json built result

make all fails if an event in our layer has been adopted upstream, naming what to delete.

Suppressed upstream removals

AlmaLinux marks libnsl2, libwmf-lite, LibRaw, libdb, libdb-utils, enchant and libmemcached-awesome as Removed on 9 to 10.

Correct for AlmaLinux. On CloudLinux all seven still ship for el10 and the CloudLinux stack links against them:

alt-python-internal-libs -> libnsl.so.3          -> libnsl2
lve-stats -> alt-ImageMagick -> libwmflite.so.7  -> libwmf-lite, LibRaw
alt-cyrus-sasl-lib, alt-openldap11-servers, alt-php-internal-dba -> libdb
alt-php-internal-enchant -> enchant
gearmand -> libmemcached-awesome

Keeping the AlmaLinux pattern would silently wipe the LVE/CageFS stack on upgrade, and machine would boot into the new environment without them.

Therefore, these PES events are suppressed at compose time.

Added removal audit for early warning

tools/removal_audit.py reports an upstream removal as wrong for CloudLinux when the package still ships for the target and a CloudLinux package requires it.

This way a similar situation in the future can be caught without having to investigate the full state of the post-upgrade machine for missing components.

ALT-ELS repositories

CloudLinux 9 serves the Selector runtimes from the main channel; CloudLinux 10 serves them from per-language ALT-ELS repositories.

Declared here for the el10 target, with URL shapes copied from the els-*-release packages.

Both kinds of source machines are mapped: cl-channel for machines built when the alt-* packages shipped in the common repositories, and php-els/alt-python/alt-ruby/alt-nodejs for machines whose cloudlinux-release already pulled alt-common-release.

GPG keys

Target repository files pointed at the pre-0.24.0 key directory, which also broke CloudLinux 8 to 9 with new leapp-repository update.
The EPEL 10 signing key was missing entirely - vendor keys install per target and there was no el10 source.
Fixed both.

prilr and others added 19 commits September 18, 2026 07:53
leapp-repository 0.24.0 raised RepoMapData.VERSION_FORMAT to 1.3.0 and made
'distro' a required field on every repository entry. It compares the format with
== rather than >=, so our 1.2.0 files are not read leniently - they are rejected,
and repositoriesmapping inhibits the upgrade with "The repository mapping file is
invalid". That applies to CL7 -> CL8 and CL8 -> CL9 as much as to the CL9 -> CL10
work this is for, so all 34 repomap files move together.

'distro' is what leapp matches entries against - get_source_distro_id() and
get_target_distro_id(), both of which are the /etc/os-release ID. An entry naming
a distro the system does not have is never selected and says nothing about it, so
the value has to be right rather than merely present. Two consequences worth
stating:

* Our target entries say cloudlinux even where the repoid is almalinux9-baseos
  and friends. The repoid is an identifier; the distro is the key. Target distro
  defaults to the source distro (commands/upgrade/util.py sets LEAPP_TARGET_OS
  from get_source_distro_id()), so on a CloudLinux box it is cloudlinux on both
  sides of the upgrade.
* vendors.d/ is shared source compiled once per distro package, so its entries
  carry a {distro} placeholder that the build substitutes, the way upstream does
  it. A placeholder reaching the built package would be a distro that matches
  nothing, so the Makefile substitutes and then validates.

tools/repomap_check.py grew from a json.load() into a check of the contract
RepoMapData.load_from_dict actually imposes: the exact version_format, the six
keys add_repository indexes (rhui stays optional), every pesid a mapping refers
to being declared, and - with --distro - that entries claim the distro whose file
they are in. The checks are copied rather than imported because leapp-data builds
without leapp-repository present; the docstring says which constants drift.

Both existing builds were exercised, and the check is wired into them rather than
left to be run by hand. Confirmed load-bearing: putting one file back to 1.2.0
fails the build, removing 'distro' from a single entry fails the build, and
restoring either makes it pass.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Worked-On: cl-aiworkspaces
leapp-repository 0.24.0 sets CONSUMED_DATA_STREAM_ID = '4.0'. Our assets top out
at major 3, and checkconsumedassets compares only the major: anything below the
consumed one is "outdated" and inhibits the upgrade with "Detected outdated Leapp
data assets". Like the repomap format bump before it, this is not specific to
CL9 -> CL10 - it would stop CL7 -> CL8 and CL8 -> CL9 just as dead.

All 53 assets that carry provided_data_streams gain "4.0". The older streams stay
alongside rather than being replaced, because the check passes when *any*
advertised major matches; keeping them means the same data files are still
readable by an older leapp that is installed next to them.

pes-events.json is 15 MB, so this is a text edit of the one array rather than a
JSON round-trip - the diff on that file is two lines. Files with no
provided_data_streams field at all are left alone: leapp treats a missing header
as outdated too, but that is a pre-existing question about which of those files
it actually reads, and is not something this change can answer.

tools/repomap_check.py now covers both contracts and applies the repomap half
only to files that are repomaps, so one tool validates every asset. The build
runs it over the built tree, so generated files are covered too. Confirmed
load-bearing: removing "4.0" from a single file fails make test with the exact
reason, restoring it passes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Worked-On: cl-aiworkspaces
Enough for `make DIST_VERSION=9 all test` to produce a leapp-data-cloudlinux that
describes the CL9 -> CL10 transition. All three paths (7->8, 8->9, 9->10) build
and test clean, and the artefacts the existing two produce are byte-identical to
before.

What the target side actually looks like, established against the live
repositories rather than assumed from the el9 file:

* CloudLinux 10 has no BaseOS/AppStream split. repo.cloudlinux.com serves
  /cloudlinux/10/cloudlinux-x86_64-server-10/ and returns 404 for
  /cloudlinux/10/BaseOS/ and /AppStream/, so every CloudLinux-side source
  repository maps onto the one cloudlinux10-channel, and there is no
  cloudlinux10-baseos or cloudlinux10-appstream to map to.
* That channel's mirrorlist names the major version literally instead of going
  through $releasever, which is what the el9 file does. Minor routing is not
  populated for 10: cl-mirrors/cloudlinux-x86_64-server-10.0 (and .1, .2) all
  resolve to a mirror root with no path, so a $releasever expanding to a minor
  version would yield a broken repository. Worth revisiting once it exists.
* AlmaLinux 10 dropped ResilientStorage, so unlike el9 there is no counterpart
  for it and the source pesid is not mapped - upstream's own el10 map does the
  same.
* The AlmaLinux 10 signing key is upstream's, fingerprint verified against the
  copy published at repo.almalinux.org: EE6D B7B9 8F5B F5ED D9DA 0DE5 DEE5 C11C
  C2A1 E572.

Vendors: epel, imunify, kernelcare, mariadb, nginx-stable, nginx-mainline and
postgresql come from upstream's el10 templates with the AlmaLinux 10 repository
names substituted. alt-common is ours and was written here, against a TuxCare
el10 repository confirmed to exist. cloudlinux_ea4, cloudlinux_ea4_testing and
cloudlinux_testing have no el10 data and are therefore not mapped - EA4 is
cPanel's and cPanel does not support CL10 - so the vendor loops now skip a vendor
with no data for the target instead of calling install on files that are not
there. That also clears the "cannot stat" noise those loops printed on every
build.

Two build defects fixed on the way:

* `find -name '*.el?'` matches exactly one character after "el", so the suffixed
  sources for a two-digit target would have shipped inside the package.
* The CL7 -> CL8 map targets almalinux8-ha while the repository file defines
  [almalinux8-highavailability]. Nothing reports this: leapp just never finds the
  target, so HighAvailability content is unreachable on a CL7 host that had that
  repository enabled. Found by the new cross-check, which validates that every
  target a map names is defined in the leapp_upgrade_repositories.repo shipping
  beside it, and is wired into the build so it cannot come back.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Worked-On: cl-aiworkspaces
CL9 -> CL10 package events go from 193 to 2428, and 7 -> 8 and 8 -> 9 pick up two
years of upstream corrections along the way (5074 -> 5199 and 3577 -> 3656).

Our file was the 2024 AlmaLinux rebase with CloudLinux events layered on, so the
merge had to work out which events are actually ours. Comparing events whole says
1874 are unique to us. That number is mostly an illusion: matching on action, the
major-version transition and the package names alone - ignoring repository, minor
version and architecture - drops it to 220. The other 1654 are the same events
with a field upstream has since reshaped, usually a renamed repository, and
re-adding them would have duplicated upstream's own data in a stale form.

So the base is upstream's file and the 220 are carried on top. 66 of them are
recognisably ours: the alt-php*-mysql5.0/5.1 and alt-php*-percona5.5/5.7 removals,
lua-cjson -> lua51-cjson (CLOS-2598), libbrotli -> brotli. The rest are generic EL
events upstream no longer lists; they are kept because dropping them would lose
coverage we ship today, and an extra event is additive rather than harmful.

rebuild_ids.py then renumbers every PES file in the CloudLinux set, so the vendor
files change too - ids and set_ids only, verified by diffing them with those
fields stripped.

Checked with the bundled validators rather than by inspection: the schema check
covers all twelve pes files including the merged one, and validate_ids reports no
duplicate ids or set_ids. Confirmed that check is load-bearing by injecting a
duplicate id into the built tree and watching make test fail on it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Worked-On: cl-aiworkspaces
Rebasing PES data should not be archaeology. This one was: our events and
AlmaLinux's sat in one 20 MB file with nothing marking which was which, so the
merge had to infer ownership by comparing event content - and most of what looked
like our divergence was upstream reshaping a repository name or a minor version
on its own events. 1654 of the 1874 apparent differences were that.

leapp reads exactly one unconditional PES file, and vendor *_pes.json files only
load when that vendor's repositories are active on the host, so our events cannot
simply move into one. The single file therefore stays - but it is now built
rather than authored:

  files/cloudlinux/pes-events-upstream.json    verbatim AlmaLinux, never hand-edited
  files/cloudlinux/pes-events-cloudlinux.json  ours, 220 events, 212 KB
  files/cloudlinux/pes-events.json             generated by make, gitignored

tools/compose_pes.py concatenates the layers and fails the build if an event in
our layer has meanwhile been adopted upstream, naming the ones to delete - so the
overlay shrinks as upstream catches up instead of silently duplicating it.
Identity there ignores repository, minor version and architecture, which is the
lesson from this merge: an event upstream reshaped is still the same event.

tools/refresh_upstream_pes.py replaces the upstream layer from a given AlmaLinux
ref and prints the event count per version transition before and after, so a
refresh that quietly drops a transition is visible rather than discovered later.

Event ids move to build time too. They are an artefact of concatenation order, and
committing them meant every PES change churned every other PES file - 42,000 lines
of the epel template moved in the previous commit for no reason but renumbering.
rebuild_ids.py now takes directories and the build points it at the built tree.

Behaviour-preserving, and checked rather than asserted: the composed artefact is
identical to the file the previous commit shipped, ids included, both from the
layers as split and after regenerating the upstream layer with the refresh script.
The collision guard was confirmed by planting an event upstream already has and
watching the build refuse it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Worked-On: cl-aiworkspaces
leapp-repository's get_path_to_gpg_certs() is per-distro since 0.24.0:
common/files/distro/<distro>/rpm-gpg/<target major>, where it used to be a
distro-agnostic common/files/rpm-gpg/<target major>. The old tree is gone
upstream, so keys installed there are simply never read.

leapp-repository now ships the CloudLinux keys at the new path itself, the way
AlmaLinux does. This keeps leapp-data installing them too, at the same place, so
a key rotation can still ship as a data update rather than needing a
leapp-repository rebuild.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Worked-On: cl-aiworkspaces
Installing the target GPG keys at leapp 0.24.0's per-distro path moved them out
from under common/files/rpm-gpg/<major>, where the el9 and el10 target
repository files still looked for them. dnf then fails the target transaction
with

    Curl error (37): Couldn't read a file:// file for file:///etc/leapp/...

naming neither the repofile nor the package, and only once a real upgrade
reaches the target repositories - after the userspace has been built.

A gpgkey= path is a plain string, so nothing tied it to where the Makefile
installs keys. tests/check_gpgkey_paths.py now resolves every leapp-owned
gpgkey in the built repofile against the build tree, so a key that moves
without its references fails the build instead of an upgrade. It ignores
/etc/pki paths, which cloudlinux-release owns rather than this package.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Worked-On: cl-aiworkspaces
EPEL keys are installed per target from a <vendor>.gpg.el<major> source, so a
vendor with no file for a new target ships no key at all. On a CloudLinux 9 to
10 run leapp then reported

    Failed to read GPG keys from provided key files
    - file:///etc/leapp/files/vendors.d/rpm-gpg/epel.gpg

which names a path, not the missing build input. epel.gpg.el10 now carries
Fedora's epel10 key, 7D8D15CBFC4E62688591FB2633D98517E37ED158.

check_gpgkey_paths.py now walks every built repofile rather than the target
repository file alone, and treats a present-but-empty key as missing too - the
exact shape this produced. That immediately caught kernelcare.repo.el10 naming
rpm-gpg/kernelcare.gpg where the shipped file, and both older targets, say
RPM-GPG-KEY-KernelCare.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Worked-On: cl-aiworkspaces
A CloudLinux 9 to 10 upgrade completed, booted, and arrived with no lve-utils,
cagefs, lvemanager or lve-stats. The cause is three AlmaLinux PES events, and
libsolv names it exactly once LEAPP_DEBUG=1 turns debugsolver on:

  package alt-python-internal-libs-3.11.12-2.el10 requires libnsl.so.3,
  but none of the providers can be installed
    solution: deljob erase pkg libnsl2-2.0.0-1.el9.x86_64

AlmaLinux marks libnsl2, libwmf-lite and LibRaw as Removed on 9 -> 10, which is
right for a stock AlmaLinux box. On CloudLinux all three exist for el10 -
libnsl2 in alt-common, the other two in EPEL 10, both enabled target
repositories - and the CloudLinux stack links against them:

  alt-python-internal-libs -> libnsl.so.3        -> libnsl2
  lve-stats -> alt-ImageMagick -> libwmflite.so.7 -> libwmf-lite, and LibRaw

leapp turns each Removed event into `job erase pkg X [cleandeps,forcebest]`.
With the provider erase-jobbed the el10 build cannot install, and the 3193
`job allowuninstall` entries that `allow_erasing` produces convert the failed
update into a silent uninstall - which is why the solve reports zero problems
and no log line ever names a cause. The cascade takes cloudlinux-venv, then
lve-utils, cagefs and alt-python27-cllib, then lve-wrappers and lvemanager.

Suppressing the three events puts every one of them back to a plain upgrade at
the same version, and to_remove drops from 61 to 58.

Adding contradicting Present events would not have worked: leapp dedups on
(from_release, in_pkgs) and, when two events tie on to_release, keeps both and
applies them as a union, so a Present beside a Removed is ambiguous rather than
an override. Winning that tie with a higher to_release minor is worse - relevant
releases are filtered to `source < r <= target`, so the event disappears for
anyone targeting a lower minor, and our upgrade paths offer 10.0, 10.1 and 10.2.

So suppression deletes the upstream event at compose time. Every rule carries a
reason, and a rule matching nothing fails the build, because upstream retiring
an event must not quietly turn a load-bearing rule into a no-op.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Worked-On: cl-aiworkspaces
libnsl2 was found by wrecking a box and reading a solver dump. That does not
scale, so the rule is now mechanical. tools/removal_audit.py reports an upstream
Removed event as wrong for CloudLinux when both hold:

  - the package still exists for the target in an enabled target repository, and
  - a CloudLinux or alt-* package built for the target requires it.

A dependent that is itself removed does not count: upstream retiring a whole
subsystem coherently (qt5 with qt5-qtbase, libdb-devel with libdb) is correct,
and over the real 9 -> 10 data that filter is the difference between 67 findings
and the 12 that break something surviving.

It is a report rather than a gate. Whether a given removal should be suppressed
is a judgement - the package may be genuinely unwanted on the target - and the
answer belongs in suppress_upstream with a reason.

Over the real data: 1314 packages removed on 9 -> 10, 406 still shipped for
el10, 12 with surviving dependents. Three were already suppressed. Four more are
clear-cut and added here:

  libdb                 alt-cyrus-sasl-lib, alt-openldap11-servers,
                        alt-php-internal-dba
  libdb-utils           alt-openldap11-servers
  enchant               alt-php-internal-enchant
  libmemcached-awesome  gearmand

libdb is not theoretical. alt-cyrus-sasl-lib was installed on a CloudLinux 9 box
and absent after an upgrade that otherwise looked clean - the same silent
erasure as the CloudLinux stack, on a package nobody was watching.

Left for a decision rather than suppressed: LibRaw-devel, libdb-devel and
libwmf-devel, whose only dependents are -static/-devel and which matter solely
to someone building against them; and php/php-cli, required by mod_suphp, where
whether a CloudLinux box should keep the base OS PHP at all is not this file's
call.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Worked-On: cl-aiworkspaces
The audit read raw PES events, so it reported removals that leapp never
resolves. Both corrections come from real false positives it produced.

Modulestreams. Upstream's php removal is scoped to streams 8.1 and 8.2:

    id=14919 action=1 in=['php'] modulestreams=[php:8.1, php:8.2]

Read without that scope it says "php is removed", which is false for a box on
8.3 - and php demonstrably upgraded 8.3.33-1.module_el9 -> 8.3.33-1.el10_2.
The scope is the finding, not a detail: whether the removal matters depends
entirely on which stream is installed. Findings now carry it.

The installed set. LibRaw-devel, libdb-devel and libwmf-devel were reported as
pending judgements when none was installed, so none could reach leapp's
to_remove. --installed takes an inventory and skips what nobody has. Without it
the audit still works as a screen, it just over-reports - which is now stated
rather than discovered.

For reference, leapp's resolved to_remove on a real CL9 box contained all four
suppressed packages (libdb, libdb-utils, enchant, libmemcached-awesome) and none
of the five this over-reported.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Worked-On: cl-aiworkspaces
CloudLinux 9 serves alt-php, alt-python, alt-ruby and alt-nodejs from the main
CloudLinux channel - `repoquery alt-php81` on a CL9 box answers cl-channel.
CloudLinux 10 serves them from per-language ALT-ELS repositories instead, gated
behind the els-*-release packages that cldeploy installs on a fresh conversion
(install_alt_els_repositories, cloud-tools/cldeploy).

Without them a CL9 -> CL10 upgrade leaves every alt-php package at its el9 build
with no repository to update from. The el10 ELS repository carries 12076
packages covering alt-php53 through alt-php86 - the whole PHP Selector - and all
of it was unreachable.

Declared here rather than in vendors.d: a vendor's repositories are only used
when that vendor is already active on the SOURCE, and on CloudLinux 9 these
repositories do not exist. The migration is precisely cl-channel -> these, which
the repomap now states.

The repo ids carry the el10- prefix that el10-alt-common and el10-epel already
use. Reusing the ids from els-*-release makes leapp see each repository twice and
inhibit with "A YUM/DNF repository defined multiple times" - a certain collision,
since the upgrade installs those very packages. Measured: without the prefix the
inhibitor fired and took the cloudlinux-release install down with it.

Both URL shapes are copied verbatim from the release packages rather than
normalised - php puts the CLN JWT in the basic-auth userinfo, the other three in
a path segment - and every one carries skip_if_unavailable, so a box with no
token still upgrades, just without ELS content.

Verified on an IP-registered CloudLinux 9 box: alt-php81 resolves to
8.1.34-28.el10 from el10-php-els inside the transaction.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Worked-On: cl-aiworkspaces
A CloudLinux 9 box can already have the ALT-ELS repositories: cloudlinux-release
pulls alt-common-release, which makes the els-*-release packages available, and a
box that installed them has php-els, alt-python, alt-ruby and alt-nodejs
configured. Machines built when the alt-* packages still shipped in the common
CloudLinux repositories do not, but that is the older case and shrinking.

Only cl-channel was mapped, which covers those older machines. A box that already
has the ELS repositories had no mapping for its source repoids at all. It worked
regardless - the target repositories are enabled unconditionally from the
repofile - but it worked by side effect rather than by statement, and a repomap
that does not describe the migration is one nobody can check.

Both paths are now declared:

    cl-channel  -> cloudlinux10-channel + the four el10 ELS repositories
    php-els     -> el10-php-els
    alt-python  -> el10-alt-python
    alt-ruby    -> el10-alt-ruby
    alt-nodejs  -> el10-alt-nodejs

On an older machine the new entries simply never match.

This case is also what makes the el10- prefix load-bearing rather than tidy: with
the source repositories present, reusing their ids fires "A YUM/DNF repository
defined multiple times" and takes the cloudlinux-release install down with it.
Tested against a box in exactly that state - had it been tested only against an
older machine, the prefix would have looked optional.

Verified on an IP-registered CloudLinux 9 box carrying all four source ELS
repositories: RC=0, no inhibitors, and every Selector resolves from its el10 ELS
repository - alt-php81 8.1.34-28.el10, alt-python39 3.9.23-26.el10, alt-ruby33
3.3.12-12.el10, alt-nodejs20 1-1.el10.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Worked-On: cl-aiworkspaces
…elog

The entry stopped at the first week and did not mention the ALT-ELS
repositories, the seven suppressed upstream removals, the removal audit, the
EPEL 10 key or the gpgkey path checks - which is most of what the package now
contains, and all of what a reader would need to judge it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Worked-On: cl-aiworkspaces
scancustomrepofile emits a CustomTargetRepository for every section of
/etc/leapp/files/leapp_upgrade_repositories.repo, unconditionally, so a
repository named there becomes a target repository on every system - whatever
its enabled flag says, and whatever the repomap does.

That is wrong for repositories needing credentials. The ALT-ELS repositories
authenticate with the CLN JWT, and on a box without one they answer 403 with
$phpelstoken unexpanded, failing the target userspace build. Measured on an
unregistered CloudLinux 9 box, where it blocked the upgrade outright.

They move to cloudlinux-els.repo, installed beside the main repofile but scanned
by nothing. copy_els_auth_to_target_userspace pulls it in only when the token
exists. The repomap entries go with them: enabling these is the actor's call
now, not the mapping's.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Worked-On: cl-aiworkspaces
The removal audit left LibRaw-devel, libdb-devel and libwmf-devel as
judgements because their only dependents are -static/-devel packages:

  LibRaw-devel  <- LibRaw-static, alt-ImageMagick-static-devel
  libdb-devel   <- libdb-devel-static
  libwmf-devel  <- alt-ImageMagick-static-devel

All three ship in EPEL 10 beside the runtime libraries already kept
(LibRaw, libdb, libwmf-lite). Leaving upstream's Removed events in place
keeps each library but erases its headers, and allow_erasing takes the
dependents with them without a word. That only bites a box that builds
against these libraries, but there it is the same silent loss the rest
of the list exists to prevent.

The audit now reports only php and php-cli, whose removal is scoped to
the base-OS php:8.1 and php:8.2 streams; no el10 build of either stream
exists, so suppressing them would be wrong.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N87iQquEgjuFPaAm1onnYe
Worked-On: cl-aiworkspaces
The first CLBS build of this branch failed on CL8 in %build:

  python3 rebuild_ids.py build/etc/leapp/files build/etc/leapp/files/vendors.d
    from dataclasses import dataclass
  ModuleNotFoundError: No module named 'dataclasses'

Renumbering the PES ids moved into the build with the upstream/ours
split, and the python3 of the el7 and el8 build roots is 3.6, which has
no dataclasses. JSONFile is now a plain class; nothing else in the
script needs more than 3.6.

CL7 failed the same way and was reported as built. %build was
`make DIST_VERSION=%{?rhel} all && make test`, and rpm runs %build under
sh -e, which does not stop at a failing command that is not the last of
an && list: the failed `make all` skipped `make test`, the scriptlet
exited 0, and el7 wrote leapp-data-cloudlinux-0.3-10.el7 with a
pes-events.json that was never renumbered and never validated. el8's
rpm propagated the status, which is the only reason one platform went
red. The two commands are now separate lines, so either can fail the
build.

Also correct the 0.3-10 date line: Sep 18 2026 is a Friday, and rpm was
warning about a bogus date.

tools/tests/test_rebuild_ids.py runs the script with dataclasses made
unimportable, as on 3.6, pins the renumbering (unique and sequential
across the tree, front file first, un-composed layers untouched), and
fails on an && or || in a %build command. Reverting either fix turns it
red. A local `make DIST_VERSION=8 all && make test` gives 18595 events
with unique ids.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N87iQquEgjuFPaAm1onnYe
Worked-On: cl-aiworkspaces
Four .pyc files for the new tools and their tests were committed with
them. Ignore __pycache__/ and *.pyc so it does not recur.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N87iQquEgjuFPaAm1onnYe
Worked-On: cl-aiworkspaces
AlmaLinux removes these on 9 -> 10 and AlmaLinux 10 has no build of any
of them, but TuxCare's el10-alt-common - a target repository of this
upgrade - ships every one, EPEL 10 ships them too, and dnf repoclosure
installs them against AlmaLinux 10 and alt-common alone:

  qt5-qtbase and -common -devel -examples -gui -mysql -odbc -postgresql
    -private-devel -static, qt5-rpm-macros, qt5-srpm-macros  5.15.17-1.el10
  libdb-cxx, libdb-cxx-devel, libdb-devel-doc, libdb-sql,
    libdb-sql-devel                                           5.3.28-61.el10
  double-conversion, double-conversion-devel                  3.3.0-4.el10
  libmemcached-awesome-devel, libmemcached-awesome-tools      1.1.4-5.el10
  enchant-devel 1.6.0-39.el10, libwmf 0.2.13-6.el10

The ten suppressed so far were the ones the CloudLinux stack needs; these
are kept because the target can carry them, so a box that has them keeps
them at their el10 build instead of losing them and, under allow_erasing,
whatever requires them. The EPEL vendor data has no 9 -> 10 event for any
of them, so upstream's removal applied with or without EPEL.

qt5 and qt5-devel stay removed: their el10 builds require 21 qt5 modules
that nothing outside EPEL ships for el10.

tools/tests/test_cloudlinux_pes_data.py composes the real layers, as the
build does, and pins both sides of that: the 33 preserved names are not
removed on 9 -> 10, and qt5 and qt5-devel still are.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N87iQquEgjuFPaAm1onnYe
Worked-On: cl-aiworkspaces
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant