Skip to content

Adds tarballs to PPG 17 - #2

Open
rjd15372 wants to merge 79 commits into
mainfrom
tarballs-simpleimage
Open

Adds tarballs to PPG 17#2
rjd15372 wants to merge 79 commits into
mainfrom
tarballs-simpleimage

Conversation

@rjd15372

Copy link
Copy Markdown
Collaborator

No description provided.

@rjd15372 rjd15372 added RockyLinux_9 no-dep-cascade If used with `obs-sync*` labels, it prevents from packages that were not changed from being built RockyLinux_8 labels Jul 27, 2026
@CLAassistant

CLAassistant commented Jul 27, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@rjd15372 rjd15372 added the obs-sync Trigger OBS sync push label Jul 27, 2026
@github-actions

github-actions Bot commented Jul 27, 2026

Copy link
Copy Markdown

OBS Build Check — ❌ Sync failed

The OBS sync step failed. See the workflow run for details.

Updated: 2026-08-28 13:01 UTC

@rjd15372
rjd15372 force-pushed the tarballs-simpleimage branch from 587f5ba to e08a04f Compare July 28, 2026 12:11
@rjd15372 rjd15372 added the keep-pr-build Ensures that the OBS PR project built for this PR is never deleted by the stale PR deletion bot. label Aug 25, 2026
@rjd15372
rjd15372 force-pushed the tarballs-simpleimage branch 2 times, most recently from 695fdf9 to e14de92 Compare August 26, 2026 07:42
Signed-off-by: Ricardo Dias <ricardo.dias@percona.com>
…ball.sh

Patch postgres.real (the wrapper shell script at bin/postgres cannot be
patchelf'd, and postmaster is gone since PG 16) and drop the error
swallowing so a failed patchelf aborts the build. RPATH the bundled
libperl.so to $ORIGIN so it finds libcrypt in CORE/ (RUNPATH is not
transitive).

Replace the ldd-based library audit with a DT_NEEDED soname audit:
inside the fully-populated buildroot ldd resolves everything from
/usr/lib64 via ld.so.cache, so it could never catch a library that
copy_deps failed to bundle. Each NEEDED soname must now be either in
the system exclude list or present under /opt.

Signed-off-by: Ricardo Dias <ricardo.dias@percona.com>
…iner

Ran build-tarball.sh to completion inside a rockylinux:9 container
provisioned with the staging:17 RockyLinux_9 RPMs (mirroring the OBS
simpleimage buildroot: EPEL + CRB + staging repo) and structure-diffed
the result against the official
percona-postgresql-17.10-ssl3-linux-x86_64.tar.gz. Fixes found:

- simpleimage: drop python3.12-idle BuildRequires — the package does not
  exist on EL9 (RHEL strips IDLE from python3.12), so the OBS build would
  be unresolvable. The official tarball ships no _tkinter either.
- simpleimage: add percona-pgpool-II-pgNN-devel and -extensions — the
  official tarball ships the pgpool2 headers (include/pgpool2/) and the
  pgpool_adm/pgpool-recovery PostgreSQL extensions.
- bundle_deps: accept extra directory trees walked recursively so
  python lib-dynload/ + site-packages C extensions (libsqlite3,
  libncursesw, libuuid, libgdbm, libpq via psycopg2) and perl XS module
  deps (libdb for DB_File, libsombok for Unicode::LineBreak) are
  bundled; perl XS .so get RPATH /opt/percona-perl/lib. These were the
  NEEDED-audit true positives; nothing was added to SYSTEM_LIBS_EXCLUDE.
- site-packages copy list: add jmespath, s3transfer, psycopg2 (all
  shipped by the official tarball; psycopg2 is required by patroni).
- license/doc staging: RPM %license files live under
  /usr/share/licenses, pgbouncer docs under unprefixed
  /usr/share/doc/pgbouncer; pgpool headers are flat in /usr/include and
  get nested under include/pgpool2/ per the reference layout; bundle the
  python man page under share/man.
- section 15: precompute the bundled-soname list once instead of a full
  'find /opt' per NEEDED entry (audit re-verified to fail on a synthetic
  missing soname and pass once bundled).

Verification: NEEDED audit 0 unresolved; smoke commands pass (initdb,
postgres.real, python+ssl+yaml, patroni import, perl, tcl); tarball
top-level is exactly the percona-* component dirs.

Accepted structure divergences vs the official tarball:
- percona-haproxy/ absent (out of scope for this build, per spec).
- percona-pgbadger/auto/ absent (perl .packlist install artifact of the
  official source build; RPM installs do not create it).
- percona-tcl/share/ absent (official bundles man pages for tcl
  extensions itcl/tdbc/thread/sqlite built from source; EL9 does not
  package those extensions — our tcl man pages live under percona-tcl/man,
  which the official tarball also has).
- percona-python3/lib64 extra (symlink to lib/pythonX.Y so the RPM
  python, compiled with platlibdir=lib64, resolves PYTHONHOME).
- percona-postgresql17/lib/pgpool-regclass.so absent (obsolete extension
  for PG < 9.4; pgpool 4.7 RPMs no longer build it).
- site-packages: psycopg (v3) absent (official pip-installs it; patroni
  works via the packaged psycopg2).

Known blocker for the OBS build, out of scope here: the published
python3.12-boto3 requires python3.12dist(jmespath) and
python3.12dist(s3transfer), which no project in the RockyLinux_9 repo
path provides — percona-patroni-aws is currently uninstallable in the
buildroot. jmespath/s3transfer packages need to be added to
ppg:common:deps (validated locally with stand-in RPMs built from the
PyPI sdists).

Signed-off-by: Ricardo Dias <ricardo.dias@percona.com>
Review fixes on top of the container validation, re-validated in the
Rocky 9 harness (audit clean, all smoke commands pass, structure diff
unchanged):

- RPATH python C extensions (lib-dynload/ + site-packages) at
  /opt/percona-python3/lib: the python3 wrapper sets LD_LIBRARY_PATH,
  but embedded interpreters (plpython3 inside postgres.real) don't run
  through the wrapper, and the executable's RUNPATH does not apply to
  dlopened extensions' own dependencies.
- Section 15: build the bundled-soname list with -xtype f so dangling
  symlinks never count as bundled. This immediately caught a real bug:
  copy_deps only copied files matching the ${base}.so* glob, so when
  the SONAME symlink resolves to a differently-named real file
  (libldap.so.2 -> libldap_r.so.2.0.200, libopenblaso.so.0 ->
  libopenblaso-r0.3.29.so on EL9) only a dangling symlink was bundled
  and the library silently resolved from the buildroot. copy_deps now
  copies the resolved real file explicitly.
- Perl XS RPATH loop: drop the 2>/dev/null || true swallow — every file
  fed in is a real XS .so, so a patchelf failure is meaningful and must
  fail the build. The new python loop keeps a file(1) ELF guard (the
  tree contains non-ELF *.so* test fixtures) but also fails loudly on
  patchelf errors.

Gate spot-check re-run: audit fails on a missing soname and on a
dangling symlink, passes once the real file (or a valid symlink to it)
is bundled.

Signed-off-by: Ricardo Dias <ricardo.dias@percona.com>
Signed-off-by: Ricardo Dias <ricardo.dias@percona.com>
Signed-off-by: Ricardo Dias <ricardo.dias@percona.com>
Signed-off-by: Ricardo Dias <ricardo.dias@percona.com>
python3.12-idle does not exist on EL9 (dropped from BuildRequires);
the ldd-based verification audit was replaced by a DT_NEEDED soname
audit, which detects unbundled libraries even inside the populated
buildroot.

Signed-off-by: Ricardo Dias <ricardo.dias@percona.com>
Signed-off-by: Ricardo Dias <ricardo.dias@percona.com>
The published python3.12-boto3 RPM carries runtime requirements
python3.12dist(jmespath) >= 0.7.1 < 2 and
python3.12dist(s3transfer) >= 0.12 < 0.13, but no project in the OBS
repository path chains provides them, leaving percona-patroni-aws (and
the tarball packages that install it) unresolvable in build chroots.

Package both, mirroring the python3-boto3/python3-botocore layout:
obs_scm service pinned to the upstream tag (jmespath 1.0.1,
s3transfer 0.12.0 - the only 0.12.x release), python3.12 stack on EL,
noarch, Epoch 1. s3transfer requires botocore, matching upstream
metadata; jmespath has no python dependencies. Add ppg:staging:17
_aggregate packages so the staging repositories stay self-contained,
same as python3-boto3.

Signed-off-by: Ricardo Dias <ricardo.dias@percona.com>
Signed-off-by: Ricardo Dias <ricardo.dias@percona.com>
…nt repos

User decision 2026-07-21: SSL variants become repositories (ssl1.1, ssl3,
ssl3.5) of one ppg:staging:17:tarballs subproject instead of three
subprojects. Per-repo differences move into %if "%_repository"
conditionals (OBS defines _repository in every build config). The
artifact is now written by the %build script directly to
/usr/src/packages/OTHER with the exact official name, since the
simpleimage recipe's raw-sed Name/Version extraction cannot vary per
repository.

Signed-off-by: Ricardo Dias <ricardo.dias@percona.com>
…t repos

Replace the three per-SSL-variant subprojects (tarballs/ssl1.1, ssl3,
ssl3.5) with a single ppg:staging:17:tarballs subproject that carries
three repositories, one per variant, each stacked on its EL base
(ssl1.1 -> RockyLinux 8, ssl3 -> RockyLinux 9, ssl3.5 -> RockyLinux 10).
User decision: variants are repositories, not subprojects.

Per-repository differences move to %if "%_repository" conditionals:
the Python 3.12 vs default-python3 BuildRequires split in the
simpleimage recipe, and the per-base resolution hints in the merged
project config. The TARBALL_SSL_VARIANT / TARBALL_PYTHON_PKG macros
and the per-variant macros.yaml files are gone.

Artifact naming changes with it: the simpleimage recipe extracts
Name:/Version: with a raw (unexpanded) sed, so the output name cannot
vary per repository. The %build script now derives the full official
name itself and writes the tarball directly into
/usr/src/packages/OTHER (the OBS result dir), skipping
/.simpleimage.tar.gz entirely (#!NoTarBall keeps the recipe's own tar
step off). The SSL variant is derived from the buildroot's EL major
version (os-release, with a glibc %dist-tag fallback) rather than the
openssl-libs version, which can no longer tell EL9 from EL10 (EL 9.8+
and EL10 both ship OpenSSL 3.5.x). Unmapped EL majors fail the build
loudly. The name now uses the official dash-arch form
(percona-postgresql-<ver>-<variant>-linux-x86_64.tar.gz).

Delete tests/test_tarball_variants.py: with a single package copy
there is no byte-identity to guard.

Signed-off-by: Ricardo Dias <ricardo.dias@percona.com>
The tarballs section still said the SSL variant is detected from the
buildroot's openssl-libs version, which is the abandoned approach:
EL 9.8+ and EL10 both ship OpenSSL 3.5.x, so the build script derives
the variant from the buildroot's EL major version instead
(/etc/os-release PLATFORM_ID, glibc %dist tag as fallback). Align the
README with the committed script.

Signed-off-by: Ricardo Dias <ricardo.dias@percona.com>
Signed-off-by: Ricardo Dias <ricardo.dias@percona.com>
…et::SSLeay

The ssl variant labels promise host compatibility (ssl1.1 = OpenSSL 1.1
hosts, ssl3 = any OpenSSL 3.0+ host, ssl3.5 = 3.5+ hosts), and what
enforces that at the ELF level is each binary's versioned symbol
references (version NEEDS) against the host-provided libssl/libcrypto.
That property was unguarded: the buildroot may ship a newer OpenSSL
than the promise (the EL9 buildroot has 3.5.x), so a staging rebuild
could silently start referencing newer version nodes.

Add an OpenSSL host-ABI audit to the section-15 verification gate:
readelf -V version NEEDS (needs only, never version definitions) are
parsed per needed soname, filtered to libssl.so.*/libcrypto.so.*, and
checked against a per-variant allowed-node pattern (ssl1.1:
OPENSSL_1_1_*, ssl3: OPENSSL_3.0.x only, ssl3.5: up to 3.5.x). The
/opt/percona-python3 tree is excluded: it bundles its own OpenSSL copy,
so its OpenSSL symbol needs are satisfied internally and are not part
of the host promise. The SSL_VARIANT detection (EL-major mapping) moves
from section 16 to the top of section 15 since the audit's policy
selection depends on it; section 16 reuses the variable.

Prune Net::SSLeay from the bundled perl (auto/Net/SSLeay XS dirs plus
Net/SSLeay.pm/.pod), for parity with the official tarball, which does
not ship it; its XS module links the host libssl/libcrypto with
OPENSSL_3.2.0 needs that would violate the variant promises.

NOTE: the gate now intentionally FAILS on the current EL9/Rocky 9.8
base — pgcrypto.so inherits an OPENSSL_3.4.0 reference from the
staging build (verified: the official tarball's pgcrypto needs only
OPENSSL_3.0.0). The ssl3 variant is being rebased onto Ubuntu 22.04
debs in a follow-up commit (its pgcrypto needs only OPENSSL_3.0.0);
the strict OPENSSL_3.0.x policy is the documented reason for that
rebase and stays as-is.

Signed-off-by: Ricardo Dias <ricardo.dias@percona.com>
Signed-off-by: Ricardo Dias <ricardo.dias@percona.com>
…-deb.sh)

Section-by-section port of build-tarball.sh to Debian/Ubuntu paths and
tools, sharing the same layout, helpers, SYSTEM_LIBS_EXCLUDE list and
verification gate (DT_NEEDED soname audit, per-variant OpenSSL host-ABI
audit, smoke commands). Key mappings: PostgreSQL under
/usr/lib/postgresql/<major> (real binaries — /usr/bin/psql is a
postgresql-common pg_wrapper symlink), ecpg + libecpg/libpgtypes from
libecpg-dev, headers from /usr/include/postgresql with the <major>/server
nesting flattened to include/server, pgbouncer/pgpool tools in /usr/sbin,
config in /etc/pgpool2, python 3.10 with platlibdir=lib (no lib64
symlink hack) and third-party modules merged into the version-agnostic
lib/python3/dist-packages that Debian's site.py resolves under a foreign
PYTHONHOME, perl flattened from archlib/privlib/vendorarch/vendorlib,
tcl stdlib from /usr/share/tcltk, multiarch libdir throughout, and
version discovery via dpkg-query (epoch and revision stripped).

Variant mapping is deliberate per deb base: ubuntu-22.04 -> ssl3,
anything else is fatal. Validated end-to-end in an ubuntu:22.04
container against the published staging Ubuntu_22.04 debs: both audits
clean under the strict ssl3 policy (only OPENSSL_3.0.x version nodes),
all six smoke commands pass, artifact
percona-postgresql-17.10-ssl3-linux-x86_64.tar.gz with the ten official
percona-* component dirs.

Signed-off-by: Ricardo Dias <ricardo.dias@percona.com>
The ssl3 variant promises to run on any OpenSSL 3.0 host, but its EL9
base can no longer deliver that: Rocky 9.8+ ships OpenSSL 3.5.x, so
staging-built binaries (pgcrypto.so) reference OPENSSL_3.4.0 symbol
version nodes and the gate added in commit cd4f043 correctly fails the
build. Ubuntu 22.04 ships OpenSSL 3.0.2 (glibc 2.35), so its binaries
can only reference OPENSSL_3.0.x nodes — verified against the published
staging Ubuntu_22.04 debs.

- simpleimage: wrap BuildRequires in %if "%_repository" == "ssl3"
  (deb binary package names, verified against the published Packages
  index) with the existing RPM list — including the nested ssl3.5
  python3 split — in the %else branch; %build becomes a small
  os-release dispatcher running build-tarball-deb.sh on deb bases and
  build-tarball.sh elsewhere.
- project.yaml: ssl3 repo paths now mirror staging's Ubuntu_22.04 chain
  (staging + common:deps + Ubuntu:22.04/universe); the EL9 hdf-libs
  Prefer block is dropped (no deb-side hints needed yet).
- build-tarball.sh: EL9 is now an unmapped (fatal) tarball base; prune
  IO::Socket::SSL alongside Net::SSLeay (pure perl, but it can never
  load without the pruned XS module, and the official percona-perl tree
  ships neither); clarify the python-tree OpenSSL audit exclusion
  comment.
- root/README.md: document the new ssl3 base, the two parallel builder
  scripts + dispatcher, and the deb-side python divergences (3.10,
  dist-packages).

Signed-off-by: Ricardo Dias <ricardo.dias@percona.com>
RHEL 9.8 rebased to OpenSSL 3.5 and the EL9 staging pgcrypto.so
references OPENSSL_3.4.0, breaking the ssl3 (OpenSSL 3.0 hosts)
promise. ssl3 now consumes staging Ubuntu 22.04 debs via a second
builder (build-tarball-deb.sh) selected by an os-release dispatcher;
the verification gate enforces each variant's OpenSSL promise via a
versioned-symbol-needs audit.

Signed-off-by: Ricardo Dias <ricardo.dias@percona.com>
Signed-off-by: Ricardo Dias <ricardo.dias@percona.com>
…r decision)

Signed-off-by: Ricardo Dias <ricardo.dias@percona.com>
…3.0 symbols

Completes WIP a772758 (task 20, user-approved plan addition).

Built on Rocky 9 (OpenSSL 3.5 headers), two extension modules picked up
versioned symbol needs newer than 3.0.0:
 - _ssl:      X509_STORE_get1_objects@OPENSSL_3.3.0
 - _hashlib:  EVP_MD_CTX_get_size_ex@OPENSSL_3.4.0 (via OpenSSL 3.4's
              EVP_MD_CTX_size macro remap)
On an OpenSSL 3.0 host (Ubuntu 22.04/24.04, Debian 12) the postgres
backend already has the HOST libcrypto/libssl mapped, soname dedup wins
over the tarball's bundled 3.5 copies, and plpython3u `import ssl` dies
with "version OPENSSL_3.3.0 not found". The official tarball carries the
identical defect; this fixes it.

python-3.12-openssl30-symbol-compat.patch forces CPython's own
3.0-compatible paths (same philosophy as the pgcrypto OpenSSL-3.4
patch): the _ssl polyfill of X509_STORE_get1_objects (upstream code,
compiled unconditionally under a renamed symbol) and the pre-3.4
EVP_MD_CTX_size expansion. Inert on EL8 (OpenSSL 1.1.1 headers):
before/after version needs are byte-identical there.

A %check gate pins the promise at the source: rpmbuild fails loudly if
_ssl/_hashlib need OPENSSL_3.[1-9]* (EL9) or RHEL-only OPENSSL_1_1_1x
nodes (EL8), or if any other lib-dynload module links OpenSSL at all.

Validated: rebuilt in rockylinux:8 and rockylinux:9; EL9 needs now floor
at OPENSSL_3.0.0; standalone `import ssl, hashlib` green on both bases;
decisive e2e on ubuntu:22.04 (OpenSSL 3.0.2) with the tarball-linkage
simulation and bare `postgres -D` from `env -i`:
`DO $$ import ssl $$ LANGUAGE plpython3u` failed before
("version OPENSSL_3.3.0 not found") and passes after.

Signed-off-by: Ricardo Dias <ricardo.dias@percona.com>
…ssing-module guard

build-tarball.sh (task-17 review riders):
- PY_BIN: append || true inside the command substitution — under set -e,
  both lookups failing aborted the assignment itself, making the crafted
  FATAL on the next line dead code.
- Section 0: assert bin/pip3 for the python tree — section 7's
  ln -sf pip3 bin/pip would silently create a dangling symlink if
  ensurepip ever stops running in the runtime RPM.

percona-tarball-python3.spec (task-20 review rider):
- %check module loop: guard the unmatched-glob case with an explicit
  missing-module FATAL. Previously a module that silently failed to
  build left $so as the literal glob, readelf errored and the || true
  swallowed it into a false PASS.

No Release bump: the package has never been synced or built outside
local validation.

Signed-off-by: Ricardo Dias <ricardo.dias@percona.com>
…Ls, haproxy, perl constraint)

root/README.md, tarballs section:
- compiled /tmp socket defaults for server, initdb and every libpq
  client via the same-length ELF string patch — no PGHOST, no
  /run/postgresql host directory;
- zero-env PLs via the /opt-prefixed percona-tarball-{perl,tcl,python3}
  runtimes in ppg:common:deps; only the psql readline shim remains;
- perl version constraint documented (5.26.3/5.32.1 distro-ABI-tied via
  plperl.so; official's 5.38 comes from building PG against its own perl);
- eleven components incl. percona-haproxy; bytecode-free python;
- ssl3 promise now also covered by the CPython _ssl/_hashlib
  OPENSSL_3.0.0 pin (import ssl works on all 3.x hosts);
- host prerequisites updated (postgres-style user, host readline for
  psql, tzdata, /opt copy step); pruned the deleted machinery (initdb
  wrapper, PGHOST, PERL5LIB/TCL_LIBRARY/PYTHONHOME exports and the
  archive_command env -u PYTHONHOME leak note).

Spec doc: dated 2026-07-28 revision note summarizing the six QA-round
changes with commit refs; pipeline/wrappers/RPATH steps updated to the
implemented mechanism; caveats refreshed (PYTHONHOME caveat obsolete,
perl constraint and host prerequisites added); superseded env/initdb
wrappers moved to Rejected alternatives.

Signed-off-by: Ricardo Dias <ricardo.dias@percona.com>
Signed-off-by: Ricardo Dias <ricardo.dias@percona.com>
Signed-off-by: Ricardo Dias <ricardo.dias@percona.com>
…eline exclusions, dlopen gate)

Signed-off-by: Ricardo Dias <ricardo.dias@percona.com>
…rl,tcl,python3}

Package names now match their /opt/percona-* install prefixes and the
tarball component directories (same scheme as the upcoming percona-psql,
percona-gdal and percona-proj). No functional change: install paths,
sources and patches are untouched.

Signed-off-by: Ricardo Dias <ricardo.dias@percona.com>
The tarball's psql was built against the host's libreadline, which minimal
Debian/Ubuntu hosts do not ship — hence the LD_PRELOAD/libreadline.so.7
symlink wrapper around psql.bin.  Build psql from the same PG 17.10 source
with --with-libedit-preferred instead, so it links BSD libedit (bundled with
the tarball) and needs nothing from the host.

The package is PG-version-bound and must be compiled against the exact EL
base of the tarball that bundles it, so it lives in the tarballs project.
That project's prjconf forced "Type: simpleimage" globally, so:

- add plain RPM build repos RockyLinux_8 / RockyLinux_9 (same path chains as
  ssl1.1 / ssl3, publish disabled),
- make "Type: simpleimage" (and Binarytype) repo-scoped to ssl1.1 / ssl3,
- give ssl1.1 / ssl3 the sibling build repo as their first path so the
  simpleimage chroots can install percona-psql,
- disable the tarball recipe on the RPM repos and percona-psql on the ssl
  repos via package.yaml build flags.

The spec's %check gates the whole point of the package: the shipped binary
must NEED libedit.so.0 and libpq.so.5 and must not mention libreadline.

Verified with local rpmbuild on rockylinux:8 and rockylinux:9 (both green,
%check passing), and by running the resulting psql against a PostgreSQL 17
server from a debian:12-slim container with no libreadline installed —
Ctrl-A/Ctrl-D line editing and history recall both work.

Signed-off-by: Ricardo Dias <ricardo.dias@percona.com>
Signed-off-by: Ricardo Dias <ricardo.dias@percona.com>
The tarball bundles the RPM-built postgis-*.so, so it has to carry a
libproj. EPEL's has its PROJ_DATA default compiled to /usr/share/proj, a
path that does not exist on the tarball's target hosts, so proj.db is
never found and ST_Transform() only works behind an env-exporting
wrapper — which QA proved is bypassable.

Build PROJ from source with the prefix /opt/percona-proj so the compiled
-in data path is /opt/percona-proj/share/proj and proj.db sits right
there: zero environment variables, the official tarball's mechanism (the
tarball is extracted at /opt, like percona-{perl,tcl,python3} already
are).

The upstream version is per base so the SONAME matches the libproj the
shipped PostGIS RPMs linked against, making ours a drop-in for the
bundled file:

  RHEL 8 (ssl1.1): PROJ 6.3.2 -> libproj.so.15.3.2  (autotools)
  RHEL 9 (ssl3):   PROJ 9.6.0 -> libproj.so.25.9.6.0 (cmake; 9.x dropped
                                                      autotools)

Both real filenames match the ones in the pr-2 tarball artifacts. The
libproj.so.NN()(64bit) Provides is deliberately NOT filtered: the ssl
chroots will use a prjconf Prefer: to let this package satisfy PostGIS's
SONAME Requires instead of EPEL's proj-libs. RockyLinux_8/9 only.

%install asserts share/proj/proj.db exists and that the prefix string is
compiled into the library — the two properties the package exists for.

Distinct from ppg:common:deps `proj`, which is a Debian-only distro
shadow built to /usr; the two never meet in a chroot.

Signed-off-by: Ricardo Dias <ricardo.dias@percona.com>
The tarball bundles the RPM-built postgis_raster.so, so it has to carry a
libgdal. EPEL's was wrong on two counts:

  1. EPEL enables every optional driver, so libgdal drags ~70 surplus
     shared objects into the artifact (armadillo, poppler, hdf4/hdf5,
     netcdf, OPeNDAP, xerces, libkml, unixODBC, mariadb, cfitsio, ogdi,
     jasper/openjpeg, ...). Among them is a BLAS/LAPACK chain via
     armadillo whose FlexiBLAS ELF constructor abort()s on Rocky hosts
     (its dlopen'ed backend plugin is not bundled), and it needs
     libtirpc/libexpat/libpcre2-posix that minimal hosts lack.
  2. Its GDAL data dir is compiled to /usr/share/gdal, absent on the
     tarball's target hosts.

Build GDAL from source with the prefix /opt/percona-gdal and exactly the
driver set the OFFICIAL Percona tarball's libgdal links — derived from
readelf -d on the official artifact's libgdal.so.36 — with an explicit
--without- for every other external-library driver. The data dir is
compiled to /opt/percona-gdal/share/gdal and PROJ comes from
percona-proj, so both resolve their resources with zero environment
variables.

Version is per base so the SONAME is a drop-in for the bundled file:

  RHEL 8 (ssl1.1): GDAL 3.0.4 -> libgdal.so.26.0.4
  RHEL 9 (ssl3):   GDAL 3.4.3 -> libgdal.so.30.0.3

Both real filenames match the ones in the pr-2 tarball artifacts.

Two deliberate reductions below official: --with-gif=internal (official
links no libgif at all) and --with-qhull=internal — same driver
functionality, two fewer shared objects. EL8 uses --with-pcre because
GDAL 3.0.4 has no --with-pcre2 (and no --with-lz4); libpcre.so.1 is
already bundled via libspatialite, so it costs nothing.

The libgdal.so.NN()(64bit) Provides is deliberately NOT filtered, same
Prefer: mechanism as percona-proj. RockyLinux_8/9 only.

%install gates both invariants: the resource files must be in the
directory libgdal was told about (GDAL also carries a
<prefix>/share/gdal fallback string, so grepping the library alone would
pass a wrong --datadir), and libgdal must link none of the libraries the
official one does without.

Signed-off-by: Ricardo Dias <ricardo.dias@percona.com>
Three things the first green local rpmbuild on each base exposed:

1. GDAL 3.4.3 APPENDS "/gdal" to --datadir; GDAL 3.0.4 uses it verbatim.
   A single --datadir=<prefix>/share/gdal therefore installed the EL9
   resource files at <prefix>/share/gdal/gdal and compiled INST_DATA to
   match. Self-consistent, so it worked, but it is not the path the
   tarball builder (task 23) or anyone reading the spec expects. Made
   --datadir per-version via %{gdal_datadir} so both bases land on
   <prefix>/share/gdal.

2. That mistake slipped past the data-path gate, because GDAL also
   carries an independent <prefix>/share/gdal fallback string, so
   grepping the library alone passes even on a wrong --datadir. The gate
   now also asserts the resource files are really there
   (share/gdal/gdalicon.png, present in both 3.0.4 and 3.4.3) and that
   share/gdal/gdal does not exist.

3. RHEL 9's %optflags carry -flto=auto, and libgdal is big enough that
   the final link degenerates into "lto-wrapper: using serial compilation
   of 128 LTRANS jobs" — minutes of single-threaded work that risks an
   OBS build timeout for no benefit (upstream's own --enable-lto is off
   by default and the autotools build is not LTO-tested). Disabled with
   %global _lto_cflags %{nil}; a no-op on RHEL 8, whose optflags have no
   LTO.

Also moved --without-sde into the RHEL 8 branch: GDAL 3.4.3 does not
register it and warned "unrecognized options". Both configures are now
warning-free.

Signed-off-by: Ricardo Dias <ricardo.dias@percona.com>
…SONAME gates

Review round 1 on task 22, three findings.

1. percona-gdal gets an explicit `Requires: percona-proj`. The automatic
   libproj.so.NN()(64bit) ELF requires is ambiguous: EPEL's proj-libs
   provides the very same symbol on both bases (6.3.2 -> libproj.so.15,
   9.6.0 -> libproj.so.25), so `dnf install percona-gdal` could satisfy it
   with proj-libs, leaving /opt/percona-proj/lib absent, libgdal's rpath
   dead and the compiled-in data path mechanism broken. The point of the
   package is *our* libproj, not any libproj with a matching SONAME.

2. libgdal gets a second rpath entry, `$ORIGIN`, next to
   /opt/percona-proj/lib. postgis_raster-3.so's own RUNPATH $ORIGIN does
   not help resolve libgdal's NEEDED libproj.so.NN — DT_RUNPATH is not
   inherited down the load chain, it applies only to the object that
   carries it. So without this, libproj resolution inside the tarball
   depends entirely on task 23 materialising /opt/percona-proj/lib; with
   it, a flattened tarball lib/ works too. Escaped as \$ORIGIN so the
   literal reaches the linker rather than an empty shell expansion.

3. Both specs now gate SONAME parity, the one property they exist for:
   test -e <prefix>/lib/libproj.so.{15,25} and libgdal.so.{26,30}, chosen
   per base by %if. A source-URL bump, or the %if picking the wrong branch
   on an unexpected %{?rhel}, would otherwise silently produce a library
   that is no longer a drop-in for what PostGIS linked. The existing
   data-path grep and the lean-driver readelf loop now address the same
   explicit SONAME path instead of a libgdal.so.*.*.* / libproj.so.*.*
   glob, so those gates fail loudly rather than on a glob mismatch.

Signed-off-by: Ricardo Dias <ricardo.dias@percona.com>
Signed-off-by: Ricardo Dias <ricardo.dias@percona.com>
…en gates

Wires the task-21/22 helper packages into the tarball build and turns the
QA findings into build-time gates.

simpleimage / project.yaml:
- BuildRequires percona-psql, percona-gdal, percona-proj.
- prjconf `Prefer: percona-gdal` / `Prefer: percona-proj` in BOTH ssl repo
  blocks: PostGIS's libgdal.so.NN()(64bit)/libproj.so.NN()(64bit) Requires
  are a "have choice" against EPEL's gdal-libs/proj and must resolve to
  ours (EPEL's dragged ~70 surplus shared objects — including the
  armadillo/FlexiBLAS chain whose ELF constructor abort()s on Rocky hosts
  — and compiled its data paths to /usr/share/{gdal,proj}).

build-tarball.sh:
- Section 0a: assert the percona-gdal/percona-proj trees and their
  compiled-in resource dirs, assert percona-psql, and FATAL if EPEL
  gdal/gdal-libs/proj/proj-libs reached the chroot anyway.
- Section 2b: bin/psql is now the real libedit-linked percona-psql binary;
  the whole psql.bin + LD_PRELOAD/libreadline.so.7-symlink wrapper is
  deleted.
- Section 12b: prune /opt/percona-{gdal,proj} to the lean tarball layout
  (lib/ + share/{gdal,proj}); they ship as two more top-level components,
  so their compiled data paths resolve with zero environment variables.
- Section 13: run the bundling passes with LD_LIBRARY_PATH pointing at the
  two /opt lib dirs (subshell-scoped, never leaks) so postgis_raster's
  NEEDED libgdal/libproj resolve to OURS and get bundled next to it.
- SYSTEM_LIBS_EXCLUDE is now a documented universal host baseline;
  libtirpc/libnsl/libeconf/libpcre2-8/libpcre2-posix/libexpat/libreadline
  are removed from it and therefore bundled (QA finding 1).
- New gates: surplus dependency-chain audit (flexiblas/armadillo/hdf/
  netcdf/dap/poppler/mariadb/odbc/kml/xerces/arpack/superlu), host-baseline
  self-consistency gate, psql link audit (libedit in, libreadline out),
  extension dlopen smoke from an empty environment (RTLD_LAZY, every
  Pg_magic_func module plus the bundled libgdal/libproj), psql --version
  smoke, component inventory 11 -> 13.

Signed-off-by: Ricardo Dias <ricardo.dias@percona.com>
…/proj layout

Review round 1 on the task-23 gates.

- CRITICAL: the dlopen smoke probe used ctypes.RTLD_LAZY, which does not
  exist (ctypes exports only RTLD_GLOBAL/RTLD_LOCAL; RTLD_LAZY lives in
  os) — every module would have failed with an AttributeError and killed
  §15 on both ssl repos. Payload is now
  'import ctypes, os, sys; ctypes.CDLL(sys.argv[1], mode=os.RTLD_LAZY)'.
- "undefined symbol" is no longer an unconditional pass: the symbol name
  is extracted from the loader message and must be defined by
  bin/postgres (nm -D --defined-only, computed once, @vers stripped) —
  the precise "postgres-internal" test. A bundled-dependency ABI
  mismatch (undefined symbol: GEOSDensify) is now FATAL instead of being
  logged as OK. Degraded mode (no nm) says so explicitly.
- Host-baseline gate resolves PER COMPONENT: a formerly-excluded soname
  must sit in the needing ELF's own component lib/ dir or next to the ELF
  ($ORIGIN), not merely somewhere under /opt — a libexpat in
  percona-python3/lib no longer satisfies haproxy's NEED.
- percona-gdal/percona-proj ship DATA ONLY: lib/ is pruned too, so the
  artifact carries no dead second copy of libgdal/libproj (unloadable,
  several MB, extra SSL-ABI audit surface). The prune moved to a new
  section 13a, AFTER the bundling pass that reads those very lib/ dirs
  through LD_LIBRARY_PATH; new assert that lib/ is gone. Inventory stays
  at 13 components.

Signed-off-by: Ricardo Dias <ricardo.dias@percona.com>
Signed-off-by: Ricardo Dias <ricardo.dias@percona.com>
Reusable acceptance battery for the OBS-built PostgreSQL binary tarballs.
It never builds anything: given an artifact it derives the SSL variant from
the file name and runs it on that variant's minimal container images
(debian:11-slim/ubuntu:20.04 for ssl1.1; debian:12-slim, ubuntu 22.04/24.04
and rockylinux 9/10-minimal for ssl3), podman or docker, autodetected.

Per host: copy every top-level percona-* component to /opt (the documented
install step), create a non-root user, initdb and start a bare
`postgres -D` under env -i, assert the compiled /tmp socket default and
that /run/postgresql is never created, then CREATE EXTENSION every shipped
share/extension/*.control (one restart with shared_preload_libraries for
the extensions that need it, from a small allowlist), the PostGIS/GDAL/PROJ
deep checks (postgis_full_version DATABASE_PATH, ST_Transform to 3857,
ST_AddBand raster, GDAL_DATA), the three PLs, psql in pipe and pty mode and
every bundled client with zero environment. Failures are reported by name;
the run exits non-zero if any check fails on any host, and prints a
host x check matrix.

The only packages it may install are the artifact's two documented host
prerequisites: tzdata, and the distro OpenSSL runtime of the variant's
generation (some minimal images ship none at all; --no-install turns even
that off and reports the image as skipped). Never readline, libtirpc,
expat, pcre2, gdal, proj, python, perl or tcl — the checks that would need
a tool the image lacks (readelf) run host-side against the extracted tree:
component inventory (13), the psql link audit (libedit in, libreadline
out), the per-component NEEDED closure against the universal host baseline
(read from build-tarball.sh when reachable, embedded copy otherwise), and
the surplus-library scan (libflexiblas & co.).

Signed-off-by: Ricardo Dias <ricardo.dias@percona.com>
…ROJ)

root/README.md's tarballs section had drifted: it described one package,
two repositories, eleven components and a host-readline psql shim. Updated
to the tree as it is: two packages (percona-postgresql-tarball,
percona-psql), four repositories (ssl1.1/ssl3 simpleimage, published;
RockyLinux_8/RockyLinux_9 RPM build repos for percona-psql, unpublished),
thirteen components including the data-only percona-gdal/percona-proj
resource trees, the libedit psql with no wrapper anywhere, the universal
host-baseline bundling contract and its two gates, and host prerequisites
that are now just a user, tzdata and copying ALL percona-* dirs to /opt
(with the caveat that a few minimal images ship no OpenSSL runtime at all).
Also mentions tools/tarball-acceptance.sh.

The design doc gets a "Revision note — 2026-07-29 QA round 2" in the style
of the 2026-07-28 one: the three findings (postgis_raster on minimal hosts,
the FlexiBLAS constructor abort on Rocky 9/10, psql needing host readline),
their root causes (an exclusion list copied from pg_tarballs_builder.sh,
EPEL's kitchen-sink GDAL with its dlopen'ed BLAS plugin, the readline psql)
and the fixes; the body sections that described the superseded mechanisms
(exclusion list, psql wrapper, eleven components, one-package project,
project-wide Type: simpleimage, the acceptance step) are updated in place.

Signed-off-by: Ricardo Dias <ricardo.dias@percona.com>
Four review findings.

1. (critical) The NEEDED-closure host check applied the per-component
   reachability rule to every non-baseline soname, so it flagged the three
   deliberate cross-component PL RUNPATHs (plperl.so ->
   /opt/percona-perl/lib/<ver>/CORE, plpython3.so -> /opt/percona-python3/lib,
   pltcl.so -> /opt/percona-tcl/lib, where libperl/libpython3.12/libtcl8.6
   live) as MISPLACED and would have failed a correct artifact. It now
   mirrors build-tarball.sh's two gates: artifact-wide bundling for sonames
   in general, the strict per-component rule only for FORMERLY_EXCLUDED_LIBS
   — read live out of the builder alongside the baseline, with an embedded
   fallback copy.
2. A run in which every image was skipped printed ACCEPTANCE PASSED. It is
   now ACCEPTANCE INCOMPLETE with exit status 3, and a partially skipped run
   says so in the PASSED line.
3. A failed OpenSSL install was indistinguishable from an image that ships
   none, so a no-network apt-get silently dropped coverage. Setup now reports
   present / missing-install-disabled / missing-after-install-attempt, and
   only the middle one is a skip — a failed install attempt fails that host.
4. Nothing but the server-ready wait was bounded. The per-image battery now
   runs under timeout(1) (--host-timeout, default 1200s, env
   TARBALL_ACCEPTANCE_HOST_TIMEOUT), and initdb, CREATE EXTENSION, the deep
   queries, both psql modes and the client checks have their own shorter
   timeouts inside the guest, which probes for a usable timeout(1) first and
   says so if it has none.

Also fixes the design doc's Rocky image names to the fully-qualified Docker
Hub form (the short rockylinux: repo has no 10-minimal tag).

Signed-off-by: Ricardo Dias <ricardo.dias@percona.com>
Signed-off-by: Ricardo Dias <ricardo.dias@percona.com>
…ew fixes

PostGIS requires the distro GDAL/PROJ BY NAME (rpmspec -P: EL8 gdal-libs +
proj, EL9 gdal3.4-libs + proj, all in the main percona-postgis35_NN package),
and `Prefer:` only breaks ties between providers of the same symbol. So the
fat EPEL packages landed in both ssl chroots regardless of the Prefer lines
and build-tarball.sh section 0a would have aborted on the first sync. Add
scoped `Ignore: percona-postgis35_%!{PG_MAJOR_VERSION}:<deps>` rules to both
ssl blocks; keep the Prefer lines, which remain load-bearing for the
automatic libgdal.so.NN()(64bit)/libproj.so.NN()(64bit) soname Requires.

Also from the whole-round review:

* build-tarball.sh section 0a: replace the name-blind EPEL-absence assert
  (which missed EL9's gdal3.4-libs) with a property check on
  /usr/lib64/lib{gdal,proj}.so.* — ours live under /opt — and print
  rpm -qf plus rpm -q --whatrequires so the log names the requirer.
* project.yaml: `Prefer: hdf-libs` is live, not inert — PostGIS requires
  hdf5 by name and hdf5's libdf.so.0 has two providers. Fix the comment.
* percona-psql.spec: %global __requires_exclude ^libpq\\.so — libpq.so.5 has
  two providers in the ssl chroots (percona-postgresql17-libs and Rocky's
  libpq); the tarball bundles its own libpq and %check still asserts NEEDED.
* percona-gdal/percona-proj: ExcludeArch: aarch64 — the tarball is x86_64
  only, but ppg:common:deps inherits aarch64 from root/project.yaml.
* tarball-acceptance.sh: postgis_full_version() only prints DATABASE_PATH=
  above a PROJ version floor and ssl1.1 links PROJ 6.3.2, so an absent
  suffix is a WARN and only a foreign path is a FAIL (ST_Transform stays the
  proj.db proof); when script(1) is missing (rockylinux:*-minimal) drive the
  pty with the artifact's own bundled python3 instead of skipping, degrade to
  a "PASS*" matrix cell plus a legend line only if that also fails, and
  assert the computed result (41+1) rather than the pty's own input echo.

Signed-off-by: Ricardo Dias <ricardo.dias@percona.com>
b90ac24 made the check property-based but left `stray_gis=$(ls … 2>/dev/null)`
unguarded. build-tarball.sh runs under `set -e`, and a bare `var=$(cmd)` takes
the substitution's exit status: with no match the globs stay literal, ls exits
2, and the script died BEFORE the `if` — so every CLEAN build (the expected
state) and every partial hit aborted silently at section 0a, and the FATAL
diagnostics could only ever print when BOTH globs matched. Same trap the file
already documents for PY_BIN and for copy_deps' find. Append `|| true` inside
the substitution and leave `if [ -n "$stray_gis" ]` as the only gate.
Verified with the block extracted verbatim: empty dir continues (rc 0), a dir
holding only libproj.so.25 takes the FATAL path with the rpm -qf owner and the
--whatrequires requirer (rc 1).

Also surface the I5 warning on the console: the host prints only the status
field into the matrix, so report the postgis cell as "PASS*" when
postgis_full_version() prints no DATABASE_PATH= (PROJ below the floor that
reports it), and generalise the existing legend line to cover both the psql
and postgis cases.

Signed-off-by: Ricardo Dias <ricardo.dias@percona.com>
Signed-off-by: Ricardo Dias <ricardo.dias@percona.com>
The first OBS build of the ssl1.1/ssl3 simpleimage repos proved that OBS
does not apply prjconf `Ignore:` rules when expanding image-type recipes:
the effective project config carried
`Ignore: percona-postgis35_17:gdal-libs,proj` (and the EL9 variant), yet
EPEL's gdal-libs/gdal3.4-libs and Rocky's proj were still installed and
build-tarball.sh section 0a FATALed as designed. `Prefer:` IS honoured.

Replace the inert Ignore: lines with a payload-free shim package,
percona-gis-compat, that Provides the distro names percona-postgis35_17
requires by name (proj/gdal-libs on EL8, proj/gdal3.4-libs on EL9,
versioned to track percona-proj/percona-gdal so the `>= 3.0.4` Requires
stays satisfied) and Requires percona-gdal/percona-proj. Each by-name
Requires becomes a real have-choice that `Prefer: percona-gis-compat`
resolves towards our lean /opt runtimes. Like percona-psql it builds only
in this project's unpublished RockyLinux_8/RockyLinux_9 repos, so the fake
Provides stay invisible to every other project, and the simpleimage recipe
BuildRequires it explicitly so its presence is deterministic.

Section 0a's property-based FATAL is unchanged; its diagnostic now queries
rpm --whatrequires with a package NAME and the soname capability instead of
the full NEVRA, which had made it print "no package requires ...".

Signed-off-by: Ricardo Dias <ricardo.dias@percona.com>
The RockyLinux_8 build of da1e31c failed with
"error: line 75: Unknown tag: %elif 08 == 9": %elif landed in rpm 4.15, so
EL8's rpm 4.14 cannot parse it (RockyLinux_9 built fine). Rewrite the
per-base Provides block as nested %if/%else/%endif and note why.

No functional change: rpmspec -P still yields proj = 6.3.2 / gdal-libs =
3.0.4 on EL8, proj = 9.6.0 / gdal3.4-libs = 3.4.3 on EL9, and %{error} on
any other base.

Signed-off-by: Ricardo Dias <ricardo.dias@percona.com>
Signed-off-by: Ricardo Dias <ricardo.dias@percona.com>
@rjd15372
rjd15372 force-pushed the tarballs-simpleimage branch from c255628 to 72cffe5 Compare August 28, 2026 10:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

keep-pr-build Ensures that the OBS PR project built for this PR is never deleted by the stale PR deletion bot. no-dep-cascade If used with `obs-sync*` labels, it prevents from packages that were not changed from being built obs-sync Trigger OBS sync push RockyLinux_8 RockyLinux_9

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants