From be39621d6cd7dc053bbe0b05a5298e50dd63590b Mon Sep 17 00:00:00 2001 From: Michael Legleux Date: Wed, 17 Jun 2026 11:19:52 -0700 Subject: [PATCH] refactor: clarify package version naming --- .github/workflows/reusable-package.yml | 2 +- cmake/XrplPackaging.cmake | 2 +- package/README.md | 139 ++++++++++++++----------- package/build_pkg.sh | 94 ++++++++--------- 4 files changed, 122 insertions(+), 115 deletions(-) diff --git a/.github/workflows/reusable-package.yml b/.github/workflows/reusable-package.yml index 0e3f657006..87bfa5d864 100644 --- a/.github/workflows/reusable-package.yml +++ b/.github/workflows/reusable-package.yml @@ -82,7 +82,7 @@ jobs: - name: Build package env: - PKG_VERSION: ${{ needs.generate-version.outputs.version }} + XRPLD_VERSION: ${{ needs.generate-version.outputs.version }} PKG_RELEASE: ${{ inputs.pkg_release }} run: ./package/build_pkg.sh diff --git a/cmake/XrplPackaging.cmake b/cmake/XrplPackaging.cmake index fe885c200c..b7746b2365 100644 --- a/cmake/XrplPackaging.cmake +++ b/cmake/XrplPackaging.cmake @@ -28,7 +28,7 @@ endif() set(package_env SRC_DIR=${CMAKE_SOURCE_DIR} BUILD_DIR=${CMAKE_BINARY_DIR} - PKG_VERSION=${xrpld_version} + XRPLD_VERSION=${xrpld_version} PKG_RELEASE=${pkg_release} ) diff --git a/package/README.md b/package/README.md index 8e7b549d40..a3bf19395e 100644 --- a/package/README.md +++ b/package/README.md @@ -6,36 +6,35 @@ This directory contains all files needed to build RPM and Debian packages for `x ``` package/ - build_pkg.sh Staging and build script (called by CMake targets and CI) + build_pkg.sh Staging and build script (called by the CMake `package` target and CI) rpm/ - xrpld.spec RPM spec (xrpld_version/pkg_release passed via rpmbuild --define) - debian/ Debian control files (control, rules, install, links, conffiles, ...) + xrpld.spec RPM spec + debian/ Debian control files (control, rules, copyright, xrpld.docs, xrpld.links, source/format) shared/ xrpld.service systemd unit file (used by both RPM and DEB) xrpld.sysusers sysusers.d config (used by both RPM and DEB) xrpld.tmpfiles tmpfiles.d config (used by both RPM and DEB) xrpld.logrotate logrotate config (installed to /etc/logrotate.d/xrpld) + 50-xrpld.preset systemd preset (RPM only; DEB enables the unit via dh_installsystemd) ``` ## Prerequisites Packaging targets and their container images are declared in [`.github/scripts/strategy-matrix/linux.json`](../.github/scripts/strategy-matrix/linux.json) -inside `package_configs` configurations. Today only -`linux/amd64` is emitted. The package format -(deb or rpm) is inferred at build time from the container's package manager -(`apt-get` -> deb, `dnf`/`yum` -> rpm). The image tag is composed as -`ghcr.io/xrplf/xrpld/packaging-:sha-` — -the same scheme used by `reusable-build-test.yml`. Bump `image_sha` in -`linux.json` and both CI and local builds pick up the new image with no -workflow edits. +under `package_configs`, one entry per distro. Today only `linux/amd64` is +emitted. Each entry pins its full container image in an `image` field; to move +to a new image, edit that field and both CI and local builds pick it up. The +package format (deb or rpm) is inferred at build time from the container's +package manager (`apt-get` -> deb, `dnf`/`yum` -> rpm). -| Package type | Image (derived from `linux.json`) | Tool required | -| ------------ | ---------------------------------------------------- | --------------------------------------------------------------- | -| RPM | `ghcr.io/xrplf/xrpld/packaging-rhel:sha-` | `rpmbuild` | -| DEB | `ghcr.io/xrplf/xrpld/packaging-debian:sha-` | `dpkg-buildpackage`, `debhelper (>= 13)`, `dh-sequence-systemd` | +| Package type | Image (`package_configs.[].image` in `linux.json`) | Tools required | +| ------------ | ---------------------------------------------------------- | ---------------------------------------------- | +| RPM | `ghcr.io/xrplf/xrpld/packaging-rhel:sha-` | `rpmbuild` | +| DEB | `ghcr.io/xrplf/xrpld/packaging-debian:sha-` | `dpkg-buildpackage`, `debhelper-compat (= 13)` | -To print the exact image tags for the current `linux.json`: +To print the full packaging matrix (artifact names and images) for the current +`linux.json`: ```bash ./.github/scripts/strategy-matrix/generate.py --packaging @@ -46,12 +45,13 @@ To print the exact image tags for the current `linux.json`: ### Via CI Caller workflows (`on-pr.yml`, `on-tag.yml`, `on-trigger.yml`) call -`reusable-strategy-matrix.yml` with `mode: packaging` to generate the matrix of -`{artifact_name, os}` entries, then fan out to -`reusable-package.yml` per entry. That workflow downloads the pre-built `xrpld` -binary artifact, detects the package format from the container, and calls -`build_pkg.sh` directly — no CMake configure or build step is needed inside -the packaging job. +`reusable-package.yml`. That workflow generates its own packaging matrix from +`package_configs` in `linux.json` (via `generate.py --packaging`) and fans out +one job per distro. Each job downloads the pre-built `xrpld` binary artifact, +runs in that distro's container (so the package format follows from the +container's package manager), and calls `build_pkg.sh` with `XRPLD_VERSION` and +`PKG_RELEASE` supplied via env — no CMake configure or build step is needed +inside the packaging job. ### Locally (mirrors CI) @@ -60,22 +60,20 @@ inside the same container CI uses. The image tag is derived from `linux.json` so you don't need to hardcode a SHA. ```bash -# From the repo root. Pick any image flagged with `"package": true` in -# linux.json; the package format is inferred from the container's package -# manager. Example for the rpm-producing image: -IMAGE=$(jq -r ' - .os | map(select(.package == true))[0] | - "ghcr.io/xrplf/ci/\(.distro_name)-\(.distro_version):\(.compiler_name)-\(.compiler_version)-sha-\(.image_sha)" -' .github/scripts/strategy-matrix/linux.json) +# From the repo root. Each distro's container image is the `image` field of its +# package_configs entry in linux.json; the package format is inferred from the +# container's package manager. Example for the rpm-producing image (use +# .package_configs.debian[0].image for the deb image): +IMAGE=$(jq -r '.package_configs.rhel[0].image' .github/scripts/strategy-matrix/linux.json) -VERSION=2.4.0-local +XRPLD_VERSION=2.4.0-local PKG_RELEASE=1 docker run --rm \ -v "$(pwd):/src" \ -w /src \ "$IMAGE" \ - ./package/build_pkg.sh --pkg-version "$VERSION" --pkg-release "$PKG_RELEASE" + ./package/build_pkg.sh --xrpld-version "$XRPLD_VERSION" --pkg-release "$PKG_RELEASE" # Output: # build/debbuild/*.deb (DEB + dbgsym .ddeb) @@ -91,19 +89,27 @@ needed, but the host toolchain replaces the pinned CI image: ```bash cmake \ -Dxrpld=ON \ - -Dxrpld_version=2.4.0-local \ + -Dpkg_release=1 \ -Dtests=OFF \ .. cmake --build . --target package # deb on Debian/Ubuntu, rpm on RHEL ``` -The `cmake/XrplPackaging.cmake` module defines the target only if at least one -of `rpmbuild` / `dpkg-buildpackage` is present; `build_pkg.sh` then infers the -package format from the host's package manager. The packaging script installs -to FHS-standard paths (`/usr/bin`, `/etc/xrpld`, etc.) regardless of +The `cmake/XrplPackaging.cmake` module defines the `package` target only if at +least one of `rpmbuild` / `dpkg-buildpackage` is present; `build_pkg.sh` then +infers the package format from the host's package manager. The packaging script +installs to FHS-standard paths (`/usr/bin`, `/etc/xrpld`, etc.) regardless of `CMAKE_INSTALL_PREFIX`. +The version is not a CMake input on this path: `cmake/XrplVersion.cmake` reads +it from `src/libxrpl/protocol/BuildInfo.cpp` into `xrpld_version`, and +`XrplPackaging.cmake` exports that to `build_pkg.sh` as `XRPLD_VERSION` (a +`-Dxrpld_version=...` on the command line is overwritten and has no effect). The +release defaults to 1 and is overridable with `-Dpkg_release=N`. To package a +custom version string, use the script or container path above with +`--xrpld-version`. + ## How `build_pkg.sh` works `build_pkg.sh` accepts long-form flags, each of which can also be set via an @@ -114,26 +120,37 @@ defaults. Run `./package/build_pkg.sh --help` for the same table: | -------------------------- | ------------------- | ----------------------------- | ----------------------------------- | | `--src-dir DIR` | `SRC_DIR` | `$PWD` | repo root | | `--build-dir DIR` | `BUILD_DIR` | `$PWD/build` | directory holding pre-built `xrpld` | -| `--pkg-version STR` | `PKG_VERSION` | parsed from `xrpld --version` | version string, e.g. `3.2.0-b1` | -| `--pkg-release N` | `PKG_RELEASE` | `1` | package release number | +| `--xrpld-version STR` | `XRPLD_VERSION` | parsed from `xrpld --version` | `xrpld` version, e.g. `3.2.0-b1` | +| `--pkg-release N` | `PKG_RELEASE` | `1` | package release iteration | | `--source-date-epoch SECS` | `SOURCE_DATE_EPOCH` | latest git commit ctime | reproducibility timestamp | -`PKG_VERSION` is the canonical input version for both package formats. -`build_pkg.sh` splits pre-release versions such as `3.2.0-b1` or -`3.2.0-rc1` into a base version and suffix, then converts the suffix separator -from `-` to `~` for package metadata so pre-releases sort before the final -release. `PKG_RELEASE` remains the package release number. For RPM builds, -those two inputs are passed to `xrpld.spec` as `xrpld_version` and -`pkg_release`; `pkg_release` is derived directly from `PKG_RELEASE` and is not -a separate user-facing input. +`XRPLD_VERSION` is the `build_pkg.sh` input for the `xrpld` software version in +both package formats. + +`build_pkg.sh` converts pre-release versions such as `3.2.0-b1` or +`3.2.0-rc1` from `-` to `~` for package metadata so pre-releases sort before +the final release. Internally, the script keeps the raw software version as +`XRPLD_VERSION` and the package-metadata form as `pkg_version`. Versions with +more than one `-` are rejected so the pre-release suffix remains a single +token. + +`PKG_RELEASE` is a different value: the package release iteration for that +`xrpld` version. RPM receives `pkg_version` and `PKG_RELEASE` as separate +`Version` and `Release` values because the spec format requires that; DEB +combines them as `deb_version` in `debian/changelog`. With `PKG_RELEASE=1`, the package metadata becomes: -| Input version | RPM version/release | Debian version | -| ------------- | --------------------- | -------------- | -| `3.2.0` | `3.2.0-1%{?dist}` | `3.2.0-1` | -| `3.2.0-b1` | `3.2.0~b1-1%{?dist}` | `3.2.0~b1-1` | -| `3.2.0-rc1` | `3.2.0~rc1-1%{?dist}` | `3.2.0~rc1-1` | +| Input version | RPM version/release | Debian version | +| ------------------ | ---------------------------- | -------------------- | +| `3.2.0` | `3.2.0-1%{?dist}` | `3.2.0-1` | +| `3.2.0-b0+abc1234` | `3.2.0~b0+abc1234-1%{?dist}` | `3.2.0~b0+abc1234-1` | +| `3.2.0-b1` | `3.2.0~b1-1%{?dist}` | `3.2.0~b1-1` | +| `3.2.0-rc1` | `3.2.0~rc1-1%{?dist}` | `3.2.0~rc1-1` | + +For Debian changelogs, final releases use the `stable` distribution, +`b0` builds, including `b0+metadata`, use `develop`, and other pre-releases use +`unstable`. The RPM path intentionally uses `~` in `Version`, matching the Debian pre-release ordering convention, so RPM filenames/NVRs begin with forms like @@ -145,7 +162,7 @@ manager (`apt-get` -> deb, `dnf`/`yum` -> rpm). Hosts without one of those fail early. Flags are for explicit invocation; environment variables are intended for -CMake/systemd/CI integration. The CI workflow and the CMake `package` target +CMake/CI integration. The CI workflow and the CMake `package` target both invoke `build_pkg.sh` with no flags, configuring it entirely via env (see `cmake/XrplPackaging.cmake`). @@ -157,9 +174,8 @@ into the staging area, and invokes the platform build tool. 1. Creates the standard `rpmbuild/{BUILD,BUILDROOT,RPMS,SOURCES,SPECS,SRPMS}` tree inside the build directory. 2. Copies `xrpld.spec` and all source files (binary, configs, service files) into `SOURCES/`. -3. Converts `PKG_VERSION` to the RPM `xrpld_version` define, passes - `PKG_RELEASE` through as the `pkg_release` define, then runs - `rpmbuild -bb --define "xrpld_version ..." --define "pkg_release ..."`. +3. Runs `rpmbuild -bb`, passing `pkg_version` as `xrpld_version` and + `PKG_RELEASE` as `pkg_release`. The spec uses manual `install` commands to place files, disables `dwz`, and writes uncompressed RPM payloads while generating debuginfo packages. 4. Output: `rpmbuild/RPMS/x86_64/xrpld-*.rpm` @@ -170,8 +186,9 @@ into the staging area, and invokes the platform build tool. 2. Stages the binary, configs, `README.md`, and `LICENSE.md`. 3. Copies `package/debian/` control files into `debbuild/source/debian/`. 4. Copies shared service/sysusers/tmpfiles into `debian/` where `dh_installsystemd`, `dh_installsysusers`, and `dh_installtmpfiles` pick them up automatically. -5. Generates a minimal `debian/changelog` (pre-release versions use `~` instead of `-`). -6. Runs `dpkg-buildpackage -b --no-sign`. `debian/rules` uses manual `install` commands. +5. Generates a minimal `debian/changelog` using + `deb_version`. +6. Runs `dpkg-buildpackage -b --no-sign -d` (`-d` skips the build-dependency check, since the binary is already built). `debian/rules` uses manual `install` commands. 7. Output: `debbuild/*.deb` and `debbuild/*.ddeb` (dbgsym package) ## Post-build verification @@ -187,11 +204,13 @@ rpm -qlp rpmbuild/RPMS/x86_64/*.rpm ## Reproducibility -The following environment variables improve build reproducibility. They are not -set automatically by `build_pkg.sh`; set them manually if needed: +`build_pkg.sh` already defaults `SOURCE_DATE_EPOCH` to the latest git commit +time and exports it (override with `--source-date-epoch` / `SOURCE_DATE_EPOCH`); +the RPM spec clamps file modification times to it via `%build_mtime_policy`. The remaining +variables below further improve reproducibility but are _not_ set by the +script — export them yourself if needed: ```bash -export SOURCE_DATE_EPOCH=$(git log -1 --pretty=%ct) export TZ=UTC export LC_ALL=C.UTF-8 export GZIP=-n diff --git a/package/build_pkg.sh b/package/build_pkg.sh index ea505fe70d..64bce76c12 100755 --- a/package/build_pkg.sh +++ b/package/build_pkg.sh @@ -4,18 +4,18 @@ set -euo pipefail # Build an RPM or Debian package from a pre-built xrpld binary. # # Flags override env vars; env vars override defaults. Env vars are intended -# for CMake/systemd/CI integration; flags are for explicit invocation. +# for CMake/CI integration; flags are for explicit invocation. usage() { cat <<'EOF' Usage: build_pkg.sh [options] Options (each can also be set via the env var shown): - --src-dir DIR repo root [SRC_DIR; default: $PWD] - --build-dir DIR directory holding xrpld [BUILD_DIR; default: $PWD/build] - --pkg-version STR version, e.g. 3.2.0-b1 [PKG_VERSION; default: parsed from xrpld --version] - --pkg-release N package release number [PKG_RELEASE; default: 1] - --source-date-epoch SECS reproducibility timestamp [SOURCE_DATE_EPOCH; default: latest git commit ctime] + --src-dir DIR repo root [SRC_DIR; default: $PWD] + --build-dir DIR directory holding xrpld [BUILD_DIR; default: $PWD/build] + --xrpld-version STR xrpld version, e.g. 3.2.0-b1 [XRPLD_VERSION; default: parsed from xrpld --version] + --pkg-release N package release iteration [PKG_RELEASE; default: 1] + --source-date-epoch SECS reproducibility timestamp [SOURCE_DATE_EPOCH; default: latest git commit ctime] -h, --help show this help and exit EOF } @@ -30,8 +30,8 @@ need_arg() { # Seed from env. CLI parsing below overrides these directly. SRC_DIR="${SRC_DIR:-}" BUILD_DIR="${BUILD_DIR:-}" -PKG_VERSION="${PKG_VERSION:-}" -PKG_RELEASE="${PKG_RELEASE:-}" +XRPLD_VERSION="${XRPLD_VERSION:-}" +PKG_RELEASE="${PKG_RELEASE:-1}" SOURCE_DATE_EPOCH="${SOURCE_DATE_EPOCH:-}" while [[ $# -gt 0 ]]; do @@ -46,9 +46,9 @@ while [[ $# -gt 0 ]]; do BUILD_DIR="$2" shift 2 ;; - --pkg-version) + --xrpld-version) need_arg "$@" - PKG_VERSION="$2" + XRPLD_VERSION="$2" shift 2 ;; --pkg-release) @@ -75,18 +75,31 @@ done SRC_DIR="$(cd "${SRC_DIR:-${PWD}}" && pwd)" BUILD_DIR="$(cd "${BUILD_DIR:-${PWD}/build}" && pwd)" -PKG_RELEASE="${PKG_RELEASE:-1}" -if [[ -z "${PKG_VERSION}" ]]; then - PKG_VERSION="$("${BUILD_DIR}/xrpld" --version | awk 'NR==1 {print $3; exit}')" +if [[ -z "${XRPLD_VERSION}" ]]; then + XRPLD_VERSION="$("${BUILD_DIR}/xrpld" --version | awk 'NR==1 {print $3; exit}')" fi -if [[ -z "${PKG_VERSION}" ]]; then - echo "PKG_VERSION is empty (not provided and could not be derived)." >&2 +if [[ -z "${XRPLD_VERSION}" ]]; then + echo "XRPLD_VERSION is empty (not provided and could not be derived)." >&2 exit 1 fi -VERSION="${PKG_VERSION}" +# A pre-release suffix must be a single token. RPM Version cannot contain '-', +# and this script uses one '-' as the version/release separator for both package +# formats, so versions carrying a second '-' ("beta-1", "rc1-15-gabc123") do +# not fit the convention. Reject those up front rather than mangling them later. +if [[ "${XRPLD_VERSION}" == *-*-* ]]; then + echo "build_pkg.sh: multi-segment version XRPLD_VERSION='${XRPLD_VERSION}'." >&2 + echo "Use a single-token pre-release like 3.2.0-b1 or 3.2.0-rc2." >&2 + exit 1 +fi + +# The version as the package formats consume it: identical to XRPLD_VERSION +# except a pre-release uses '~' (3.2.0-b1 -> 3.2.0~b1), which also sorts before +# the final 3.2.0; a no-op for a final release. Lowercase = derived internally, +# not an input (cf. pkg_type). +pkg_version="${XRPLD_VERSION/-/~}" if command -v apt-get >/dev/null 2>&1; then pkg_type=deb @@ -108,23 +121,6 @@ fi export SOURCE_DATE_EPOCH CHANGELOG_DATE="$(date -u -R -d "@$SOURCE_DATE_EPOCH")" -# Split VERSION at the first '-' into base and optional pre-release suffix. -# Examples: "3.2.0" -> ("3.2.0", ""); "3.2.0-b1" -> ("3.2.0", "b1"). -VER_BASE="${VERSION%%-*}" -VER_SUFFIX="${VERSION#*-}" -[[ "${VER_SUFFIX}" == "${VERSION}" ]] && VER_SUFFIX="" - -# Reject multi-segment suffixes (e.g. "beta-1", "rc1-15-gabc123"). Neither an -# RPM Version nor a Debian upstream version may contain '-' (it's the NVR / -# version-revision separator), and the convention here is single-token -# suffixes like b1 or rc2. Fail early with a clear message rather than letting -# the package tooling blow up or silently mangle dashes. -if [[ "${VER_SUFFIX}" == *-* ]]; then - echo "build_pkg.sh: multi-segment pre-release in VERSION='${VERSION}' (suffix '${VER_SUFFIX}')." >&2 - echo "Use single-token suffixes like 3.2.0-b1 or 3.2.0-rc2." >&2 - exit 1 -fi - SHARED="${SRC_DIR}/package/shared" DEBIAN_DIR="${SRC_DIR}/package/debian" @@ -154,17 +150,10 @@ build_rpm() { cp "${SRC_DIR}/package/rpm/xrpld.spec" "${topdir}/SPECS/xrpld.spec" stage_common "${topdir}/SOURCES" - # Pre-releases use the modern rpm '~' convention (rpm >= 4.10): the suffix - # goes in Version (e.g. 3.2.0~b1), which rpmvercmp sorts *before* the final - # 3.2.0 — identical semantics to Debian's '~'. Release is just the package - # release number. This replaces the older "0.." Release - # hack and keeps the RPM and DEB version strings symmetric. - local rpm_version="${VER_BASE}${VER_SUFFIX:+~${VER_SUFFIX}}" - set -x rpmbuild -bb \ --define "_topdir ${topdir}" \ - --define "xrpld_version ${rpm_version}" \ + --define "xrpld_version ${pkg_version}" \ --define "pkg_release ${PKG_RELEASE}" \ "${topdir}/SPECS/xrpld.spec" } @@ -182,23 +171,22 @@ build_deb() { cp "${staging}/xrpld.tmpfiles" "${staging}/debian/xrpld.tmpfiles" cp "${staging}/xrpld.logrotate" "${staging}/debian/xrpld.logrotate" - # Debian '~' marks a pre-release; 3.2.0~b1 sorts before 3.2.0. - local deb_full_version="${VER_BASE}${VER_SUFFIX:+~${VER_SUFFIX}}-${PKG_RELEASE}" + # Full Debian version: [~
]-.
+    local deb_version="${pkg_version}-${PKG_RELEASE}"
 
-    # Derive release channel from the version suffix:
-    #   (none)      -> stable    (tagged release)
-    #   b0          -> develop   (develop-branch build)
-    #   b, rc -> unstable  (pre-release)
+    # Release channel drives the changelog distribution (RPM has no equivalent):
+    #   3.2.0 -> stable, *-b0[+metadata] -> develop,
+    #   any other pre-release -> unstable.
     local deb_distribution
-    case "${VER_SUFFIX}" in
-        "") deb_distribution="stable" ;;
-        b0) deb_distribution="develop" ;;
-        *) deb_distribution="unstable" ;;
+    case "${XRPLD_VERSION}" in
+        *-b0 | *-b0+*) deb_distribution="develop" ;;
+        *-*) deb_distribution="unstable" ;;
+        *) deb_distribution="stable" ;;
     esac
 
     cat >"${staging}/debian/changelog" <  ${CHANGELOG_DATE}
 EOF