From dc02510d03eb9e4a85dbc13f77d015e736cd3cff Mon Sep 17 00:00:00 2001 From: Michael Legleux Date: Wed, 22 Apr 2026 17:36:03 -0700 Subject: [PATCH] package: refactor packaging; make builds reproducible; stop requiring git --- .github/workflows/reusable-package.yml | 16 +--- package/README.md | 15 +-- package/build_pkg.sh | 99 ++++++++++++------- package/{deb => }/debian/control | 0 package/{deb => }/debian/copyright | 0 package/{deb => }/debian/rules | 13 ++- package/{deb => }/debian/source/format | 0 package/{deb => }/debian/xrpld.conffiles | 0 package/{deb => }/debian/xrpld.install | 0 package/{deb => }/debian/xrpld.links | 0 package/rpm/xrpld.spec | 116 +++++++++++------------ package/shared/50-xrpld.preset | 4 + package/test/check_install_paths.sh | 2 +- 13 files changed, 140 insertions(+), 125 deletions(-) rename package/{deb => }/debian/control (100%) rename package/{deb => }/debian/copyright (100%) rename package/{deb => }/debian/rules (73%) rename package/{deb => }/debian/source/format (100%) rename package/{deb => }/debian/xrpld.conffiles (100%) rename package/{deb => }/debian/xrpld.install (100%) rename package/{deb => }/debian/xrpld.links (100%) create mode 100644 package/shared/50-xrpld.preset diff --git a/.github/workflows/reusable-package.yml b/.github/workflows/reusable-package.yml index 2a51782bc9..756d5afcd4 100644 --- a/.github/workflows/reusable-package.yml +++ b/.github/workflows/reusable-package.yml @@ -46,6 +46,10 @@ jobs: - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - name: Set source date epoch + run: | + echo "SOURCE_DATE_EPOCH=$(git log -1 --format=%ct "$GITHUB_SHA")" >> "$GITHUB_ENV" + - name: Download pre-built binary uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: @@ -55,22 +59,12 @@ jobs: - name: Make binary executable run: chmod +x "${BUILD_DIR}/xrpld" - - name: Generate RPM spec from template - if: ${{ inputs.pkg_type == 'rpm' }} - env: - PKG_VERSION: ${{ inputs.version }} - PKG_RELEASE: ${{ inputs.pkg_release }} - run: | - mkdir -p "${BUILD_DIR}/package/rpm" - sed -e "s|@xrpld_version@|${PKG_VERSION}|" \ - -e "s|@pkg_release@|${PKG_RELEASE}|" \ - package/rpm/xrpld.spec.in > "${BUILD_DIR}/package/rpm/xrpld.spec" - - name: Build package env: PKG_TYPE: ${{ inputs.pkg_type }} PKG_VERSION: ${{ inputs.version }} PKG_RELEASE: ${{ inputs.pkg_release }} + SOURCE_DATE_EPOCH: ${{ env.SOURCE_DATE_EPOCH }} run: | ./package/build_pkg.sh "$PKG_TYPE" . "$BUILD_DIR" "$PKG_VERSION" "$PKG_RELEASE" diff --git a/package/README.md b/package/README.md index 4b71d8e417..6f6fa3b42f 100644 --- a/package/README.md +++ b/package/README.md @@ -8,7 +8,7 @@ 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) rpm/ - xrpld.spec.in RPM spec template (substitutes @xrpld_version@, @pkg_release@) + xrpld.spec RPM spec (xrpld_version/pkg_release passed via rpmbuild --define) deb/ debian/ Debian control files (control, rules, install, links, conffiles, ...) shared/ @@ -73,15 +73,6 @@ IMAGE=$(jq -r --arg pkg "$PKG_TYPE" ' "ghcr.io/xrplf/ci/\(.distro_name)-\(.distro_version):\(.compiler_name)-\(.compiler_version)-sha-\(.image_sha)" ' .github/scripts/strategy-matrix/linux.json) -# RPM only: generate the spec from the template (CMake does this automatically -# during configure; this mirrors the CI step for direct invocations). -if [ "$PKG_TYPE" = "rpm" ] && [ ! -f build/package/rpm/xrpld.spec ]; then - mkdir -p build/package/rpm - sed -e "s|@xrpld_version@|$VERSION|" \ - -e "s|@pkg_release@|$PKG_RELEASE|" \ - package/rpm/xrpld.spec.in > build/package/rpm/xrpld.spec -fi - # Run the packaging in the container. docker run --rm \ -v "$(pwd):/src" \ @@ -128,8 +119,8 @@ config files, and shared support files into the staging area. ### RPM 1. Creates the standard `rpmbuild/{BUILD,BUILDROOT,RPMS,SOURCES,SPECS,SRPMS}` tree inside the build directory. -2. Copies the generated `xrpld.spec` and all source files (binary, configs, service files) into `SOURCES/`. -3. Runs `rpmbuild -bb`. The spec uses manual `install` commands to place files. +2. Copies `xrpld.spec` and all source files (binary, configs, service files) into `SOURCES/`. +3. Runs `rpmbuild -bb --define "xrpld_version ..." --define "pkg_release ..."`. The spec uses manual `install` commands to place files. 4. Output: `rpmbuild/RPMS/x86_64/xrpld-*.rpm` ### DEB diff --git a/package/build_pkg.sh b/package/build_pkg.sh index 9d30c8584a..af9cc8e8dc 100755 --- a/package/build_pkg.sh +++ b/package/build_pkg.sh @@ -1,4 +1,6 @@ #!/usr/bin/env bash +set -euo pipefail + # Build an RPM or Debian package from a pre-built xrpld binary. # # Usage: build_pkg.sh [version] [pkg_release] @@ -8,45 +10,71 @@ # version : package version string (e.g. 3.2.0-b1) # pkg_release : package release number (default: 1) -set -euo pipefail - PKG_TYPE="${1:?pkg_type required}" SRC_DIR="$(cd "${2:?src_dir required}" && pwd)" BUILD_DIR="$(cd "${3:?build_dir required}" && pwd)" VERSION="${4:-$("${BUILD_DIR}/xrpld" --version | awk 'NR==1 {print $3}')}" PKG_RELEASE="${5:-1}" -SHARED="${SRC_DIR}/package/shared" +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")" -# Stage files common to both package types into a target directory. +# 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="" + +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" - 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 "${SHARED}/xrpld.logrotate" "${dest}/xrpld.logrotate" - cp "${SHARED}/update-xrpld.sh" "${dest}/update-xrpld.sh" - cp "${SHARED}/update-xrpld-cron" "${dest}/update-xrpld-cron" + 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}/update-xrpld.sh" "${dest}/update-xrpld.sh" + cp "${SHARED}/update-xrpld-cron" "${dest}/update-xrpld-cron" + cp "${SHARED}/update-xrpld.service" "${dest}/update-xrpld.service" + cp "${SHARED}/update-xrpld.timer" "${dest}/update-xrpld.timer" + 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" - cp "${SHARED}/xrpld.service" "${topdir}/SOURCES/xrpld.service" - cp "${SHARED}/xrpld.sysusers" "${topdir}/SOURCES/xrpld.sysusers" - cp "${SHARED}/xrpld.tmpfiles" "${topdir}/SOURCES/xrpld.tmpfiles" - cp "${SRC_DIR}/LICENSE.md" "${topdir}/SOURCES/LICENSE.md" - cp "${SRC_DIR}/README.md" "${topdir}/SOURCES/README.md" + + # RPM Version can't contain '-'. A pre-release goes in Release with a + # leading "0." so 3.2.0-b1 sorts before the final 3.2.0-. + local rpm_release="${PKG_RELEASE}" + [[ -n "${VER_SUFFIX}" ]] && rpm_release="0.${VER_SUFFIX}.${PKG_RELEASE}" set -x rpmbuild -bb \ --define "_topdir ${topdir}" \ - --define "xrpld_version ${VERSION}" \ - --define "pkg_release ${PKG_RELEASE}" \ + --define "xrpld_version ${VER_BASE}" \ + --define "xrpld_release ${rpm_release}" \ "${topdir}/SPECS/xrpld.spec" } @@ -56,26 +84,33 @@ build_deb() { mkdir -p "${staging}" stage_common "${staging}" - cp "${SRC_DIR}/README.md" "${staging}/" - cp "${SRC_DIR}/LICENSE.md" "${staging}/" + cp -r "${DEBIAN_DIR}" "${staging}/debian" - # debian/ control files - cp -r "${SRC_DIR}/package/deb/debian" "${staging}/debian" + # Debhelper auto-discovers these only from 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}/update-xrpld.service" "${staging}/debian/xrpld.update-xrpld.service" + cp "${staging}/update-xrpld.timer" "${staging}/debian/xrpld.update-xrpld.timer" + # 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}" - # Shared support files for dh_installsystemd / sysusers / tmpfiles - cp "${SHARED}/xrpld.service" "${staging}/debian/xrpld.service" - cp "${SHARED}/xrpld.sysusers" "${staging}/debian/xrpld.sysusers" - cp "${SHARED}/xrpld.tmpfiles" "${staging}/debian/xrpld.tmpfiles" + # Derive release channel from the version suffix: + # (none) -> stable (tagged release) + # b0 -> develop (develop-branch build) + # b, rc -> unstable (pre-release) + local deb_distribution + case "${VER_SUFFIX}" in + "") deb_distribution="stable" ;; + b0) deb_distribution="develop" ;; + *) deb_distribution="unstable" ;; + esac - # Generate debian/changelog (pre-release versions use ~ instead of -). - local deb_version="${VERSION//-/\~}" - # TODO: Add facility for generating the changelog cat > "${staging}/debian/changelog" < $(LC_ALL=C date -u -R) + -- XRPL Foundation ${CHANGELOG_DATE} EOF chmod +x "${staging}/debian/rules" diff --git a/package/deb/debian/control b/package/debian/control similarity index 100% rename from package/deb/debian/control rename to package/debian/control diff --git a/package/deb/debian/copyright b/package/debian/copyright similarity index 100% rename from package/deb/debian/copyright rename to package/debian/copyright diff --git a/package/deb/debian/rules b/package/debian/rules similarity index 73% rename from package/deb/debian/rules rename to package/debian/rules index 500966e704..e6cb297f3e 100644 --- a/package/deb/debian/rules +++ b/package/debian/rules @@ -19,17 +19,16 @@ override_dh_auto_install: install -Dm0644 README.md debian/tmp/usr/share/doc/xrpld/README.md install -Dm0644 LICENSE.md debian/tmp/usr/share/doc/xrpld/LICENSE.md +# update-xrpld.service is a Type=oneshot fired by update-xrpld.timer; installing +# it without --no-start would run the update on package install. override_dh_installsystemd: - dh_installsystemd + dh_installsystemd xrpld.service + dh_installsystemd --name=update-xrpld --no-enable --no-start update-xrpld.service update-xrpld.timer -override_dh_installsysusers: +execute_before_dh_installtmpfiles: dh_installsysusers -override_dh_installtmpfiles: - dh_installtmpfiles - -override_dh_install: - dh_install +override_dh_installsysusers: override_dh_dwz: @: diff --git a/package/deb/debian/source/format b/package/debian/source/format similarity index 100% rename from package/deb/debian/source/format rename to package/debian/source/format diff --git a/package/deb/debian/xrpld.conffiles b/package/debian/xrpld.conffiles similarity index 100% rename from package/deb/debian/xrpld.conffiles rename to package/debian/xrpld.conffiles diff --git a/package/deb/debian/xrpld.install b/package/debian/xrpld.install similarity index 100% rename from package/deb/debian/xrpld.install rename to package/debian/xrpld.install diff --git a/package/deb/debian/xrpld.links b/package/debian/xrpld.links similarity index 100% rename from package/deb/debian/xrpld.links rename to package/debian/xrpld.links diff --git a/package/rpm/xrpld.spec b/package/rpm/xrpld.spec index f61d95ceef..ddaecbdf00 100644 --- a/package/rpm/xrpld.spec +++ b/package/rpm/xrpld.spec @@ -1,35 +1,19 @@ -%global _opt_prefix /opt/xrpld -%global ver_base %(v=%{xrpld_version}; echo ${v%%-*}) -%global _has_dash %(v=%{xrpld_version}; [ "${v#*-}" != "$v" ] && echo 1 || echo 0) -%if 0%{?_has_dash} - %global ver_suffix %(v=%{xrpld_version}; printf %s "${v#*-}") -%endif Name: xrpld -Version: %{ver_base} -Release: %{?ver_suffix:0.%{ver_suffix}.}%{pkg_release}%{?dist} +Version: %{xrpld_version} +Release: %{xrpld_release}%{?dist} Summary: XRP Ledger daemon License: ISC URL: https://github.com/XRPLF/rippled -Source0: xrpld -Source1: xrpld.cfg -Source2: validators.txt -Source3: xrpld.service -Source4: xrpld.sysusers -Source5: xrpld.tmpfiles -Source6: xrpld.logrotate -Source7: update-xrpld.sh -Source8: update-xrpld-cron -Source9: LICENSE.md -Source10: README.md - -ExclusiveArch: x86_64 +ExclusiveArch: x86_64 aarch64 BuildRequires: systemd-rpm-macros %undefine _debugsource_packages %debug_package +%build_mtime_policy clamp_to_source_date_epoch + %{?systemd_requires} %{?sysusers_requires_compat} @@ -38,73 +22,81 @@ xrpld is the reference implementation of the XRP Ledger protocol. It participates in the peer-to-peer XRP Ledger network, processes transactions, and maintains the ledger database. +%prep +: + %install rm -rf %{buildroot} -# Suppress debugsource subpackage — no source files in the build tree. -touch %{_builddir}/debugsourcefiles.list +SRC=%{_sourcedir} -# Install binary and config files. -install -Dm0755 %{SOURCE0} %{buildroot}%{_opt_prefix}/bin/xrpld -install -Dm0644 %{SOURCE1} %{buildroot}%{_opt_prefix}/etc/xrpld.cfg -install -Dm0644 %{SOURCE2} %{buildroot}%{_opt_prefix}/etc/validators.txt +install -Dm0755 ${SRC}/xrpld %{buildroot}/opt/xrpld/bin/xrpld +install -Dm0644 ${SRC}/xrpld.cfg %{buildroot}/opt/xrpld/etc/xrpld.cfg +install -Dm0644 ${SRC}/validators.txt %{buildroot}/opt/xrpld/etc/validators.txt -mkdir -p %{buildroot}/etc/opt %{buildroot}/usr/bin %{buildroot}/usr/local/bin -ln -s %{_opt_prefix}/etc %{buildroot}/etc/opt/xrpld -ln -s %{_opt_prefix}/bin/xrpld %{buildroot}/usr/bin/xrpld +mkdir -p %{buildroot}/etc/opt %{buildroot}/usr/bin %{buildroot}/usr/local/bin +ln -s /opt/xrpld/etc %{buildroot}/etc/opt/xrpld +ln -s /opt/xrpld/bin/xrpld %{buildroot}/usr/bin/xrpld -## remove when "rippled" deprecated -ln -s xrpld %{buildroot}%{_opt_prefix}/bin/rippled -ln -s %{_opt_prefix}/bin/xrpld %{buildroot}/usr/local/bin/rippled -ln -s xrpld.cfg %{buildroot}%{_opt_prefix}/etc/rippled.cfg -ln -s %{_opt_prefix} %{buildroot}/opt/ripple -ln -s /etc/opt/xrpld %{buildroot}/etc/opt/ripple +# TODO: remove when rippled deprecated +ln -s xrpld %{buildroot}/opt/xrpld/bin/rippled +ln -s /opt/xrpld/bin/xrpld %{buildroot}/usr/local/bin/rippled +ln -s xrpld.cfg %{buildroot}/opt/xrpld/etc/rippled.cfg +ln -s /opt/xrpld %{buildroot}/opt/ripple +ln -s /etc/opt/xrpld %{buildroot}/etc/opt/ripple -# Install systemd/sysusers/tmpfiles support files. -install -Dm0644 %{SOURCE3} %{buildroot}%{_unitdir}/xrpld.service -install -Dm0644 %{SOURCE4} %{buildroot}%{_sysusersdir}/xrpld.conf -install -Dm0644 %{SOURCE5} %{buildroot}%{_tmpfilesdir}/xrpld.conf -install -Dm0644 %{SOURCE6} %{buildroot}%{_opt_prefix}/bin/xrpld.logrotate -install -Dm0755 %{SOURCE7} %{buildroot}%{_opt_prefix}/bin/update-xrpld.sh -install -Dm0644 %{SOURCE8} %{buildroot}%{_opt_prefix}/bin/update-xrpld-cron +install -Dm0644 ${SRC}/xrpld.service %{buildroot}%{_unitdir}/xrpld.service +install -Dm0644 ${SRC}/update-xrpld.service %{buildroot}%{_unitdir}/update-xrpld.service +install -Dm0644 ${SRC}/update-xrpld.timer %{buildroot}%{_unitdir}/update-xrpld.timer -# Install doc/license files. -install -Dm0644 %{SOURCE9} %{buildroot}%{_opt_prefix}/share/LICENSE.md -install -Dm0644 %{SOURCE10} %{buildroot}%{_opt_prefix}/share/README.md +install -Dm0644 ${SRC}/xrpld.sysusers %{buildroot}%{_sysusersdir}/xrpld.conf +install -Dm0644 ${SRC}/xrpld.tmpfiles %{buildroot}%{_tmpfilesdir}/xrpld.conf + +install -Dm0644 ${SRC}/50-xrpld.preset %{buildroot}%{_presetdir}/50-xrpld.preset + +install -Dm0755 ${SRC}/update-xrpld.sh %{buildroot}/opt/xrpld/bin/update-xrpld.sh +install -Dm0644 ${SRC}/update-xrpld-cron %{buildroot}/opt/xrpld/bin/update-xrpld-cron +install -Dm0644 ${SRC}/xrpld.logrotate %{buildroot}/opt/xrpld/bin/xrpld.logrotate + +install -Dm0644 ${SRC}/LICENSE.md %{buildroot}/opt/xrpld/share/LICENSE.md +install -Dm0644 ${SRC}/README.md %{buildroot}/opt/xrpld/share/README.md %pre -%sysusers_create_compat %{SOURCE4} +%sysusers_create_package xrpld %{_sourcedir}/xrpld.sysusers %post systemd-tmpfiles --create %{_tmpfilesdir}/xrpld.conf || : -%systemd_post xrpld.service +%systemd_post xrpld.service update-xrpld.timer %preun -%systemd_preun xrpld.service +%systemd_preun xrpld.service update-xrpld.timer %postun %systemd_postun_with_restart xrpld.service %files -%license %{_opt_prefix}/share/LICENSE.md -%doc %{_opt_prefix}/share/README.md +%license /opt/xrpld/share/LICENSE.md +%doc /opt/xrpld/share/README.md -%dir %{_opt_prefix} -%dir %{_opt_prefix}/bin -%dir %{_opt_prefix}/etc +%dir /opt/xrpld +%dir /opt/xrpld/bin +%dir /opt/xrpld/etc -%{_opt_prefix}/bin/xrpld -%{_opt_prefix}/bin/xrpld.logrotate -%{_opt_prefix}/bin/update-xrpld.sh -%{_opt_prefix}/bin/update-xrpld-cron +/opt/xrpld/bin/xrpld +/opt/xrpld/bin/xrpld.logrotate +/opt/xrpld/bin/update-xrpld.sh +/opt/xrpld/bin/update-xrpld-cron /usr/bin/xrpld /etc/opt/xrpld -%config(noreplace) %{_opt_prefix}/etc/xrpld.cfg -%config(noreplace) %{_opt_prefix}/etc/validators.txt +%config(noreplace) /opt/xrpld/etc/xrpld.cfg +%config(noreplace) /opt/xrpld/etc/validators.txt %{_unitdir}/xrpld.service +%{_unitdir}/update-xrpld.service +%{_unitdir}/update-xrpld.timer +%{_presetdir}/50-xrpld.preset %{_sysusersdir}/xrpld.conf %{_tmpfilesdir}/xrpld.conf @@ -112,8 +104,8 @@ systemd-tmpfiles --create %{_tmpfilesdir}/xrpld.conf || : %ghost %dir /var/log/xrpld # TODO: remove when rippled deprecated -%{_opt_prefix}/bin/rippled +/opt/xrpld/bin/rippled /usr/local/bin/rippled -%{_opt_prefix}/etc/rippled.cfg +/opt/xrpld/etc/rippled.cfg /etc/opt/ripple /opt/ripple diff --git a/package/shared/50-xrpld.preset b/package/shared/50-xrpld.preset new file mode 100644 index 0000000000..6264e00131 --- /dev/null +++ b/package/shared/50-xrpld.preset @@ -0,0 +1,4 @@ +# /usr/lib/systemd/system-preset/50-xrpld.preset +enable xrpld.service +# Don't enable automatic updates +disable update-xrpld.timer diff --git a/package/test/check_install_paths.sh b/package/test/check_install_paths.sh index 56878fc917..6e6a871cc7 100755 --- a/package/test/check_install_paths.sh +++ b/package/test/check_install_paths.sh @@ -37,7 +37,7 @@ if systemctl is-system-running >/dev/null 2>&1; then fi # binary accessible via all expected paths -/opt/xrpld/bin/xrpld --version +/opt/xrpld/bin/xrpld --version /opt/xrpld/bin/rippled --version /opt/ripple/bin/xrpld --version /opt/ripple/bin/rippled --version