diff --git a/.github/scripts/strategy-matrix/generate.py b/.github/scripts/strategy-matrix/generate.py index 0e44b1be54..ac21544f14 100755 --- a/.github/scripts/strategy-matrix/generate.py +++ b/.github/scripts/strategy-matrix/generate.py @@ -29,17 +29,20 @@ We will further set additional CMake arguments as follows: - All release builds will have the `assert` option. - Certain Debian Bookworm configurations will change the reference fee, enable codecov, and enable voidstar in PRs. -""" - - -def generate_strategy_matrix(all: bool, config: Config) -> list: +''' +def generate_strategy_matrix(all_configs: bool, config: Config) -> list: configurations = [] for architecture, os, build_type, cmake_args in itertools.product( config.architecture, config.os, config.build_type, config.cmake_args ): # The default CMake target is 'all' for Linux and MacOS and 'install' # for Windows, but it can get overridden for certain configurations. - cmake_target = "install" if os["distro_name"] == "windows" else "all" + + arch = architecture['platform'].split('/')[-1] + compiler = f'{os['compiler_name']}-{os['compiler_version']}' + distro = f'{os['distro_name']}-{os['distro_version']}' + cmake_target = 'install' if os["distro_name"] == 'windows' else 'all' + build_package = False # We build and test all configurations by default, except for Windows in # Debug, because it is too slow, as well as when code coverage is @@ -48,8 +51,15 @@ def generate_strategy_matrix(all: bool, config: Config) -> list: if os["distro_name"] == "windows" and build_type == "Debug": build_only = True + if build_type == "Release" \ + and arch == 'amd64' \ + and cmake_args == '-Dunity=OFF' \ + and compiler == "gcc-12" \ + and distro in ('debian-bullseye', 'rhel-9.4'): + build_package = True + # Only generate a subset of configurations in PRs. - if not all: + if not all_configs and not build_package: # Debian: # - Bookworm using GCC 13: Release and Unity on linux/amd64, set # the reference fee to 500. @@ -62,70 +72,38 @@ def generate_strategy_matrix(all: bool, config: Config) -> list: # - Bookworm using Clang 20: Debug and Unity on linux/amd64. if os["distro_name"] == "debian": skip = True - if os["distro_version"] == "bookworm": - if ( - f"{os['compiler_name']}-{os['compiler_version']}" == "gcc-13" - and build_type == "Release" - and "-Dunity=ON" in cmake_args - and architecture["platform"] == "linux/amd64" - ): - cmake_args = f"-DUNIT_TEST_REFERENCE_FEE=500 {cmake_args}" + if os['distro_version'] == 'bookworm': + if compiler == 'gcc-13' and build_type == 'Release' and '-Dunity=ON' in cmake_args and arch == 'amd64': + cmake_args = f'-DUNIT_TEST_REFERENCE_FEE=500 {cmake_args}' skip = False - if ( - f"{os['compiler_name']}-{os['compiler_version']}" == "gcc-15" - and build_type == "Debug" - and "-Dunity=OFF" in cmake_args - and architecture["platform"] == "linux/amd64" - ): + if compiler == 'gcc-15' and build_type == 'Debug' and '-Dunity=OFF' in cmake_args and arch == 'amd64': skip = False - if ( - f"{os['compiler_name']}-{os['compiler_version']}" == "clang-16" - and build_type == "Debug" - and "-Dunity=OFF" in cmake_args - and architecture["platform"] == "linux/arm64" - ): - cmake_args = f"-Dvoidstar=ON {cmake_args}" + if compiler == 'clang-16' and build_type == 'Debug' and '-Dunity=OFF' in cmake_args and arch =='arm64': + cmake_args = f'-Dvoidstar=ON {cmake_args}' skip = False - if ( - f"{os['compiler_name']}-{os['compiler_version']}" == "clang-17" - and build_type == "Release" - and "-Dunity=ON" in cmake_args - and architecture["platform"] == "linux/amd64" - ): - cmake_args = f"-DUNIT_TEST_REFERENCE_FEE=1000 {cmake_args}" + if compiler == 'clang-17' and build_type == 'Release' and '-Dunity=ON' in cmake_args and arch == 'amd64': + cmake_args = f'-DUNIT_TEST_REFERENCE_FEE=1000 {cmake_args}' skip = False - if ( - f"{os['compiler_name']}-{os['compiler_version']}" == "clang-20" - and build_type == "Debug" - and "-Dunity=ON" in cmake_args - and architecture["platform"] == "linux/amd64" - ): + if compiler == 'clang-20' and build_type == 'Debug' and '-Dunity=ON' in cmake_args and arch == 'amd64': skip = False - if skip: + if skip and not args.package: continue # RHEL: - # - 9 using GCC 12: Debug and Unity on linux/amd64. - # - 10 using Clang: Release and no Unity on linux/amd64. - if os["distro_name"] == "rhel": + # - 9.4 using GCC 12: Debug and Unity on linux/amd64. + # - 9.6 using Clang: Release and no Unity on linux/amd64. + + if os['distro_name'] == 'rhel': skip = True - if os["distro_version"] == "9": - if ( - f"{os['compiler_name']}-{os['compiler_version']}" == "gcc-12" - and build_type == "Debug" - and "-Dunity=ON" in cmake_args - and architecture["platform"] == "linux/amd64" - ): + if os['distro_version'] == '9.4': + if compiler == 'gcc-12' and build_type == 'Release' and '-Dunity=OFF' in cmake_args and arch == 'amd64': skip = False - elif os["distro_version"] == "10": - if ( - f"{os['compiler_name']}-{os['compiler_version']}" == "clang-any" - and build_type == "Release" - and "-Dunity=OFF" in cmake_args - and architecture["platform"] == "linux/amd64" - ): + if compiler == 'gcc-12' and build_type == 'Debug' and '-Dunity=ON' in cmake_args and arch == 'amd64': skip = False - if skip: + elif os['distro_version'] == '9.6': + if compiler == 'clang-any' and build_type == 'Release' and '-Dunity=OFF' in cmake_args and arch == 'amd64': + skip = False + if skip and not build_package: continue # Ubuntu: @@ -135,35 +113,15 @@ def generate_strategy_matrix(all: bool, config: Config) -> list: # - Noble using Clang 19: Release and Unity on linux/arm64. if os["distro_name"] == "ubuntu": skip = True - if os["distro_version"] == "jammy": - if ( - f"{os['compiler_name']}-{os['compiler_version']}" == "gcc-12" - and build_type == "Debug" - and "-Dunity=OFF" in cmake_args - and architecture["platform"] == "linux/arm64" - ): + if os['distro_version'] == 'jammy': + if compiler == 'gcc-12' and build_type == 'Debug' and '-Dunity=OFF' in cmake_args and arch =='arm64': skip = False - elif os["distro_version"] == "noble": - if ( - f"{os['compiler_name']}-{os['compiler_version']}" == "gcc-14" - and build_type == "Release" - and "-Dunity=ON" in cmake_args - and architecture["platform"] == "linux/amd64" - ): + elif os['distro_version'] == 'noble': + if compiler == 'gcc-14' and build_type == 'Release' and '-Dunity=ON' in cmake_args and arch == 'amd64': skip = False - if ( - f"{os['compiler_name']}-{os['compiler_version']}" == "clang-18" - and build_type == "Debug" - and "-Dunity=OFF" in cmake_args - and architecture["platform"] == "linux/amd64" - ): + if compiler == 'clang-18' and build_type == 'Debug' and '-Dunity=OFF' in cmake_args and arch == 'amd64': skip = False - if ( - f"{os['compiler_name']}-{os['compiler_version']}" == "clang-19" - and build_type == "Release" - and "-Dunity=ON" in cmake_args - and architecture["platform"] == "linux/arm64" - ): + if compiler == 'clang-19' and build_type == 'Release' and '-Dunity=ON' in cmake_args and arch =='arm64': skip = False if skip: continue @@ -187,151 +145,86 @@ def generate_strategy_matrix(all: bool, config: Config) -> list: continue # Additional CMake arguments. - cmake_args = f"{cmake_args} -Dtests=ON -Dwerr=ON -Dxrpld=ON" - if not f"{os['compiler_name']}-{os['compiler_version']}" in [ - "gcc-12", - "clang-16", - ]: - cmake_args = f"{cmake_args} -Dwextra=ON" - if build_type == "Release": - cmake_args = f"{cmake_args} -Dassert=ON" + cmake_args = f'{cmake_args} -Dtests=ON -Dxrpld=ON' + if compiler not in ('gcc-12', 'clang-16'): + cmake_args = f'{cmake_args} -Dwerr -Dwextra=ON' + + if build_type == 'Release': + if build_package: + cmake_args = f'{cmake_args} -Dvalidator-keys=ON' + cmake_target = 'rippled validator-keys' + else: + cmake_args = f'{cmake_args} -Dwerr -Dassert=ON' # We skip all RHEL on arm64 due to a build failure that needs further # investigation. - if os["distro_name"] == "rhel" and architecture["platform"] == "linux/arm64": + if os['distro_name'] == 'rhel' and arch =='arm64': continue - # We skip all clang 20+ on arm64 due to Boost build error. - if ( - f"{os['compiler_name']}-{os['compiler_version']}" - in ["clang-20", "clang-21"] - and architecture["platform"] == "linux/arm64" - ): + # We skip all clang-20 on arm64 due to boost 1.86 build error + if compiler == 'clang-20' and arch =='arm64': continue # Enable code coverage for Debian Bookworm using GCC 15 in Debug and no # Unity on linux/amd64 - if ( - f"{os['compiler_name']}-{os['compiler_version']}" == "gcc-15" - and build_type == "Debug" - and "-Dunity=OFF" in cmake_args - and architecture["platform"] == "linux/amd64" - ): - cmake_args = f"-Dcoverage=ON -Dcoverage_format=xml -DCODE_COVERAGE_VERBOSE=ON -DCMAKE_C_FLAGS=-O0 -DCMAKE_CXX_FLAGS=-O0 {cmake_args}" + if compiler == 'gcc-15' and build_type == 'Debug' and '-Dunity=OFF' in cmake_args and arch == 'amd64': + cmake_args = f'-Dcoverage=ON -Dcoverage_format=xml -DCODE_COVERAGE_VERBOSE=ON -DCMAKE_C_FLAGS=-O0 -DCMAKE_CXX_FLAGS=-O0 {cmake_args}' + cmake_target = 'coverage' + build_only = True # Generate a unique name for the configuration, e.g. macos-arm64-debug # or debian-bookworm-gcc-12-amd64-release-unity. - config_name = os["distro_name"] - if (n := os["distro_version"]) != "": - config_name += f"-{n}" - if (n := os["compiler_name"]) != "": - config_name += f"-{n}" - if (n := os["compiler_version"]) != "": - config_name += f"-{n}" - config_name += ( - f"-{architecture['platform'][architecture['platform'].find('/')+1:]}" - ) - config_name += f"-{build_type.lower()}" - if "-Dcoverage=ON" in cmake_args: - config_name += "-coverage" - if "-Dunity=ON" in cmake_args: - config_name += "-unity" + config_name = os['distro_name'] + if (n := os['distro_version']) != '': + config_name += f'-{n}' + if (n := os['compiler_name']) != '': + config_name += f'-{n}' + if (n := os['compiler_version']) != '': + config_name += f'-{n}' + config_name += f'-{arch}' + config_name += f'-{build_type.lower()}' + + if '-Dunity=ON' in cmake_args: + config_name += '-unity' # Add the configuration to the list, with the most unique fields first, # so that they are easier to identify in the GitHub Actions UI, as long # names get truncated. - # Add Address and Thread (both coupled with UB) sanitizers for specific bookworm distros. - # GCC-Asan rippled-embedded tests are failing because of https://github.com/google/sanitizers/issues/856 - if ( - os["distro_version"] == "bookworm" - and f"{os['compiler_name']}-{os['compiler_version']}" == "clang-20" - ): - # Add ASAN + UBSAN configuration. - configurations.append( - { - "config_name": config_name + "-asan-ubsan", - "cmake_args": cmake_args, - "cmake_target": cmake_target, - "build_only": build_only, - "build_type": build_type, - "os": os, - "architecture": architecture, - "sanitizers": "address,undefinedbehavior", - } - ) - # TSAN is deactivated due to seg faults with latest compilers. - activate_tsan = False - if activate_tsan: - configurations.append( - { - "config_name": config_name + "-tsan-ubsan", - "cmake_args": cmake_args, - "cmake_target": cmake_target, - "build_only": build_only, - "build_type": build_type, - "os": os, - "architecture": architecture, - "sanitizers": "thread,undefinedbehavior", - } - ) - else: - configurations.append( - { - "config_name": config_name, - "cmake_args": cmake_args, - "cmake_target": cmake_target, - "build_only": build_only, - "build_type": build_type, - "os": os, - "architecture": architecture, - "sanitizers": "", - } - ) + if args.package and not build_package: + continue + configurations.append({ + 'config_name': config_name, + 'cmake_args': cmake_args, + 'cmake_target': cmake_target, + 'build_only': build_only, + 'build_type': build_type, + 'package': build_package, + 'os': os, + 'architecture': architecture, + }) return configurations def read_config(file: Path) -> Config: config = json.loads(file.read_text()) - if ( - config["architecture"] is None - or config["os"] is None - or config["build_type"] is None - or config["cmake_args"] is None - ): - raise Exception("Invalid configuration file.") - return Config(**config) if __name__ == "__main__": parser = argparse.ArgumentParser() - parser.add_argument( - "-a", - "--all", - help="Set to generate all configurations (generally used when merging a PR) or leave unset to generate a subset of configurations (generally used when committing to a PR).", - action="store_true", - ) - parser.add_argument( - "-c", - "--config", - help="Path to the JSON file containing the strategy matrix configurations.", - required=False, - type=Path, - ) + parser.add_argument('-a', '--all', help='Set to generate all configurations (generally used when merging a PR) or leave unset to generate a subset of configurations (generally used when committing to a PR).', action="store_true") + parser.add_argument('-c', '--config', type=Path, help='Path to the JSON file containing the strategy matrix configurations.') + parser.add_argument('-p', '--package', action='store_true', help='Build packages') args = parser.parse_args() matrix = [] - if args.config is None or args.config == "": - matrix += generate_strategy_matrix( - args.all, read_config(THIS_DIR / "linux.json") - ) - matrix += generate_strategy_matrix( - args.all, read_config(THIS_DIR / "macos.json") - ) - matrix += generate_strategy_matrix( - args.all, read_config(THIS_DIR / "windows.json") - ) + if args.package: + matrix += generate_strategy_matrix(args.all, read_config(THIS_DIR / "linux.json")) + elif args.config is None or args.config == '': + matrix += generate_strategy_matrix(args.all, read_config(THIS_DIR / "linux.json")) + # matrix += generate_strategy_matrix(args.all, read_config(THIS_DIR / "macos.json")) + # matrix += generate_strategy_matrix(args.all, read_config(THIS_DIR / "windows.json")) else: matrix += generate_strategy_matrix(args.all, read_config(args.config)) diff --git a/.github/scripts/strategy-matrix/linux.json b/.github/scripts/strategy-matrix/linux.json index e64a05f925..8d032f987d 100644 --- a/.github/scripts/strategy-matrix/linux.json +++ b/.github/scripts/strategy-matrix/linux.json @@ -17,194 +17,11 @@ "compiler_version": "12", "image_sha": "ab4d1f0" }, - { - "distro_name": "debian", - "distro_version": "bookworm", - "compiler_name": "gcc", - "compiler_version": "13", - "image_sha": "ab4d1f0" - }, - { - "distro_name": "debian", - "distro_version": "bookworm", - "compiler_name": "gcc", - "compiler_version": "14", - "image_sha": "ab4d1f0" - }, - { - "distro_name": "debian", - "distro_version": "bookworm", - "compiler_name": "gcc", - "compiler_version": "15", - "image_sha": "ab4d1f0" - }, - { - "distro_name": "debian", - "distro_version": "bookworm", - "compiler_name": "clang", - "compiler_version": "16", - "image_sha": "ab4d1f0" - }, - { - "distro_name": "debian", - "distro_version": "bookworm", - "compiler_name": "clang", - "compiler_version": "17", - "image_sha": "ab4d1f0" - }, - { - "distro_name": "debian", - "distro_version": "bookworm", - "compiler_name": "clang", - "compiler_version": "18", - "image_sha": "ab4d1f0" - }, - { - "distro_name": "debian", - "distro_version": "bookworm", - "compiler_name": "clang", - "compiler_version": "19", - "image_sha": "ab4d1f0" - }, - { - "distro_name": "debian", - "distro_version": "bookworm", - "compiler_name": "clang", - "compiler_version": "20", - "image_sha": "ab4d1f0" - }, - { - "distro_name": "debian", - "distro_version": "trixie", - "compiler_name": "gcc", - "compiler_version": "14", - "image_sha": "ab4d1f0" - }, - { - "distro_name": "debian", - "distro_version": "trixie", - "compiler_name": "gcc", - "compiler_version": "15", - "image_sha": "ab4d1f0" - }, - { - "distro_name": "debian", - "distro_version": "trixie", - "compiler_name": "clang", - "compiler_version": "20", - "image_sha": "ab4d1f0" - }, - { - "distro_name": "debian", - "distro_version": "trixie", - "compiler_name": "clang", - "compiler_version": "21", - "image_sha": "ab4d1f0" - }, - { - "distro_name": "rhel", - "distro_version": "8", - "compiler_name": "gcc", - "compiler_version": "14", - "image_sha": "ab4d1f0" - }, - { - "distro_name": "rhel", - "distro_version": "8", - "compiler_name": "clang", - "compiler_version": "any", - "image_sha": "ab4d1f0" - }, { "distro_name": "rhel", "distro_version": "9", "compiler_name": "gcc", - "compiler_version": "12", - "image_sha": "ab4d1f0" - }, - { - "distro_name": "rhel", - "distro_version": "9", - "compiler_name": "gcc", - "compiler_version": "13", - "image_sha": "ab4d1f0" - }, - { - "distro_name": "rhel", - "distro_version": "9", - "compiler_name": "gcc", - "compiler_version": "14", - "image_sha": "ab4d1f0" - }, - { - "distro_name": "rhel", - "distro_version": "9", - "compiler_name": "clang", - "compiler_version": "any", - "image_sha": "ab4d1f0" - }, - { - "distro_name": "rhel", - "distro_version": "10", - "compiler_name": "gcc", - "compiler_version": "14", - "image_sha": "ab4d1f0" - }, - { - "distro_name": "rhel", - "distro_version": "10", - "compiler_name": "clang", - "compiler_version": "any", - "image_sha": "ab4d1f0" - }, - { - "distro_name": "ubuntu", - "distro_version": "jammy", - "compiler_name": "gcc", - "compiler_version": "12", - "image_sha": "ab4d1f0" - }, - { - "distro_name": "ubuntu", - "distro_version": "noble", - "compiler_name": "gcc", - "compiler_version": "13", - "image_sha": "ab4d1f0" - }, - { - "distro_name": "ubuntu", - "distro_version": "noble", - "compiler_name": "gcc", - "compiler_version": "14", - "image_sha": "ab4d1f0" - }, - { - "distro_name": "ubuntu", - "distro_version": "noble", - "compiler_name": "clang", - "compiler_version": "16", - "image_sha": "ab4d1f0" - }, - { - "distro_name": "ubuntu", - "distro_version": "noble", - "compiler_name": "clang", - "compiler_version": "17", - "image_sha": "ab4d1f0" - }, - { - "distro_name": "ubuntu", - "distro_version": "noble", - "compiler_name": "clang", - "compiler_version": "18", - "image_sha": "ab4d1f0" - }, - { - "distro_name": "ubuntu", - "distro_version": "noble", - "compiler_name": "clang", - "compiler_version": "19", - "image_sha": "ab4d1f0" + "compiler_version": "12" } ], "build_type": ["Debug", "Release"], diff --git a/.github/workflows/on-pr.yml b/.github/workflows/on-pr.yml index 46f6b7500a..a5a8e54cc7 100644 --- a/.github/workflows/on-pr.yml +++ b/.github/workflows/on-pr.yml @@ -1,11 +1,14 @@ -# This workflow runs all workflows to check, build and test the project on -# various Linux flavors, as well as on MacOS and Windows, on every push to a +# This workflow runs all workflows to check, build, package and test the project on +# various Linux flavors, as well as on macOS and Windows, on every push to a # user branch. However, it will not run if the pull request is a draft unless it # has the 'DraftRunCI' label. For commits to PRs that target a release branch, # it also uploads the libxrpl recipe to the Conan remote. name: PR on: + push: + branches: + - legleux/linux_packages merge_group: types: - checks_requested @@ -65,6 +68,9 @@ jobs: .github/workflows/reusable-build.yml .github/workflows/reusable-build-test-config.yml .github/workflows/reusable-build-test.yml + .github/workflows/reusable-build-pkg.yml + .github/workflows/reusable-pkg.yml + .github/workflows/reusable-package.yml .github/workflows/reusable-strategy-matrix.yml .github/workflows/reusable-test.yml .github/workflows/reusable-upload-recipe.yml @@ -73,6 +79,7 @@ jobs: conan/** external/** include/** + pkgs/** src/** tests/** CMakeLists.txt @@ -97,68 +104,57 @@ jobs: outputs: go: ${{ steps.go.outputs.go == 'true' }} - check-levelization: - needs: should-run - if: ${{ needs.should-run.outputs.go == 'true' }} - uses: ./.github/workflows/reusable-check-levelization.yml + # check-levelization: + # needs: should-run + # if: ${{ needs.should-run.outputs.go == 'true' }} + # uses: ./.github/workflows/reusable-check-levelization.yml - check-rename: - needs: should-run - if: ${{ needs.should-run.outputs.go == 'true' }} - uses: ./.github/workflows/reusable-check-rename.yml + # build-test: + # needs: should-run + # if: ${{ needs.should-run.outputs.go == 'true' }} + # uses: ./.github/workflows/reusable-build-test.yml + # strategy: + # matrix: + # os: [linux, macos, windows] + # with: + # os: ${{ matrix.os }} + # secrets: + # CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} - build-test: + build-package: + name: Build ${{ matrix.pkg_type }} ${{ matrix.arch }} packages needs: should-run if: ${{ needs.should-run.outputs.go == 'true' }} - uses: ./.github/workflows/reusable-build-test.yml + uses: ./.github/workflows/reusable-build-pkg.yml + secrets: inherit strategy: fail-fast: false matrix: - os: [linux, macos, windows] + # pkg_type: [rpm] + pkg_type: [deb, rpm] + arch: [amd64] + # arch: [amd64, arm64] with: - # Enable ccache only for events targeting the XRPLF repository, since - # other accounts will not have access to our remote cache storage. - ccache_enabled: ${{ github.repository_owner == 'XRPLF' }} - os: ${{ matrix.os }} - secrets: - CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} + pkg_type: ${{ matrix.pkg_type }} + arch: ${{ matrix.arch }} - upload-recipe: - needs: - - should-run - - build-test - # Only run when committing to a PR that targets a release branch in the - # XRPLF repository. - if: ${{ github.repository_owner == 'XRPLF' && needs.should-run.outputs.go == 'true' && startsWith(github.ref, 'refs/heads/release') }} - uses: ./.github/workflows/reusable-upload-recipe.yml - secrets: - remote_username: ${{ secrets.CONAN_REMOTE_USERNAME }} - remote_password: ${{ secrets.CONAN_REMOTE_PASSWORD }} - - notify-clio: - needs: upload-recipe - runs-on: ubuntu-latest - steps: - # Notify the Clio repository about the newly proposed release version, so - # it can be checked for compatibility before the release is actually made. - - name: Notify Clio - env: - GH_TOKEN: ${{ secrets.CLIO_NOTIFY_TOKEN }} - PR_URL: ${{ github.event.pull_request.html_url }} - run: | - gh api --method POST -H "Accept: application/vnd.github+json" -H "X-GitHub-Api-Version: 2022-11-28" \ - /repos/xrplf/clio/dispatches -f "event_type=check_libxrpl" \ - -F "client_payload[ref]=${{ needs.upload-recipe.outputs.recipe_ref }}" \ - -F "client_payload[pr_url]=${PR_URL}" + # notify-clio: + # needs: + # - should-run + # - build-test + # if: ${{ needs.should-run.outputs.go == 'true' && contains(fromJSON('["release", "master"]'), github.ref_name) }} + # uses: ./.github/workflows/reusable-notify-clio.yml + # secrets: + # clio_notify_token: ${{ secrets.CLIO_NOTIFY_TOKEN }} + # conan_remote_username: ${{ secrets.CONAN_REMOTE_USERNAME }} + # conan_remote_password: ${{ secrets.CONAN_REMOTE_PASSWORD }} passed: if: failure() || cancelled() needs: - - check-levelization - - check-rename - - build-test - - upload-recipe - - notify-clio + # - build-test + # - check-levelization + - build-package runs-on: ubuntu-latest steps: - name: Fail diff --git a/.github/workflows/on-trigger.new.yml b/.github/workflows/on-trigger.new.yml new file mode 100644 index 0000000000..3873a4cf64 --- /dev/null +++ b/.github/workflows/on-trigger.new.yml @@ -0,0 +1,97 @@ +# This workflow runs all workflows to build and test the code on various Linux +# flavors, as well as on MacOS and Windows, on a scheduled basis, on merge into +# the 'develop' or 'release*' branches, or when requested manually. Upon pushes +# to the develop branch it also uploads the libxrpl recipe to the Conan remote. +name: Trigger + +on: + push: + branches: + - legleux/linux_packages + - develop + - release + - master + paths: + # These paths are unique to `on-trigger.yml`. + - ".github/workflows/on-trigger.yml" + + # Keep the paths below in sync with those in `on-pr.yml`. + - ".github/actions/build-deps/**" + - ".github/actions/build-test/**" + - ".github/actions/generate-version/**" + - ".github/actions/setup-conan/**" + - ".github/scripts/strategy-matrix/**" + - ".github/workflows/reusable-build.yml" + - ".github/workflows/reusable-build-test-config.yml" + - ".github/workflows/reusable-build-test.yml" + - ".github/workflows/reusable-build-pkg.yml" + - ".github/workflows/reusable-pkg.yml" + - ".github/workflows/reusable-package.yml" + - ".github/workflows/reusable-strategy-matrix.yml" + - ".github/workflows/reusable-test.yml" + - ".github/workflows/reusable-upload-recipe.yml" + - ".codecov.yml" + - "cmake/**" + - "conan/**" + - "external/**" + - "include/**" + - "pkgs/**" + - "src/**" + - "tests/**" + - "CMakeLists.txt" + - "conanfile.py" + - "conan.lock" + + # Run at 06:32 UTC on every day of the week from Monday through Friday. This + # will force all dependencies to be rebuilt, which is useful to verify that + # all dependencies can be built successfully. Only the dependencies that + # are actually missing from the remote will be uploaded. + schedule: + - cron: "32 6 * * 1-5" + + # Run when manually triggered via the GitHub UI or API. + workflow_dispatch: + +concurrency: + # When a PR is merged into the develop branch it will be assigned a unique + # group identifier, so execution will continue even if another PR is merged + # while it is still running. In all other cases the group identifier is shared + # per branch, so that any in-progress runs are cancelled when a new commit is + # pushed. + group: ${{ github.workflow }}-${{ github.event_name == 'push' && github.ref == 'refs/heads/develop' && github.sha || github.ref }} + cancel-in-progress: true + +defaults: + run: + shell: bash + +jobs: + # check-missing-commits: + # if: ${{ github.event_name == 'push' && github.ref_type == 'branch' && contains(fromJSON('["develop", "release"]'), github.ref_name) }} + # uses: ./.github/workflows/reusable-check-missing-commits.yml + + # build-test: + # uses: ./.github/workflows/reusable-build-test.yml + # strategy: + # matrix: + # os: [linux, macos, windows] + # with: + # os: ${{ matrix.os }} + # strategy_matrix: ${{ github.event_name == 'schedule' && 'all' || 'minimal' }} + # secrets: + # CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} + + build-package: + name: Build ${{ matrix.pkg_type }} ${{ matrix.arch }} packages + uses: ./.github/workflows/reusable-build-pkg.yml + secrets: inherit + strategy: + fail-fast: ${{ github.event_name == 'merge_group' }} + matrix: + # pkg_type: [rpm] + pkg_type: [deb, rpm] + arch: [amd64] + # arch: [amd64, arm64] + with: + pkg_type: ${{ matrix.pkg_type }} + arch: ${{ matrix.arch }} diff --git a/.github/workflows/on-trigger.yml b/.github/workflows/on-trigger.yml index 210670f5a1..31467b72d4 100644 --- a/.github/workflows/on-trigger.yml +++ b/.github/workflows/on-trigger.yml @@ -9,6 +9,7 @@ on: branches: - "develop" - "release*" + - "linux_packages_squashed" paths: # These paths are unique to `on-trigger.yml`. - ".github/workflows/on-trigger.yml" @@ -22,6 +23,9 @@ on: - ".github/workflows/reusable-build.yml" - ".github/workflows/reusable-build-test-config.yml" - ".github/workflows/reusable-build-test.yml" + - ".github/workflows/reusable-build-pkg.yml" + - ".github/workflows/reusable-pkg.yml" + - ".github/workflows/reusable-package.yml" - ".github/workflows/reusable-strategy-matrix.yml" - ".github/workflows/reusable-test.yml" - ".github/workflows/reusable-upload-recipe.yml" @@ -30,6 +34,7 @@ on: - "conan/**" - "external/**" - "include/**" + - "pkgs/**" - "src/**" - "tests/**" - "CMakeLists.txt" @@ -78,6 +83,21 @@ jobs: secrets: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} + build-package: + name: Build ${{ matrix.pkg_type }} ${{ matrix.arch }} packages + uses: ./.github/workflows/reusable-build-pkg.yml + secrets: inherit + strategy: + fail-fast: ${{ github.event_name == 'merge_group' }} + matrix: + # pkg_type: [rpm] + pkg_type: [deb, rpm] + arch: [amd64] + # arch: [amd64, arm64] + with: + pkg_type: ${{ matrix.pkg_type }} + arch: ${{ matrix.arch }} + upload-recipe: needs: build-test # Only run when pushing to the develop branch in the XRPLF repository. diff --git a/.github/workflows/package-test.yml b/.github/workflows/package-test.yml new file mode 100644 index 0000000000..ac97dc5ef8 --- /dev/null +++ b/.github/workflows/package-test.yml @@ -0,0 +1,34 @@ +name: Test rippled + +on: + workflow_call: + inputs: + pkg_type: + description: "Whether to run unit tests" + required: true + type: boolean + + arch: + description: Runner to run the job on as a JSON string + required: true + type: string +jobs: + test: + name: Test ${{ inputs.pkg_type }}-${{ inputs.arch }} + strategy: + fail-fast: false + matrix: + include: + - { pkg: rpm, distro: "rocky:9" } + - { pkg: deb, distro: "ubuntu:jammy" } + - { pkg: deb, distro: "debian:trixie" } + runs-on: ubuntu-latest + container: ${{ matrix.distro }} + steps: + - name: run unittests + run: | + ls -lh + # - name: Download rippled artifact + # uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 + # with: + # name: rippled-${{ inputs.config_name }} diff --git a/.github/workflows/reusable-build-pkg.yml b/.github/workflows/reusable-build-pkg.yml new file mode 100644 index 0000000000..b252b5e100 --- /dev/null +++ b/.github/workflows/reusable-build-pkg.yml @@ -0,0 +1,99 @@ +on: + workflow_call: + inputs: + pkg_type: + required: false + type: string + arch: + required: false + type: string + # secrets: + # GPG_KEY_B64: + # description: "The gpg key to sign packages." + # required: true + # GPG_KEY_PASS_B64: + # description: "The gpg key passphrase." + # required: true +defaults: + run: + shell: bash +jobs: + build: + name: Build ${{ inputs.pkg_type }} ${{ inputs.arch }} package + runs-on: heavy${{ inputs.arch == 'arm64' && '-arm64' || '' }} + container: ghcr.io/xrplf/ci/${{ inputs.pkg_type == 'rpm' && 'rhel-9' || 'debian-bookworm' }}:gcc-12 + steps: + - name: Checkout repository + uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 + + - name: Build packages + run: | + ./pkgs/build.sh + # cp pkgs/rippled-3.0.0-1.x86_64.rpm rippled-3.0.0-1.amd64.rpm + find . -name "*.rpm" + printf '%s\n' \ + "rpm_md5sum=$(rpm -q --queryformat '%{SIGMD5}\n' -p ./rippled-[0-9]*.rpm 2>/dev/null)" \ + "rpm_sha256=$(sha256sum ./rippled-[0-9]*.rpm | awk '{ print $1 }')" \ + > build_vars + cat build_vars >> $GITHUB_STEP_SUMMARY + + - uses: actions/upload-artifact@v4 + with: + name: ${{ inputs.pkg_type }}-${{ inputs.arch }} + path: "*${{ inputs.arch }}.${{ inputs.pkg_type }}" + if-no-files-found: error + + test: + name: Test ${{ inputs.pkg_type }} ${{ inputs.arch }} package + needs: build + runs-on: heavy${{ inputs.arch == 'arm64' && '-arm64' || '' }} + container: ghcr.io/xrplf/ci/${{ inputs.pkg_type == 'rpm' && 'rhel-9' || 'debian-bookworm' }}:gcc-12 + steps: + - uses: actions/download-artifact@v4 + with: + name: ${{ inputs.pkg_type }}-${{ inputs.arch }} + - name: Running tests + run: echo "Running tests..." + + sign: + name: Sign ${{ inputs.pkg_type }} ${{ inputs.arch }} package + needs: build + runs-on: ubuntu-latest + container: ghcr.io/astral-sh/uv:python3.13-bookworm-slim + + steps: + - name: Install gpg & rpm + run: apt-get update && apt-get install -y gpg rpm + + - name: Checkout repository + uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 + + - uses: actions/download-artifact@v4 + with: + name: ${{ inputs.pkg_type }}-${{ inputs.arch }} + + ## Also possibility + # - name: Write passphrase file securely + # run: | + # install -m 600 /dev/null "$RUNNER_TEMP/gpg-pass" + # # newline OK; gpg reads to EOF + # printf '%s\n' "${{ secrets.GPG_PASSPHRASE }}" > "$RUNNER_TEMP/gpg-pass" + - name: Sign + env: + PYTHONUNBUFFERED: 1 + GPG_KEY_B64: ${{ secrets.GPG_KEY_B64 }} + GPG_KEY_PASS_B64: ${{ secrets.GPG_KEY_PASS_B64 }} + run: | + for i in $(find . -maxdepth 1 -type f -name "rippled-[0-9]*.rpm"); do + echo "found $i" + ./pkgs/sign_packages.py $i + done + # find . -name "*.${{ inputs.pkg_type }}" -print0 | xargs -0 -I{} ./pkgs/sign_packages.py "{}" + #find . -maxdepth 1 -name "*.rpm" -print0 | xargs -0 -I{} ./pkgs/sign_packages.py "{}" + #find . -maxdepth 1 -name "*.rpm" -print0 | xargs -0 -I{} echo "found-{}" + + - uses: actions/upload-artifact@v4 + with: + name: signed-rippled-${{ inputs.pkg_type }}-${{ inputs.arch }} + path: "*${{ inputs.arch }}.${{ inputs.pkg_type }}" + if-no-files-found: error diff --git a/.github/workflows/reusable-package.yml b/.github/workflows/reusable-package.yml new file mode 100644 index 0000000000..18aeb7bf8e --- /dev/null +++ b/.github/workflows/reusable-package.yml @@ -0,0 +1,69 @@ +name: Package rippled + +on: + workflow_call: + inputs: + build_type: + description: 'The build type to use ("Debug", "Release").' + required: false + type: string + default: 'Release' + cmake_args: + description: "Additional arguments to pass to CMake." + required: false + type: string + cmake_target: + description: "The CMake target to build." + required: false + type: string + + runs_on: + description: Runner to run the job on as a JSON string + required: true + type: string + image: + description: "The image to run in (leave empty to run natively)" + required: false + type: string + default: '' + + config_name: + description: "The name of the configuration." + required: false + type: string + +defaults: + run: + shell: bash + +jobs: + build: + name: Package ${{ inputs.config_name }} + runs-on: ${{ fromJSON(inputs.runs_on) }} + container: ${{ inputs.image != '' && inputs.image || null }} + + steps: + - name: Checkout repository + uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 + + - name: Build packages + run: | + export BUILD_TYPE=${{ inputs.build_type }} + export CMAKE_ARGS=${{ inputs.cmake_args }} + export CMAKE_TARGETS=${{ inputs.cmake_target }} + + ./pkgs/build.sh + + { + echo "" + while IFS='=' read -r k v; do + printf '\n' "$k" "$v" + done < build_vars + echo "
%s%s
" + } >> "$GITHUB_STEP_SUMMARY" + + - uses: actions/upload-artifact@v4 + with: + name: ${{ inputs.config_name }} + path: '**/*.{deb,rpm}' + if-no-files-found: error diff --git a/.github/workflows/reusable-pkg.yml b/.github/workflows/reusable-pkg.yml new file mode 100644 index 0000000000..e77119a6fe --- /dev/null +++ b/.github/workflows/reusable-pkg.yml @@ -0,0 +1,41 @@ +name: Package + +on: + workflow_call: + inputs: + build_dir: + description: "The directory where to build." + required: false + type: string + default: ".build" + os: + description: 'The operating system to use for the build ("linux", "macos", "windows").' + required: false + type: string + default: linux + strategy_matrix_subset: + description: 'The strategy matrix to use for generating a subset of configurations.' + required: false + type: string + default: "package" + +jobs: + generate-matrix: + uses: ./.github/workflows/reusable-strategy-matrix.yml + with: + os: ${{ inputs.os }} + strategy_matrix_subset: ${{ inputs.strategy_matrix_subset }} + + package: + needs: + - generate-matrix + uses: ./.github/workflows/reusable-package.yml + strategy: + matrix: ${{ fromJson(needs.generate-matrix.outputs.matrix) }} + with: + build_type: ${{ matrix.build_type }} + cmake_args: ${{ matrix.cmake_args }} + cmake_target: ${{ matrix.cmake_target }} + runs_on: ${{ toJSON(matrix.architecture.runner) }} + image: ${{ contains(matrix.architecture.platform, 'linux') && format('ghcr.io/xrplf/ci/{0}-{1}:{2}-{3}-sha-5dd7158', matrix.os.distro_name, matrix.os.distro_version, matrix.os.compiler_name, matrix.os.compiler_version) || '' }} + config_name: ${{ matrix.config_name }} diff --git a/.github/workflows/reusable-strategy-matrix.yml b/.github/workflows/reusable-strategy-matrix.yml index c975347307..b616d35c87 100644 --- a/.github/workflows/reusable-strategy-matrix.yml +++ b/.github/workflows/reusable-strategy-matrix.yml @@ -13,6 +13,10 @@ on: required: false type: string default: "minimal" + strategy_matrix_subset: + description: 'The strategy matrix to use for generating a subset of configs).' + required: false + type: string outputs: matrix: description: "The generated strategy matrix." @@ -42,4 +46,5 @@ jobs: env: GENERATE_CONFIG: ${{ inputs.os != '' && format('--config={0}.json', inputs.os) || '' }} GENERATE_OPTION: ${{ inputs.strategy_matrix == 'all' && '--all' || '' }} - run: ./generate.py ${GENERATE_OPTION} ${GENERATE_CONFIG} >> "${GITHUB_OUTPUT}" + GENERATE_SUBSET: ${{ inputs.strategy_matrix_subset != '' && format('--{0}', inputs.strategy_matrix_subset) || '' }} + run: ./generate.py ${{ env.GENERATE_SUBSET }} ${{ env.GENERATE_OPTION }} ${{ env.GENERATE_CONFIG }} >> "${GITHUB_OUTPUT}" diff --git a/pkgs/build.sh b/pkgs/build.sh new file mode 100755 index 0000000000..024c7ac4db --- /dev/null +++ b/pkgs/build.sh @@ -0,0 +1,202 @@ +#!/usr/bin/env bash + +set -o errexit +set -o xtrace + +. /etc/os-release + +case "$ID $ID_LIKE" in + *rhel*|*fedora*) + # dnf -y module install "nodejs:20/common" + PKG="rpm" + ;; + *debian*|*ubuntu*) + # curl -fsSL https://deb.nodesource.com/setup_20.x -o nodesource_setup.sh + # chmod +x nodesource_setup.sh + # ./nodesource_setup.sh + # apt-get install -y nodejs + PKG="deb" + ;; +esac + +# build_dir="/root/build/${PKG}/packages" +# ./pkgs/build_rippled.${PKG}.sh + +# echo "my build_vars" > build_vars +# exit 0 +# if [ 1 -eq 0 ]; then +repo_dir=$PWD +set -a +repo_name="rippled" +pkgs_dir="${repo_dir}/pkgs" +shared_files="${pkgs_dir}/shared" +pkg_files="${pkgs_dir}/packaging/${PKG}" +build_info_src="${repo_dir}/src/libxrpl/protocol/BuildInfo.cpp" +xrpl_version=$(grep -E -i -o "\b(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(-[0-9a-z\-]+(\.[0-9a-z\-]+)*)?(\+[0-9a-z\-]+(\.[0-9a-z\-]+)*)?\b" "${build_info_src}") + +git config --global --add safe.directory '*' +branch=$(git rev-parse --abbrev-ref HEAD) +commit=$(git rev-parse HEAD) +short_commit=$(git rev-parse --short=7 HEAD) +date=$(git show -s --format=%cd --date=format-local:"%Y%m%d%H%M%S") + +conan_remote_name="${CONAN_REMOTE_NAME:-xrplf}" +conan_remote_url="${CONAN_REMOTE_URL:-https://conan.ripplex.io}" + +BUILD_TYPE=${BUILD_TYPE:-Release} +set +a + +if [ "${branch}" = 'develop' ]; then + # TODO: Can remove when CMake sets version + dev_version="${date}~${short_commit}" + xrpl_version="${xrpl_version}+${dev_version}" +fi + +if [ "${PKG}" = 'rpm' ]; then + IFS='-' read -r RIPPLED_RPM_VERSION RELEASE <<< "${xrpl_version}" + export RIPPLED_RPM_VERSION + RPM_RELEASE=${RPM_RELEASE-1} + # post-release version + if [ "hf" = "$(echo "$RELEASE" | cut -c -2)" ]; then + RPM_RELEASE="${RPM_RELEASE}.${RELEASE}" + # pre-release version (-b or -rc) + elif [[ $RELEASE ]]; then + RPM_RELEASE="0.${RPM_RELEASE}.${RELEASE}" + fi + export RPM_RELEASE + + if [[ $RPM_PATCH ]]; then + RPM_PATCH=".${RPM_PATCH}" + export RPM_PATCH + fi + + build_dir="build/${PKG}/packages" + rm -rf ${build_dir} + mkdir -p ${build_dir}/rpmbuild/{BUILD,RPMS,SOURCES,SPECS,SRPMS} + cp "${pkgs_dir}/rippled.patch" ${build_dir}/rpmbuild/SOURCES/ + git archive \ + --remote "${repo_dir}" HEAD \ + --prefix ${repo_name}/ \ + --format tar.gz \ + --output ${build_dir}/rpmbuild/SOURCES/rippled.tar.gz + ln --symbolic "${repo_dir}" ${build_dir}/rippled + cp -r "${pkgs_dir}/packaging/rpm/rippled.spec" ${build_dir} + pushd "${build_dir}" || exit + + rpmbuild \ + --define "_topdir ${PWD}/rpmbuild" \ + --define "_smp_build_ncpus %(nproc --ignore=2 2>/dev/null || echo 1)" \ + -ba rippled.spec + + RPM_VERSION_RELEASE=$(rpm -qp --qf='%{NAME}-%{VERSION}-%{RELEASE}' ./rpmbuild/RPMS/x86_64/rippled-[0-9]*.rpm) + tar_file=$RPM_VERSION_RELEASE.tar.gz + + printf '%s\n' \ + "rpm_md5sum=$(rpm -q --queryformat '%{SIGMD5}\n' -p ./rpmbuild/RPMS/x86_64/rippled-[0-9]*.rpm 2>/dev/null)" \ + "rpm_sha256=$(sha256sum ./rpmbuild/RPMS/x86_64/rippled-[0-9]*.rpm | awk '{ print $1 }')" \ + "rippled_version=${xrpl_version}" \ + "rippled_git_hash=${commit}" \ + "rpm_version=${RIPPLED_RPM_VERSION}" \ + "rpm_file_name=${tar_file}" \ + "rpm_version_release=${RPM_VERSION_RELEASE}" \ + > build_vars + + # Rename the files to match the debs + mv rpmbuild/RPMS/x86_64/* . + for f in *x86_64.rpm; do + new="${f/x86_64/amd64}" + mv "$f" "$new" + echo "Renamed $f -> $new" + done + rm -rf rpmbuild + rm -f rippled rippled.tar.gz rippled.spec + pushd -0 && dirs -c + + mv "${build_dir}/build_vars" . + +elif [ "${PKG}" = 'deb' ]; then + dpkg_version=$(echo "${xrpl_version}" | sed 's:-:~:g') + full_version="${dpkg_version}" + build_dir="build/${PKG}/packages" + rm -rf ${build_dir} + mkdir -p ${build_dir} + + git archive \ + --remote "${repo_dir}" HEAD \ + --prefix ${repo_name}/ \ + --format tar.gz \ + --output "${build_dir}/${repo_name}_${dpkg_version}.orig.tar.gz" + + pushd ${build_dir} || exit + tar -zxf "${repo_name}_${dpkg_version}.orig.tar.gz" + + pushd ${repo_name} || exit + + # Prepare the package metadata directory, `debian/`, within `rippled/`. + cp -r "${pkg_files}/debian" . + cp "${shared_files}/rippled.service" debian/ + cp "${shared_files}/update-rippled.sh" . + cp "${shared_files}/update-rippled-cron" . + cp "${shared_files}/rippled-logrotate" . + + if [ "${branch}" = 'develop' ]; then + # TODO: Can remove when CMake sets version + sed --in-place "s/versionString = \"\([^\"]*\)\"/versionString = \"${xrpl_version}\"/" "${build_info_src}" + fi + + cat << CHANGELOG > ./debian/changelog +rippled (${xrpl_version}) unstable; urgency=low + + * see RELEASENOTES + + -- Ripple Labs Inc. $(TZ=UTC date -R) +CHANGELOG + + cat ./debian/changelog + dpkg-buildpackage -b -d -us -uc + + popd || exit + rm -rf ${repo_name} + # for f in *.ddeb; do mv -- "$f" "${f%.ddeb}.deb"; done + popd || exit + cp ${build_dir}/${repo_name}_${xrpl_version}_amd64.changes . + + awk '/Checksums-Sha256:/{hit=1;next}/Files:/{hit=0}hit' ${repo_name}_${xrpl_version}_amd64.changes | sed -E 's!^[[:space:]]+!!' > shasums + sha() { + build_vars + + pushd -0 && dirs -c +fi + +# fi + +# find . -name "*.${PKG}" +# mkdir -p $build_dir +# if [ "${PKG}" = 'rpm' ]; then +# mv /root/rpmbuild/RPMS/x86_64/* . +# for f in *x86_64.rpm; do +# new="${f/x86_64/amd64}" +# mv "$f" "$build_dir/$new" +# echo "Renamed $f -> $new" +# done +# # mv /root/rpmbuild/RPMS/x86_64/* $build_dir/ +# else +# echo $PWD +# find / -name "rippled-3.0.0_amd64.deb" +# mv *.deb $build_dir +# fi +# printf '%s\n' \ +# "rippled_version=3.0.0" \ +# "rippled_git_hash=deadbeef" \ +# > build_vars +cp "${build_dir}/"*.$PKG . diff --git a/pkgs/packaging/deb/debian/README.Debian b/pkgs/packaging/deb/debian/README.Debian new file mode 100644 index 0000000000..25ba6b55f7 --- /dev/null +++ b/pkgs/packaging/deb/debian/README.Debian @@ -0,0 +1,3 @@ +rippled daemon + + -- Mike Ellery Tue, 04 Dec 2018 18:19:03 +0000 diff --git a/pkgs/packaging/deb/debian/control b/pkgs/packaging/deb/debian/control new file mode 100644 index 0000000000..010ce28099 --- /dev/null +++ b/pkgs/packaging/deb/debian/control @@ -0,0 +1,20 @@ +Source: rippled +Section: net +Priority: optional +Maintainer: Ripple Labs Inc. +Build-Depends: cmake, + debhelper (>= 13), + debhelper-compat (= 13) +# debhelper (>= 14), +# debhelper-compat (= 14), +# TODO: Let's go for 14! +Standards-Version: 4.6.0 +Homepage: https://github.com/XRPLF/rippled.git +Rules-Requires-Root: no + +Package: rippled +Architecture: any +Depends: ${shlibs:Depends}, ${misc:Depends} +Description: XRP Ledger server (rippled) + rippled is the core server of the XRP Ledger, providing a peer-to-peer + network node for validating and processing transactions. diff --git a/pkgs/packaging/deb/debian/copyright b/pkgs/packaging/deb/debian/copyright new file mode 100644 index 0000000000..dce318fd76 --- /dev/null +++ b/pkgs/packaging/deb/debian/copyright @@ -0,0 +1,86 @@ +Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: rippled +Source: https://github.com/ripple/rippled + +Files: * +Copyright: 2012-2019 Ripple Labs Inc. + +License: __UNKNOWN__ + +The accompanying files under various copyrights. + +Copyright (c) 2012, 2013, 2014 Ripple Labs Inc. + +Permission to use, copy, modify, and distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +The accompanying files incorporate work covered by the following copyright +and previous license notice: + +Copyright (c) 2011 Arthur Britto, David Schwartz, Jed McCaleb, +Vinnie Falco, Bob Way, Eric Lombrozo, Nikolaos D. Bougalis, Howard Hinnant + +Some code from Raw Material Software, Ltd., provided under the terms of the + ISC License. See the corresponding source files for more details. + Copyright (c) 2013 - Raw Material Software Ltd. + Please visit http://www.juce.com + +Some code from ASIO examples: +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +Some code from Bitcoin: +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2011 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file license.txt or http://www.opensource.org/licenses/mit-license.php. + +Some code from Tom Wu: +This software is covered under the following copyright: + +/* + * Copyright (c) 2003-2005 Tom Wu + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, + * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY + * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + * + * IN NO EVENT SHALL TOM WU BE LIABLE FOR ANY SPECIAL, INCIDENTAL, + * INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES WHATSOEVER + * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER OR NOT ADVISED OF + * THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF LIABILITY, ARISING OUT + * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * In addition, the following condition applies: + * + * All redistributions must retain an intact copy of this copyright notice + * and disclaimer. + */ + +Address all questions regarding this license to: + + Tom Wu + tjw@cs.Stanford.EDU diff --git a/pkgs/packaging/deb/debian/dirs b/pkgs/packaging/deb/debian/dirs new file mode 100644 index 0000000000..aed307ee17 --- /dev/null +++ b/pkgs/packaging/deb/debian/dirs @@ -0,0 +1,3 @@ +/var/log/rippled/ +/var/lib/rippled/ +/etc/systemd/system/rippled.service.d/ diff --git a/pkgs/packaging/deb/debian/docs b/pkgs/packaging/deb/debian/docs new file mode 100644 index 0000000000..1217b6db43 --- /dev/null +++ b/pkgs/packaging/deb/debian/docs @@ -0,0 +1,2 @@ +README.md +LICENSE.md diff --git a/pkgs/packaging/deb/debian/rippled-dev.install b/pkgs/packaging/deb/debian/rippled-dev.install new file mode 100644 index 0000000000..4f7b103f4a --- /dev/null +++ b/pkgs/packaging/deb/debian/rippled-dev.install @@ -0,0 +1,3 @@ +opt/ripple/include +opt/ripple/lib/*.a +opt/ripple/lib/cmake/* diff --git a/pkgs/packaging/deb/debian/rippled.conffiles b/pkgs/packaging/deb/debian/rippled.conffiles new file mode 100644 index 0000000000..0c6d1c36d4 --- /dev/null +++ b/pkgs/packaging/deb/debian/rippled.conffiles @@ -0,0 +1,2 @@ +/opt/ripple/etc/rippled.cfg +/opt/ripple/etc/validators.txt diff --git a/pkgs/packaging/deb/debian/rippled.install b/pkgs/packaging/deb/debian/rippled.install new file mode 100644 index 0000000000..f984560c41 --- /dev/null +++ b/pkgs/packaging/deb/debian/rippled.install @@ -0,0 +1,10 @@ +etc/logrotate.d/rippled +opt/ripple/bin/rippled +opt/ripple/bin/update-rippled.sh +opt/ripple/bin/validator-keys +opt/ripple/bin/xrpld +opt/ripple/etc/rippled.cfg +opt/ripple/etc/update-rippled-cron +opt/ripple/etc/validators.txt +opt/ripple/include/* +opt/ripple/lib/* diff --git a/pkgs/packaging/deb/debian/rippled.links b/pkgs/packaging/deb/debian/rippled.links new file mode 100644 index 0000000000..a5889f9db7 --- /dev/null +++ b/pkgs/packaging/deb/debian/rippled.links @@ -0,0 +1,4 @@ +opt/ripple/etc/rippled.cfg etc/opt/ripple/rippled.cfg +opt/ripple/etc/validators.txt etc/opt/ripple/validators.txt +opt/ripple/bin/rippled usr/local/bin/rippled +opt/ripple/bin/rippled opt/ripple/bin/xrpld diff --git a/pkgs/packaging/deb/debian/rippled.postinst b/pkgs/packaging/deb/debian/rippled.postinst new file mode 100644 index 0000000000..468aea5695 --- /dev/null +++ b/pkgs/packaging/deb/debian/rippled.postinst @@ -0,0 +1,39 @@ +#!/bin/sh +set -e + +USER_NAME=rippled +GROUP_NAME=rippled +case "$1" in + configure) + id -u $USER_NAME >/dev/null 2>&1 || \ + useradd --system \ + --home-dir /nonexistent \ + --no-create-home \ + --shell /usr/sbin/nologin \ + --comment "system user for rippled" \ + --user-group \ + ${USER_NAME} + + chown -R $USER_NAME:$GROUP_NAME /var/log/rippled/ + chown -R $USER_NAME:$GROUP_NAME /var/lib/rippled/ + chown -R $USER_NAME:$GROUP_NAME /opt/ripple + chmod 755 /var/log/rippled/ + chmod 755 /var/lib/rippled/ + chmod 644 /opt/ripple/etc/update-rippled-cron + chmod 644 /etc/logrotate.d/rippled + chown -R root:$GROUP_NAME /opt/ripple/etc/update-rippled-cron + ;; + + abort-upgrade|abort-remove|abort-deconfigure) + ;; + + *) + echo "postinst called with unknown argument \`$1'" >&2 + exit 1 + ;; +esac + + +#DEBHELPER# + +exit 0 diff --git a/pkgs/packaging/deb/debian/rippled.postrm b/pkgs/packaging/deb/debian/rippled.postrm new file mode 100644 index 0000000000..9086993a1f --- /dev/null +++ b/pkgs/packaging/deb/debian/rippled.postrm @@ -0,0 +1,17 @@ +#!/bin/sh +set -e + +case "$1" in + purge|remove|upgrade|failed-upgrade|abort-install|abort-upgrade|disappear) + ;; + + *) + echo "postrm called with unknown argument \`$1'" >&2 + exit 1 + ;; +esac + + +#DEBHELPER# + +exit 0 diff --git a/pkgs/packaging/deb/debian/rippled.preinst b/pkgs/packaging/deb/debian/rippled.preinst new file mode 100644 index 0000000000..10575345a2 --- /dev/null +++ b/pkgs/packaging/deb/debian/rippled.preinst @@ -0,0 +1,20 @@ +#!/bin/sh +set -e + +case "$1" in + install|upgrade) + ;; + + abort-upgrade) + ;; + + *) + echo "preinst called with unknown argument \`$1'" >&2 + exit 1 + ;; +esac + + +#DEBHELPER# + +exit 0 diff --git a/pkgs/packaging/deb/debian/rippled.prerm b/pkgs/packaging/deb/debian/rippled.prerm new file mode 100644 index 0000000000..adabdbfb72 --- /dev/null +++ b/pkgs/packaging/deb/debian/rippled.prerm @@ -0,0 +1,20 @@ +#!/bin/sh +set -e + +case "$1" in + remove|upgrade|deconfigure) + ;; + + failed-upgrade) + ;; + + *) + echo "prerm called with unknown argument \`$1'" >&2 + exit 1 + ;; +esac + + +#DEBHELPER# + +exit 0 diff --git a/pkgs/packaging/deb/debian/rules b/pkgs/packaging/deb/debian/rules new file mode 100755 index 0000000000..2ea1988c10 --- /dev/null +++ b/pkgs/packaging/deb/debian/rules @@ -0,0 +1,88 @@ +#!/usr/bin/make -f +export DH_VERBOSE = 1 +export DH_OPTIONS = -v +## TODO: Confirm these are still required. +# debuild sets some warnings that don't work well +# for our curent build..so try to remove those flags here: +export CFLAGS:=$(subst -Wformat,,$(CFLAGS)) +export CFLAGS:=$(subst -Werror=format-security,,$(CFLAGS)) +export CXXFLAGS:=$(subst -Wformat,,$(CXXFLAGS)) +export CXXFLAGS:=$(subst -Werror=format-security,,$(CXXFLAGS)) + +## TODO: Confirm these are still required. +export DEB_BUILD_MAINT_OPTIONS = hardening=+all +export DEB_BUILD_OPTIONS = nodwz + +export RIPPLE_REMOTE = xrplf +export RIPPLE_REMOTE_URL = https://conan.ripplex.io + +# ## CMake Configure args +# export DEB_CMAKE_GENERATOR = Ninja +export DEB_CMAKE_BUILD_TYPE = Release + +NPROC := $(shell nproc --ignore=2) +export DEB_BUILD_OPTIONS += parallel=$(NPROC) + +CONAN_HOME := $(shell conan config home) +CONAN_PROFILE := $(shell conan profile path default) +CONAN_GCONF := $(CONAN_HOME)/global.conf +INSTALL_PREFIX := "/opt/ripple" +BUILD_DIR := obj-$(DEB_BUILD_GNU_TYPE) + +.ONESHELL: +SHELL := /bin/bash + +%: + dh $@ --buildsystem=cmake +override_dh_installsystemd: + dh_installsystemd --no-start + +override_dh_auto_configure: + conan remote add --index 0 $(RIPPLE_REMOTE) $(RIPPLE_REMOTE_URL) --force + conan config install ./conan/profiles/default --target-folder $(CONAN_HOME)/profiles + echo "tools.build:jobs={{ os.cpu_count() - 2 }}" >> ${CONAN_HOME}/global.conf + echo "core.download:parallel={{ os.cpu_count() }}" >> $(CONAN_GCONF) + + conan install . \ + --settings:all build_type=$(DEB_CMAKE_BUILD_TYPE) \ + --output-folder=$(BUILD_DIR) \ + --options:host "&:xrpld=True" \ + --options:host "&:tests=True" \ + --build=missing + + # Debian assumes an offline build process and sets CMake's FETCHCONTENT_FULLY_DISCONNECTED variable to ON + # To use as much as the default settings as possible we'll clone it where CMake's FetchContent expects it. + mkdir -p "$(BUILD_DIR)/_deps" + git clone https://github.com/ripple/validator-keys-tool.git "$(BUILD_DIR)/_deps/validator_keys-src" + + dh_auto_configure --builddirectory=$(BUILD_DIR) -- \ + -DCMAKE_BUILD_TYPE:STRING=$(DEB_CMAKE_BUILD_TYPE) \ + -Dxrpld=ON -Dtests=ON -Dvalidator_keys=ON \ + -DCMAKE_INSTALL_PREFIX:PATH=$(INSTALL_PREFIX) \ + -DCMAKE_VERBOSE_MAKEFILE:BOOL=ON \ + -DCMAKE_TOOLCHAIN_FILE:FILEPATH=$(BUILD_DIR)/build/generators/conan_toolchain.cmake + +override_dh_auto_build: + dh_auto_build \ + --builddirectory=$(BUILD_DIR) -- rippled validator-keys + +# cmake \ +# --build $(BUILD_DIR) \ +# --target rippled \ +# --target validator-keys \ +# --parallel $(NPROC) + + + + +override_dh_auto_install: + cmake --install $(BUILD_DIR) --prefix debian/tmp/opt/ripple +# install -D $(BUILD_DIR)/_deps/validator_keys_src-build/validator-keys/validator-keys debian/tmp/opt/ripple/bin/validator-keys + install -D $(BUILD_DIR)/_deps/validator_keys_src-build/validator-keys debian/tmp/opt/ripple/bin/validator-keys + install -D update-rippled.sh debian/tmp/opt/ripple/bin/update-rippled.sh + install -D update-rippled-cron debian/tmp/opt/ripple/etc/update-rippled-cron + install -D rippled-logrotate debian/tmp/etc/logrotate.d/rippled + rm -rf debian/tmp/opt/ripple/lib64/cmake/date + +override_dh_dwz: + @echo "Skipping DWZ due to huge debug info" diff --git a/pkgs/packaging/deb/debian/rules.orig b/pkgs/packaging/deb/debian/rules.orig new file mode 100755 index 0000000000..a195c17e11 --- /dev/null +++ b/pkgs/packaging/deb/debian/rules.orig @@ -0,0 +1,84 @@ +#!/usr/bin/make -f +export DH_VERBOSE = 1 +export DH_OPTIONS = -v +# debuild sets some warnings that don't work well +# for our curent build..so try to remove those flags here: +export CFLAGS:=$(subst -Wformat,,$(CFLAGS)) +export CFLAGS:=$(subst -Werror=format-security,,$(CFLAGS)) +export CXXFLAGS:=$(subst -Wformat,,$(CXXFLAGS)) +export CXXFLAGS:=$(subst -Werror=format-security,,$(CXXFLAGS)) + +export DEB_BUILD_MAINT_OPTIONS = hardening=+all +export DEB_BUILD_OPTIONS = nodwz + +export RIPPLE_REMOTE="xrplf" +export RIPPLE_REMOTE_URL="https://conan.ripplex.io" + +export CONAN_HOME := $(shell conan config home) +export CONAN_PROFILE := $(shell conan profile path default) + +export DEB_CMAKE_GENERATOR = Ninja +export DEB_CMAKE_BUILD_TYPE = Release +export DEB_CMAKE_EXTRA_FLAGS = -Dvalidator_keys=ON -Dtests=ON -Dxrpld=ON -DCMAKE_TOOLCHAIN_FILE=build/generators/conan_toolchain.cmake + + +.ONESHELL: +SHELL := /bin/bash +NPROC := $(shell expr $(shell nproc) - 2) +BUILD_DIR := build.rippled +VKT_PATH := $(BUILD_DIR)/vkt + +%: + dh $@ --buildsystem=cmake + +override_dh_installsystemd: + dh_installsystemd --no-start + +override_dh_auto_configure: + + dpkg-buildflags --get CFLAGS + dpkg-buildflags --get LDFLAGS + dpkg-buildflags --status + + conan remote add --index 0 $(RIPPLE_REMOTE) $(RIPPLE_REMOTE_URL) --force + sed -i "s/gnu17/20/" $(CONAN_PROFILE) + + git clone https://github.com/ripple/validator-keys-tool.git $(VKT_PATH) +conan install . --options:a "&:xrpld=True" --options:a "&:tests=True" --build "missing" + + dh_auto_configure --builddirectory=$(BUILD_DIR) -- \ + cmake .. \ + -DCMAKE_BUILD_TYPE=Release \ + -Dvalidator_keys=ON \ + -Dtests=ON \ + -Dxrpld=ON \ + -DCMAKE_VERBOSE_MAKEFILE=ON \ + -DCMAKE_TOOLCHAIN_FILE:FILEPATH=build/generators/conan_toolchain.cmake +# -DFETCHCONTENT_FULLY_DISCONNECTED=OFF \ +# -DFETCHCONTENT_SOURCE_DIR_VALIDATOR_KEYS_SRC=$(VKT_PATH) \ + + +# dh_auto_configure --builddirectory=$(BUILD_DIR) +override_dh_auto_build: +cmake --build . --target rippled --target validator-keys --parallel 30 + + +#manually run: +FETCHCONTENT_BASE_DIR:PATH=/home/emel/dev/Ripple/rippled/rippled/github_linux_packages/build/dpkg/packages/rippled/build.rippled/_deps +FETCHCONTENT_FULLY_DISCONNECTED:BOOL=OFF +FETCHCONTENT_QUIET:BOOL=ON +FETCHCONTENT_SOURCE_DIR_VALIDATOR_KEYS_SRC:PATH= +FETCHCONTENT_UPDATES_DISCONNECTED:BOOL=OFF +FETCHCONTENT_UPDATES_DISCONNECTED_VALIDATOR_KEYS_SRC:BOOL=OFF + +override_dh_auto_install: + cmake --install $(BUILD_DIR) --prefix debian/tmp/opt/ripple + install -D $(BUILD_DIR)/validator-keys/validator-keys debian/tmp/opt/ripple/bin/validator-keys + install -D bin/getRippledInfo debian/tmp/opt/ripple/bin/getRippledInfo + install -D update-rippled.sh debian/tmp/opt/ripple/bin/update-rippled.sh + install -D update-rippled-cron debian/tmp/opt/ripple/etc/update-rippled-cron + install -D rippled-logrotate debian/tmp/etc/logrotate.d/rippled + rm -rf debian/tmp/opt/ripple/lib64/cmake/date + +override_dh_dwz: + @echo "Skipping DWZ due to huge debug info" diff --git a/pkgs/packaging/deb/debian/source/format b/pkgs/packaging/deb/debian/source/format new file mode 100644 index 0000000000..89ae9db8f8 --- /dev/null +++ b/pkgs/packaging/deb/debian/source/format @@ -0,0 +1 @@ +3.0 (native) diff --git a/pkgs/packaging/deb/debian/source/local-options b/pkgs/packaging/deb/debian/source/local-options new file mode 100644 index 0000000000..00131ee8c4 --- /dev/null +++ b/pkgs/packaging/deb/debian/source/local-options @@ -0,0 +1,2 @@ +#abort-on-upstream-changes +#unapply-patches diff --git a/pkgs/packaging/rpm/50-rippled.preset b/pkgs/packaging/rpm/50-rippled.preset new file mode 100644 index 0000000000..854e20a087 --- /dev/null +++ b/pkgs/packaging/rpm/50-rippled.preset @@ -0,0 +1 @@ +enable rippled.service \ No newline at end of file diff --git a/pkgs/packaging/rpm/rippled.spec b/pkgs/packaging/rpm/rippled.spec new file mode 100644 index 0000000000..7186bf0359 --- /dev/null +++ b/pkgs/packaging/rpm/rippled.spec @@ -0,0 +1,171 @@ +%global pkg_name %{getenv:repo_name} +%global branch %{getenv:branch} +%global commit %{getenv:commit} +%global shortcommit %{getenv:shortcommit} +%global date %{getenv:commit_date} +%global conan_remote_name %{getenv:conan_remote_name} +%global conan_remote_url %{getenv:conan_remote_url} +%global shared_files %{getenv:shared_files} +%global pkg_files %{getenv:pkg_files} +%global build_type %{getenv:BUILD_TYPE} + +%global _prefix /opt/ripple +%global srcdir %{_builddir}/rippled +%global blddir %{srcdir}/bld.rippled + +%global xrpl_version %{getenv:xrpl_version} +%global ver_base %(v=%{xrpl_version}; echo ${v%%-*}) +%global _has_dash %(v=%{xrpl_version}; [ "${v#*-}" != "$v" ] && echo 1 || echo 0) +%if 0%{?_has_dash} + %global ver_suffix %(v=%{xrpl_version}; printf %s "${v#*-}") +%endif + +Name: %{pkg_name} +Version: %{ver_base} +Release: %{?ver_suffix:0.%{ver_suffix}}%{!?ver_suffix:1}%{?dist} +Summary: %{name} XRPL daemon + +License: ISC +URL: https://github.com/XRPLF/rippled +Source0: rippled.tar.gz +Patch0: rippled.patch +%{warn:name=%{name}} +%{warn:version=%{version}} +%{warn:ver_base=%{ver_base}} +%{warn:ver_suffix=%{ver_suffix}} +%{warn:release=%{release}} +%{warn:FullReleaseVersion=%{name}-%{version}-%{release}.%{_arch}.rpm} + +%description +%{name} with p2p server for the XRP Ledger. + +%prep +%autosetup -p1 -n %{name} + +# TODO: Remove when version set with CMake. +if [ %{branch} == 'develop' ]; then + sed --in-place "s/versionString = \"\([^\"]*\)\"/versionString = \"\1+%{ver_input}\"/" src/libxrpl/protocol/BuildInfo.cpp +fi + +%build +conan remote add --index 0 %{conan_remote_name} %{conan_remote_url} --force +conan config install conan/profiles/default --target-folder $(conan config home)/profiles/ +echo "tools.build:jobs={{ os.cpu_count() - 2 }}" >> ${CONAN_HOME}/global.conf +echo "core.download:parallel={{ os.cpu_count() }}" >> ${CONAN_HOME}/global.conf + +conan install %{srcdir} \ + --settings:all build_type=%{build_type} \ + --output-folder %{srcdir}/conan_deps \ + --options:host "&:xrpld=True" \ + --options:host "&:tests=True" \ + --build=missing + +cmake \ + -S %{srcdir} \ + -B %{blddir} \ + -Dxrpld=ON \ + -Dvalidator_keys=ON \ + -Dtests=ON \ + -DCMAKE_BUILD_TYPE:STRING=%{build_type} \ + -DCMAKE_INSTALL_PREFIX:PATH=%{_prefix} \ + -DCMAKE_VERBOSE_MAKEFILE:BOOL=ON \ + -DCMAKE_TOOLCHAIN_FILE:FILEPATH=%{srcdir}/conan_deps/build/generators/conan_toolchain.cmake + +cmake \ + --build %{blddir} \ + --parallel %{_smp_build_ncpus} \ + --target rippled \ + --target validator-keys + +%install +rm -rf %{buildroot} +DESTDIR=%{buildroot} cmake --install %{blddir} + +install -Dm0755 %{shared_files}/update-rippled.sh %{buildroot}%{_bindir}/update-rippled.sh +ln -s rippled %{buildroot}%{_bindir}/xrpld +ln -s update-rippled.sh %{buildroot}%{_bindir}/update-xrpld.sh + +# configs +install -Dm0644 %{srcdir}/cfg/rippled-example.cfg %{buildroot}%{_prefix}/etc/rippled.cfg +install -Dm0644 %{srcdir}/cfg/validators-example.txt %{buildroot}%{_prefix}/etc/validators.txt +mkdir -p %{buildroot}%{_sysconfdir}/opt/ripple + +#/etc points to /opt +ln -s ../../../opt/ripple/rippled.cfg %{buildroot}%{_sysconfdir}/opt/ripple/xrpld.cfg +ln -s ../../../opt/ripple/etc/rippled.cfg %{buildroot}%{_sysconfdir}/opt/ripple/rippled.cfg +ln -s ../../../opt/ripple/etc/validators.txt %{buildroot}%{_sysconfdir}/opt/ripple/validators.txt + +# systemd/sysusers/tmpfiles +install -Dm0644 %{shared_files}/rippled.service %{buildroot}%{_unitdir}/rippled.service +install -Dm0644 %{pkg_files}/rippled.sysusers %{buildroot}%{_sysusersdir}/rippled.conf +install -Dm0644 %{pkg_files}/rippled.tmpfiles %{buildroot}%{_tmpfilesdir}/rippled.conf + +%files +%license LICENSE* +%doc README* + +# Files/dirs the pkgs owns +%dir %{_prefix} +%dir %{_prefix}/bin +%dir %{_prefix}/etc +%if 0 + %dir %{_sysconfdir}/opt # Add this if rpmlint cries. +%endif +%dir %{_sysconfdir}/opt/ripple + +# Binaries and symlinks under our (non-standard) _prefix (/opt/ripple) +%{_bindir}/rippled +%{_bindir}/xrpld +%{_bindir}/update-rippled.sh +%{_bindir}/update-xrpld.sh +%{_bindir}/validator-keys + +# We won't ship these but we'll create them. +%ghost /usr/local/bin/rippled +%ghost /usr/local/bin/xrpld + +%config(noreplace) %{_prefix}/etc/rippled.cfg +%config(noreplace) %{_prefix}/etc/validators.txt + +%config(noreplace) %{_sysconfdir}/opt/ripple/rippled.cfg +%config(noreplace) %{_sysconfdir}/opt/ripple/xrpld.cfg +%config(noreplace) %{_sysconfdir}/opt/ripple/validators.txt + +# systemd and service user creation +%{_unitdir}/rippled.service +%{_sysusersdir}/rippled.conf +%{_tmpfilesdir}/rippled.conf + +# Let tmpfiles create the db and log dirs +%ghost %dir /var/opt/ripple +%ghost %dir /var/opt/ripple/lib +%ghost %dir /var/opt/ripple/log + +# TODO: Fix the CMake install() calls so we don't need to exclude these. +%exclude %{_prefix}/include/* +%exclude %{_prefix}/lib/* +%exclude %{_prefix}/lib/pkgconfig/* +%exclude /usr/lib/debug/** + +%post +# Add a link to $PATH /usr/local/bin/rippled %{_bindir}/rippled (also non-standard) +mkdir -p /usr/local/bin +for i in rippled xrpld +do + if [ ! -e /usr/local/bin/${i} ]; then + ln -s %{_bindir}/${i} /usr/local/bin/${i} + elif [ -L /usr/local/bin/${i} ] && \ + [ "$(readlink -f /usr/local/bin/${i})" != "%{_bindir}/${i}" ]; then + ln -sfn %{_bindir}/${i} /usr/local/bin/${i} + fi +done + +%preun +# remove the link only if it points to us (on erase, $1 == 0) +for i in rippled xrpld +do + if [ "$1" -eq 0 ] && [ -L /usr/local/bin/${i} ] && \ + [ "$(readlink -f /usr/local/bin/${i})" = "%{_bindir}/${i}" ]; then + rm -f /usr/local/bin/${i} + fi +done diff --git a/pkgs/packaging/rpm/rippled.sysusers b/pkgs/packaging/rpm/rippled.sysusers new file mode 100644 index 0000000000..ba59fd50d9 --- /dev/null +++ b/pkgs/packaging/rpm/rippled.sysusers @@ -0,0 +1,2 @@ +u rippled - "System user for rippled service" +g rippled - - diff --git a/pkgs/packaging/rpm/rippled.tmpfiles b/pkgs/packaging/rpm/rippled.tmpfiles new file mode 100644 index 0000000000..7b78f9b28b --- /dev/null +++ b/pkgs/packaging/rpm/rippled.tmpfiles @@ -0,0 +1,2 @@ +d /var/opt/ripple/lib 0750 rippled rippled - +d /var/opt/ripple/log 0750 rippled adm - diff --git a/pkgs/rippled-3.0.0-1.x86_64.rpm b/pkgs/rippled-3.0.0-1.x86_64.rpm new file mode 100644 index 0000000000..7518f3de8c Binary files /dev/null and b/pkgs/rippled-3.0.0-1.x86_64.rpm differ diff --git a/pkgs/rippled.patch b/pkgs/rippled.patch new file mode 100644 index 0000000000..9b2091678d --- /dev/null +++ b/pkgs/rippled.patch @@ -0,0 +1,42 @@ +diff --git a/cmake/RippledInstall.cmake b/cmake/RippledInstall.cmake +index 9ce288d785..f5f859fc7f 100644 +--- a/cmake/RippledInstall.cmake ++++ b/cmake/RippledInstall.cmake +@@ -32,12 +32,12 @@ install( + DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" + ) + +-install(CODE " +- set(CMAKE_MODULE_PATH \"${CMAKE_MODULE_PATH}\") +- include(create_symbolic_link) +- create_symbolic_link(xrpl \ +- \${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_INCLUDEDIR}/ripple) +-") ++# install(CODE " ++# set(CMAKE_MODULE_PATH \"${CMAKE_MODULE_PATH}\") ++# include(create_symbolic_link) ++# create_symbolic_link(xrpl \ ++# \$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_INCLUDEDIR}/ripple) ++# ") + + install (EXPORT RippleExports + FILE RippleTargets.cmake +@@ -66,12 +66,12 @@ if (is_root_project AND TARGET rippled) + copy_if_not_exists(\"${CMAKE_CURRENT_SOURCE_DIR}/cfg/rippled-example.cfg\" etc rippled.cfg) + copy_if_not_exists(\"${CMAKE_CURRENT_SOURCE_DIR}/cfg/validators-example.txt\" etc validators.txt) + ") +- install(CODE " +- set(CMAKE_MODULE_PATH \"${CMAKE_MODULE_PATH}\") +- include(create_symbolic_link) +- create_symbolic_link(rippled${suffix} \ +- \${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}/xrpld${suffix}) +- ") ++ # install(CODE " ++ # set(CMAKE_MODULE_PATH \"${CMAKE_MODULE_PATH}\") ++ # include(create_symbolic_link) ++ # create_symbolic_link(rippled${suffix} \ ++ # \$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}/xrpld${suffix}) ++ # ") + endif () + + install ( diff --git a/pkgs/shared/rippled-logrotate b/pkgs/shared/rippled-logrotate new file mode 100644 index 0000000000..120aa91d3c --- /dev/null +++ b/pkgs/shared/rippled-logrotate @@ -0,0 +1,15 @@ +/var/log/rippled/*.log { + daily + minsize 200M + rotate 7 + nocreate + missingok + notifempty + compress + compresscmd /usr/bin/nice + compressoptions -n19 ionice -c3 gzip + compressext .gz + postrotate + /opt/ripple/bin/rippled --conf /opt/ripple/etc/rippled.cfg logrotate + endscript +} diff --git a/pkgs/shared/rippled.service b/pkgs/shared/rippled.service new file mode 100644 index 0000000000..24d9dd9759 --- /dev/null +++ b/pkgs/shared/rippled.service @@ -0,0 +1,15 @@ +[Unit] +Description=Ripple Daemon +After=network-online.target +Wants=network-online.target + +[Service] +Type=simple +ExecStart=/opt/ripple/bin/rippled --net --silent --conf /etc/opt/ripple/rippled.cfg +Restart=on-failure +User=rippled +Group=rippled +LimitNOFILE=65536 + +[Install] +WantedBy=multi-user.target diff --git a/pkgs/shared/update-rippled-cron b/pkgs/shared/update-rippled-cron new file mode 100644 index 0000000000..c7744219f9 --- /dev/null +++ b/pkgs/shared/update-rippled-cron @@ -0,0 +1,10 @@ +# For automatic updates, symlink this file to /etc/cron.d/ +# Do not remove the newline at the end of this cron script + +# bash required for use of RANDOM below. +SHELL=/bin/bash +PATH=/sbin;/bin;/usr/sbin;/usr/bin + +# invoke check/update script with random delay up to 59 mins +0 * * * * root sleep $((RANDOM*3540/32768)) && /opt/ripple/bin/update-rippled.sh + diff --git a/pkgs/shared/update-rippled.sh b/pkgs/shared/update-rippled.sh new file mode 100755 index 0000000000..19409ece0c --- /dev/null +++ b/pkgs/shared/update-rippled.sh @@ -0,0 +1,65 @@ +#!/usr/bin/env bash + +# auto-update script for rippled daemon + +# Check for sudo/root permissions +if [[ $(id -u) -ne 0 ]] ; then + echo "This update script must be run as root or sudo" + exit 1 +fi + +LOCKDIR=/tmp/rippleupdate.lock +UPDATELOG=/var/log/rippled/update.log + +function cleanup { + # If this directory isn't removed, future updates will fail. + rmdir $LOCKDIR +} + +# Use mkdir to check if process is already running. mkdir is atomic, as against file create. +if ! mkdir $LOCKDIR 2>/dev/null; then + echo $(date -u) "lockdir exists - won't proceed." >> $UPDATELOG + exit 1 +fi +trap cleanup EXIT + +source /etc/os-release +can_update=false + +if [[ "$ID" == "ubuntu" || "$ID" == "debian" ]] ; then + # Silent update + apt-get update -qq + + # The next line is an "awk"ward way to check if the package needs to be updated. + RIPPLE=$(apt-get install -s --only-upgrade rippled | awk '/^Inst/ { print $2 }') + test "$RIPPLE" == "rippled" && can_update=true + + function apply_update { + apt-get install rippled -qq + } +elif [[ "$ID" == "fedora" || "$ID" == "centos" || "$ID" == "rhel" || "$ID" == "scientific" ]] ; then + RIPPLE_REPO=${RIPPLE_REPO-stable} + yum --disablerepo=* --enablerepo=ripple-$RIPPLE_REPO clean expire-cache + + yum check-update -q --enablerepo=ripple-$RIPPLE_REPO rippled || can_update=true + + function apply_update { + yum update -y --enablerepo=ripple-$RIPPLE_REPO rippled + } +else + echo "unrecognized distro!" + exit 1 +fi + +# Do the actual update and restart the service after reloading systemctl daemon. +if [ "$can_update" = true ] ; then + exec 3>&1 1>>${UPDATELOG} 2>&1 + set -e + apply_update + systemctl daemon-reload + systemctl restart rippled.service + echo $(date -u) "rippled daemon updated." +else + echo $(date -u) "no updates available" >> $UPDATELOG +fi + diff --git a/pkgs/sign_packages.py b/pkgs/sign_packages.py new file mode 100755 index 0000000000..4e726dc36f --- /dev/null +++ b/pkgs/sign_packages.py @@ -0,0 +1,194 @@ +#!/usr/bin/env -S uv run --script +# /// script +# requires-python = ">=3.13" +# dependencies = [ +# "python-gnupg", +# ] +# /// +import argparse +import base64 +import gnupg +import os +import re +import shutil +import subprocess +import sys +import tempfile +from dataclasses import dataclass +from pathlib import Path + + +@dataclass(slots=True) +class SignCfg: + gnupghome: Path + fingerprint: str + passphrase: str + + +def set_tty(): + try: + tty = subprocess.check_output(["tty"], text=True, stderr=subprocess.DEVNULL).strip() + os.environ["GPG_TTY"] = tty + # print(f"GPG_TTY set to {tty}") + except subprocess.CalledProcessError: + print("No TTY detected. Skipping setting GPG_TTY.") + + +def make_cfg(passphrase: str, armored_private_key: str) -> SignCfg: + ghome = Path(tempfile.mkdtemp()) + ghome.chmod(0o700) + gpg = gnupg.GPG(gnupghome=str(ghome)) + imp = gpg.import_keys(armored_private_key) + fp = imp.fingerprints[0] + return SignCfg(gnupghome=ghome, fingerprint=fp, passphrase=passphrase) + + +def import_pubkey_into_rpmdb(gnupghome: Path, fingerprint: str, rpmdb: Path): + env = {**os.environ, "GNUPGHOME": str(gnupghome)} + cp = subprocess.run( + ["gpg", "--batch", "--yes", "--armor", "--export", fingerprint], + env=env, text=True, capture_output=True, check=True, + ) + pub = rpmdb / "pubkey.asc" + pub.write_text(cp.stdout) + + rpmdb.mkdir(parents=True, exist_ok=True) + subprocess.run(["rpm", "--dbpath", str(rpmdb), "--import", str(pub)], check=True) + + +def sign_rpm(pkg: Path, cfg: SignCfg) -> subprocess.CompletedProcess: + fd, pfile = tempfile.mkstemp(text=True) + os.write(fd, cfg.passphrase.rstrip("\r\n").encode()); os.close(fd); os.chmod(pfile, 0o600) + rpm_sign_cmd = [ + "rpm", + "--define", "%__gpg /usr/bin/gpg", + "--define", "_signature gpg", + "--define", f"_gpg_name {cfg.fingerprint}", + "--define", f"_gpg_path {cfg.gnupghome}", + "--define", f"_gpg_passfile {pfile}", + "--define", "__gpg_check_password_cmd /bin/true", + "--define", + "__gpg_sign_cmd %{__gpg} --batch --no-tty --no-armor " + "--digest-algo sha512 --pinentry-mode loopback " + "--passphrase-file %{_gpg_passfile} " + "-u '%{_gpg_name}' --sign --detach-sign " + "--output %{__signature_filename} %{__plaintext_filename}", + "--addsign", str(pkg), + ] + + return subprocess.run( + rpm_sign_cmd, + text=True, + check=False, + capture_output=True, + ) + + +def sign_deb(pkg: Path, cfg: SignCfg) -> subprocess.CompletedProcess: + sig = pkg.with_suffix(pkg.suffix + ".asc") + env = {**os.environ, "GNUPGHOME": str(cfg.gnupghome)} + return subprocess.run( + [ + "gpg", + "--batch", "--yes", "--armor", + "--pinentry-mode", "loopback", + "--local-user", cfg.fingerprint, + "--passphrase", cfg.passphrase, + "--output", str(sig), + "--detach-sign", str(pkg), + ], + env=env, check=False, capture_output=True, text=True, + ) + + +def sign_package(pkg: Path, cfg: SignCfg) -> subprocess.CompletedProcess: + if pkg.suffix == ".rpm": + return sign_rpm(pkg, cfg) + if pkg.suffix == ".deb": + return sign_deb(pkg, cfg) + raise ValueError(f"unsupported package type: {pkg}") + + +def verify_signature(pkg: Path, *, gnupghome: Path, expected_fp: str): + print(f"Verifying {pkg.resolve()}") + suf = pkg.suffix.lower() + if suf == ".rpm": + return verify_rpm_signature(pkg, gnupghome=gnupghome, expected_fp=expected_fp) + elif suf == ".deb": + return verify_deb_signature(pkg, gnupghome=gnupghome, expected_fp=expected_fp) + else: + raise ValueError(f"unsupported package type: {pkg}") + + +def verify_deb_signature(pkg: Path, gnupghome: Path, expected_fp: str) -> None: + pkg = Path(pkg) + sig = pkg.with_suffix(pkg.suffix + ".asc") + env = {**os.environ, "GNUPGHOME": str(gnupghome)} + VALIDSIG_RE = re.compile(r"\[GNUPG:\]\s+VALIDSIG\s+([0-9A-Fa-f]{40})") + verify_cmd = ["gpg", "--batch", "--status-fd", "1", "--verify", str(sig), str(pkg)] + result = subprocess.run(verify_cmd, env=env, text=True, capture_output=True) + + if result.returncode != 0: + print(result.stderr or result.stdout) + sys.exit(result.returncode) + + m = VALIDSIG_RE.search(result.stdout) + if not m or m.group(1).upper() != expected_fp.upper(): + print(f"Signature invalid or wrong signer. Expected {expected_fp}") + sys.exit(result.returncode) + print("********* deb signature verification *********") + print(f"✅ Signature verified for {pkg.name} ({m.group(1)})") + + +def verify_rpm_signature(pkg: Path, *, gnupghome: Path, expected_fp: str): + env = {**os.environ, "GNUPGHOME": str(gnupghome)} + export_cmd = ["gpg", "--batch", "--yes", "--armor", "--export", expected_fp] + cp = subprocess.run(export_cmd, env=env, text=True, capture_output=True, check=True) + rpmdb = Path(tempfile.mkdtemp()) + try: + pub = rpmdb / "pubkey.asc" + pub.write_text(cp.stdout) + # rpm needs the rpmdb for verification + subprocess.run(["rpm", "--dbpath", str(rpmdb), "--import", str(pub)], check=True) + verify_cmd = ["rpm", "--dbpath", str(rpmdb), "-Kv", str(pkg)] + result = subprocess.run(verify_cmd, text=True, capture_output=True) + if result.returncode != 0: + print(result.stdout or result.stderr) + sys.exit(result.returncode) + print("********* rpm signature verification *********") + print(result.stdout) + print(f"✅ Signature verified for {pkg.name}") + return True + finally: + try: + for p in rpmdb.iterdir(): p.unlink() + rpmdb.rmdir() + except Exception: + pass + + +def main(): + set_tty() + GPG_KEY_B64 = os.environ["GPG_KEY_B64"] + GPG_KEY_PASS_B64 = os.environ["GPG_KEY_PASS_B64"] + gpg_passphrase = base64.b64decode(GPG_KEY_PASS_B64).decode("utf-8").strip() + gpg_key = base64.b64decode(GPG_KEY_B64).decode("utf-8").strip() + + parser = argparse.ArgumentParser() + parser.add_argument("package") + args = parser.parse_args() + cfg = make_cfg(passphrase=gpg_passphrase, armored_private_key=gpg_key) + try: + pkg = Path(args.package) + res = sign_package(pkg, cfg) + if res.returncode: + print(res.stderr.strip() or res.stdout.strip()) + raise sys.exit(res.returncode) + verify_signature(pkg, gnupghome=cfg.gnupghome, expected_fp=cfg.fingerprint) + finally: + shutil.rmtree(cfg.gnupghome, ignore_errors=True) + sys.exit(0) + + +if __name__ == "__main__": + main()