mirror of
https://github.com/xcat2/xcat-dep.git
synced 2026-09-12 04:26:25 +00:00
fix(xcat-dep): pin the FULL deb version, and collect only real build output
The manifest pinned only the UPSTREAM version: deb_version() and
resolve_present_names() both reduced the version with deb_upstream_version()
before comparing, dropping the epoch and the debian_revision. A package can
carry the right upstream version and still be the wrong package:
* the debian_revision is the PACKAGING revision -- elilo-xcat 3.14-5 and
3.14-6 are different builds of the same upstream 3.14;
* the epoch overrides version comparison outright -- an un-epoched 2.19.0
sorts BELOW 2:2.18.0.
That matters here because xCAT's own debian/control declares versioned
dependencies on these packages -- goconserver (>= 0.3.3-snap...), ipmitool-xcat
(>= 1.8.17-1), grub2-xcat (>= 2.02-...) in xCAT, xCATsn and xCAT-server -- so an
upstream-only pin can accept a deb that the gate calls good and that
`apt install xCAT` then refuses.
The pin is now matched against the FULL [epoch:]upstream[-revision], exactly as
it appears in the built .deb and in the published Packages index. No new
operator was needed: the existing exact/glob/'*' grammar simply applies to the
whole version, which is stricter than a >= floor because it also rejects an
epoch the pin does not name. Six pins become fully exact; two stay globbed
because their revision is not owned by the checkout:
* goconserver=0.3.3-snap* -- upstream exact; the revision is the CD stamp
snap<SOURCE_DATE_EPOCH>, which changes every run. The glob still REQUIRES a
snap-stamped revision, so an unstamped deb is rejected.
* xcat-genesis-base=2.* -- its version is not owned by xcat-dep; it walks
with whatever xcat-core the genesis was built from (XCAT_CORE_REF), and the
two arches legitimately differ since each is converted from its own rpm.
The gate still enforces exactly one genesis FOR THIS ARCH.
deb_version() gained an optional $arch for that logical name: both arch-suffixed
genesis debs are staged on the amd64 host (the cross-arch ppc one for #7610) and
carry different revisions, so without it the pair would look like a version
conflict. This mirrors resolve_present_names, which already refuses to borrow
another arch's genesis.
Comparing full versions also exposed a pre-existing defect it had been masking:
the collector treated PREBUILT .deb files checked into the source tree as build
output. elilo/ ships elilo-xcat_3.14-5_all.deb, elilo-xcat_3.14-6_all.deb and
gnu-efi_3.0v-5_amd64.deb; the first and third were being published into the apt
repo under this run's name, and the stale 3.14-5 collided with the freshly built
3.14-6 once revisions were compared. The build now snapshots the .debs present
before the build and subtracts them afterwards -- a file the build overwrites
changes size/mtime and still counts as output. Verified in the noble chroot: only
elilo-xcat_3.14-6_all.deb is collected; gnu-efi and 3.14-5 are reported and left
behind.
deb_upstream_version() has no remaining callers and is removed.
Tests: version_matches over whole versions (stale revision rejected, unnamed
epoch rejected, both globs still enforcing what they should); deb_version
returning the full version, dying on two revisions of one upstream version (the
elilo case), and selecting the per-arch genesis; resolve_present_names handing
the full version to the comparator; and two end-to-end cases in t/verify-repo.t
asserting the gate now fails a stale packaging revision and an unnamed epoch.
Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com>
This commit is contained in:
@@ -438,8 +438,21 @@ parsing/resolution), separate from the disk/gpg I/O.
|
||||
- **Duplicate = hard error:** a package appearing in the index with **two distinct versions** (a stale
|
||||
`.deb` not cleaned from the pool) makes `parse_packages_index` **die loudly** rather than keep one —
|
||||
identical behaviour to the EL `rpm_version` gate.
|
||||
- **Version pins** are the manifest's *upstream* version; the published Debian version's epoch/revision
|
||||
is stripped (`deb_upstream_version`) before the pin compare.
|
||||
- **Version pins** match the **full** Debian version — `[epoch:]upstream[-revision]` — exactly as it
|
||||
appears in the built `.deb` and in the published index. The revision is the *packaging* revision
|
||||
(`elilo-xcat` 3.14-5 and 3.14-6 are different builds of the same upstream 3.14) and the epoch
|
||||
overrides version comparison outright, so an upstream-only pin would accept a deb that the gate
|
||||
calls good and `apt install xCAT` then refuses — xCAT's own `debian/control` declares versioned
|
||||
dependencies (`goconserver (>= 0.3.3-snap…)`, `ipmitool-xcat (>= 1.8.17-1)`, `grub2-xcat (>= 2.02-…)`).
|
||||
Two pins stay globbed on purpose: `goconserver=0.3.3-snap*` (upstream exact; the revision is the CD
|
||||
stamp, which changes every run — the glob still *requires* a snap stamp) and `xcat-genesis-base=2.*`
|
||||
(its version is not owned by xcat-dep, and each arch is converted from its own genesis rpm).
|
||||
- **Only build output is collected.** Some package directories carry prebuilt `.deb` files in the
|
||||
checkout — `elilo/` ships `elilo-xcat_3.14-5_all.deb` and `gnu-efi_3.0v-5_amd64.deb`. Those arrive
|
||||
with the copied source tree and are **not** output of the build, so the collector snapshots the
|
||||
`.deb`s present before the build and subtracts them afterwards (a file the build overwrites changes
|
||||
size/mtime and still counts). Without this, a stale checked-in artifact is republished under the
|
||||
current run's name — and with full-version pins it also collides with the freshly built one.
|
||||
|
||||
# Packages notes
|
||||
|
||||
|
||||
+51
-34
@@ -30,7 +30,7 @@ our @EXPORT_OK = qw(
|
||||
codename_to_version version_to_codename known_codenames
|
||||
chroot_name chroot_sources_list chroot_is_disposable chroot_build_script
|
||||
control_field genesis_deb_control
|
||||
deb_field deb_version deb_upstream_version deb_hash cross_copy_genesis_deb
|
||||
deb_field deb_version deb_hash cross_copy_genesis_deb
|
||||
build_deb_in_chroot
|
||||
);
|
||||
|
||||
@@ -253,7 +253,7 @@ sub parse_release_architectures {
|
||||
# parse_packages_index($text) -> \%{ package_name => version }
|
||||
# Parse a Debian 'Packages' index: RFC822 stanzas separated by blank line(s); each carries a
|
||||
# 'Package:' and a 'Version:'. Returns name => version (the FULL Debian version verbatim, epoch +
|
||||
# revision included -- the caller strips to the upstream part with deb_upstream_version). A stanza
|
||||
# revision included -- which is exactly what the manifest pin is compared against). A stanza
|
||||
# lacking either field is skipped; malformed/empty input yields an empty hash.
|
||||
# DUPLICATE = LOUD ERROR: apt-ftparchive emits one stanza per package, so a name appearing twice with
|
||||
# DISTINCT versions means a stale .deb was not cleaned from the pool before assemble -- a version pin
|
||||
@@ -322,10 +322,13 @@ sub skip_arch_all_on {
|
||||
return ((control_binary_arch($control_text, $binpkg) // '') eq 'all') ? 1 : 0;
|
||||
}
|
||||
|
||||
# resolve_present_names(\%parsed, $arch, \@names) -> \%present (name => upstream version | undef)
|
||||
# resolve_present_names(\%parsed, $arch, \@names) -> \%present (name => FULL version | undef)
|
||||
# PURE. Resolves each manifest package NAME to the version actually in the parsed index (\%parsed from
|
||||
# parse_packages_index), reducing to the upstream version so it compares against the manifest's
|
||||
# upstream pins. Resolution order:
|
||||
# parse_packages_index), returning the FULL Debian version -- [epoch:]upstream[-revision], verbatim --
|
||||
# because that is what the manifest pins. Comparing only the upstream part would accept a package with
|
||||
# the right upstream version but the wrong epoch or a stale packaging revision, which is exactly what
|
||||
# xCAT's own versioned Depends (goconserver >= 0.3.3-snap..., ipmitool-xcat >= 1.8.17-1) care about.
|
||||
# Resolution order:
|
||||
# - exact index key (e.g. ipmitool-xcat), else
|
||||
# - the arch-suffixed key for THIS cell's arch (e.g. xcat-genesis-base -> xcat-genesis-base-<arch>).
|
||||
# It deliberately does NOT fall back to a DIFFERENT-arch suffix: the xcat-genesis-base-<arch> debs are
|
||||
@@ -338,7 +341,7 @@ sub resolve_present_names {
|
||||
my $full;
|
||||
if (exists $parsed->{$name}) { $full = $parsed->{$name}; }
|
||||
elsif (exists $parsed->{"$name-$arch"}) { $full = $parsed->{"$name-$arch"}; }
|
||||
$present{$name} = defined $full ? deb_upstream_version($full) : undef;
|
||||
$present{$name} = $full;
|
||||
}
|
||||
return \%present;
|
||||
}
|
||||
@@ -479,36 +482,38 @@ sub deb_field {
|
||||
return defined $v ? $v : '';
|
||||
}
|
||||
|
||||
# deb_upstream_version: the UPSTREAM part of a Debian version — strip a leading "epoch:" and the
|
||||
# trailing "-<debian_revision>" (dpkg splits the revision at the LAST dash). So '1.8.18-4' -> '1.8.18',
|
||||
# '2:0.3.3-snap202608101400.57' -> '0.3.3'. This is what the manifest pins (the Release/revision
|
||||
# carries the codename/snap stamp and is intentionally NOT pinned), mirroring EL rpm_version's %{version}.
|
||||
sub deb_upstream_version {
|
||||
my ($v) = @_;
|
||||
return $v unless defined $v;
|
||||
$v =~ s/^\d+://; # drop epoch
|
||||
$v =~ s/-[^-]*$//; # drop the last -revision segment
|
||||
return $v;
|
||||
}
|
||||
|
||||
# deb_version: the UPSTREAM Version of the built binary .deb named <pkg>_*.deb under $dir (undef if
|
||||
# absent). $pkg is the binary package name (e.g. 'ipmitool-xcat', 'goconserver', or the logical
|
||||
# 'xcat-genesis-base' which matches the arch-suffixed xcat-genesis-base-amd64/-ppc64el). Dies if the
|
||||
# dir holds more than one DISTINCT upstream version of the package (a stale artifact not cleaned
|
||||
# before the build — a version pin could otherwise pass against the wrong deb and both could ship).
|
||||
# deb_version: the FULL Version ([epoch:]upstream[-revision]) of the built binary .deb named
|
||||
# <pkg>_*.deb under $dir (undef if absent) -- the same string the published index carries, so
|
||||
# build-time validation and publish-time verification compare like with like against the manifest pin.
|
||||
# $pkg is the binary package name (e.g. 'ipmitool-xcat', 'goconserver') or the logical
|
||||
# 'xcat-genesis-base'.
|
||||
#
|
||||
# $arch (optional) disambiguates that logical name: the genesis debs are arch-SUFFIXED
|
||||
# (xcat-genesis-base-amd64 / -ppc64el) and are legitimately built from two DIFFERENT genesis rpms, so
|
||||
# their revisions differ (2.19.0-snap202607261133 vs ...271832). On the amd64 host both are staged
|
||||
# (the cross-arch ppc one for #7610), so without $arch the two would look like a version conflict.
|
||||
# Pass the target arch and only that arch's genesis is considered -- matching resolve_present_names,
|
||||
# which likewise never borrows another arch's genesis.
|
||||
#
|
||||
# Dies if the dir holds more than one DISTINCT version of the package (a stale artifact not cleaned
|
||||
# before the build -- a version pin could otherwise pass against the wrong deb and both could ship).
|
||||
# Mirrors MockBuildUtils::rpm_version.
|
||||
sub deb_version {
|
||||
my ($dir, $pkg) = @_;
|
||||
my $glob = ($pkg eq 'xcat-genesis-base')
|
||||
? "$dir/xcat-genesis-base-*_*.deb"
|
||||
my ($dir, $pkg, $arch) = @_;
|
||||
my $is_genesis = ($pkg eq 'xcat-genesis-base');
|
||||
my $glob = $is_genesis
|
||||
? ((defined $arch && $arch ne '') ? "$dir/xcat-genesis-base-$arch\_*.deb"
|
||||
: "$dir/xcat-genesis-base-*_*.deb")
|
||||
: "$dir/${pkg}_*.deb";
|
||||
my %vers;
|
||||
for my $f (sort glob($glob)) {
|
||||
my $n = deb_field($f, 'Package');
|
||||
my $match = ($pkg eq 'xcat-genesis-base')
|
||||
? ($n =~ /^xcat-genesis-base-/) : ($n eq $pkg);
|
||||
my $match = $is_genesis
|
||||
? ($n =~ /^xcat-genesis-base-/
|
||||
&& (!defined $arch || $arch eq '' || $n eq "xcat-genesis-base-$arch"))
|
||||
: ($n eq $pkg);
|
||||
next unless $match;
|
||||
my $v = deb_upstream_version(deb_field($f, 'Version'));
|
||||
my $v = deb_field($f, 'Version');
|
||||
$vers{$v} = 1 if defined $v && $v ne '';
|
||||
}
|
||||
return undef unless %vers;
|
||||
@@ -614,6 +619,13 @@ W=$(mktemp -d)
|
||||
cp -a "$PKGSRC" "$W/pkg"
|
||||
cd "$W/pkg"
|
||||
|
||||
# Some package directories carry PREBUILT .deb files in the checkout (elilo/ ships
|
||||
# elilo-xcat_3.14-5_all.deb and gnu-efi_3.0v-5_amd64.deb). Those arrive with the copied source tree
|
||||
# and are NOT output of this build -- collecting them republishes a stale, unrelated artifact under
|
||||
# this run's name. Record what is already present so the collector can subtract it. A file the build
|
||||
# OVERWRITES (elilo-xcat_3.14-6_all.deb) changes size/mtime, so it still counts as build output.
|
||||
find "$W" -maxdepth 3 -name '*.deb' -printf '%s %T@ %p\n' | sort > "$W/.debs-before"
|
||||
|
||||
# Declared Build-Depends, resolved by mk-build-deps: it hands debian/control's relationships to apt
|
||||
# verbatim, so versions/alternatives/arch-qualifiers are honoured. FATAL on failure -- an unsatisfied
|
||||
# build dependency must stop the build, never be papered over by whatever the chroot already carries.
|
||||
@@ -626,14 +638,19 @@ fi
|
||||
printf '%s' "$B64" | base64 -d > "$W/pkgbuild.sh"
|
||||
( cd "$W/pkg" && bash "$W/pkgbuild.sh" )
|
||||
|
||||
# Collect the built binaries. The mk-build-deps dummy package (<src>-build-deps_*.deb) and debug
|
||||
# symbols are not build output and must never reach the repo.
|
||||
mapfile -t found < <(find "$W" -maxdepth 3 -name '*.deb' \
|
||||
! -name '*-dbgsym_*' ! -name '*-build-deps_*' -print | sort)
|
||||
# Collect ONLY what this build produced: everything that is new or changed since the snapshot taken
|
||||
# before the build. Prebuilt debs that came in with the checkout are subtracted, and the mk-build-deps
|
||||
# dummy package (<src>-build-deps_*.deb) and debug symbols are excluded outright -- none of the three
|
||||
# is build output, and none may reach the repo.
|
||||
find "$W" -maxdepth 3 -name '*.deb' ! -name '*-dbgsym_*' ! -name '*-build-deps_*' \
|
||||
-printf '%s %T@ %p\n' | sort > "$W/.debs-after"
|
||||
mapfile -t found < <(comm -13 "$W/.debs-before" "$W/.debs-after" | sed 's/^[^ ]* [^ ]* //')
|
||||
if [ "${#found[@]}" -eq 0 ]; then
|
||||
echo "FATAL: the package build produced no .deb" >&2
|
||||
echo "FATAL: the package build produced no .deb (only prebuilt artifacts from the checkout)" >&2
|
||||
exit 1
|
||||
fi
|
||||
comm -12 "$W/.debs-before" "$W/.debs-after" | sed 's/^[^ ]* [^ ]* //' \
|
||||
| while read -r f; do echo "prebuilt in the checkout, NOT collected: $f"; done
|
||||
mkdir -p "$OUT"
|
||||
for d in "${found[@]}"; do cp -v "$d" "$OUT/"; done
|
||||
INNER
|
||||
|
||||
+82
-63
@@ -5,12 +5,28 @@
|
||||
# where <binary-package> is the produced .deb's binary name (or the logical 'xcat-genesis-base',
|
||||
# which matches the arch-suffixed xcat-genesis-base-amd64 / xcat-genesis-base-ppc64el) and
|
||||
# <version-pin> is one of:
|
||||
# - an exact upstream version (e.g. 1.8.18) -- the build must produce exactly it;
|
||||
# - a shell-style glob (e.g. 2.*) -- the built upstream version must match it (* and ?);
|
||||
# - '*' -- any version accepted.
|
||||
# Only the UPSTREAM version is matched, never the Debian revision (which carries the -snap<ts>.<n> CD
|
||||
# stamp and the per-codename build). Bump an exact pin here when the in-tree debian/changelog upstream
|
||||
# version is bumped.
|
||||
# - an exact version (e.g. 1.8.18-4) -- the build must produce exactly it;
|
||||
# - a shell-style glob (e.g. 2.*) -- the built version must match it (* and ?);
|
||||
# - '*' -- any version accepted.
|
||||
#
|
||||
# The pin is matched against the FULL Debian version -- [epoch:]upstream[-revision] -- exactly as it
|
||||
# appears in the built .deb and in the published Packages index. That is deliberate: a package can
|
||||
# carry the right upstream version and still be wrong for xCAT, because
|
||||
# * the debian_revision is the PACKAGING revision (elilo-xcat 3.14-5 vs 3.14-6 are different builds
|
||||
# of the same upstream 3.14), and
|
||||
# * the epoch overrides version comparison outright (2:2.18.0 outranks any un-epoched 2.19.0).
|
||||
# xCAT's own debian/control declares versioned dependencies on these packages -- goconserver
|
||||
# (>= 0.3.3-snap...), ipmitool-xcat (>= 1.8.17-1), grub2-xcat (>= 2.02-...) -- so an upstream-only pin
|
||||
# would accept a deb the gate calls good and `apt install xCAT` then refuses. Bump the pin here when
|
||||
# the in-tree debian/changelog is bumped (the revision is part of the pin now, so a changelog bump
|
||||
# that only moves the revision must be reflected here too).
|
||||
#
|
||||
# Two pins are deliberately NOT fully exact, because their revision is not owned by the checkout:
|
||||
# * goconserver=0.3.3-snap* -- upstream is pinned exactly; the revision is the CD stamp
|
||||
# snap<SOURCE_DATE_EPOCH>, which changes every run. The glob still REQUIRES a snap-stamped
|
||||
# revision, so an unstamped or hand-built deb is rejected.
|
||||
# * xcat-genesis-base=2.* -- its version is not owned by xcat-dep at all (see the note below);
|
||||
# and the two arches legitimately differ, since each is converted from its own genesis rpm.
|
||||
#
|
||||
# PER-ARCH SETS (review concern #3 -- the arch matrix must be valid):
|
||||
# * Compiled, arch-specific deps that genuinely build on BOTH arches are listed for amd64 AND
|
||||
@@ -27,7 +43,10 @@
|
||||
# * xcat-genesis-base is arch:all but arch-SPECIFIC content (the netboot image for that arch), so
|
||||
# each arch produces its own (xcat-genesis-base-<arch>); the finalize step cross-populates both
|
||||
# into every codename pool for cross-arch netboot (issue #7610). Pinned 2.* -- its Version walks
|
||||
# with the paired xcat-core (not owned by xcat-dep), exactly as on the EL side.
|
||||
# with the paired xcat-core (not owned by xcat-dep), exactly as on the EL side. The gate still
|
||||
# enforces that EXACTLY ONE genesis is present FOR THIS ARCH: the name resolves to
|
||||
# xcat-genesis-base-<arch> and never borrows the other arch's, and two distinct versions of it in
|
||||
# one staging dir is a hard error.
|
||||
#
|
||||
# sbuild-all.pl reads this file and, per target, builds ONLY the listed packages and then validates
|
||||
# that EVERY listed package was produced at the pinned version -- any missing or mismatched package
|
||||
@@ -36,84 +55,84 @@
|
||||
|
||||
# ============================ focal (ubuntu20.04) ============================
|
||||
[focal-amd64]
|
||||
ipmitool-xcat=1.8.18
|
||||
conserver-xcat=8.2.1
|
||||
goconserver=0.3.3
|
||||
syslinux-xcat=3.86
|
||||
grub2-xcat=2.12
|
||||
elilo-xcat=3.14
|
||||
xnba-undi=1.21.1
|
||||
ipmitool-xcat=1.8.18-4
|
||||
conserver-xcat=8.2.1-1
|
||||
goconserver=0.3.3-snap*
|
||||
syslinux-xcat=3.86-2
|
||||
grub2-xcat=2.12-1
|
||||
elilo-xcat=3.14-6
|
||||
xnba-undi=1.21.1-1
|
||||
xcat-genesis-base=2.*
|
||||
|
||||
[focal-ppc64el]
|
||||
ipmitool-xcat=1.8.18
|
||||
conserver-xcat=8.2.1
|
||||
goconserver=0.3.3
|
||||
syslinux-xcat=3.86
|
||||
grub2-xcat=2.12
|
||||
elilo-xcat=3.14
|
||||
xnba-undi=1.21.1
|
||||
ipmitool-xcat=1.8.18-4
|
||||
conserver-xcat=8.2.1-1
|
||||
goconserver=0.3.3-snap*
|
||||
syslinux-xcat=3.86-2
|
||||
grub2-xcat=2.12-1
|
||||
elilo-xcat=3.14-6
|
||||
xnba-undi=1.21.1-1
|
||||
xcat-genesis-base=2.*
|
||||
|
||||
# ============================ jammy (ubuntu22.04) ============================
|
||||
[jammy-amd64]
|
||||
ipmitool-xcat=1.8.18
|
||||
conserver-xcat=8.2.1
|
||||
goconserver=0.3.3
|
||||
syslinux-xcat=3.86
|
||||
grub2-xcat=2.12
|
||||
elilo-xcat=3.14
|
||||
xnba-undi=1.21.1
|
||||
ipmitool-xcat=1.8.18-4
|
||||
conserver-xcat=8.2.1-1
|
||||
goconserver=0.3.3-snap*
|
||||
syslinux-xcat=3.86-2
|
||||
grub2-xcat=2.12-1
|
||||
elilo-xcat=3.14-6
|
||||
xnba-undi=1.21.1-1
|
||||
xcat-genesis-base=2.*
|
||||
|
||||
[jammy-ppc64el]
|
||||
ipmitool-xcat=1.8.18
|
||||
conserver-xcat=8.2.1
|
||||
goconserver=0.3.3
|
||||
syslinux-xcat=3.86
|
||||
grub2-xcat=2.12
|
||||
elilo-xcat=3.14
|
||||
xnba-undi=1.21.1
|
||||
ipmitool-xcat=1.8.18-4
|
||||
conserver-xcat=8.2.1-1
|
||||
goconserver=0.3.3-snap*
|
||||
syslinux-xcat=3.86-2
|
||||
grub2-xcat=2.12-1
|
||||
elilo-xcat=3.14-6
|
||||
xnba-undi=1.21.1-1
|
||||
xcat-genesis-base=2.*
|
||||
|
||||
# ============================ noble (ubuntu24.04) ============================
|
||||
[noble-amd64]
|
||||
ipmitool-xcat=1.8.18
|
||||
conserver-xcat=8.2.1
|
||||
goconserver=0.3.3
|
||||
syslinux-xcat=3.86
|
||||
grub2-xcat=2.12
|
||||
elilo-xcat=3.14
|
||||
xnba-undi=1.21.1
|
||||
ipmitool-xcat=1.8.18-4
|
||||
conserver-xcat=8.2.1-1
|
||||
goconserver=0.3.3-snap*
|
||||
syslinux-xcat=3.86-2
|
||||
grub2-xcat=2.12-1
|
||||
elilo-xcat=3.14-6
|
||||
xnba-undi=1.21.1-1
|
||||
xcat-genesis-base=2.*
|
||||
|
||||
[noble-ppc64el]
|
||||
ipmitool-xcat=1.8.18
|
||||
conserver-xcat=8.2.1
|
||||
goconserver=0.3.3
|
||||
syslinux-xcat=3.86
|
||||
grub2-xcat=2.12
|
||||
elilo-xcat=3.14
|
||||
xnba-undi=1.21.1
|
||||
ipmitool-xcat=1.8.18-4
|
||||
conserver-xcat=8.2.1-1
|
||||
goconserver=0.3.3-snap*
|
||||
syslinux-xcat=3.86-2
|
||||
grub2-xcat=2.12-1
|
||||
elilo-xcat=3.14-6
|
||||
xnba-undi=1.21.1-1
|
||||
xcat-genesis-base=2.*
|
||||
|
||||
# ============================ resolute (ubuntu26.04) =========================
|
||||
[resolute-amd64]
|
||||
ipmitool-xcat=1.8.18
|
||||
conserver-xcat=8.2.1
|
||||
goconserver=0.3.3
|
||||
syslinux-xcat=3.86
|
||||
grub2-xcat=2.12
|
||||
elilo-xcat=3.14
|
||||
xnba-undi=1.21.1
|
||||
ipmitool-xcat=1.8.18-4
|
||||
conserver-xcat=8.2.1-1
|
||||
goconserver=0.3.3-snap*
|
||||
syslinux-xcat=3.86-2
|
||||
grub2-xcat=2.12-1
|
||||
elilo-xcat=3.14-6
|
||||
xnba-undi=1.21.1-1
|
||||
xcat-genesis-base=2.*
|
||||
|
||||
[resolute-ppc64el]
|
||||
ipmitool-xcat=1.8.18
|
||||
conserver-xcat=8.2.1
|
||||
goconserver=0.3.3
|
||||
syslinux-xcat=3.86
|
||||
grub2-xcat=2.12
|
||||
elilo-xcat=3.14
|
||||
xnba-undi=1.21.1
|
||||
ipmitool-xcat=1.8.18-4
|
||||
conserver-xcat=8.2.1-1
|
||||
goconserver=0.3.3-snap*
|
||||
syslinux-xcat=3.86-2
|
||||
grub2-xcat=2.12-1
|
||||
elilo-xcat=3.14-6
|
||||
xnba-undi=1.21.1-1
|
||||
xcat-genesis-base=2.*
|
||||
|
||||
+10
-6
@@ -46,7 +46,7 @@ use BuildUtils qw(sh_quote print_step version_matches required_pkgs read_manifes
|
||||
codename_to_version known_codenames chroot_name chroot_sources_list
|
||||
chroot_is_disposable
|
||||
control_field genesis_deb_control
|
||||
deb_field deb_version deb_upstream_version deb_hash cross_copy_genesis_deb);
|
||||
deb_field deb_version deb_hash cross_copy_genesis_deb);
|
||||
|
||||
my $script_dir = abs_path(dirname(__FILE__));
|
||||
my $repo_root = $script_dir;
|
||||
@@ -640,7 +640,10 @@ sub validate_manifest {
|
||||
# presence on this arch is verified later against the PUBLISHED index (verify_assembled_repo).
|
||||
next if pkg_skip_on_arch($pkg, $arch);
|
||||
my $want = $MANIFEST{$tgt}{$pkg};
|
||||
my $got = deb_version($dir, $pkg);
|
||||
# $arch disambiguates the logical 'xcat-genesis-base': both arch-suffixed genesis debs are
|
||||
# staged on the amd64 host (the cross-arch ppc one for #7610) and they carry DIFFERENT
|
||||
# revisions, so only this target's arch may be considered.
|
||||
my $got = deb_version($dir, $pkg, $arch);
|
||||
if (!defined $got) { push @fail, "[$tgt] MISSING $pkg"; next; }
|
||||
push @fail, "[$tgt] $pkg: built $got, manifest pins $want"
|
||||
unless version_matches($got, $want);
|
||||
@@ -661,13 +664,14 @@ sub validate_manifest {
|
||||
# ---------------------------------------------------------------------------------------------------
|
||||
|
||||
# repo_present_from_index($idx, @names): parse the PUBLISHED $idx (a binary-<arch>/Packages file) and
|
||||
# resolve each required manifest @names against it, returning %present = (reqname => upstream-version |
|
||||
# resolve each required manifest @names against it, returning %present = (reqname => FULL version |
|
||||
# undef). Name-resolution mirrors deb_version/validate_manifest: try an EXACT index key first (most
|
||||
# packages -- ipmitool-xcat, goconserver, grub2-xcat, the Architecture:all boot bits keep their plain
|
||||
# names), else exactly <name>-<arch> for THIS cell's arch (the arch-suffixed xcat-genesis-base ->
|
||||
# xcat-genesis-base-<arch>, never a different arch's). The published Version carries epoch+revision, so
|
||||
# it is reduced to the UPSTREAM part (what the manifest pins) via deb_upstream_version before the pure
|
||||
# comparator. (Resolution itself lives in the pure BuildUtils::resolve_present_names.)
|
||||
# xcat-genesis-base-<arch>, never a different arch's). The version is passed to the comparator WHOLE --
|
||||
# [epoch:]upstream[-revision] -- because the manifest pins the whole thing, so a stale packaging
|
||||
# revision or a wrong epoch is caught. (Resolution itself lives in the pure
|
||||
# BuildUtils::resolve_present_names.)
|
||||
sub repo_present_from_index {
|
||||
my ($idx, $arch, @names) = @_;
|
||||
my $text = do { local $/; open my $fh, '<', $idx or die "FATAL: cannot read $idx: $!\n"; <$fh> };
|
||||
|
||||
+61
-11
@@ -19,7 +19,7 @@ use BuildUtils qw(required_pkgs version_matches read_manifest standard_options
|
||||
codename_to_version version_to_codename known_codenames
|
||||
chroot_name chroot_sources_list chroot_is_disposable chroot_build_script
|
||||
control_field genesis_deb_control
|
||||
deb_field deb_version deb_upstream_version deb_hash cross_copy_genesis_deb
|
||||
deb_field deb_version deb_hash cross_copy_genesis_deb
|
||||
build_deb_in_chroot);
|
||||
|
||||
# Run a printing sub with STDOUT muted so its progress lines do not pollute TAP.
|
||||
@@ -215,10 +215,28 @@ PKG
|
||||
is(($m2 && $m2->{a}), '1-1', 'an identical repeated version is kept, not an error');
|
||||
}
|
||||
|
||||
# ---- deb_upstream_version: strip epoch + debian revision ----------------------------------------
|
||||
is(deb_upstream_version('1.8.18-4'), '1.8.18', 'strip -revision');
|
||||
is(deb_upstream_version('2:0.3.3-snap202608101400.57'), '0.3.3', 'strip epoch and -revision');
|
||||
is(deb_upstream_version('3.86'), '3.86', 'native-ish version returned as-is');
|
||||
# ---- pins bite on the FULL version: revision and epoch are part of the comparison -----------------
|
||||
# A package can carry the right upstream version and still be wrong for xCAT -- the debian_revision is
|
||||
# the packaging revision (elilo-xcat 3.14-5 vs 3.14-6) and the epoch overrides version comparison
|
||||
# outright. These assert version_matches over WHOLE Debian versions, which is what the manifest pins.
|
||||
{
|
||||
ok( version_matches('3.14-6', '3.14-6'), 'exact full pin matches the same revision');
|
||||
ok(!version_matches('3.14-5', '3.14-6'),
|
||||
'a STALE packaging revision is rejected (3.14-5 vs pin 3.14-6) -- upstream-only would pass');
|
||||
ok(!version_matches('1.8.18-3', '1.8.18-4'), 'an older ipmitool revision is rejected');
|
||||
ok(!version_matches('2:2.18.0-1', '2.18.0-1'),
|
||||
'an EPOCH that the pin does not name is rejected (2:2.18.0-1 vs pin 2.18.0-1)');
|
||||
ok( version_matches('2:2.18.0-1', '2:2.18.0-1'), 'an epoch IS accepted when the pin names it');
|
||||
|
||||
# the two pins that must stay globbed, and what they still enforce
|
||||
ok( version_matches('0.3.3-snap202608212338', '0.3.3-snap*'), 'goconserver CD stamp matches 0.3.3-snap*');
|
||||
ok(!version_matches('0.3.3-1', '0.3.3-snap*'),
|
||||
'... but an UNSTAMPED goconserver revision is rejected (the glob still demands a snap stamp)');
|
||||
ok(!version_matches('0.3.4-snap202608212338', '0.3.3-snap*'),
|
||||
'... and upstream is still pinned exactly under the glob');
|
||||
ok( version_matches('2.19.0-snap202607261133', '2.*'), 'genesis 2.* walks with xcat-core');
|
||||
ok(!version_matches('1.9.0-snap202607261133', '2.*'), '... but not below 2');
|
||||
}
|
||||
|
||||
# ---- .deb inspection + cross-arch genesis provisioning (needs dpkg-deb for real debs) -----------
|
||||
SKIP: {
|
||||
@@ -248,15 +266,38 @@ SKIP: {
|
||||
{
|
||||
my $dir = "$tmp/vdir1"; make_path($dir);
|
||||
system("cp '$ipmi' '$dir/'");
|
||||
is(deb_version($dir, 'ipmitool-xcat'), '1.8.18', 'deb_version returns UPSTREAM version');
|
||||
is(deb_version($dir, 'ipmitool-xcat'), '1.8.18-4',
|
||||
'deb_version returns the FULL version, revision included');
|
||||
}
|
||||
# deb_version dies when a dir holds two DIFFERENT upstream versions of the same package.
|
||||
# deb_version dies when a dir holds two DIFFERENT versions of the same package -- now including two
|
||||
# revisions of the SAME upstream version, which upstream-only comparison used to collapse into one.
|
||||
{
|
||||
my $dir = "$tmp/vdir2"; make_path($dir);
|
||||
system("cp '" . $mk->('ipmitool-xcat', '1.8.18-4', 'amd64') . "' '$dir/'");
|
||||
system("cp '" . $mk->('ipmitool-xcat', '1.8.19-1', 'amd64') . "' '$dir/'");
|
||||
my $died = !eval { deb_version($dir, 'ipmitool-xcat'); 1 };
|
||||
ok($died, 'deb_version dies on multiple distinct upstream versions (stale artifact)');
|
||||
ok($died, 'deb_version dies on multiple distinct versions (stale artifact)');
|
||||
}
|
||||
{
|
||||
my $dir = "$tmp/vdir3"; make_path($dir);
|
||||
system("cp '" . $mk->('elilo-xcat', '3.14-5', 'all') . "' '$dir/'");
|
||||
system("cp '" . $mk->('elilo-xcat', '3.14-6', 'all') . "' '$dir/'");
|
||||
my $died = !eval { deb_version($dir, 'elilo-xcat'); 1 };
|
||||
ok($died, 'deb_version dies on two REVISIONS of one upstream version (the elilo 3.14-5/-6 case)');
|
||||
}
|
||||
# The logical 'xcat-genesis-base' spans two arch-suffixed packages that legitimately carry
|
||||
# different revisions (each converted from its own genesis rpm) and are both staged on the amd64
|
||||
# host. $arch selects one; without it the pair would look like a version conflict.
|
||||
{
|
||||
my $dir = "$tmp/gdir"; make_path($dir);
|
||||
system("cp '" . $mk->('xcat-genesis-base-amd64', '2.19.0-snap202607261133', 'all') . "' '$dir/'");
|
||||
system("cp '" . $mk->('xcat-genesis-base-ppc64el', '2.19.0-snap202607271832', 'all') . "' '$dir/'");
|
||||
is(deb_version($dir, 'xcat-genesis-base', 'amd64'), '2.19.0-snap202607261133',
|
||||
'deb_version picks THIS arch genesis when both are staged');
|
||||
is(deb_version($dir, 'xcat-genesis-base', 'ppc64el'), '2.19.0-snap202607271832',
|
||||
'... and the other arch resolves to its own');
|
||||
my $died = !eval { deb_version($dir, 'xcat-genesis-base'); 1 };
|
||||
ok($died, 'without an arch the two genesis revisions are a conflict (so callers must pass it)');
|
||||
}
|
||||
|
||||
# genesis cross-copy: content identity by deb_hash, refresh-stale + idempotent + sign callback.
|
||||
@@ -316,11 +357,20 @@ SKIP: {
|
||||
my @names = ('ipmitool-xcat', 'xcat-genesis-base');
|
||||
|
||||
my $amd = resolve_present_names(\%parsed, 'amd64', \@names);
|
||||
is($amd->{'ipmitool-xcat'}, '1.8.18', 'resolve: exact name reduced to upstream');
|
||||
is($amd->{'xcat-genesis-base'}, '2.19.0', 'resolve: genesis -> amd64-suffixed for the amd64 cell');
|
||||
is($amd->{'ipmitool-xcat'}, '1.8.18-snap202601010000',
|
||||
'resolve: exact name, FULL version handed to the comparator (revision kept)');
|
||||
is($amd->{'xcat-genesis-base'}, '2.19.0-snap202601010000',
|
||||
'resolve: genesis -> amd64-suffixed for the amd64 cell');
|
||||
|
||||
my $ppc = resolve_present_names(\%parsed, 'ppc64el', \@names);
|
||||
is($ppc->{'xcat-genesis-base'}, '2.19.0', 'resolve: genesis -> ppc64el-suffixed for the ppc64el cell');
|
||||
is($ppc->{'xcat-genesis-base'}, '2.19.0-snap202601010000',
|
||||
'resolve: genesis -> ppc64el-suffixed for the ppc64el cell');
|
||||
|
||||
# The point of keeping the revision: a stale packaging revision in the published index is now a
|
||||
# VERSION problem, where upstream-only comparison reported the repo as complete.
|
||||
my @stale = verify_repo_packages({ 'elilo-xcat' => '3.14-6' }, { 'elilo-xcat' => '3.14-5' });
|
||||
like($stale[0], qr/^VERSION elilo-xcat: repo has 3\.14-5, manifest pins 3\.14-6$/,
|
||||
'a stale REVISION in the published index is caught (was a false PASS on upstream 3.14)');
|
||||
|
||||
# The false-PASS the reviewer caught: only the amd64 genesis is published. The ppc64el cell MUST
|
||||
# NOT resolve to it (that would mask a missing native ppc genesis, #7610) -> undef -> later MISSING.
|
||||
|
||||
+31
-8
@@ -53,9 +53,9 @@ sub arch_all_stanzas {
|
||||
}
|
||||
|
||||
sub native_stanzas {
|
||||
my ($a) = @_;
|
||||
return stanza('ipmitool-xcat', "1.8.18-snap202608211200", $a)
|
||||
. stanza('goconserver', "0.3.3-snap202608211200", $a);
|
||||
my ($a, %o) = @_;
|
||||
return stanza('ipmitool-xcat', $o{ipmi_version} // '1.8.18-snap202608211200', $a)
|
||||
. stanza('goconserver', '0.3.3-snap202608211200', $a);
|
||||
}
|
||||
|
||||
# make_repo(%opt): a fixture apt tree for codename 'noble'.
|
||||
@@ -70,7 +70,8 @@ sub make_repo {
|
||||
my %native = map { $_ => 1 } @{ $o{native} // [@arches] };
|
||||
my $dir = "$tmp/repo" . (++$repo_seq);
|
||||
for my $a (@arches) {
|
||||
my $body = ($native{$a} ? native_stanzas($a) : '') . arch_all_stanzas($a);
|
||||
my $body = ($native{$a} ? native_stanzas($a, ipmi_version => $o{ipmi_version}) : '')
|
||||
. arch_all_stanzas($a);
|
||||
write_file("$dir/dists/noble/main/binary-$a/Packages", $body);
|
||||
}
|
||||
write_file("$dir/dists/noble/Release",
|
||||
@@ -82,16 +83,17 @@ sub make_repo {
|
||||
|
||||
# The manifest is the source of truth for what each codename x arch must carry.
|
||||
my $manifest = "$tmp/debs-manifest.conf";
|
||||
# Pins are matched against the FULL Debian version, so the fixtures pin revisions too.
|
||||
write_file($manifest, <<'MAN');
|
||||
[noble-amd64]
|
||||
ipmitool-xcat=1.8.18
|
||||
goconserver=0.3.3
|
||||
ipmitool-xcat=1.8.18-snap202608211200
|
||||
goconserver=0.3.3-snap*
|
||||
grub2-xcat=*
|
||||
xcat-genesis-base=2.*
|
||||
|
||||
[noble-ppc64el]
|
||||
ipmitool-xcat=1.8.18
|
||||
goconserver=0.3.3
|
||||
ipmitool-xcat=1.8.18-snap202608211200
|
||||
goconserver=0.3.3-snap*
|
||||
grub2-xcat=*
|
||||
xcat-genesis-base=2.*
|
||||
MAN
|
||||
@@ -194,6 +196,27 @@ sub run_gate {
|
||||
'... and the log names the flag that turned it off');
|
||||
}
|
||||
|
||||
# ---- a stale PACKAGING REVISION is caught, not just a wrong upstream version ----------------------
|
||||
# The manifest pin is matched against the FULL [epoch:]upstream[-revision]. An upstream-only gate
|
||||
# reported this repo as complete: upstream 1.8.18 is what the pin used to say, and it matches -- while
|
||||
# the published deb is an older packaging revision than xCAT's own Depends require.
|
||||
{
|
||||
my $repo = make_repo(ipmi_version => '1.8.18-snap202501010000'); # older revision, same upstream
|
||||
my ($rc, $out) = run_gate($repo, '--no-verify-signature');
|
||||
isnt($rc, 0, 'a stale packaging revision FAILS the gate') or diag($out);
|
||||
like($out, qr/VERSION ipmitool-xcat: repo has 1\.8\.18-snap202501010000, manifest pins 1\.8\.18-snap202608211200/,
|
||||
'... reported as a VERSION problem naming both revisions');
|
||||
}
|
||||
|
||||
# An EPOCH the pin does not name is likewise a mismatch -- the epoch outranks the version entirely,
|
||||
# so silently accepting it would let an un-pinned epoch bump through.
|
||||
{
|
||||
my $repo = make_repo(ipmi_version => '2:1.8.18-snap202608211200');
|
||||
my ($rc, $out) = run_gate($repo, '--no-verify-signature');
|
||||
isnt($rc, 0, 'an epoch the pin does not name FAILS the gate') or diag($out);
|
||||
like($out, qr/VERSION ipmitool-xcat: repo has 2:1\.8\.18-snap202608211200/, '... naming the epoch');
|
||||
}
|
||||
|
||||
# ---- a cell with no manifest section is a configuration error, not a free pass --------------------
|
||||
{
|
||||
my $thin = "$tmp/thin-manifest.conf";
|
||||
|
||||
Reference in New Issue
Block a user