diff --git a/.github/scripts/strategy-matrix/generate.py b/.github/scripts/strategy-matrix/generate.py index 6870d39f1f..438e0ff5bb 100755 --- a/.github/scripts/strategy-matrix/generate.py +++ b/.github/scripts/strategy-matrix/generate.py @@ -4,14 +4,15 @@ import itertools import json from dataclasses import dataclass from pathlib import Path +from typing import Any THIS_DIR = Path(__file__).parent.resolve() @dataclass class Config: - architecture: list[dict] - os: list[dict] + architecture: list[dict[str, Any]] + os: list[dict[str, Any]] build_type: list[str] cmake_args: list[str] @@ -32,30 +33,29 @@ We will further set additional CMake arguments as follows: """ -def build_config_name(os_entry: dict, architecture: dict, build_type: str) -> str: - name = os_entry["distro_name"] - if (n := os_entry["distro_version"]) != "": - name += f"-{n}" - if (n := os_entry["compiler_name"]) != "": - name += f"-{n}" - if (n := os_entry["compiler_version"]) != "": - name += f"-{n}" +def build_config_name( + os_entry: dict[str, Any], architecture: dict[str, Any], build_type: str +) -> str: + parts = [os_entry["distro_name"]] + for key in ("distro_version", "compiler_name", "compiler_version"): + if value := os_entry[key]: + parts.append(value) platform = architecture["platform"] - name += f"-{platform[platform.find('/') + 1:]}" - name += f"-{build_type.lower()}" - return name + parts.append(platform[platform.find("/") + 1 :]) + parts.append(build_type.lower()) + return "-".join(parts) -def build_container_image(os_entry: dict) -> str: - return ( - f"ghcr.io/xrplf/ci/{os_entry['distro_name']}-{os_entry['distro_version']}:" - f"{os_entry['compiler_name']}-{os_entry['compiler_version']}-sha-{os_entry['image_sha']}" - ) +def build_container_image(os_entry: dict[str, Any]) -> str: + image = f"ghcr.io/xrplf/ci/{os_entry['distro_name']}-{os_entry['distro_version']}" + tag = f"{os_entry['compiler_name']}-{os_entry['compiler_version']}-sha-{os_entry['image_sha']}" + return f"{image}:{tag}" -def generate_packaging_matrix(config: Config) -> list: - """Emit packaging entries for each os entry with a 'packaging' field. - Packaging always uses Release build on linux/amd64. +def generate_packaging_matrix(config: Config) -> list[dict[str, str]]: + """Emit packaging entries for each os entry with `package: true`. + Packaging always uses Release build on linux/amd64. The package format + (deb or rpm) is inferred at runtime from the container's package manager. """ architecture = next( (a for a in config.architecture if a["platform"] == "linux/amd64"), @@ -66,19 +66,19 @@ def generate_packaging_matrix(config: Config) -> list: entries = [] for os_entry in config.os: - for pkg_type in os_entry.get("packaging", []): - config_name = build_config_name(os_entry, architecture, "Release") - entries.append( - { - "pkg_type": pkg_type, - "artifact_name": f"xrpld-{config_name}", - "container_image": build_container_image(os_entry), - } - ) + if not os_entry.get("package", False): + continue + config_name = build_config_name(os_entry, architecture, "Release") + entries.append( + { + "artifact_name": f"xrpld-{config_name}", + "container_image": build_container_image(os_entry), + } + ) return entries -def generate_strategy_matrix(all: bool, config: Config) -> list: +def generate_strategy_matrix(all: bool, config: Config) -> list[dict[str, Any]]: configurations = [] for architecture, os, build_type, cmake_args in itertools.product( config.architecture, config.os, config.build_type, config.cmake_args @@ -377,7 +377,7 @@ if __name__ == "__main__": parser.add_argument( "-p", "--packaging", - help="Emit the packaging matrix (derived from the 'packaging' field on os entries) instead of the build/test matrix.", + help="Emit the packaging matrix (derived from the 'package' field on os entries) instead of the build/test matrix.", action="store_true", ) args = parser.parse_args() diff --git a/.github/scripts/strategy-matrix/linux.json b/.github/scripts/strategy-matrix/linux.json index 864116474d..0dd8e922de 100644 --- a/.github/scripts/strategy-matrix/linux.json +++ b/.github/scripts/strategy-matrix/linux.json @@ -121,7 +121,7 @@ "compiler_name": "gcc", "compiler_version": "12", "image_sha": "ab4d1f0", - "packaging": ["rpm"] + "package": true }, { "distro_name": "rhel", @@ -164,7 +164,7 @@ "compiler_name": "gcc", "compiler_version": "12", "image_sha": "ab4d1f0", - "packaging": ["deb"] + "package": true }, { "distro_name": "ubuntu", diff --git a/.github/workflows/on-pr.yml b/.github/workflows/on-pr.yml index f62854f46b..3a4f8de8df 100644 --- a/.github/workflows/on-pr.yml +++ b/.github/workflows/on-pr.yml @@ -157,24 +157,12 @@ jobs: id: version uses: ./.github/actions/generate-version - generate-packaging-matrix: - needs: should-run - if: ${{ needs.should-run.outputs.go == 'true' }} - uses: ./.github/workflows/reusable-strategy-matrix.yml - with: - mode: packaging - package: - needs: [should-run, build-test, generate-version, generate-packaging-matrix] - strategy: - fail-fast: false - matrix: ${{ fromJson(needs.generate-packaging-matrix.outputs.matrix) }} + needs: [should-run, build-test, generate-version] + if: ${{ needs.should-run.outputs.go == 'true' }} uses: ./.github/workflows/reusable-package.yml with: - pkg_type: ${{ matrix.pkg_type }} - artifact_name: ${{ matrix.artifact_name }} version: ${{ needs.generate-version.outputs.version }} - container_image: ${{ matrix.container_image }} upload-recipe: needs: diff --git a/.github/workflows/on-tag.yml b/.github/workflows/on-tag.yml index 80f9230492..bc7edcfc52 100644 --- a/.github/workflows/on-tag.yml +++ b/.github/workflows/on-tag.yml @@ -53,19 +53,8 @@ jobs: id: version uses: ./.github/actions/generate-version - generate-packaging-matrix: - uses: ./.github/workflows/reusable-strategy-matrix.yml - with: - mode: packaging - package: - needs: [build-test, generate-version, generate-packaging-matrix] - strategy: - fail-fast: false - matrix: ${{ fromJson(needs.generate-packaging-matrix.outputs.matrix) }} + needs: [build-test, generate-version] uses: ./.github/workflows/reusable-package.yml with: - pkg_type: ${{ matrix.pkg_type }} - artifact_name: ${{ matrix.artifact_name }} version: ${{ needs.generate-version.outputs.version }} - container_image: ${{ matrix.container_image }} diff --git a/.github/workflows/on-trigger.yml b/.github/workflows/on-trigger.yml index fbe37aa6c6..400b55d14c 100644 --- a/.github/workflows/on-trigger.yml +++ b/.github/workflows/on-trigger.yml @@ -116,19 +116,8 @@ jobs: id: version uses: ./.github/actions/generate-version - generate-packaging-matrix: - uses: ./.github/workflows/reusable-strategy-matrix.yml - with: - mode: packaging - package: - needs: [build-test, generate-version, generate-packaging-matrix] - strategy: - fail-fast: false - matrix: ${{ fromJson(needs.generate-packaging-matrix.outputs.matrix) }} + needs: [build-test, generate-version] uses: ./.github/workflows/reusable-package.yml with: - pkg_type: ${{ matrix.pkg_type }} - artifact_name: ${{ matrix.artifact_name }} version: ${{ needs.generate-version.outputs.version }} - container_image: ${{ matrix.container_image }} diff --git a/.github/workflows/reusable-package.yml b/.github/workflows/reusable-package.yml index 756d5afcd4..f242a745d8 100644 --- a/.github/workflows/reusable-package.yml +++ b/.github/workflows/reusable-package.yml @@ -1,26 +1,16 @@ -# Build a Linux package (DEB or RPM) from a pre-built binary artifact. +# Build Linux packages (DEB and RPM) from pre-built binary artifacts. +# Discovers which configurations to package from linux.json (entries with +# "package": true) and fans out one job per entry. name: Package on: workflow_call: inputs: - artifact_name: - description: "Name of the GitHub pre-built binary artifact to download (e.g. xrpld-ubuntu-jammy-gcc-12-amd64-release)." - required: true - type: string - container_image: - description: "Container image to use for packaging." - required: true - type: string pkg_release: description: "Package release number. Increment when repackaging the same executable." required: false type: string default: "1" - pkg_type: - description: "Package type to build: deb or rpm." - required: true - type: string version: description: "Version string used for naming the output artifact." required: true @@ -34,12 +24,35 @@ env: BUILD_DIR: build jobs: + generate-matrix: + runs-on: ubuntu-latest + outputs: + matrix: ${{ steps.generate.outputs.matrix }} + steps: + - name: Checkout repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + + - name: Set up Python + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + with: + python-version: 3.13 + + - name: Generate packaging matrix + id: generate + working-directory: .github/scripts/strategy-matrix + run: | + ./generate.py --packaging --config=linux.json >> "${GITHUB_OUTPUT}" + package: - name: "${{ inputs.pkg_type }} (${{ inputs.version }})" + needs: generate-matrix + strategy: + fail-fast: false + matrix: ${{ fromJson(needs.generate-matrix.outputs.matrix) }} + name: "${{ matrix.artifact_name }}" permissions: contents: read runs-on: ["self-hosted", "Linux", "X64", "heavy"] - container: ${{ inputs.container_image }} + container: ${{ matrix.container_image }} timeout-minutes: 30 steps: @@ -53,7 +66,7 @@ jobs: - name: Download pre-built binary uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: - name: ${{ inputs.artifact_name }} + name: ${{ matrix.artifact_name }} path: ${{ env.BUILD_DIR }} - name: Make binary executable @@ -61,17 +74,16 @@ jobs: - 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" + ./package/build_pkg.sh . "$BUILD_DIR" "$PKG_VERSION" "$PKG_RELEASE" - name: Upload package artifact uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: - name: xrpld-${{ inputs.pkg_type }}-${{ inputs.version }} + name: ${{ matrix.artifact_name }}-pkg-${{ inputs.version }} path: | ${{ env.BUILD_DIR }}/debbuild/*.deb ${{ env.BUILD_DIR }}/debbuild/*.ddeb diff --git a/.github/workflows/reusable-strategy-matrix.yml b/.github/workflows/reusable-strategy-matrix.yml index cff51ee897..8eb78983fd 100644 --- a/.github/workflows/reusable-strategy-matrix.yml +++ b/.github/workflows/reusable-strategy-matrix.yml @@ -3,11 +3,6 @@ name: Generate strategy matrix on: workflow_call: inputs: - mode: - description: 'What matrix to emit: "build-test" (default) or "packaging".' - required: false - type: string - default: "build-test" os: description: 'The operating system to use for the build ("linux", "macos", "windows").' required: false @@ -44,13 +39,8 @@ jobs: - name: Generate strategy matrix working-directory: .github/scripts/strategy-matrix id: generate - env: - GENERATE_CONFIG: ${{ inputs.os != '' && format('--config={0}.json', inputs.os) || '' }} - GENERATE_MODE: ${{ inputs.mode == 'packaging' && '--packaging --config=linux.json' || '' }} - GENERATE_OPTION: ${{ inputs.strategy_matrix == 'all' && '--all' || '' }} run: | - if [ -n "${GENERATE_MODE}" ]; then - ./generate.py ${GENERATE_MODE} >> "${GITHUB_OUTPUT}" - else - ./generate.py ${GENERATE_OPTION} ${GENERATE_CONFIG} >> "${GITHUB_OUTPUT}" - fi + ./generate.py \ + ${{ inputs.strategy_matrix == 'all' && '--all' || '' }} \ + ${{ inputs.os != '' && format('--config={0}.json', inputs.os) || '' }} \ + >> "${GITHUB_OUTPUT}" diff --git a/cmake/XrplPackaging.cmake b/cmake/XrplPackaging.cmake index ce6632b6f6..08178054f7 100644 --- a/cmake/XrplPackaging.cmake +++ b/cmake/XrplPackaging.cmake @@ -16,37 +16,22 @@ if(NOT DEFINED pkg_release) endif() find_program(RPMBUILD_EXECUTABLE rpmbuild) -if(RPMBUILD_EXECUTABLE) - add_custom_target( - package-rpm - COMMAND - ${CMAKE_SOURCE_DIR}/package/build_pkg.sh rpm ${CMAKE_SOURCE_DIR} - ${CMAKE_BINARY_DIR} "${xrpld_version}" ${pkg_release} - WORKING_DIRECTORY ${CMAKE_BINARY_DIR} - DEPENDS xrpld - COMMENT "Building RPM package" - VERBATIM - ) -else() - message(STATUS "rpmbuild not found; 'package-rpm' target not available") -endif() - find_program(DPKG_BUILDPACKAGE_EXECUTABLE dpkg-buildpackage) -if(DPKG_BUILDPACKAGE_EXECUTABLE) +if(RPMBUILD_EXECUTABLE OR DPKG_BUILDPACKAGE_EXECUTABLE) add_custom_target( - package-deb + package COMMAND - ${CMAKE_SOURCE_DIR}/package/build_pkg.sh deb ${CMAKE_SOURCE_DIR} + ${CMAKE_SOURCE_DIR}/package/build_pkg.sh ${CMAKE_SOURCE_DIR} ${CMAKE_BINARY_DIR} "${xrpld_version}" ${pkg_release} WORKING_DIRECTORY ${CMAKE_BINARY_DIR} DEPENDS xrpld - COMMENT "Building Debian package" + COMMENT "Building Linux package (deb/rpm inferred from host tooling)" VERBATIM ) else() message( STATUS - "dpkg-buildpackage not found; 'package-deb' target not available" + "Neither rpmbuild nor dpkg-buildpackage found; 'package' target not available" ) endif() @@ -67,7 +52,17 @@ set(RPM_TEST_IMAGE "geerlingguy/docker-rockylinux9-ansible@sha256:790c2db9add93c0daa903ace816f352c9c04abb046ecfa12c581e8d4c59f41d6" ) -foreach(PKG deb rpm) +# Only register install-test fixtures for package formats the host can build, +# since the smoketest needs a corresponding .deb/.rpm artifact in build/. +set(PKG_TYPES "") +if(DPKG_BUILDPACKAGE_EXECUTABLE) + list(APPEND PKG_TYPES deb) +endif() +if(RPMBUILD_EXECUTABLE) + list(APPEND PKG_TYPES rpm) +endif() + +foreach(PKG IN LISTS PKG_TYPES) if(PKG STREQUAL "deb") set(IMAGE ${DEB_TEST_IMAGE}) else() @@ -85,11 +80,8 @@ foreach(PKG deb rpm) --cgroupns host \ --volume '${CMAKE_SOURCE_DIR}:/root:ro' \ --volume /sys/fs/cgroup:/sys/fs/cgroup:rw \ - --tmpfs /tmp \ - --tmpfs /run \ --tmpfs /run/lock \ - ${IMAGE} \ - /usr/sbin/init" + ${IMAGE}" ) set_tests_properties( ${PKG}_container_start diff --git a/package/README.md b/package/README.md index 6f6fa3b42f..f444a0e5f6 100644 --- a/package/README.md +++ b/package/README.md @@ -9,8 +9,7 @@ package/ build_pkg.sh Staging and build script (called by CMake targets and CI) rpm/ xrpld.spec RPM spec (xrpld_version/pkg_release passed via rpmbuild --define) - deb/ - debian/ Debian control files (control, rules, install, links, conffiles, ...) + debian/ Debian control files (control, rules, install, links, conffiles, ...) shared/ xrpld.service systemd unit file (used by both RPM and DEB) xrpld.sysusers sysusers.d config (used by both RPM and DEB) @@ -27,8 +26,10 @@ package/ Packaging targets and their container images are declared in [`.github/scripts/strategy-matrix/linux.json`](../.github/scripts/strategy-matrix/linux.json) -via a `"packaging"` field on specific os entries. The image tag is composed -as `ghcr.io/xrplf/ci/{distro}-{version}:{compiler}-{cver}-sha-{image_sha}` — +via a `"package": true` field on specific os entries. 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/ci/{distro}-{version}:{compiler}-{cver}-sha-{image_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. @@ -50,10 +51,11 @@ To print the exact image tags for the current `linux.json`: Caller workflows (`on-pr.yml`, `on-tag.yml`, `on-trigger.yml`) call `reusable-strategy-matrix.yml` with `mode: packaging` to generate the matrix of -`{pkg_type, artifact_name, container_image}` entries, then fan out to +`{artifact_name, container_image}` entries, then fan out to `reusable-package.yml` per entry. That workflow downloads the pre-built `xrpld` -binary artifact and calls `build_pkg.sh` directly — no CMake configure or -build step is needed inside the packaging job. +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. ### Locally (mirrors CI) @@ -62,23 +64,22 @@ 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. -PKG_TYPE=deb # or rpm -VERSION=2.4.0-local -PKG_RELEASE=1 - -# Derive the correct image for this package type from linux.json. -IMAGE=$(jq -r --arg pkg "$PKG_TYPE" ' - .os[] | select((.packaging // []) | index($pkg)) | +# 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) -# Run the packaging in the container. +VERSION=2.4.0-local +PKG_RELEASE=1 + docker run --rm \ -v "$(pwd):/src" \ -w /src \ "$IMAGE" \ - ./package/build_pkg.sh "$PKG_TYPE" . build "$VERSION" "$PKG_RELEASE" + ./package/build_pkg.sh . build "$VERSION" "$PKG_RELEASE" # Output: # build/debbuild/*.deb (DEB + dbgsym .ddeb) @@ -88,7 +89,7 @@ docker run --rm \ ### Via CMake (host-side target) If you run CMake configure on a host that has `rpmbuild` or `dpkg-buildpackage` -installed natively, you can use the CMake targets directly — no container +installed natively, you can use the CMake target directly — no container needed, but the host toolchain replaces the pinned CI image: ```bash @@ -99,22 +100,23 @@ cmake \ -Dtests=OFF \ .. -cmake --build . --target package-rpm # requires rpmbuild -cmake --build . --target package-deb # requires dpkg-buildpackage +cmake --build . --target package # deb on Debian/Ubuntu, rpm on RHEL ``` -The `cmake/XrplPackaging.cmake` module gates each target on whether the -required tool is present at configure time, so configuring on a host that -lacks one simply omits the corresponding target. `CMAKE_INSTALL_PREFIX` must +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. `CMAKE_INSTALL_PREFIX` must be `/opt/xrpld`; if it is not, both targets are skipped with a `STATUS` message. ## How `build_pkg.sh` works -`build_pkg.sh [version] [pkg_release]` stages +`build_pkg.sh [version] [pkg_release]` stages all files and invokes the platform build tool. It resolves `src_dir` and `build_dir` to absolute paths, then calls `stage_common()` to copy the binary, -config files, and shared support files into the staging area. +config files, and shared support files into the staging area. The package +format is taken from the `PKG_TYPE` env var if set; otherwise it is inferred +from the available package manager (`apt-get` -> deb, `dnf`/`yum` -> rpm). ### RPM diff --git a/package/build_pkg.sh b/package/build_pkg.sh index af9cc8e8dc..c562a448fd 100755 --- a/package/build_pkg.sh +++ b/package/build_pkg.sh @@ -3,18 +3,30 @@ set -euo pipefail # Build an RPM or Debian package from a pre-built xrpld binary. # -# Usage: build_pkg.sh [version] [pkg_release] -# pkg_type : rpm | deb +# Usage: build_pkg.sh [version] [pkg_release] # src_dir : path to repository root # build_dir : directory containing the pre-built xrpld binary # version : package version string (e.g. 3.2.0-b1) # pkg_release : package release number (default: 1) +# +# The package format is taken from the PKG_TYPE env var if set; otherwise it +# is inferred from the available package manager (apt-get -> deb, dnf/yum -> rpm). -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}" +SRC_DIR="$(cd "${1:?src_dir required}" && pwd)" +BUILD_DIR="$(cd "${2:?build_dir required}" && pwd)" +VERSION="${3:-$("${BUILD_DIR}/xrpld" --version | awk 'NR==1 {print $3}')}" +PKG_RELEASE="${4:-1}" + +if [[ -z "${PKG_TYPE:-}" ]]; then + 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 +fi if [[ -z "${SOURCE_DATE_EPOCH:-}" ]]; then if git -C "$SRC_DIR" rev-parse --is-inside-work-tree >/dev/null 2>&1; then @@ -119,11 +131,4 @@ EOF ( cd "${staging}" && dpkg-buildpackage -b --no-sign -d ) } -case "${PKG_TYPE}" in - rpm) build_rpm ;; - deb) build_deb ;; - *) - echo "Unknown package type: ${PKG_TYPE}" >&2 - exit 1 - ;; -esac +"build_${PKG_TYPE}" diff --git a/package/debian/copyright b/package/debian/copyright index 8f654cb4b8..d8426d0b88 100644 --- a/package/debian/copyright +++ b/package/debian/copyright @@ -3,7 +3,7 @@ Upstream-Name: rippled Source: https://github.com/XRPLF/rippled Files: * -Copyright: 2012-2025 Ripple Labs Inc. +Copyright: 2012-2026 Ripple Labs Inc. License: ISC License: ISC diff --git a/package/debian/xrpld.links b/package/debian/xrpld.links index 4ff239a546..775f43a1fe 100644 --- a/package/debian/xrpld.links +++ b/package/debian/xrpld.links @@ -2,7 +2,7 @@ opt/xrpld/etc etc/opt/xrpld opt/xrpld/bin/xrpld usr/bin/xrpld -## remove when "rippled" deprecated +# remove when "rippled" deprecated opt/xrpld/bin/xrpld opt/xrpld/bin/rippled opt/xrpld/bin/xrpld usr/local/bin/rippled opt/xrpld/etc/xrpld.cfg opt/xrpld/etc/rippled.cfg diff --git a/package/test/smoketest.sh b/package/test/smoketest.sh index ad9ddd451c..f3b5e0bd69 100755 --- a/package/test/smoketest.sh +++ b/package/test/smoketest.sh @@ -39,7 +39,7 @@ if [ "${pkgtype}" = "dpkg" ] ; then echo "No .deb files found" exit 1 fi - dpkg --no-debsig -i "${debs[@]}" || apt-get -y install -f + dpkg --no-debsig -i "${debs[@]}" || apt-get -y install -f || { echo "DEB install failed"; exit 1; } elif [ "${pkgtype}" = "rpm" ] ; then # Find .rpm files — check both possible output locations mapfile -t rpms < <(find build/rpmbuild/RPMS/ build/rpm/packages/ -name '*.rpm' \ @@ -52,7 +52,10 @@ elif [ "${pkgtype}" = "rpm" ] ; then fi # Verify installed version -VERSION_OUTPUT=$(/opt/xrpld/bin/xrpld --version) +if ! VERSION_OUTPUT=$(/opt/xrpld/bin/xrpld --version); then + echo "xrpld --version failed; binary not installed correctly" + exit 1 +fi INSTALLED=$(echo "$VERSION_OUTPUT" | head -1 | awk '{print $NF}') echo "Installed version: ${INSTALLED}"