Files
rippled/package/build_pkg.sh
Michael Legleux 4bccbbb9e2 refactor: Update RPM version scheme; remove auto-update script; service hardening
- **RPM version scheme**: pre-releases now use `~` in the `Version` field instead of the `0.<release>.<suffix>` `Release`-field hack. Matches Debian's `~` convention, so RPM and DEB version strings are symmetric. Requires rpm ≥ 4.10 (RHEL 9 ships 4.17).

  Before/after for a pre-release build:
  ```
  # before
  xrpld-3.2.0-0.1.rc3+202606011647.d4cb68d5.el9.x86_64.rpm

  # after (symmetric with DEB)
  xrpld-3.2.0~rc2+202606010139.7679a310-1.el9.x86_64.rpm
  xrpld_3.2.0~rc2+202606010139.7679a310-1_amd64.deb
  ```
- **Auto-update removed**: `update-xrpld`, `update-xrpld.service`, and `update-xrpld.timer` deleted. The `50-xrpld.preset` `disable` line for the timer is dropped too.
- **Service hardening** (two new `[Service]` directives in `xrpld.service`):
  - `CapabilityBoundingSet=CAP_NET_BIND_SERVICE` — drops every Linux capability except `CAP_NET_BIND_SERVICE`, capping the privilege ceiling to least-privilege while still letting operators bind ports <1024 (e.g. WS/HTTPS on 443).
  - `SystemCallArchitectures=native` — restricts the service to the native syscall ABI, blocking alternate-ABI (32-bit/x32) syscalls used to evade seccomp filtering.

- [ ] Build RPM from a pre-release version (e.g. `3.2.0-b1`) and confirm `rpm -qi` shows `Version: 3.2.0~b1`, `Release: 1`
- [ ] Confirm `3.2.0~b1` sorts before `3.2.0` via `rpmvercmp`
- [ ] Install package and confirm no `update-xrpld*` units appear in `systemctl list-unit-files`
- [ ] Confirm `systemctl show xrpld` reflects the new `CapabilityBoundingSet` and `SystemCallArchitectures`
2026-06-01 14:57:05 -07:00

213 lines
6.8 KiB
Bash
Executable File

#!/usr/bin/env bash
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.
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]
-h, --help show this help and exit
EOF
}
need_arg() {
if [[ $# -lt 2 || "$2" == --* ]]; then
echo "Missing value for $1" >&2
exit 2
fi
}
# 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:-}"
SOURCE_DATE_EPOCH="${SOURCE_DATE_EPOCH:-}"
while [[ $# -gt 0 ]]; do
case "$1" in
--src-dir)
need_arg "$@"
SRC_DIR="$2"
shift 2
;;
--build-dir)
need_arg "$@"
BUILD_DIR="$2"
shift 2
;;
--pkg-version)
need_arg "$@"
PKG_VERSION="$2"
shift 2
;;
--pkg-release)
need_arg "$@"
PKG_RELEASE="$2"
shift 2
;;
--source-date-epoch)
need_arg "$@"
SOURCE_DATE_EPOCH="$2"
shift 2
;;
-h | --help)
usage
exit 0
;;
*)
echo "Unknown argument: $1" >&2
usage >&2
exit 2
;;
esac
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}')"
fi
if [[ -z "${PKG_VERSION}" ]]; then
echo "PKG_VERSION is empty (not provided and could not be derived)." >&2
exit 1
fi
VERSION="${PKG_VERSION}"
if command -v apt-get >/dev/null 2>&1; then
pkg_type=deb
elif command -v dnf >/dev/null 2>&1 || command -v yum >/dev/null 2>&1; then
pkg_type=rpm
else
echo "Cannot infer pkg_type: no apt-get, dnf, or yum on PATH." >&2
exit 1
fi
if [[ -z "${SOURCE_DATE_EPOCH}" ]]; then
if git -C "$SRC_DIR" rev-parse --is-inside-work-tree >/dev/null 2>&1; then
SOURCE_DATE_EPOCH="$(git -C "$SRC_DIR" log -1 --format=%ct)"
else
SOURCE_DATE_EPOCH="$(date +%s)"
fi
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"
# Stage files that both packaging systems consume using the same filenames.
stage_common() {
local dest="$1"
mkdir -p "${dest}"
cp "${BUILD_DIR}/xrpld" "${dest}/xrpld"
cp "${SRC_DIR}/cfg/xrpld-example.cfg" "${dest}/xrpld.cfg"
cp "${SRC_DIR}/cfg/validators-example.txt" "${dest}/validators.txt"
cp "${SRC_DIR}/LICENSE.md" "${dest}/LICENSE.md"
cp "${SRC_DIR}/README.md" "${dest}/README.md"
cp "${SHARED}/xrpld.service" "${dest}/xrpld.service"
cp "${SHARED}/xrpld.sysusers" "${dest}/xrpld.sysusers"
cp "${SHARED}/xrpld.tmpfiles" "${dest}/xrpld.tmpfiles"
cp "${SHARED}/xrpld.logrotate" "${dest}/xrpld.logrotate"
cp "${SHARED}/50-xrpld.preset" "${dest}/50-xrpld.preset"
}
build_rpm() {
local topdir="${BUILD_DIR}/rpmbuild"
rm -rf "${topdir}"
mkdir -p "${topdir}"/{BUILD,BUILDROOT,RPMS,SOURCES,SPECS,SRPMS}
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>.<suffix>" 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_release ${PKG_RELEASE}" \
"${topdir}/SPECS/xrpld.spec"
}
build_deb() {
local staging="${BUILD_DIR}/debbuild/source"
rm -rf "${staging}"
mkdir -p "${staging}"
stage_common "${staging}"
cp -r "${DEBIAN_DIR}" "${staging}/debian"
cp "${staging}/xrpld.service" "${staging}/debian/xrpld.service"
cp "${staging}/xrpld.sysusers" "${staging}/debian/xrpld.sysusers"
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}"
# Derive release channel from the version suffix:
# (none) -> stable (tagged release)
# b0 -> develop (develop-branch build)
# b<N>, rc<N> -> unstable (pre-release)
local deb_distribution
case "${VER_SUFFIX}" in
"") deb_distribution="stable" ;;
b0) deb_distribution="develop" ;;
*) deb_distribution="unstable" ;;
esac
cat >"${staging}/debian/changelog" <<EOF
xrpld (${deb_full_version}) ${deb_distribution}; urgency=medium
* Release ${VERSION}.
-- XRPL Foundation <contact@xrplf.org> ${CHANGELOG_DATE}
EOF
chmod +x "${staging}/debian/rules"
set -x
(cd "${staging}" && dpkg-buildpackage -b --no-sign -d)
}
"build_${pkg_type}"