diff --git a/.github/workflows/build-pre-commit-image.yml b/.github/workflows/build-pre-commit-image.yml index 1d0a479846..9b20dc7951 100644 --- a/.github/workflows/build-pre-commit-image.yml +++ b/.github/workflows/build-pre-commit-image.yml @@ -7,10 +7,12 @@ on: paths: - ".github/workflows/build-pre-commit-image.yml" - "bin/pre-commit/Dockerfile" + - "rust-toolchain.toml" pull_request: paths: - ".github/workflows/build-pre-commit-image.yml" - "bin/pre-commit/Dockerfile" + - "rust-toolchain.toml" workflow_dispatch: concurrency: diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml index 37207c41ef..9970e9a07d 100644 --- a/.github/workflows/pre-commit.yml +++ b/.github/workflows/pre-commit.yml @@ -17,4 +17,4 @@ jobs: uses: XRPLF/actions/.github/workflows/pre-commit.yml@1bde119a1ab71305ba5d3716e7a82cea1c7bdede with: runs_on: ubuntu-latest - container: '{ "image": "ghcr.io/xrplf/xrpld/pre-commit:sha-4c869ee" }' + container: '{ "image": "ghcr.io/xrplf/xrpld/pre-commit:sha-f56b79f" }' diff --git a/.github/workflows/reusable-build-test-config.yml b/.github/workflows/reusable-build-test-config.yml index d4135207fe..69fa7a7bef 100644 --- a/.github/workflows/reusable-build-test-config.yml +++ b/.github/workflows/reusable-build-test-config.yml @@ -223,11 +223,13 @@ jobs: BUILD_TYPE: ${{ inputs.build_type }} CMAKE_TARGET: ${{ inputs.cmake_target }} run: | + set -o pipefail cmake \ --build . \ --config "${BUILD_TYPE}" \ --parallel "${BUILD_NPROC}" \ - --target "${CMAKE_TARGET}" + --target "${CMAKE_TARGET}" \ + 2>&1 | tee "${GITHUB_WORKSPACE}/build.log" - name: Show ccache statistics if: ${{ inputs.ccache_enabled }} @@ -322,7 +324,7 @@ jobs: PRELOAD="" fi - LD_PRELOAD="$PRELOAD" ./xrpld --unittest --unittest-jobs "${BUILD_NPROC}" 2>&1 | tee unittest.log + LD_PRELOAD="$PRELOAD" ./xrpld --unittest --unittest-jobs "${BUILD_NPROC}" 2>&1 | tee "${GITHUB_WORKSPACE}/unittest.log" # Smoke-run every benchmark module with a single repetition to confirm the # benchmarks still build and execute. This is a correctness check, not a @@ -341,25 +343,27 @@ jobs: done < <(find src/benchmarks -type f -perm -u+x -name 'xrpl.bench.*') exit "${rc}" - - name: Show test failure summary - if: ${{ failure() && !inputs.build_only }} - env: - WORKING_DIR: ${{ runner.os == 'Windows' && format('{0}\{1}', env.BUILD_DIR, inputs.build_type) || env.BUILD_DIR }} + - name: Show build/test failure summary + if: ${{ failure() }} run: | - if [ ! -d "${WORKING_DIR}" ]; then - echo "Working directory '${WORKING_DIR}' does not exist." - exit 0 - fi + cd "${GITHUB_WORKSPACE}" - cd "${WORKING_DIR}" - - if [ ! -f unittest.log ]; then - echo "unittest.log not found; embedded tests may not have run." - exit 0 - fi - - if ! grep -E "failed" unittest.log; then - echo "Log present but no failure lines found in unittest.log." + if [ -f unittest.log ]; then + if ! grep -E "failed" unittest.log | grep -vE "^I[0-9]|^[0-9]+> (ERR:|FTL:)"; then + echo "unittest.log present but no failure lines found." + fi + elif [ -f build.log ]; then + # GCC/Clang emit "error:" (covers "fatal error:"); MSVC emits + # "error C####:", "error LNK####:", and "fatal error LNK####:". + # -A6 prints the lines that follow each match (source line, caret, + # notes, and the "N errors generated" tally) to capture the whole + # diagnostic block. + if ! grep -E -A6 "error:|error C[0-9]{4}|error LNK[0-9]{4}|fatal error" build.log; then + echo "build.log present but no compile errors found." + fi + else + echo "unittest.log/build.log not found; something went wrong." + exit 1 fi - name: Debug failure (Linux) if: ${{ failure() && runner.os == 'Linux' && !inputs.build_only }} diff --git a/.github/workflows/reusable-package.yml b/.github/workflows/reusable-package.yml index 6feecbfb75..55bc20dc5c 100644 --- a/.github/workflows/reusable-package.yml +++ b/.github/workflows/reusable-package.yml @@ -30,7 +30,7 @@ jobs: uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: Set up Python - uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 + uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 with: python-version: "3.13" diff --git a/.github/workflows/reusable-strategy-matrix.yml b/.github/workflows/reusable-strategy-matrix.yml index b6091b99d9..de8d9cfc8e 100644 --- a/.github/workflows/reusable-strategy-matrix.yml +++ b/.github/workflows/reusable-strategy-matrix.yml @@ -26,7 +26,7 @@ jobs: uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: Set up Python - uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 + uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 with: python-version: "3.13" diff --git a/bin/pre-commit/Dockerfile b/bin/pre-commit/Dockerfile index fcd07146c3..a96f3e4c25 100644 --- a/bin/pre-commit/Dockerfile +++ b/bin/pre-commit/Dockerfile @@ -10,6 +10,7 @@ ARG DEBIAN_FRONTEND=noninteractive RUN < #include #include +#include #include #include #include @@ -101,6 +102,12 @@ class Invariants_test : public beast::unit_test::Suite return xrpl::test::jtx::testableAmendments() | fixCleanup3_1_3 | fixCleanup3_2_0; } + test::jtx::Env + makeEnv(FeatureBitset features) + { + return {*this, test::jtx::envconfig(), features, nullptr, beast::Severity::Disabled}; + } + /** * Run a specific test case to put the ledger into a state that will be * detected by an invariant. Simulates the actions of a transaction that @@ -130,7 +137,7 @@ class Invariants_test : public beast::unit_test::Suite TxAccount setTxAccount = TxAccount::None) { doInvariantCheck( - test::jtx::Env(*this, defaultAmendments()), + makeEnv(defaultAmendments()), expectLogs, precheck, fee, @@ -1494,7 +1501,7 @@ class Invariants_test : public beast::unit_test::Suite testcase << "PermissionedDomain" + std::string(fixEnabled ? " fix" : ""); doInvariantCheck( - Env(*this, features), + makeEnv(features), {{"permissioned domain with no rules."}}, [](Account const& a1, Account const& a2, ApplyContext& ac) { return createPermissionedDomain(ac, a1, a2, 0).get(); @@ -1507,7 +1514,7 @@ class Invariants_test : public beast::unit_test::Suite static constexpr auto kTooBig = kMaxPermissionedDomainCredentialsArraySize + 1; doInvariantCheck( - Env(*this, features), + makeEnv(features), {{"permissioned domain bad credentials size " + std::to_string(kTooBig)}}, [](Account const& a1, Account const& a2, ApplyContext& ac) { return !!createPermissionedDomain(ac, a1, a2, kTooBig); @@ -1518,7 +1525,7 @@ class Invariants_test : public beast::unit_test::Suite testcase << "PermissionedDomain 3"; doInvariantCheck( - Env(*this, features), + makeEnv(features), {{"permissioned domain credentials aren't sorted"}}, [](Account const& a1, Account const& a2, ApplyContext& ac) { auto slePd = createPermissionedDomain(ac, a1, a2, 0); @@ -1542,7 +1549,7 @@ class Invariants_test : public beast::unit_test::Suite testcase << "PermissionedDomain 4"; doInvariantCheck( - Env(*this, features), + makeEnv(features), {{"permissioned domain credentials aren't unique"}}, [](Account const& a1, Account const& a2, ApplyContext& ac) { auto slePd = createPermissionedDomain(ac, a1, a2, 0); @@ -1565,7 +1572,7 @@ class Invariants_test : public beast::unit_test::Suite testcase << "PermissionedDomain Set 1"; doInvariantCheck( - Env(*this, features), + makeEnv(features), {{"permissioned domain with no rules."}}, [&](Account const& a1, Account const& a2, ApplyContext& ac) { // create PD @@ -1586,7 +1593,7 @@ class Invariants_test : public beast::unit_test::Suite testcase << "PermissionedDomain Set 2"; doInvariantCheck( - Env(*this, features), + makeEnv(features), {{"permissioned domain bad credentials size " + std::to_string(kTooBig)}}, [&](Account const& a1, Account const& a2, ApplyContext& ac) { // create PD @@ -1617,7 +1624,7 @@ class Invariants_test : public beast::unit_test::Suite testcase << "PermissionedDomain Set 3"; doInvariantCheck( - Env(*this, features), + makeEnv(features), {{"permissioned domain credentials aren't sorted"}}, [&](Account const& a1, Account const& a2, ApplyContext& ac) { // create PD @@ -1647,7 +1654,7 @@ class Invariants_test : public beast::unit_test::Suite testcase << "PermissionedDomain Set 4"; doInvariantCheck( - Env(*this, features), + makeEnv(features), {{"permissioned domain credentials aren't unique"}}, [&](Account const& a1, Account const& a2, ApplyContext& ac) { // create PD @@ -1688,7 +1695,7 @@ class Invariants_test : public beast::unit_test::Suite { testcase << "PermissionedDomain set 2 domains "; doInvariantCheck( - Env(*this, features), + makeEnv(features), fixEnabled ? badMoreThan1 : emptyV, [](Account const& a1, Account const& a2, ApplyContext& ac) { createPermissionedDomain(ac, a1, a2); @@ -1734,7 +1741,7 @@ class Invariants_test : public beast::unit_test::Suite { testcase << "PermissionedDomain set 0 domains "; doInvariantCheck( - Env(*this, features), + makeEnv(features), fixEnabled ? badNoDomains : emptyV, [](Account const&, Account const&, ApplyContext&) { return true; }, XRPAmount{}, @@ -1757,7 +1764,7 @@ class Invariants_test : public beast::unit_test::Suite env1.close(); doInvariantCheck( - Env(*this, features), + makeEnv(features), a1, a2, fixEnabled ? badNoDomains : emptyV, @@ -1798,7 +1805,7 @@ class Invariants_test : public beast::unit_test::Suite { testcase << "PermissionedDomain del, create domain "; doInvariantCheck( - Env(*this, features), + makeEnv(features), fixEnabled ? badNotDeleted : emptyV, [](Account const& a1, Account const& a2, ApplyContext& ac) { createPermissionedDomain(ac, a1, a2); @@ -1995,7 +2002,7 @@ class Invariants_test : public beast::unit_test::Suite testcase << "PermissionedDEX" + std::string(fixEnabled ? " fix" : ""); doInvariantCheck( - Env(*this, features), + makeEnv(features), {{"domain doesn't exist"}}, [](Account const& a1, Account const&, ApplyContext& ac) { Keylet const offerKey = keylet::offer(a1.id(), 10); @@ -2022,7 +2029,7 @@ class Invariants_test : public beast::unit_test::Suite // missing domain ID in offer object doInvariantCheck( - Env(*this, features), + makeEnv(features), {{"hybrid offer is malformed"}}, [&](Account const& a1, Account const& a2, ApplyContext& ac) { Keylet const offerKey = keylet::offer(a2.id(), 10); @@ -4342,7 +4349,7 @@ class Invariants_test : public beast::unit_test::Suite }; doInvariantCheck( - Env{*this, defaultAmendments() - fixCleanup3_2_0}, + makeEnv(defaultAmendments() - fixCleanup3_2_0), {}, [](Account const&, Account const&, ApplyContext&) { return true; }, XRPAmount{}, @@ -5468,7 +5475,7 @@ class Invariants_test : public beast::unit_test::Suite // sfHighLimit issue, not the keylet currency). testcase << "overwrite: NoXRPTrustLines" + std::string(fixEnabled ? " fix" : ""); doInvariantCheck( - Env(*this, features), + makeEnv(features), fixEnabled ? std::vector{{"an XRP trust line was created"}} : std::vector{}, [&insertOrderedTrustLinePair](Account const& a1, Account const& a2, ApplyContext& ac) { @@ -5496,7 +5503,7 @@ class Invariants_test : public beast::unit_test::Suite // Regression: bad deep-freeze trust line followed by a valid one. testcase << "overwrite: NoDeepFreeze" + std::string(fixEnabled ? " fix" : ""); doInvariantCheck( - Env(*this, features), + makeEnv(features), fixEnabled ? std::vector{{"a trust line with deep freeze flag without " "normal freeze was created"}} : std::vector{}, @@ -5530,7 +5537,7 @@ class Invariants_test : public beast::unit_test::Suite // still fires ("a MPT issuance was created"). testcase << "overwrite: NoZeroEscrow MPT" + std::string(fixEnabled ? " fix" : ""); doInvariantCheck( - Env(*this, features), + makeEnv(features), fixEnabled ? std::vector{{"escrow specifies invalid amount"}} : std::vector{{"a MPT issuance was created"}}, [](Account const& a1, Account const&, ApplyContext& ac) {