Skip to content
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions .github/scripts/strategy-matrix/generate.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,8 @@ class MatrixEntry:
class PackagingEntry:
"""One entry in the generated packaging strategy matrix."""

artifact_name: str
artifact_name: str # artifact holding the xrpld binary
validator_keys_artifact_name: str # artifact holding the validator-keys binary
Comment thread
bthomee marked this conversation as resolved.
Outdated
image: str
distro: str # e.g. "debian" or "rhel"; drives package-format-specific steps

Expand Down Expand Up @@ -210,14 +211,20 @@ def expand_linux_packaging(linux: LinuxFile) -> list[PackagingEntry]:
the nix-based build images, because deb/rpm tooling (debhelper, rpm-build)
is taken from the distro's archive rather than from nixpkgs. Each config
entry carries its own 'image'.

The binaries that go into a package (xrpld and validator-keys) are uploaded
as separate artifacts by the build job, both named after the build config,
so each entry names both artifacts.
"""
entries = []
for distro, configs in linux.package_configs.items():
for cfg in configs:
for compiler, build_type in itertools.product(cfg.compiler, cfg.build_type):
config_name = f"{distro}-{compiler}-{build_type.lower()}-amd64"
entries.append(
PackagingEntry(
artifact_name=f"xrpld-{distro}-{compiler}-{build_type.lower()}-amd64",
artifact_name=f"xrpld-{config_name}",
validator_keys_artifact_name=f"validator-keys-{config_name}",
image=cfg.image,
distro=distro,
)
Expand Down
6 changes: 4 additions & 2 deletions .github/scripts/strategy-matrix/linux.json
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,8 @@
"compiler": ["gcc"],
"build_type": ["Release"],
"arch": ["amd64"],
"minimal": false
"minimal": false,
"extra_cmake_args": "-Dvalidator_keys=ON"
}
],

Expand All @@ -77,7 +78,8 @@
"compiler": ["gcc"],
"build_type": ["Release"],
"arch": ["amd64"],
"minimal": false
"minimal": false,
"extra_cmake_args": "-Dvalidator_keys=ON"
}
]
},
Expand Down
27 changes: 27 additions & 0 deletions .github/workflows/reusable-build-test-config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,33 @@ jobs:
retention-days: 3
if-no-files-found: error

# Only the configurations built with -Dvalidator_keys=ON produce this
# binary (see linux.json), so probe for it rather than keying the upload
# off a list of configuration names.
- name: Check for the validator-keys binary (Linux)
id: validator_keys
if: ${{ runner.os == 'Linux' }}
Comment thread
bthomee marked this conversation as resolved.
Outdated
run: |
if [ -x "${BUILD_DIR}/validator-keys" ]; then
echo "present=true" >>"${GITHUB_OUTPUT}"
else
echo "present=false" >>"${GITHUB_OUTPUT}"
fi

- name: Run the validator-keys tests (Linux)
if: ${{ steps.validator_keys.outputs.present == 'true' }}
working-directory: ${{ env.BUILD_DIR }}
run: ./validator-keys --unittest

- name: Upload the validator-keys binary (Linux)
if: ${{ github.event.repository.visibility == 'public' && steps.validator_keys.outputs.present == 'true' }}
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: validator-keys-${{ inputs.config_name }}
path: ${{ env.BUILD_DIR }}/validator-keys
retention-days: 3
if-no-files-found: error

- name: Upload the test binary (Linux)
if: ${{ github.event.repository.visibility == 'public' && runner.os == 'Linux' }}
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
Expand Down
20 changes: 13 additions & 7 deletions .github/workflows/reusable-package.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Build Linux packages (DEB and RPM) from pre-built binary artifacts.
# Discovers which configurations to package from linux.json (configs in
# "package_configs") and fans out one job per distro. Only linux/amd64 is
# supported; the runner is hardcoded in the job below.
# Build Linux packages (DEB and RPM) from pre-built binary artifacts (xrpld and
# validator-keys). Discovers which configurations to package from linux.json
# (configs in "package_configs") and fans out one job per distro. Only
# linux/amd64 is supported; the runner is hardcoded in the job below.
name: Package

on:
Expand Down Expand Up @@ -56,14 +56,20 @@ jobs:
- name: Checkout repository
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0

- name: Download pre-built binary
- name: Download pre-built xrpld binary
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: ${{ matrix.artifact_name }}
path: ${{ env.BUILD_DIR }}

- name: Make binary executable
run: chmod +x "${BUILD_DIR}/xrpld"
- name: Download pre-built validator-keys binary
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: ${{ matrix.validator_keys_artifact_name }}
path: ${{ env.BUILD_DIR }}

- name: Make binaries executable
run: chmod +x "${BUILD_DIR}/xrpld" "${BUILD_DIR}/validator-keys"

- name: Build package
env:
Expand Down
4 changes: 3 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -137,9 +137,11 @@ endif()

include(XrplCore)
include(XrplProtocolAutogen)
include(XrplValidatorKeys)
include(XrplInstall)
# Must come after XrplValidatorKeys: the 'package' target depends on the
# validator-keys target existing.
include(XrplPackaging)
Comment thread
mathbunnyru marked this conversation as resolved.
Outdated
include(XrplValidatorKeys)

if(tests)
include(CTest)
Expand Down
47 changes: 36 additions & 11 deletions cmake/PatchNixBinary.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@

The Nix-based CI image links binaries against an ELF interpreter (loader)
that lives in the Nix store, so the resulting binaries don't run elsewhere
(including once installed from the .deb package). `patch_nix_binary` adds a
POST_BUILD step that resets the interpreter to the system default loader and
drops the rpath.
(including once installed from the .deb package). `patch_nix_binary` resets
the interpreter to the system default loader and drops the rpath, once the
binary has been linked.

This is only active inside the Nix-based image, detected by the presence of
/tmp/loader-path.sh (shipped by that image, resolves the default loader). It
Expand Down Expand Up @@ -41,13 +41,38 @@ function(patch_nix_binary target)
if(NOT PATCH_NIX_BINARIES)
return()
endif()
add_custom_command(
TARGET ${target}
POST_BUILD
COMMAND
"${PATCHELF_COMMAND}" --set-interpreter "${DEFAULT_LOADER_PATH}"
--remove-rpath "$<TARGET_FILE:${target}>"
COMMENT "Patching ${target}: set default loader, remove rpath"
VERBATIM

set(patch_command
"${PATCHELF_COMMAND}"
--set-interpreter
"${DEFAULT_LOADER_PATH}"
--remove-rpath
"$<TARGET_FILE:${target}>"
)
set(comment "Patching ${target}: set default loader, remove rpath")

# POST_BUILD is the cheap way to do this: it runs only when the binary is
# relinked. It is also only available in the directory that defined the
# target, so for a target from elsewhere (e.g. a FetchContent subproject)
# fall back to a custom target that runs after the binary is linked. That
# one runs on every build, which is harmless because patchelf is idempotent.
get_target_property(target_source_dir ${target} SOURCE_DIR)
if("${target_source_dir}" STREQUAL "${CMAKE_CURRENT_SOURCE_DIR}")
add_custom_command(
TARGET ${target}
POST_BUILD
COMMAND ${patch_command}
COMMENT "${comment}"
VERBATIM
)
else()
add_custom_target(
${target}-patch-nix
ALL
COMMAND ${patch_command}
COMMENT "${comment}"
VERBATIM
)
add_dependencies(${target}-patch-nix ${target})
endif()
endfunction()
16 changes: 15 additions & 1 deletion cmake/XrplPackaging.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,20 @@ if(NOT (RPMBUILD_EXECUTABLE OR DPKG_BUILDPACKAGE_EXECUTABLE))
return()
endif()

# The packages ship both binaries, so both targets must exist.
if(NOT TARGET xrpld)
message(STATUS "xrpld=ON is required; 'package' target not available")
return()
endif()

if(NOT TARGET validator-keys)
message(
STATUS
"validator_keys=ON is required; 'package' target not available"
)
return()
endif()

set(package_env
SRC_DIR=${CMAKE_SOURCE_DIR}
BUILD_DIR=${CMAKE_BINARY_DIR}
Expand All @@ -37,7 +51,7 @@ add_custom_target(
${CMAKE_COMMAND} -E env ${package_env}
${CMAKE_SOURCE_DIR}/package/build_pkg.sh
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
DEPENDS xrpld
DEPENDS xrpld validator-keys
COMMENT "Building Linux package (deb/rpm inferred from host tooling)"
VERBATIM
)
26 changes: 18 additions & 8 deletions cmake/XrplValidatorKeys.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,32 @@ option(
)

if(validator_keys)
git_branch(current_branch)
# default to tracking VK master branch unless we are on release
if(NOT (current_branch STREQUAL "release"))
set(current_branch "master")
endif()
message(STATUS "Tracking ValidatorKeys branch: ${current_branch}")
# Pinned to an exact commit, not a branch: the tool ships inside our
# packages, so the same xrpld version must always package the same
# validator-keys. Bump this deliberately.
set(validator_keys_commit "4c0fb75eec9601c711645998c904507e87e910ae")
Comment thread
bthomee marked this conversation as resolved.
message(STATUS "Using ValidatorKeys commit: ${validator_keys_commit}")

FetchContent_Declare(
validator_keys
GIT_REPOSITORY https://github.com/ripple/validator-keys-tool.git
Comment thread
bthomee marked this conversation as resolved.
GIT_TAG "${current_branch}"
GIT_TAG "${validator_keys_commit}"
)
FetchContent_MakeAvailable(validator_keys)
# The tool's own CMakeLists excludes the target from 'all' when it is built
# as a subproject. Undo that: the packages ship validator-keys, so a build
# configured with validator_keys=ON must produce it as part of the default
# target, next to xrpld.
set_target_properties(
validator-keys
PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}"
PROPERTIES
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}"
EXCLUDE_FROM_ALL OFF
EXCLUDE_FROM_DEFAULT_BUILD OFF
)
# Like xrpld, this binary leaves the Nix-based build image (we ship it in
# the deb/rpm), so it needs the system ELF loader instead of the one in the
# Nix store. Without this it cannot run on the target distro at all.
patch_nix_binary(validator-keys)
install(TARGETS validator-keys RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
endif()
52 changes: 36 additions & 16 deletions package/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# Linux Packaging

This directory contains all files needed to build RPM and Debian packages for `xrpld`.
This directory contains all files needed to build RPM and Debian packages for
`xrpld`. The packages also ship the `validator-keys` tool, so packaging requires
a build configured with `-Dvalidator_keys=ON`.

## Directory layout

Expand Down Expand Up @@ -46,17 +48,28 @@ To print the full packaging matrix (artifact names and images) for the current
Caller workflows (`on-pr.yml`, `on-tag.yml`, `on-trigger.yml`) call
`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 and
runs in that distro's container, so the package format follows from the
container's package manager. The packaging script derives the package version
from the downloaded binary's `xrpld --version` output; no CMake configure or
build step is needed inside the packaging job.
one job per distro. Each job downloads the pre-built `xrpld` and `validator-keys`
binary artifacts and runs in that distro's container, so the package format
follows from the container's package manager. The packaging script derives the
package version from the downloaded binary's `xrpld --version` output; no CMake
configure or build step is needed inside the packaging job.

The binaries come from the `debian` and `rhel` build configurations in
`linux.json`'s `configs` section, which pass `-Dvalidator_keys=ON` so that the
build job produces `validator-keys` next to `xrpld` and uploads it as the
`validator-keys-<config name>` artifact. The packaging entry for a distro names
both artifacts (`artifact_name` and `validator_keys_artifact_name`), so a
packaged configuration must keep `-Dvalidator_keys=ON`.

`validator-keys` is fetched from an exact commit pinned in
[`cmake/XrplValidatorKeys.cmake`](../cmake/XrplValidatorKeys.cmake), so a given
`xrpld` version always packages the same tool; bump that commit deliberately.

### Locally (mirrors CI)

With an `xrpld` binary already built at `build/xrpld`, run the packaging step
inside the same container CI uses. The image tag is derived from `linux.json`
so you don't need to hardcode a SHA.
With `xrpld` and `validator-keys` binaries already built at `build/xrpld` and
`build/validator-keys`, run the packaging step 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. Each distro's container image is the `image` field of its
Expand Down Expand Up @@ -87,6 +100,7 @@ needed, but the host toolchain replaces the pinned CI image:
```bash
cmake \
-Dxrpld=ON \
-Dvalidator_keys=ON \
-Dpkg_release=1 \
-Dtests=OFF \
..
Expand All @@ -95,9 +109,11 @@ cmake --build . --target package # deb on Debian/Ubuntu, rpm on RHEL
```

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
least one of `rpmbuild` / `dpkg-buildpackage` is present and both the `xrpld` and
`validator-keys` targets exist (`-Dxrpld=ON -Dvalidator_keys=ON`); the target
builds both binaries before packaging. `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 package version is not a CMake input on this path: `build_pkg.sh` derives it
Expand Down Expand Up @@ -156,13 +172,17 @@ CMake/CI integration. The CI workflow and the CMake `package` target both invoke
and lets the script use defaults for the rest.

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, and invokes the platform build tool.
`stage_common()` to copy the `xrpld` and `validator-keys` binaries, config files,
and shared support files into the staging area, and invokes the platform build
tool. Both binaries must be present in `BUILD_DIR` and must run in the packaging
environment; a missing or non-runnable one fails early. That runtime check is
what catches a binary still linked against the Nix store's ELF loader (see
`patch_nix_binary` in `cmake/PatchNixBinary.cmake`).

### RPM

1. Creates the standard `rpmbuild/{BUILD,BUILDROOT,RPMS,SOURCES,SPECS,SRPMS}` tree inside the build directory.
2. Copies `xrpld.spec` and all shared source files (binary, configs, service files) into `SOURCES/`.
2. Copies `xrpld.spec` and all shared source files (binaries, configs, service files) into `SOURCES/`.
3. Runs `rpmbuild -bb`, passing the normalized package metadata version as the
`pkg_version` RPM macro and `PKG_RELEASE` as the `pkg_release` RPM macro.
The spec uses manual `install` commands to place files, disables `dwz`, and
Expand All @@ -182,7 +202,7 @@ service restart.
### DEB

1. Creates a staging source tree at `debbuild/source/` inside the build directory.
2. Stages the binary, configs, `README.md`, and `LICENSE.md`.
2. Stages the binaries, 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` using `${pkg_version}-${PKG_RELEASE}`,
Expand Down
Loading
Loading