diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 83185d90..806bea73 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -64,9 +64,24 @@ jobs: # Setup language-specific dependencies BEFORE CodeQL init for proper tracing setup - name: Setup Rust - if: matrix.language == 'rust' || matrix.language == 'c-cpp' uses: ./.github/actions/toolchains/rust + - name: Cache cargo + uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2 + with: + shared-key: ${{ runner.os }}-regorus + + - name: Fetch workspace dependencies + run: cargo fetch --locked + + - name: Fetch FFI crate dependencies + if: matrix.language == 'c-cpp' || matrix.language == 'go' || matrix.language == 'csharp' + run: cargo fetch --locked --manifest-path bindings/ffi/Cargo.toml + + - name: Fetch Java crate dependencies + if: matrix.language == 'java-kotlin' + run: cargo fetch --locked --manifest-path bindings/java/Cargo.toml + - name: Setup Python if: matrix.language == 'python' uses: actions/setup-python@v5 @@ -92,6 +107,10 @@ jobs: with: global-json-file: ./bindings/csharp/global.json + - name: Invoke dotnet directly + if: matrix.language == 'csharp' + run: dotnet --info + - name: Setup Node.js if: matrix.language == 'javascript-typescript' uses: actions/setup-node@v4 @@ -132,79 +151,39 @@ jobs: cargo install wasm-pack # Manual build steps for different languages - - name: Build C/C++ FFI bindings + - name: Build C/C++ bindings via xtask if: matrix.language == 'c-cpp' - working-directory: ${{ matrix.working-directory }} run: | - # Build FFI library in no_std mode for embedded/constrained environments - cargo build --release --locked --features "ast,coverage,regorus/opa-no-std" --no-default-features - - # Build the Rust FFI library that provides C-compatible interface - cargo build --release --locked - - # Build C bindings using CMake - cd ../c - mkdir -p build - cd build - cmake .. - make - - # Build C++ bindings using CMake - cd ../../cpp - mkdir -p build - cd build - cmake .. - make - - - name: Build Java bindings + cargo xtask test-c --release --frozen + cargo xtask test-cpp --release --frozen --skip-ffi + cargo xtask test-c-no-std --release --frozen --skip-ffi + + - name: Build Java bindings via xtask if: matrix.language == 'java-kotlin' - working-directory: ${{ matrix.working-directory }} - run: | - # Build the Rust JNI library that provides Java-compatible interface - cargo fetch - cargo build --release --locked - # Compile Java source and create JAR package with Maven - mvn package + run: cargo xtask test-java --release --frozen - - name: Build Go bindings + - name: Build Go bindings via xtask if: matrix.language == 'go' - working-directory: ${{ matrix.working-directory }} - run: | - # Build the FFI library that Go bindings depend on via CGO - cd ../ffi - cargo fetch - cargo build --release --locked - cd ../go - # Download Go dependencies - go mod tidy - # Set up environment for CGO linking to Rust FFI library - export CGO_ENABLED=1 - export LD_LIBRARY_PATH="$(pwd)/../ffi/target/release:$LD_LIBRARY_PATH" - # Build Go packages with verbose output for CodeQL tracing - go build -v ./pkg/regorus - go build -v -o regorus_test . - - - name: Build C# bindings + run: cargo xtask test-go --release --frozen + + - name: Build C# bindings manually if: matrix.language == 'csharp' working-directory: ${{ matrix.working-directory }} run: | + # Temporary workaround: CodeQL's tracer replaces dotnet with a missing shim when cargo xtask test-csharp runs, + # so invoke dotnet directly here until the upstream fix lands. + # Ideal command once fixed: cargo xtask test-csharp --release # Build the FFI library that C# bindings access via P/Invoke cd ../ffi - cargo fetch cargo build --release --locked cd ../csharp # Restore NuGet packages and build .NET assemblies in release mode - # Build the main Regorus library project only (tests require packaged version) dotnet restore Regorus/Regorus.csproj dotnet build Regorus/Regorus.csproj --no-restore /p:Configuration=Release /p:IgnoreMissingArtifacts=true - - name: Build WASM bindings + - name: Build WASM bindings via xtask if: matrix.language == 'javascript-typescript' - working-directory: ${{ matrix.working-directory }} - run: | - # Build WebAssembly module with wasm-pack for Node.js target - cargo fetch - wasm-pack build --target nodejs --release + run: cargo xtask build-wasm --release - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v3 diff --git a/.github/workflows/pr-extensions.yml b/.github/workflows/pr-extensions.yml index c964d527..1c81fb27 100644 --- a/.github/workflows/pr-extensions.yml +++ b/.github/workflows/pr-extensions.yml @@ -21,18 +21,15 @@ jobs: - uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4 - name: Setup Rust toolchain uses: ./.github/actions/toolchains/rust - - name: Build only std - run: cargo build -r --example regorus --no-default-features --features "std,rego-extensions" - - name: Doc Tests - run: cargo test -r --doc --features rego-extensions - - name: Run tests - run: cargo test -r --features rego-extensions - - name: Run example - run: cargo run --example regorus --features rego-extensions -- eval -d examples/server/allowed_server.rego -i examples/server/input.json data.example - - name: Run tests (ACI) - run: cargo test -r --test aci --features rego-extensions - - name: Run tests (KATA) - run: cargo test -r --test kata --features rego-extensions - - name: Run tests (OPA Conformance) + - name: Cache cargo + uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2 + with: + shared-key: ${{ runner.os }}-regorus + - name: Fetch dependencies + run: cargo fetch --locked + - name: Run rego extensions CI suite run: >- - cargo test -r --test opa --features opa-testutil,serde_json/arbitrary_precision,rego-extensions -- $(tr '\n' ' ' < tests/opa.passing) + cargo xtask ci-release --frozen --features rego-extensions + --skip-all-features-build --skip-no-default-features-tests + --skip-azure-policy --skip-azure-rbac + --opa-features "opa-testutil,serde_json/arbitrary_precision,rego-extensions" diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 065fc56b..e86cd48a 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -21,33 +21,11 @@ jobs: - uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4 - name: Setup Rust toolchain uses: ./.github/actions/toolchains/rust - - name: Format Check - run: cargo fmt --check - - name: Fetch - run: cargo fetch - - name: Build (all features) - run: cargo build -r --all-features --frozen - - name: Build - run: cargo build -r --frozen - - name: Test no_std - run: cargo test -r --no-default-features --frozen - - name: Build only std - run: cargo build -r --example regorus --no-default-features --features "std" --frozen - - name: Doc Tests - run: cargo test -r --doc --frozen - - name: Run tests - run: cargo test -r --frozen - - name: Run example - run: cargo run --example regorus --frozen -- eval -d examples/server/allowed_server.rego -i examples/server/input.json data.example - - name: Run tests (ACI) - run: cargo test -r --test aci --frozen - - name: Run tests (KATA) - run: cargo test -r --test kata --frozen - - name: Run tests (OPA Conformance) - run: >- - cargo test -r --test opa --frozen --features opa-testutil,serde_json/arbitrary_precision -- $(tr '\n' ' ' < tests/opa.passing) - - name: Run tests (Azure Policy) - run: >- - cargo test --frozen --features azure_policy - - name: Run tests (Azure RBAC) - run: cargo test -r --frozen --features azure-rbac + - name: Cache cargo + uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2 + with: + shared-key: ${{ runner.os }}-regorus + - name: Fetch dependencies + run: cargo fetch --locked + - name: Run release CI suite + run: cargo xtask ci-release --frozen diff --git a/.github/workflows/publish-java.yml b/.github/workflows/publish-java.yml index b9833026..53c88bc6 100644 --- a/.github/workflows/publish-java.yml +++ b/.github/workflows/publish-java.yml @@ -48,7 +48,8 @@ jobs: python-version: "3.11" - if: ${{ matrix.build_cmd == 'zigbuild' }} run: pip install cargo-zigbuild - - run: cargo fetch + - run: cargo fetch --locked + - run: cargo fetch --locked --manifest-path bindings/java/Cargo.toml - run: cargo ${{ matrix.build_cmd || 'build' }} --release --frozen --target ${{ matrix.target }}${{ matrix.glibc && format('.{0}', matrix.glibc) || '' }} --manifest-path ./bindings/java/Cargo.toml - run: mkdir -p native/${{ matrix.target }} - run: mv target/${{ matrix.target }}/release/*.${{ matrix.extension }} ./native/${{ matrix.target }}/ diff --git a/.github/workflows/publish-python.yml b/.github/workflows/publish-python.yml index aa1fc5da..778348c3 100644 --- a/.github/workflows/publish-python.yml +++ b/.github/workflows/publish-python.yml @@ -26,7 +26,7 @@ jobs: - name: Build Python extension run: | - cargo fetch + cargo fetch --locked cargo clippy --all-targets --no-deps -- -Dwarnings cargo build --release --target ${{ matrix.target }} --frozen working-directory: bindings/python @@ -59,7 +59,7 @@ jobs: - name: Build Python extension run: | - cargo fetch + cargo fetch --locked cargo clippy --all-targets --no-deps -- -Dwarnings cargo build --release --target ${{ matrix.host.target }} --frozen working-directory: bindings/python @@ -90,7 +90,7 @@ jobs: - name: Build Python extension run: | - cargo fetch + cargo fetch --locked cargo clippy --all-targets --no-deps -- -Dwarnings cargo build --release --target ${{ matrix.host.target }} --frozen working-directory: bindings/python diff --git a/.github/workflows/rust-clippy.yml b/.github/workflows/rust-clippy.yml index 3a4b73c9..13ba0a9c 100644 --- a/.github/workflows/rust-clippy.yml +++ b/.github/workflows/rust-clippy.yml @@ -34,20 +34,19 @@ jobs: - name: Setup Rust toolchain uses: ./.github/actions/toolchains/rust + - name: Cache cargo + uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2 + with: + shared-key: ${{ runner.os }}-regorus - name: Install required cargo run: cargo install clippy-sarif sarif-fmt - name: Fetch - run: cargo fetch + run: cargo fetch --locked - name: Run rust-clippy - run: - cargo clippy - --all-features - --frozen - --message-format=json | clippy-sarif | tee rust-clippy-results.sarif | sarif-fmt - + run: cargo xtask clippy --sarif rust-clippy-results.sarif continue-on-error: true - name: Upload analysis results to GitHub diff --git a/.github/workflows/test-c-cpp.yml b/.github/workflows/test-c-cpp.yml index 5389ab31..d8ee86d2 100644 --- a/.github/workflows/test-c-cpp.yml +++ b/.github/workflows/test-c-cpp.yml @@ -19,36 +19,24 @@ jobs: fetch-depth: 0 - uses: ./.github/actions/toolchains/rust + - name: Cache cargo + uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2 + with: + shared-key: ${{ runner.os }}-regorus + - name: Fetch dependencies + run: cargo fetch --locked + + - name: Fetch FFI crate dependencies + run: cargo fetch --locked --manifest-path bindings/ffi/Cargo.toml - name: Setup gcc, g++, cmake, ninja run: sudo apt update && sudo apt install -y gcc g++ cmake ninja-build - - name: Workaround to ensure that regorus.h is generated - run: | - cargo fetch - cargo build -r --frozen - working-directory: ./bindings/ffi - - - name: Test c binding - run: | - mkdir bindings/c/build - cd bindings/c/build - cmake -G Ninja .. - ninja - ./regorus_test - - - name: Test c-nostd binding - run: | - mkdir bindings/c-nostd/build - cd bindings/c-nostd/build - cmake -G Ninja .. - ninja - ./regorus_test - - - name: Test cpp binding - run: | - mkdir bindings/cpp/build - cd bindings/cpp/build - cmake -G Ninja .. - ninja - ./regorus_test + - name: Test C binding via xtask + run: cargo xtask test-c --release --frozen + + - name: Test C (no-std) binding via xtask + run: cargo xtask test-c-nostd --release --frozen --skip-ffi + + - name: Test C++ binding via xtask + run: cargo xtask test-cpp --release --frozen --skip-ffi diff --git a/.github/workflows/test-csharp.yml b/.github/workflows/test-csharp.yml index d0e2d433..555bdfce 100644 --- a/.github/workflows/test-csharp.yml +++ b/.github/workflows/test-csharp.yml @@ -42,22 +42,18 @@ jobs: with: fetch-depth: 0 - uses: ./.github/actions/toolchains/rust - - - name: Fetch crates - run: cargo fetch - working-directory: ./bindings/ffi - - - name: Check Regorus binding formatting - run: cargo fmt --check - working-directory: ./bindings/ffi + - name: Cache cargo + uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2 + with: + shared-key: ${{ runner.os }}-regorus + - name: Fetch dependencies + run: cargo fetch --locked - - name: Check Clippy linting for Regorus binding - run: cargo clippy --frozen -- -D warnings - working-directory: ./bindings/ffi + - name: Fetch FFI crate dependencies + run: cargo fetch --locked --manifest-path bindings/ffi/Cargo.toml --target ${{ matrix.runtime.target }} - - name: Build Regorus binding - run: cargo build -r --target ${{ matrix.runtime.target }} --locked - working-directory: ./bindings/ffi + - name: Build Regorus FFI via xtask + run: cargo xtask build-ffi --release --target ${{ matrix.runtime.target }} - name: Upload regorus ffi shared library uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 @@ -68,7 +64,7 @@ jobs: if-no-files-found: error retention-days: 1 - build-nuget: + build-csharp: name: 'Build Regorus nuget' runs-on: ubuntu-latest needs: build-ffi @@ -77,12 +73,21 @@ jobs: uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4 with: fetch-depth: 0 + - uses: ./.github/actions/toolchains/rust - uses: actions/setup-dotnet@3e891b0cb619bf60e2c25674b222b8940e2c1c25 # v4.1.0 with: global-json-file: ./bindings/csharp/global.json - run: echo '${{ steps.stepid.outputs.dotnet-version }}' + - name: Cache cargo + uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2 + with: + shared-key: ${{ runner.os }}-regorus + + - name: Fetch dependencies + run: cargo fetch --locked + - name: Download regorus ffi shared libraries uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0 with: @@ -93,14 +98,8 @@ jobs: - name: Display regorus ffi artifacts run: ls -R ./bindings/csharp/Regorus/tmp - # Note that we need to supply the target folder within the folder where artifacts are downloaded. - - name: Build Regorus binding - run: dotnet build /p:Configuration=Release /p:RegorusFFIArtifactsDir=./tmp/bindings/ffi/target - working-directory: ./bindings/csharp/Regorus - - - name: Pack - run: dotnet pack /p:RegorusFFIArtifactsDir=./tmp/bindings/ffi/target - working-directory: ./bindings/csharp/Regorus + - name: Build Regorus nuget via xtask + run: cargo xtask build-csharp --release --artifacts-dir ./bindings/csharp/Regorus/tmp/bindings/ffi/target --enforce-artifacts - name: Upload Regorus nuget uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 @@ -112,7 +111,7 @@ jobs: test-nuget: name: 'Test Regorus Nuget: (${{ matrix.runtime.target }})' - needs: build-nuget + needs: build-csharp runs-on: ${{ matrix.runtime.os }} strategy: # let us get failures from other jobs even if one fails @@ -131,48 +130,32 @@ jobs: with: fetch-depth: 0 + - uses: ./.github/actions/toolchains/rust + - uses: actions/setup-dotnet@3e891b0cb619bf60e2c25674b222b8940e2c1c25 # v4.1.0 with: global-json-file: ./bindings/csharp/global.json - run: echo '${{ steps.stepid.outputs.dotnet-version }}' + - name: Cache cargo + uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2 + with: + shared-key: ${{ runner.os }}-regorus + + - name: Fetch dependencies + run: cargo fetch --locked + - name: Download regorus nuget uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0 with: name: regorus-nuget - path: ./bindings/csharp/regorus-nuget/ - - - name: Restore Regorus.Tests - run: dotnet restore /p:RestoreAdditionalProjectSources=../regorus-nuget - working-directory: ./bindings/csharp/Regorus.Tests - - - name: Run Regorus.Tests - run: dotnet test --no-restore - working-directory: ./bindings/csharp/Regorus.Tests - - - name: Restore TestApp - run: dotnet restore /p:RestoreAdditionalProjectSources=../regorus-nuget - working-directory: ./bindings/csharp/TestApp - - - name: Build TestApp - run: dotnet build --no-restore - working-directory: ./bindings/csharp/TestApp - - - name: Run TestApp - run: dotnet run --no-build --framework net8.0 - working-directory: ./bindings/csharp/TestApp - - - name: Restore TargetExampleApp - run: dotnet restore /p:RestoreAdditionalProjectSources=../regorus-nuget - working-directory: ./bindings/csharp/TargetExampleApp + path: ./bindings/csharp/Regorus/bin/Release - - name: Build TargetExampleApp - run: dotnet build --no-restore - working-directory: ./bindings/csharp/TargetExampleApp + - name: Display regorus nuget + run: ls -R ./bindings/csharp/Regorus/bin/Release - - name: Run TargetExampleApp - run: dotnet run --no-build --framework net8.0 - working-directory: ./bindings/csharp/TargetExampleApp + - name: Run C# tests via xtask + run: cargo xtask test-csharp --release diff --git a/.github/workflows/test-ffi.yml b/.github/workflows/test-ffi.yml index 3d05de43..65755b60 100644 --- a/.github/workflows/test-ffi.yml +++ b/.github/workflows/test-ffi.yml @@ -18,11 +18,15 @@ jobs: with: fetch-depth: 0 - uses: ./.github/actions/toolchains/rust + - name: Cache cargo + uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2 + with: + shared-key: ${{ runner.os }}-regorus + - name: Fetch dependencies + run: cargo fetch --locked + + - name: Fetch FFI crate dependencies + run: cargo fetch --locked --manifest-path bindings/ffi/Cargo.toml - name: Test FFI - run: | - cargo fetch - cargo build -r --frozen - cargo clippy --all-targets --no-deps -- -Dwarnings - cargo test --features contention_checks --frozen - working-directory: ./bindings/ffi + run: cargo xtask test-ffi --release --frozen diff --git a/.github/workflows/test-go.yml b/.github/workflows/test-go.yml index 98f19dda..fb65be30 100644 --- a/.github/workflows/test-go.yml +++ b/.github/workflows/test-go.yml @@ -18,19 +18,19 @@ jobs: with: fetch-depth: 0 - uses: ./.github/actions/toolchains/rust - - + - name: Cache cargo + uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2 + with: + shared-key: ${{ runner.os }}-regorus + - name: Fetch dependencies + run: cargo fetch --locked + + - name: Fetch FFI crate dependencies + run: cargo fetch --locked --manifest-path bindings/ffi/Cargo.toml + - uses: actions/setup-go@41dfa10bad2bb2ae585af6ee5bb4d7d973ad74ed # v5.1.0 with: architecture: x64 - - name: Build ffi - run: cargo build -r - working-directory: ./bindings/ffi - - - name: Test go - run: | - go mod tidy - go build - LD_LIBRARY_PATH=../ffi/target/release ./regorus_test - working-directory: ./bindings/go + - name: Test Go binding via xtask + run: cargo xtask test-go --release --frozen diff --git a/.github/workflows/test-java.yml b/.github/workflows/test-java.yml index a498d6d4..4c64da34 100644 --- a/.github/workflows/test-java.yml +++ b/.github/workflows/test-java.yml @@ -23,25 +23,15 @@ jobs: java-version: 8 distribution: "corretto" - uses: ./.github/actions/toolchains/rust + - name: Cache cargo + uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2 + with: + shared-key: ${{ runner.os }}-regorus + - name: Fetch dependencies + run: cargo fetch --locked - - name: Building binding - run: | - cargo clippy --all-targets --no-deps -- -Dwarnings - cargo build --release --manifest-path bindings/java/Cargo.toml --locked - - - name: Capture binding version - run: | - version=$(cargo metadata --manifest-path bindings/java/Cargo.toml --format-version 1 \ - | jq -r '.packages[] | select(.name == "regorus-java") | .version') - echo "REGORUS_JAVA_VERSION=$version" >> "$GITHUB_ENV" - - - name: Build jar - run: mvn package - working-directory: ./bindings/java + - name: Fetch Java crate dependencies + run: cargo fetch --locked --manifest-path bindings/java/Cargo.toml - - name: Test jar - run: | - jar="regorus-java-${REGORUS_JAVA_VERSION}.jar" - javac -cp "target/${jar}" Test.java - java -Djava.library.path=target/release -cp "target/${jar}:." Test - working-directory: ./bindings/java + - name: Run Java smoke tests via xtask + run: cargo xtask test-java --release --frozen diff --git a/.github/workflows/test-musl.yml b/.github/workflows/test-musl.yml index 1e30d104..be7c4a50 100644 --- a/.github/workflows/test-musl.yml +++ b/.github/workflows/test-musl.yml @@ -22,18 +22,15 @@ jobs: - uses: ./.github/actions/toolchains/rust with: targets: x86_64-unknown-linux-musl + - name: Cache cargo + uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2 + with: + shared-key: ${{ runner.os }}-regorus + - name: Fetch dependencies + run: cargo fetch --locked + - name: Fetch MUSL target dependencies + run: cargo fetch --locked --target x86_64-unknown-linux-musl - name: Install musl-gcc run: sudo apt update && sudo apt install -y musl-tools - - name: Fetch - run: cargo fetch - - name: Build (MUSL) - run: cargo build --verbose --all-targets --target x86_64-unknown-linux-musl --frozen - - name: Run tests (MUSL) - run: cargo test -r --verbose --target x86_64-unknown-linux-musl --frozen - - name: Run tests (MUSL ACI) - run: cargo test -r --test aci --target x86_64-unknown-linux-musl --frozen - - name: Run tests (KATA ACI) - run: cargo test -r --test kata --target x86_64-unknown-linux-musl --frozen - - name: Run tests (MUSL OPA Conformance) - run: >- - cargo test -r --test opa --frozen --features opa-testutil,serde_json/arbitrary_precision --target x86_64-unknown-linux-musl -- $(tr '\n' ' ' < tests/opa.passing) + - name: Run MUSL suite via xtask + run: cargo xtask test-musl --release --frozen --target x86_64-unknown-linux-musl diff --git a/.github/workflows/test-no-std.yml b/.github/workflows/test-no-std.yml index 8e35b63d..b6398fb8 100644 --- a/.github/workflows/test-no-std.yml +++ b/.github/workflows/test-no-std.yml @@ -22,9 +22,14 @@ jobs: - uses: ./.github/actions/toolchains/rust with: targets: thumbv7m-none-eabi - - name: Fetch - run: cargo fetch - - name: Build - run: cargo build -r --target thumbv7m-none-eabi --frozen - working-directory: ./tests/ensure_no_std + - name: Cache cargo + uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2 + with: + shared-key: ${{ runner.os }}-regorus + - name: Fetch dependencies + run: cargo fetch --locked + - name: Fetch ensure_no_std crate dependencies + run: cargo fetch --locked --manifest-path tests/ensure_no_std/Cargo.toml --target thumbv7m-none-eabi + - name: Test no-std + run: cargo xtask test-no-std --release --frozen diff --git a/.github/workflows/test-python.yml b/.github/workflows/test-python.yml index d459fe72..7c5e0f1d 100644 --- a/.github/workflows/test-python.yml +++ b/.github/workflows/test-python.yml @@ -9,9 +9,6 @@ on: # Run at 8:00 AM every day - cron: "0 8 * * *" -env: - PYTHON_VERSION: "3.10" - jobs: build: strategy: @@ -28,66 +25,65 @@ jobs: with: fetch-depth: 0 - uses: ./.github/actions/toolchains/rust + with: + targets: ${{ matrix.host.target }} + - name: Cache cargo + uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2 + with: + shared-key: ${{ runner.os }}-regorus + - name: Fetch dependencies + run: cargo fetch --locked + + - name: Fetch Python crate dependencies + run: cargo fetch --locked --manifest-path bindings/python/Cargo.toml --target ${{ matrix.host.target }} - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 with: - python-version: ${{ env.PYTHON_VERSION }} + python-version: "3.10" architecture: x64 - - name: Build Python extension - run: | - cargo fetch - cargo clippy --all-targets --no-deps -- -Dwarnings - cargo build --release --target ${{ matrix.host.target }} --frozen - working-directory: bindings/python + - name: Install maturin + run: python -m pip install maturin==1.5.1 - - name: Build Wheel - uses: PyO3/maturin-action@63b75c597b83e247fbf4fb7719801cc4220ae9f3 # v1.43.0 - with: - target: x86_64 - args: --release --out dist --manifest-path bindings/python/Cargo.toml --offline --strip - sccache: 'true' + - name: Build Python wheel via xtask + run: cargo xtask build-python --release --target ${{ matrix.host.target }} --target-dir bindings/python/dist --frozen - - name: Upload Wheel + - name: Upload wheel artefacts uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: name: regorus-wheel-${{ matrix.host.name }} - path: dist/regorus-*.whl + path: bindings/python/dist/regorus-*.whl test: + needs: build strategy: matrix: + host: [ubuntu-24.04, ubuntu-22.04, windows-latest] python-version: ["3.10", "3.11", "3.12", "3.13"] - host: - - name: ubuntu-24.04 - wheel: regorus-0.5.0-cp310-abi3-manylinux_2_34_x86_64.whl - - name: ubuntu-22.04 - wheel: regorus-0.5.0-cp310-abi3-manylinux_2_34_x86_64.whl - - name: windows-latest - wheel: regorus-0.5.0-cp310-abi3-win_amd64.whl - - needs: build - runs-on: ${{ matrix.host.name }} + runs-on: ${{ matrix.host }} steps: - name: Checkout repository uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4 with: fetch-depth: 0 - - - name: Download Regorus wheel - uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0 + - uses: ./.github/actions/toolchains/rust + - name: Cache cargo + uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2 with: - path: wheels - pattern: regorus-wheel-* - merge-multiple: true + shared-key: ${{ runner.os }}-regorus + - name: Fetch dependencies + run: cargo fetch --locked + + - name: Fetch Python crate dependencies + run: cargo fetch --locked --manifest-path bindings/python/Cargo.toml - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 with: python-version: ${{ matrix.python-version }} architecture: x64 - - name: Test Wheel - run: | - pip3 install ../../wheels/${{ matrix.host.wheel }} - python3 test.py - working-directory: bindings/python + - name: Install maturin + run: python -m pip install maturin==1.5.1 + + - name: Run Python smoke tests via xtask + run: cargo xtask test-python --release --python python diff --git a/.github/workflows/test-ruby.yml b/.github/workflows/test-ruby.yml index bde2c261..87f0f357 100644 --- a/.github/workflows/test-ruby.yml +++ b/.github/workflows/test-ruby.yml @@ -27,10 +27,16 @@ jobs: cargo-cache: true working-directory: "bindings/ruby" + - name: Cache cargo + uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2 + with: + shared-key: ${{ runner.os }}-regorus + + - name: Fetch dependencies + run: cargo fetch --locked + + - name: Fetch Ruby crate dependencies + run: cargo fetch --locked --manifest-path bindings/ruby/Cargo.toml + - name: Run ruby tests - run: | - cd bindings/ruby - gem install bundler - bundle install - cargo clippy --all-targets --no-deps -- -Dwarnings - bundle exec rake + run: cargo xtask test-ruby --release --frozen diff --git a/.github/workflows/test-wasm.yml b/.github/workflows/test-wasm.yml index 3a27b0b7..e70ebfab 100644 --- a/.github/workflows/test-wasm.yml +++ b/.github/workflows/test-wasm.yml @@ -20,6 +20,15 @@ jobs: - name: Setup Rust toolchain uses: ./.github/actions/toolchains/rust + - name: Cache cargo + uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2 + with: + shared-key: ${{ runner.os }}-regorus + - name: Fetch dependencies + run: cargo fetch --locked + + - name: Fetch WASM crate dependencies + run: cargo fetch --locked --manifest-path bindings/wasm/Cargo.toml - name: Setup Node uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 @@ -29,11 +38,5 @@ jobs: - name: Install wasmlpack run: cargo install wasm-pack - - name: Test wasm binding - run: | - cd bindings/wasm - cargo fetch - cargo clippy --all-targets --no-deps -- -Dwarnings - wasm-pack build --target nodejs --release - wasm-pack test --release --node - node test.js + - name: Test wasm binding via xtask + run: cargo xtask test-wasm --release --frozen --node node diff --git a/.github/workflows/tests-debug.yml b/.github/workflows/tests-debug.yml index 889c5118..8d1891c2 100644 --- a/.github/workflows/tests-debug.yml +++ b/.github/workflows/tests-debug.yml @@ -21,26 +21,11 @@ jobs: - uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4 - name: Setup Rust toolchain uses: ./.github/actions/toolchains/rust - - name: Fetch - run: cargo fetch - - name: Build (all features) - run: cargo build --all-features --frozen - - name: Build - run: cargo build --frozen - - name: Test no_std - run: cargo test --no-default-features --frozen - - name: Build only std - run: cargo build --example regorus --no-default-features --features "std" --frozen - - name: Doc Tests - run: cargo test --doc --frozen - - name: Run tests - run: cargo test --frozen - - name: Run tests (ACI) - run: cargo test --test aci --frozen - - name: Run tests (KATA) - run: cargo test --test kata --frozen - - name: Run tests (OPA Conformance) - run: >- - cargo test --test opa --frozen --features opa-testutil,serde_json/arbitrary_precision -- $(tr '\n' ' ' < tests/opa.passing) - - name: Run tests (Azure RBAC) - run: cargo test --frozen --features azure-rbac + - name: Cache cargo + uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2 + with: + shared-key: ${{ runner.os }}-regorus + - name: Fetch dependencies + run: cargo fetch --locked + - name: Run debug CI suite + run: cargo xtask ci-debug --frozen diff --git a/Cargo.lock b/Cargo.lock index 8038fb62..35625ad7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,12 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + [[package]] name = "ahash" version = "0.8.12" @@ -166,9 +172,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.19.0" +version = "3.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" +checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510" [[package]] name = "bytecount" @@ -176,6 +182,12 @@ version = "0.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "175812e0be2bccb6abe50bb8d566126198344f707e304f45c648fd8f2cc0365e" +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + [[package]] name = "cast" version = "0.3.0" @@ -184,9 +196,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.2.47" +version = "1.2.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd405d82c84ff7f35739f175f67d8b9fb7687a0e84ccdc78bd3568839827cf07" +checksum = "cd4932aefd12402b36c60956a4fe0035421f544799057659ff86f923657aada3" dependencies = [ "find-msvc-tools", "shlex", @@ -250,9 +262,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.53" +version = "4.5.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9e340e012a1bf4935f5282ed1436d1489548e8f72308207ea5df0e23d2d03f8" +checksum = "c6e6ff9dcd79cff5cd969a17a545d79e84ab086e444102a591e288a8aa3ce394" dependencies = [ "clap_builder", "clap_derive", @@ -260,9 +272,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.53" +version = "4.5.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d76b5d13eaa18c901fd2f7fca939fefe3a0727a953561fefdf3b2922b8569d00" +checksum = "fa42cf4d2b7a41bc8f663a7cab4031ebafa1bf3875705bfaf8466dc60ab52c00" dependencies = [ "anstream", "anstyle", @@ -277,16 +289,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a0b5487afeab2deb2ff4e03a807ad1a03ac532ff5a2cee5d86884440c7f7671" dependencies = [ "heck", - "proc-macro2 1.0.103", - "quote 1.0.42", - "syn 2.0.111", + "proc-macro2 1.0.105", + "quote 1.0.43", + "syn 2.0.114", ] [[package]] name = "clap_lex" -version = "0.7.6" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d" +checksum = "c3e64b0cc0439b12df2fa678eae89a1c56a529fd067a9115f7827f1fffd22b32" [[package]] name = "colorchoice" @@ -300,6 +312,15 @@ version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" +[[package]] +name = "crc32fast" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" +dependencies = [ + "cfg-if", +] + [[package]] name = "criterion" version = "0.8.1" @@ -382,9 +403,9 @@ dependencies = [ [[package]] name = "data-encoding" -version = "2.9.0" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476" +checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea" [[package]] name = "displaydoc" @@ -392,9 +413,9 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ - "proc-macro2 1.0.103", - "quote 1.0.42", - "syn 2.0.111", + "proc-macro2 1.0.105", + "quote 1.0.43", + "syn 2.0.114", ] [[package]] @@ -439,9 +460,19 @@ dependencies = [ [[package]] name = "find-msvc-tools" -version = "0.1.5" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844" +checksum = "f449e6c6c08c865631d4890cfacf252b3d396c9bcc83adb6623cdb02a8336c41" + +[[package]] +name = "flate2" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b375d6465b98090a5f25b1c7703f3859783755aa9a80433b36e0379a3ec2f369" +dependencies = [ + "crc32fast", + "miniz_oxide", +] [[package]] name = "fluent-uri" @@ -610,9 +641,9 @@ checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" [[package]] name = "icu_properties" -version = "2.1.1" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e93fcd3157766c0c8da2f8cff6ce651a31f0810eaa1c51ec363ef790bbb5fb99" +checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec" dependencies = [ "icu_collections", "icu_locale_core", @@ -624,9 +655,9 @@ dependencies = [ [[package]] name = "icu_properties_data" -version = "2.1.1" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02845b3647bb045f1100ecd6480ff52f34c35f82d9880e029d329c21d1054899" +checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af" [[package]] name = "icu_provider" @@ -666,9 +697,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.12.1" +version = "2.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2" +checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" dependencies = [ "equivalent", "hashbrown 0.16.1", @@ -699,15 +730,15 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.15" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" +checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" [[package]] name = "js-sys" -version = "0.3.82" +version = "0.3.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b011eec8cc36da2aab2d5cff675ec18454fad408585853910a202391cf9f8e65" +checksum = "464a3709c7f55f1f721e5389aa6ea4e3bc6aba669353300af094b29ffbdde1d8" dependencies = [ "once_cell", "wasm-bindgen", @@ -750,9 +781,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.177" +version = "0.2.180" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" +checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc" [[package]] name = "litemap" @@ -771,9 +802,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.28" +version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" [[package]] name = "memchr" @@ -795,6 +826,16 @@ dependencies = [ "cc", ] +[[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", + "simd-adler32", +] + [[package]] name = "msvc_spectre_libs" version = "0.1.3" @@ -1046,9 +1087,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.103" +version = "1.0.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" +checksum = "535d180e0ecab6268a3e718bb9fd44db66bbbc256257165fc699dadf70d16fe7" dependencies = [ "unicode-ident", ] @@ -1064,11 +1105,11 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.42" +version = "1.0.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" +checksum = "dc74d9a594b72ae6656596548f56f667211f8a97b3d4c3d467150794690dc40a" dependencies = [ - "proc-macro2 1.0.103", + "proc-macro2 1.0.105", ] [[package]] @@ -1099,9 +1140,9 @@ dependencies = [ [[package]] name = "rand_core" -version = "0.9.3" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +checksum = "4f1b3bc831f92381018fd9c6350b917c7b21f1eed35a65a51900e0e55a3d7afa" dependencies = [ "getrandom", ] @@ -1150,9 +1191,9 @@ version = "1.0.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" dependencies = [ - "proc-macro2 1.0.103", - "quote 1.0.42", - "syn 2.0.111", + "proc-macro2 1.0.105", + "quote 1.0.43", + "syn 2.0.114", ] [[package]] @@ -1243,9 +1284,9 @@ checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" [[package]] name = "ryu" -version = "1.0.20" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" +checksum = "a50f4cf475b65d88e057964e0e9bb1f0aa9bbb2036dc65c64596b42932536984" [[package]] name = "same-file" @@ -1293,22 +1334,22 @@ version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ - "proc-macro2 1.0.103", - "quote 1.0.42", - "syn 2.0.111", + "proc-macro2 1.0.105", + "quote 1.0.43", + "syn 2.0.114", ] [[package]] name = "serde_json" -version = "1.0.145" +version = "1.0.149" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" dependencies = [ "itoa", "memchr", - "ryu", "serde", "serde_core", + "zmij", ] [[package]] @@ -1330,6 +1371,12 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +[[package]] +name = "simd-adler32" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2" + [[package]] name = "siphasher" version = "1.0.1" @@ -1373,12 +1420,12 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.111" +version = "2.0.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87" +checksum = "d4d107df263a3013ef9b1879b0df87d706ff80f65a86ea879bd9c31f9b307c2a" dependencies = [ - "proc-macro2 1.0.103", - "quote 1.0.42", + "proc-macro2 1.0.105", + "quote 1.0.43", "unicode-ident", ] @@ -1388,9 +1435,9 @@ version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ - "proc-macro2 1.0.103", - "quote 1.0.42", - "syn 2.0.111", + "proc-macro2 1.0.105", + "quote 1.0.43", + "syn 2.0.114", ] [[package]] @@ -1420,9 +1467,9 @@ version = "2.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" dependencies = [ - "proc-macro2 1.0.103", - "quote 1.0.42", - "syn 2.0.111", + "proc-macro2 1.0.105", + "quote 1.0.43", + "syn 2.0.114", ] [[package]] @@ -1495,9 +1542,9 @@ checksum = "6d49784317cd0d1ee7ec5c716dd598ec5b4483ea832a2dced265471cc0f690ae" [[package]] name = "url" -version = "2.5.7" +version = "2.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08bc136a29a3d1758e07a9cca267be308aeebf5cfd5a10f3f67ab2097683ef5b" +checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" dependencies = [ "form_urlencoded", "idna", @@ -1519,9 +1566,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.18.1" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f87b8aa10b915a06587d0dec516c282ff295b475d94abf425d62b57710070a2" +checksum = "e2e054861b4bd027cd373e18e8d8d8e6548085000e41290d95ce0c373a654b4a" dependencies = [ "getrandom", "js-sys", @@ -1573,9 +1620,9 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.105" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da95793dfc411fbbd93f5be7715b0578ec61fe87cb1a42b12eb625caa5c5ea60" +checksum = "0d759f433fa64a2d763d1340820e46e111a7a5ab75f993d1852d70b03dbb80fd" dependencies = [ "cfg-if", "once_cell", @@ -1586,41 +1633,41 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.105" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04264334509e04a7bf8690f2384ef5265f05143a4bff3889ab7a3269adab59c2" +checksum = "48cb0d2638f8baedbc542ed444afc0644a29166f1595371af4fecf8ce1e7eeb3" dependencies = [ - "quote 1.0.42", + "quote 1.0.43", "wasm-bindgen-macro-support", ] [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.105" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "420bc339d9f322e562942d52e115d57e950d12d88983a14c79b86859ee6c7ebc" +checksum = "cefb59d5cd5f92d9dcf80e4683949f15ca4b511f4ac0a6e14d4e1ac60c6ecd40" dependencies = [ "bumpalo", - "proc-macro2 1.0.103", - "quote 1.0.42", - "syn 2.0.111", + "proc-macro2 1.0.105", + "quote 1.0.43", + "syn 2.0.114", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.105" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76f218a38c84bcb33c25ec7059b07847d465ce0e0a76b995e134a45adcb6af76" +checksum = "cbc538057e648b67f72a982e708d485b2efa771e1ac05fec311f9f63e5800db4" dependencies = [ "unicode-ident", ] [[package]] name = "web-sys" -version = "0.3.82" +version = "0.3.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a1f95c0d03a47f4ae1f7a64643a6bb97465d9b740f0fa8f90ea33915c99a9a1" +checksum = "9b32828d774c412041098d182a8b38b16ea816958e07cf40eec2bc080ae137ac" dependencies = [ "js-sys", "wasm-bindgen", @@ -1676,9 +1723,9 @@ version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ - "proc-macro2 1.0.103", - "quote 1.0.42", - "syn 2.0.111", + "proc-macro2 1.0.105", + "quote 1.0.43", + "syn 2.0.114", ] [[package]] @@ -1687,9 +1734,9 @@ version = "0.59.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ - "proc-macro2 1.0.103", - "quote 1.0.42", - "syn 2.0.111", + "proc-macro2 1.0.105", + "quote 1.0.43", + "syn 2.0.114", ] [[package]] @@ -1727,9 +1774,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.7.13" +version = "0.7.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" +checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829" dependencies = [ "memchr", ] @@ -1755,6 +1802,7 @@ dependencies = [ "regex", "semver", "toml_edit", + "zip", ] [[package]] @@ -1774,30 +1822,30 @@ version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" dependencies = [ - "proc-macro2 1.0.103", - "quote 1.0.42", - "syn 2.0.111", + "proc-macro2 1.0.105", + "quote 1.0.43", + "syn 2.0.114", "synstructure", ] [[package]] name = "zerocopy" -version = "0.8.28" +version = "0.8.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43fa6694ed34d6e57407afbccdeecfa268c470a7d2a5b0cf49ce9fcc345afb90" +checksum = "668f5168d10b9ee831de31933dc111a459c97ec93225beb307aed970d1372dfd" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.28" +version = "0.8.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c640b22cd9817fae95be82f0d2f90b11f7605f6c319d16705c459b27ac2cbc26" +checksum = "2c7962b26b0a8685668b671ee4b54d007a67d4eaf05fda79ac0ecf41e32270f1" dependencies = [ - "proc-macro2 1.0.103", - "quote 1.0.42", - "syn 2.0.111", + "proc-macro2 1.0.105", + "quote 1.0.43", + "syn 2.0.114", ] [[package]] @@ -1815,9 +1863,9 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ - "proc-macro2 1.0.103", - "quote 1.0.42", - "syn 2.0.111", + "proc-macro2 1.0.105", + "quote 1.0.43", + "syn 2.0.114", "synstructure", ] @@ -1849,7 +1897,25 @@ version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" dependencies = [ - "proc-macro2 1.0.103", - "quote 1.0.42", - "syn 2.0.111", + "proc-macro2 1.0.105", + "quote 1.0.43", + "syn 2.0.114", ] + +[[package]] +name = "zip" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "760394e246e4c28189f19d488c058bf16f564016aefac5d32bb1f3b51d5e9261" +dependencies = [ + "byteorder", + "crc32fast", + "crossbeam-utils", + "flate2", +] + +[[package]] +name = "zmij" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd8f3f50b848df28f887acb68e41201b5aea6bc8a8dacc00fb40635ff9a72fea" diff --git a/README.md b/README.md index cbad5742..ab1326b9 100644 --- a/README.md +++ b/README.md @@ -274,6 +274,19 @@ Benchmark 1: opa eval -b tests/aci -d tests/aci/data.json -i tests/aci/input.jso Range (min … max): 43.8 ms … 46.7 ms 62 runs ``` + +## Contributor Workflow + +Regorus uses a small companion CLI under the `xtask` package to keep CI and local development in sync. +The commands mirror our GitHub Actions jobs, making it easy to dry-run CI steps before sending a pull request. + +- Run the full release pipeline with `cargo xtask ci-release` and the debug checks with `cargo xtask ci-debug`. +- Exercise language bindings through focused helpers such as `cargo xtask test-java --release --frozen` or `cargo xtask test-go`. +- Use `cargo xtask test-musl --release --frozen` for the cross-compilation matrix and `cargo xtask test-no-std` for embedded targets. +- Formatting (`cargo xtask fmt`) and linting (`cargo xtask clippy --sarif`) wrap the usual Cargo tooling while matching CI defaults. + +The workflows in `.github/workflows` invoke the same commands, so keeping local runs green is usually enough to satisfy the checks enforced on `main`. + ## OPA Conformance Regorus has been verified to be compliant with [OPA v1.2.0](https://github.com/open-policy-agent/opa/releases/tag/v1.2.0) diff --git a/bindings/csharp/README.md b/bindings/csharp/README.md index 615ce8f6..7b79e77e 100644 --- a/bindings/csharp/README.md +++ b/bindings/csharp/README.md @@ -29,4 +29,8 @@ Once the workflow run completes, the generated Nuget can be downloaded by follow ## Local -TODO +The `cargo xtask` runner provides helpers for local builds: + +1. `cargo xtask ffi` builds the `bindings/ffi` crate for the host platform in debug mode. Add `--target ` (repeatable) to cross-compile, or `--release` to produce optimised artefacts. Results land under `bindings/ffi/target//`. +2. `cargo xtask nuget` reuses those artefacts to pack the C# library. It defaults to debug builds for the host but accepts `--target`, `--release`, `--artifacts-dir ` to reuse existing binaries, and `--enforce-artifacts` to require every officially supported platform. +3. `cargo xtask test-csharp` ensures a NuGet is available (rebuilding when required or when `--force-nuget` is passed) and then runs `Regorus.Tests`, `TestApp`, and `TargetExampleApp` against it. The command accepts the same build flags as `cargo xtask nuget`. diff --git a/bindings/csharp/Regorus/Regorus.csproj b/bindings/csharp/Regorus/Regorus.csproj index e4964976..fd3ed812 100644 --- a/bindings/csharp/Regorus/Regorus.csproj +++ b/bindings/csharp/Regorus/Regorus.csproj @@ -13,6 +13,10 @@ README.md + + release + + @@ -23,7 +27,7 @@ by the publishing pipeline. For each target triple, `Pack` expects the regorus ffi shared library - to be found in $(RegorusFFIArtifactsDir)//release. + to be found in $(RegorusFFIArtifactsDir)//$(RegorusFFIArtifactsProfile). If $(IgnoreMissingArtifacts) is not set, ensure that the binaries for officially supported platforms exists. --> @@ -31,27 +35,27 @@ - - + + - + - - + + - - + + - + - + diff --git a/bindings/ffi/Cargo.lock b/bindings/ffi/Cargo.lock index 64604e13..3b995fc3 100644 --- a/bindings/ffi/Cargo.lock +++ b/bindings/ffi/Cargo.lock @@ -141,9 +141,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.19.0" +version = "3.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" +checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510" [[package]] name = "bytecount" @@ -172,9 +172,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.47" +version = "1.2.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd405d82c84ff7f35739f175f67d8b9fb7687a0e84ccdc78bd3568839827cf07" +checksum = "cd4932aefd12402b36c60956a4fe0035421f544799057659ff86f923657aada3" dependencies = [ "find-msvc-tools", "shlex", @@ -211,18 +211,18 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.53" +version = "4.5.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9e340e012a1bf4935f5282ed1436d1489548e8f72308207ea5df0e23d2d03f8" +checksum = "c6e6ff9dcd79cff5cd969a17a545d79e84ab086e444102a591e288a8aa3ce394" dependencies = [ "clap_builder", ] [[package]] name = "clap_builder" -version = "4.5.53" +version = "4.5.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d76b5d13eaa18c901fd2f7fca939fefe3a0727a953561fefdf3b2922b8569d00" +checksum = "fa42cf4d2b7a41bc8f663a7cab4031ebafa1bf3875705bfaf8466dc60ab52c00" dependencies = [ "anstream", "anstyle", @@ -232,9 +232,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.7.6" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d" +checksum = "c3e64b0cc0439b12df2fa678eae89a1c56a529fd067a9115f7827f1fffd22b32" [[package]] name = "colorchoice" @@ -270,9 +270,9 @@ dependencies = [ [[package]] name = "data-encoding" -version = "2.9.0" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476" +checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea" [[package]] name = "displaydoc" @@ -329,9 +329,9 @@ checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] name = "find-msvc-tools" -version = "0.1.5" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844" +checksum = "f449e6c6c08c865631d4890cfacf252b3d396c9bcc83adb6623cdb02a8336c41" [[package]] name = "fluent-uri" @@ -477,9 +477,9 @@ checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" [[package]] name = "icu_properties" -version = "2.1.1" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e93fcd3157766c0c8da2f8cff6ce651a31f0810eaa1c51ec363ef790bbb5fb99" +checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec" dependencies = [ "icu_collections", "icu_locale_core", @@ -491,9 +491,9 @@ dependencies = [ [[package]] name = "icu_properties_data" -version = "2.1.1" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02845b3647bb045f1100ecd6480ff52f34c35f82d9880e029d329c21d1054899" +checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af" [[package]] name = "icu_provider" @@ -533,9 +533,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.12.1" +version = "2.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2" +checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" dependencies = [ "equivalent", "hashbrown 0.16.1", @@ -555,15 +555,15 @@ checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" [[package]] name = "itoa" -version = "1.0.15" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" +checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" [[package]] name = "js-sys" -version = "0.3.82" +version = "0.3.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b011eec8cc36da2aab2d5cff675ec18454fad408585853910a202391cf9f8e65" +checksum = "464a3709c7f55f1f721e5389aa6ea4e3bc6aba669353300af094b29ffbdde1d8" dependencies = [ "once_cell", "wasm-bindgen", @@ -603,9 +603,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.177" +version = "0.2.180" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" +checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc" [[package]] name = "linux-raw-sys" @@ -630,9 +630,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.28" +version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" [[package]] name = "memchr" @@ -827,18 +827,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.103" +version = "1.0.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" +checksum = "535d180e0ecab6268a3e718bb9fd44db66bbbc256257165fc699dadf70d16fe7" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.42" +version = "1.0.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" +checksum = "dc74d9a594b72ae6656596548f56f667211f8a97b3d4c3d467150794690dc40a" dependencies = [ "proc-macro2", ] @@ -871,9 +871,9 @@ dependencies = [ [[package]] name = "rand_core" -version = "0.9.3" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +checksum = "4f1b3bc831f92381018fd9c6350b917c7b21f1eed35a65a51900e0e55a3d7afa" dependencies = [ "getrandom", ] @@ -991,9 +991,9 @@ dependencies = [ [[package]] name = "rustix" -version = "1.1.2" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" +checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34" dependencies = [ "bitflags", "errno", @@ -1010,9 +1010,9 @@ checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" [[package]] name = "ryu" -version = "1.0.20" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" +checksum = "a50f4cf475b65d88e057964e0e9bb1f0aa9bbb2036dc65c64596b42932536984" [[package]] name = "scopeguard" @@ -1058,15 +1058,15 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.145" +version = "1.0.149" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" dependencies = [ "itoa", "memchr", - "ryu", "serde", "serde_core", + "zmij", ] [[package]] @@ -1123,9 +1123,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "syn" -version = "2.0.111" +version = "2.0.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87" +checksum = "d4d107df263a3013ef9b1879b0df87d706ff80f65a86ea879bd9c31f9b307c2a" dependencies = [ "proc-macro2", "quote", @@ -1145,9 +1145,9 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.23.0" +version = "3.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d31c77bdf42a745371d260a26ca7163f1e0924b64afa0b688e61b5a9fa02f16" +checksum = "655da9c7eb6305c55742045d5a8d2037996d61d8de95806335c7c86ce0f82e9c" dependencies = [ "fastrand", "getrandom", @@ -1241,9 +1241,9 @@ checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" [[package]] name = "url" -version = "2.5.7" +version = "2.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08bc136a29a3d1758e07a9cca267be308aeebf5cfd5a10f3f67ab2097683ef5b" +checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" dependencies = [ "form_urlencoded", "idna", @@ -1265,9 +1265,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.18.1" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f87b8aa10b915a06587d0dec516c282ff295b475d94abf425d62b57710070a2" +checksum = "e2e054861b4bd027cd373e18e8d8d8e6548085000e41290d95ce0c373a654b4a" dependencies = [ "getrandom", "js-sys", @@ -1309,9 +1309,9 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.105" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da95793dfc411fbbd93f5be7715b0578ec61fe87cb1a42b12eb625caa5c5ea60" +checksum = "0d759f433fa64a2d763d1340820e46e111a7a5ab75f993d1852d70b03dbb80fd" dependencies = [ "cfg-if", "once_cell", @@ -1322,9 +1322,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.105" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04264334509e04a7bf8690f2384ef5265f05143a4bff3889ab7a3269adab59c2" +checksum = "48cb0d2638f8baedbc542ed444afc0644a29166f1595371af4fecf8ce1e7eeb3" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1332,9 +1332,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.105" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "420bc339d9f322e562942d52e115d57e950d12d88983a14c79b86859ee6c7ebc" +checksum = "cefb59d5cd5f92d9dcf80e4683949f15ca4b511f4ac0a6e14d4e1ac60c6ecd40" dependencies = [ "bumpalo", "proc-macro2", @@ -1345,9 +1345,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.105" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76f218a38c84bcb33c25ec7059b07847d465ce0e0a76b995e134a45adcb6af76" +checksum = "cbc538057e648b67f72a982e708d485b2efa771e1ac05fec311f9f63e5800db4" dependencies = [ "unicode-ident", ] @@ -1422,9 +1422,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.7.13" +version = "0.7.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" +checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829" dependencies = [ "memchr", ] @@ -1466,18 +1466,18 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.28" +version = "0.8.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43fa6694ed34d6e57407afbccdeecfa268c470a7d2a5b0cf49ce9fcc345afb90" +checksum = "668f5168d10b9ee831de31933dc111a459c97ec93225beb307aed970d1372dfd" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.28" +version = "0.8.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c640b22cd9817fae95be82f0d2f90b11f7605f6c319d16705c459b27ac2cbc26" +checksum = "2c7962b26b0a8685668b671ee4b54d007a67d4eaf05fda79ac0ecf41e32270f1" dependencies = [ "proc-macro2", "quote", @@ -1537,3 +1537,9 @@ dependencies = [ "quote", "syn", ] + +[[package]] +name = "zmij" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd8f3f50b848df28f887acb68e41201b5aea6bc8a8dacc00fb40635ff9a72fea" diff --git a/bindings/java/Cargo.lock b/bindings/java/Cargo.lock index a7804efa..1c269ac5 100644 --- a/bindings/java/Cargo.lock +++ b/bindings/java/Cargo.lock @@ -91,9 +91,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.19.0" +version = "3.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" +checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510" [[package]] name = "bytecount" @@ -109,9 +109,9 @@ checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3" [[package]] name = "cc" -version = "1.2.47" +version = "1.2.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd405d82c84ff7f35739f175f67d8b9fb7687a0e84ccdc78bd3568839827cf07" +checksum = "cd4932aefd12402b36c60956a4fe0035421f544799057659ff86f923657aada3" dependencies = [ "find-msvc-tools", "shlex", @@ -170,9 +170,9 @@ checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "data-encoding" -version = "2.9.0" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476" +checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea" [[package]] name = "displaydoc" @@ -213,9 +213,9 @@ dependencies = [ [[package]] name = "find-msvc-tools" -version = "0.1.5" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844" +checksum = "f449e6c6c08c865631d4890cfacf252b3d396c9bcc83adb6623cdb02a8336c41" [[package]] name = "fluent-uri" @@ -349,9 +349,9 @@ checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" [[package]] name = "icu_properties" -version = "2.1.1" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e93fcd3157766c0c8da2f8cff6ce651a31f0810eaa1c51ec363ef790bbb5fb99" +checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec" dependencies = [ "icu_collections", "icu_locale_core", @@ -363,9 +363,9 @@ dependencies = [ [[package]] name = "icu_properties_data" -version = "2.1.1" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02845b3647bb045f1100ecd6480ff52f34c35f82d9880e029d329c21d1054899" +checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af" [[package]] name = "icu_provider" @@ -405,9 +405,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.12.1" +version = "2.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2" +checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" dependencies = [ "equivalent", "hashbrown", @@ -421,9 +421,9 @@ checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" [[package]] name = "itoa" -version = "1.0.15" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" +checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" [[package]] name = "jni" @@ -449,9 +449,9 @@ checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" [[package]] name = "js-sys" -version = "0.3.82" +version = "0.3.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b011eec8cc36da2aab2d5cff675ec18454fad408585853910a202391cf9f8e65" +checksum = "464a3709c7f55f1f721e5389aa6ea4e3bc6aba669353300af094b29ffbdde1d8" dependencies = [ "once_cell", "wasm-bindgen", @@ -491,9 +491,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.177" +version = "0.2.180" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" +checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc" [[package]] name = "litemap" @@ -512,9 +512,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.28" +version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" [[package]] name = "memchr" @@ -703,18 +703,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.103" +version = "1.0.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" +checksum = "535d180e0ecab6268a3e718bb9fd44db66bbbc256257165fc699dadf70d16fe7" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.42" +version = "1.0.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" +checksum = "dc74d9a594b72ae6656596548f56f667211f8a97b3d4c3d467150794690dc40a" dependencies = [ "proc-macro2", ] @@ -747,9 +747,9 @@ dependencies = [ [[package]] name = "rand_core" -version = "0.9.3" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +checksum = "4f1b3bc831f92381018fd9c6350b917c7b21f1eed35a65a51900e0e55a3d7afa" dependencies = [ "getrandom", ] @@ -871,9 +871,9 @@ checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" [[package]] name = "ryu" -version = "1.0.20" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" +checksum = "a50f4cf475b65d88e057964e0e9bb1f0aa9bbb2036dc65c64596b42932536984" [[package]] name = "same-file" @@ -928,15 +928,15 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.145" +version = "1.0.149" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" dependencies = [ "itoa", "memchr", - "ryu", "serde", "serde_core", + "zmij", ] [[package]] @@ -978,9 +978,9 @@ checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" [[package]] name = "syn" -version = "2.0.111" +version = "2.0.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87" +checksum = "d4d107df263a3013ef9b1879b0df87d706ff80f65a86ea879bd9c31f9b307c2a" dependencies = [ "proc-macro2", "quote", @@ -1062,9 +1062,9 @@ checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" [[package]] name = "url" -version = "2.5.7" +version = "2.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08bc136a29a3d1758e07a9cca267be308aeebf5cfd5a10f3f67ab2097683ef5b" +checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" dependencies = [ "form_urlencoded", "idna", @@ -1080,9 +1080,9 @@ checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" [[package]] name = "uuid" -version = "1.18.1" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f87b8aa10b915a06587d0dec516c282ff295b475d94abf425d62b57710070a2" +checksum = "e2e054861b4bd027cd373e18e8d8d8e6548085000e41290d95ce0c373a654b4a" dependencies = [ "getrandom", "js-sys", @@ -1134,9 +1134,9 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.105" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da95793dfc411fbbd93f5be7715b0578ec61fe87cb1a42b12eb625caa5c5ea60" +checksum = "0d759f433fa64a2d763d1340820e46e111a7a5ab75f993d1852d70b03dbb80fd" dependencies = [ "cfg-if", "once_cell", @@ -1147,9 +1147,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.105" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04264334509e04a7bf8690f2384ef5265f05143a4bff3889ab7a3269adab59c2" +checksum = "48cb0d2638f8baedbc542ed444afc0644a29166f1595371af4fecf8ce1e7eeb3" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1157,9 +1157,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.105" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "420bc339d9f322e562942d52e115d57e950d12d88983a14c79b86859ee6c7ebc" +checksum = "cefb59d5cd5f92d9dcf80e4683949f15ca4b511f4ac0a6e14d4e1ac60c6ecd40" dependencies = [ "bumpalo", "proc-macro2", @@ -1170,9 +1170,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.105" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76f218a38c84bcb33c25ec7059b07847d465ce0e0a76b995e134a45adcb6af76" +checksum = "cbc538057e648b67f72a982e708d485b2efa771e1ac05fec311f9f63e5800db4" dependencies = [ "unicode-ident", ] @@ -1357,18 +1357,18 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.28" +version = "0.8.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43fa6694ed34d6e57407afbccdeecfa268c470a7d2a5b0cf49ce9fcc345afb90" +checksum = "668f5168d10b9ee831de31933dc111a459c97ec93225beb307aed970d1372dfd" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.28" +version = "0.8.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c640b22cd9817fae95be82f0d2f90b11f7605f6c319d16705c459b27ac2cbc26" +checksum = "2c7962b26b0a8685668b671ee4b54d007a67d4eaf05fda79ac0ecf41e32270f1" dependencies = [ "proc-macro2", "quote", @@ -1428,3 +1428,9 @@ dependencies = [ "quote", "syn", ] + +[[package]] +name = "zmij" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd8f3f50b848df28f887acb68e41201b5aea6bc8a8dacc00fb40635ff9a72fea" diff --git a/bindings/java/README.md b/bindings/java/README.md index 6edb6f52..a4479909 100644 --- a/bindings/java/README.md +++ b/bindings/java/README.md @@ -32,6 +32,13 @@ $ mvn package And you will have a JAR at `./target/regorus-java-0.1.5.jar`. +### Automation + +The repository exposes helper commands for local workflows: + +- `cargo xtask build-java` runs `mvn package` with quiet output helpers. +- `cargo xtask test-java` rebuilds the native library via the Maven exec plugin and executes the binding tests. + ## Usage You can use Regorus Java bindings as: diff --git a/bindings/python/Cargo.lock b/bindings/python/Cargo.lock index 59bec80a..f7dcea51 100644 --- a/bindings/python/Cargo.lock +++ b/bindings/python/Cargo.lock @@ -91,9 +91,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.19.0" +version = "3.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" +checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510" [[package]] name = "bytecount" @@ -103,9 +103,9 @@ checksum = "175812e0be2bccb6abe50bb8d566126198344f707e304f45c648fd8f2cc0365e" [[package]] name = "cc" -version = "1.2.47" +version = "1.2.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd405d82c84ff7f35739f175f67d8b9fb7687a0e84ccdc78bd3568839827cf07" +checksum = "cd4932aefd12402b36c60956a4fe0035421f544799057659ff86f923657aada3" dependencies = [ "find-msvc-tools", "shlex", @@ -148,9 +148,9 @@ checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "data-encoding" -version = "2.9.0" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476" +checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea" [[package]] name = "displaydoc" @@ -191,9 +191,9 @@ dependencies = [ [[package]] name = "find-msvc-tools" -version = "0.1.5" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844" +checksum = "f449e6c6c08c865631d4890cfacf252b3d396c9bcc83adb6623cdb02a8336c41" [[package]] name = "fluent-uri" @@ -333,9 +333,9 @@ checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" [[package]] name = "icu_properties" -version = "2.1.1" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e93fcd3157766c0c8da2f8cff6ce651a31f0810eaa1c51ec363ef790bbb5fb99" +checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec" dependencies = [ "icu_collections", "icu_locale_core", @@ -347,9 +347,9 @@ dependencies = [ [[package]] name = "icu_properties_data" -version = "2.1.1" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02845b3647bb045f1100ecd6480ff52f34c35f82d9880e029d329c21d1054899" +checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af" [[package]] name = "icu_provider" @@ -389,9 +389,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.12.1" +version = "2.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2" +checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" dependencies = [ "equivalent", "hashbrown", @@ -414,15 +414,15 @@ checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" [[package]] name = "itoa" -version = "1.0.15" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" +checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" [[package]] name = "js-sys" -version = "0.3.82" +version = "0.3.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b011eec8cc36da2aab2d5cff675ec18454fad408585853910a202391cf9f8e65" +checksum = "464a3709c7f55f1f721e5389aa6ea4e3bc6aba669353300af094b29ffbdde1d8" dependencies = [ "once_cell", "wasm-bindgen", @@ -462,9 +462,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.177" +version = "0.2.180" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" +checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc" [[package]] name = "litemap" @@ -483,9 +483,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.28" +version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" [[package]] name = "memchr" @@ -674,9 +674,9 @@ dependencies = [ [[package]] name = "portable-atomic" -version = "1.11.1" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" +checksum = "f89776e4d69bb58bc6993e99ffa1d11f228b839984854c7daeb5d37f87cbe950" [[package]] name = "potential_utf" @@ -698,9 +698,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.103" +version = "1.0.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" +checksum = "535d180e0ecab6268a3e718bb9fd44db66bbbc256257165fc699dadf70d16fe7" dependencies = [ "unicode-ident", ] @@ -771,9 +771,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.42" +version = "1.0.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" +checksum = "dc74d9a594b72ae6656596548f56f667211f8a97b3d4c3d467150794690dc40a" dependencies = [ "proc-macro2", ] @@ -806,9 +806,9 @@ dependencies = [ [[package]] name = "rand_core" -version = "0.9.3" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +checksum = "4f1b3bc831f92381018fd9c6350b917c7b21f1eed35a65a51900e0e55a3d7afa" dependencies = [ "getrandom", ] @@ -931,9 +931,9 @@ checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" [[package]] name = "ryu" -version = "1.0.20" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" +checksum = "a50f4cf475b65d88e057964e0e9bb1f0aa9bbb2036dc65c64596b42932536984" [[package]] name = "scopeguard" @@ -979,15 +979,15 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.145" +version = "1.0.149" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" dependencies = [ "itoa", "memchr", - "ryu", "serde", "serde_core", + "zmij", ] [[package]] @@ -1029,9 +1029,9 @@ checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" [[package]] name = "syn" -version = "2.0.111" +version = "2.0.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87" +checksum = "d4d107df263a3013ef9b1879b0df87d706ff80f65a86ea879bd9c31f9b307c2a" dependencies = [ "proc-macro2", "quote", @@ -1051,9 +1051,9 @@ dependencies = [ [[package]] name = "target-lexicon" -version = "0.13.3" +version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df7f62577c25e07834649fc3b39fafdc597c0a3527dc1c60129201ccfcbaa50c" +checksum = "b1dd07eb858a2067e2f3c7155d54e929265c264e6f37efe3ee7a8d1b5a1dd0ba" [[package]] name = "thiserror" @@ -1105,9 +1105,9 @@ checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" [[package]] name = "url" -version = "2.5.7" +version = "2.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08bc136a29a3d1758e07a9cca267be308aeebf5cfd5a10f3f67ab2097683ef5b" +checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" dependencies = [ "form_urlencoded", "idna", @@ -1123,9 +1123,9 @@ checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" [[package]] name = "uuid" -version = "1.18.1" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f87b8aa10b915a06587d0dec516c282ff295b475d94abf425d62b57710070a2" +checksum = "e2e054861b4bd027cd373e18e8d8d8e6548085000e41290d95ce0c373a654b4a" dependencies = [ "getrandom", "js-sys", @@ -1167,9 +1167,9 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.105" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da95793dfc411fbbd93f5be7715b0578ec61fe87cb1a42b12eb625caa5c5ea60" +checksum = "0d759f433fa64a2d763d1340820e46e111a7a5ab75f993d1852d70b03dbb80fd" dependencies = [ "cfg-if", "once_cell", @@ -1180,9 +1180,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.105" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04264334509e04a7bf8690f2384ef5265f05143a4bff3889ab7a3269adab59c2" +checksum = "48cb0d2638f8baedbc542ed444afc0644a29166f1595371af4fecf8ce1e7eeb3" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1190,9 +1190,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.105" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "420bc339d9f322e562942d52e115d57e950d12d88983a14c79b86859ee6c7ebc" +checksum = "cefb59d5cd5f92d9dcf80e4683949f15ca4b511f4ac0a6e14d4e1ac60c6ecd40" dependencies = [ "bumpalo", "proc-macro2", @@ -1203,9 +1203,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.105" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76f218a38c84bcb33c25ec7059b07847d465ce0e0a76b995e134a45adcb6af76" +checksum = "cbc538057e648b67f72a982e708d485b2efa771e1ac05fec311f9f63e5800db4" dependencies = [ "unicode-ident", ] @@ -1306,18 +1306,18 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.28" +version = "0.8.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43fa6694ed34d6e57407afbccdeecfa268c470a7d2a5b0cf49ce9fcc345afb90" +checksum = "668f5168d10b9ee831de31933dc111a459c97ec93225beb307aed970d1372dfd" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.28" +version = "0.8.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c640b22cd9817fae95be82f0d2f90b11f7605f6c319d16705c459b27ac2cbc26" +checksum = "2c7962b26b0a8685668b671ee4b54d007a67d4eaf05fda79ac0ecf41e32270f1" dependencies = [ "proc-macro2", "quote", @@ -1377,3 +1377,9 @@ dependencies = [ "quote", "syn", ] + +[[package]] +name = "zmij" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd8f3f50b848df28f887acb68e41201b5aea6bc8a8dacc00fb40635ff9a72fea" diff --git a/bindings/python/README.md b/bindings/python/README.md index 20e31b93..c06cc2ca 100644 --- a/bindings/python/README.md +++ b/bindings/python/README.md @@ -10,6 +10,10 @@ Regorus can be used in Python via `regorus` package. (It is not yet available in See [Repository](https://github.com/microsoft/regorus). +## Automation + +Run `cargo xtask build-python` to produce wheels via maturin, or `cargo xtask test-python` to reinstall the package locally and execute the sample script and pytest suite. + To build this binding, see [building](https://github.com/microsoft/regorus/blob/main/bindings/python/building.md) ## Usage diff --git a/bindings/ruby/Cargo.lock b/bindings/ruby/Cargo.lock index bd63a512..a5b4ef2d 100644 --- a/bindings/ruby/Cargo.lock +++ b/bindings/ruby/Cargo.lock @@ -950,7 +950,6 @@ dependencies = [ "serde", "serde_json", "serde_yaml", - "smol_str", "thiserror", "url", "uuid", @@ -1093,15 +1092,6 @@ version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" -[[package]] -name = "smol_str" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd538fb6910ac1099850255cf94a94df6551fbdd602454387d0adb2d1ca6dead" -dependencies = [ - "serde", -] - [[package]] name = "stable_deref_trait" version = "1.2.1" diff --git a/bindings/wasm/Cargo.lock b/bindings/wasm/Cargo.lock index 28d8ca8b..c28512b2 100644 --- a/bindings/wasm/Cargo.lock +++ b/bindings/wasm/Cargo.lock @@ -40,6 +40,17 @@ version = "1.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" +[[package]] +name = "async-trait" +version = "0.1.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "autocfg" version = "1.5.0" @@ -91,9 +102,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.19.0" +version = "3.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" +checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510" [[package]] name = "bytecount" @@ -101,11 +112,17 @@ version = "0.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "175812e0be2bccb6abe50bb8d566126198344f707e304f45c648fd8f2cc0365e" +[[package]] +name = "cast" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" + [[package]] name = "cc" -version = "1.2.47" +version = "1.2.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd405d82c84ff7f35739f175f67d8b9fb7687a0e84ccdc78bd3568839827cf07" +checksum = "cd4932aefd12402b36c60956a4fe0035421f544799057659ff86f923657aada3" dependencies = [ "find-msvc-tools", "shlex", @@ -148,9 +165,9 @@ checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "data-encoding" -version = "2.9.0" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476" +checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea" [[package]] name = "displaydoc" @@ -191,9 +208,9 @@ dependencies = [ [[package]] name = "find-msvc-tools" -version = "0.1.5" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844" +checksum = "f449e6c6c08c865631d4890cfacf252b3d396c9bcc83adb6623cdb02a8336c41" [[package]] name = "fluent-uri" @@ -227,9 +244,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" dependencies = [ "cfg-if", "js-sys", @@ -342,9 +359,9 @@ checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" [[package]] name = "icu_properties" -version = "2.1.1" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e93fcd3157766c0c8da2f8cff6ce651a31f0810eaa1c51ec363ef790bbb5fb99" +checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec" dependencies = [ "icu_collections", "icu_locale_core", @@ -356,9 +373,9 @@ dependencies = [ [[package]] name = "icu_properties_data" -version = "2.1.1" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02845b3647bb045f1100ecd6480ff52f34c35f82d9880e029d329c21d1054899" +checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af" [[package]] name = "icu_provider" @@ -398,9 +415,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.12.1" +version = "2.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2" +checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" dependencies = [ "equivalent", "hashbrown", @@ -414,15 +431,15 @@ checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" [[package]] name = "itoa" -version = "1.0.15" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" +checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" [[package]] name = "js-sys" -version = "0.3.82" +version = "0.3.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b011eec8cc36da2aab2d5cff675ec18454fad408585853910a202391cf9f8e65" +checksum = "464a3709c7f55f1f721e5389aa6ea4e3bc6aba669353300af094b29ffbdde1d8" dependencies = [ "once_cell", "wasm-bindgen", @@ -462,9 +479,15 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.177" +version = "0.2.180" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" +checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc" + +[[package]] +name = "libm" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" [[package]] name = "litemap" @@ -483,9 +506,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.28" +version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" [[package]] name = "memchr" @@ -509,9 +532,9 @@ dependencies = [ [[package]] name = "minicov" -version = "0.3.7" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f27fe9f1cc3c22e1687f9446c2083c4c5fc7f0bcf1c7a86bdbded14985895b4b" +checksum = "4869b6a491569605d66d3952bcdf03df789e5b536e5f0cf7758a7f08a55ae24d" dependencies = [ "cc", "walkdir", @@ -526,6 +549,15 @@ dependencies = [ "cc", ] +[[package]] +name = "nu-ansi-term" +version = "0.50.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" +dependencies = [ + "windows-sys", +] + [[package]] name = "num" version = "0.4.3" @@ -603,6 +635,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", + "libm", ] [[package]] @@ -611,6 +644,12 @@ version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" +[[package]] +name = "oorandom" +version = "11.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e" + [[package]] name = "outref" version = "0.5.2" @@ -684,18 +723,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.103" +version = "1.0.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" +checksum = "535d180e0ecab6268a3e718bb9fd44db66bbbc256257165fc699dadf70d16fe7" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.42" +version = "1.0.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" +checksum = "dc74d9a594b72ae6656596548f56f667211f8a97b3d4c3d467150794690dc40a" dependencies = [ "proc-macro2", ] @@ -728,9 +767,9 @@ dependencies = [ [[package]] name = "rand_core" -version = "0.9.3" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +checksum = "4f1b3bc831f92381018fd9c6350b917c7b21f1eed35a65a51900e0e55a3d7afa" dependencies = [ "getrandom 0.3.4", ] @@ -838,7 +877,7 @@ dependencies = [ name = "regorusjs" version = "0.5.0" dependencies = [ - "getrandom 0.2.16", + "getrandom 0.2.17", "getrandom 0.3.4", "regorus", "serde_json", @@ -855,9 +894,9 @@ checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" [[package]] name = "ryu" -version = "1.0.20" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" +checksum = "a50f4cf475b65d88e057964e0e9bb1f0aa9bbb2036dc65c64596b42932536984" [[package]] name = "same-file" @@ -912,15 +951,15 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.145" +version = "1.0.149" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" dependencies = [ "itoa", "memchr", - "ryu", "serde", "serde_core", + "zmij", ] [[package]] @@ -962,9 +1001,9 @@ checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" [[package]] name = "syn" -version = "2.0.111" +version = "2.0.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87" +checksum = "d4d107df263a3013ef9b1879b0df87d706ff80f65a86ea879bd9c31f9b307c2a" dependencies = [ "proc-macro2", "quote", @@ -1026,9 +1065,9 @@ checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" [[package]] name = "url" -version = "2.5.7" +version = "2.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08bc136a29a3d1758e07a9cca267be308aeebf5cfd5a10f3f67ab2097683ef5b" +checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" dependencies = [ "form_urlencoded", "idna", @@ -1044,9 +1083,9 @@ checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" [[package]] name = "uuid" -version = "1.18.1" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f87b8aa10b915a06587d0dec516c282ff295b475d94abf425d62b57710070a2" +checksum = "e2e054861b4bd027cd373e18e8d8d8e6548085000e41290d95ce0c373a654b4a" dependencies = [ "getrandom 0.3.4", "js-sys", @@ -1104,9 +1143,9 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.105" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da95793dfc411fbbd93f5be7715b0578ec61fe87cb1a42b12eb625caa5c5ea60" +checksum = "0d759f433fa64a2d763d1340820e46e111a7a5ab75f993d1852d70b03dbb80fd" dependencies = [ "cfg-if", "once_cell", @@ -1117,9 +1156,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.55" +version = "0.4.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "551f88106c6d5e7ccc7cd9a16f312dd3b5d36ea8b4954304657d5dfba115d4a0" +checksum = "836d9622d604feee9e5de25ac10e3ea5f2d65b41eac0d9ce72eb5deae707ce7c" dependencies = [ "cfg-if", "js-sys", @@ -1130,9 +1169,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.105" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04264334509e04a7bf8690f2384ef5265f05143a4bff3889ab7a3269adab59c2" +checksum = "48cb0d2638f8baedbc542ed444afc0644a29166f1595371af4fecf8ce1e7eeb3" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1140,9 +1179,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.105" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "420bc339d9f322e562942d52e115d57e950d12d88983a14c79b86859ee6c7ebc" +checksum = "cefb59d5cd5f92d9dcf80e4683949f15ca4b511f4ac0a6e14d4e1ac60c6ecd40" dependencies = [ "bumpalo", "proc-macro2", @@ -1153,21 +1192,29 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.105" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76f218a38c84bcb33c25ec7059b07847d465ce0e0a76b995e134a45adcb6af76" +checksum = "cbc538057e648b67f72a982e708d485b2efa771e1ac05fec311f9f63e5800db4" dependencies = [ "unicode-ident", ] [[package]] name = "wasm-bindgen-test" -version = "0.3.55" +version = "0.3.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfc379bfb624eb59050b509c13e77b4eb53150c350db69628141abce842f2373" +checksum = "25e90e66d265d3a1efc0e72a54809ab90b9c0c515915c67cdf658689d2c22c6c" dependencies = [ + "async-trait", + "cast", "js-sys", + "libm", "minicov", + "nu-ansi-term", + "num-traits", + "oorandom", + "serde", + "serde_json", "wasm-bindgen", "wasm-bindgen-futures", "wasm-bindgen-test-macro", @@ -1175,9 +1222,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-test-macro" -version = "0.3.55" +version = "0.3.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "085b2df989e1e6f9620c1311df6c996e83fe16f57792b272ce1e024ac16a90f1" +checksum = "7150335716dce6028bead2b848e72f47b45e7b9422f64cccdc23bedca89affc1" dependencies = [ "proc-macro2", "quote", @@ -1186,9 +1233,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.82" +version = "0.3.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a1f95c0d03a47f4ae1f7a64643a6bb97465d9b740f0fa8f90ea33915c99a9a1" +checksum = "9b32828d774c412041098d182a8b38b16ea816958e07cf40eec2bc080ae137ac" dependencies = [ "js-sys", "wasm-bindgen", @@ -1308,18 +1355,18 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.28" +version = "0.8.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43fa6694ed34d6e57407afbccdeecfa268c470a7d2a5b0cf49ce9fcc345afb90" +checksum = "668f5168d10b9ee831de31933dc111a459c97ec93225beb307aed970d1372dfd" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.28" +version = "0.8.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c640b22cd9817fae95be82f0d2f90b11f7605f6c319d16705c459b27ac2cbc26" +checksum = "2c7962b26b0a8685668b671ee4b54d007a67d4eaf05fda79ac0ecf41e32270f1" dependencies = [ "proc-macro2", "quote", @@ -1379,3 +1426,9 @@ dependencies = [ "quote", "syn", ] + +[[package]] +name = "zmij" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd8f3f50b848df28f887acb68e41201b5aea6bc8a8dacc00fb40635ff9a72fea" diff --git a/bindings/wasm/README.md b/bindings/wasm/README.md index 902b01f3..36aa33c3 100644 --- a/bindings/wasm/README.md +++ b/bindings/wasm/README.md @@ -12,6 +12,10 @@ See [Repository](https://github.com/microsoft/regorus). To build this binding, see [building.md](https://github.com/microsoft/regorus/blob/main/bindings/wasm/building.md) +## Automation + +Run `cargo xtask build-wasm` to invoke wasm-pack with sensible defaults, or `cargo xtask test-wasm` to rebuild the package and execute `node test.js`. + ## Usage diff --git a/scripts/pre-commit b/scripts/pre-commit index f4974633..27cf387f 100755 --- a/scripts/pre-commit +++ b/scripts/pre-commit @@ -4,25 +4,4 @@ set -eo pipefail -if [ -f Cargo.toml ]; then - # Ensure that all targets can be built. - cargo build --all-targets - - #Ensure that code is correctly formatted. - cargo fmt --check || (echo "Run cargo fmt to fix formatting" && exit 1) - - # Ensure that clippy warnings are addressed. - cargo clippy --all-targets --no-deps -- -Dwarnings - - # Ensure binding versions reflect the staged changes. - cargo xtask bindings --check - - # Ensure that all modifications are included. - # TODO refine status checking. - if git status -s | grep -e "MM " -e "??" -e "AM " -e " M " > /dev/null; then - printf "\nUnstaged changes found:\n" - git status -s | grep -e "MM " -e "??" -e "AM " -e " M " - echo "Stage them and try again" - exit 1 - fi -fi +cargo xtask pre-commit diff --git a/scripts/pre-push b/scripts/pre-push index a95a46eb..112dc4e1 100755 --- a/scripts/pre-push +++ b/scripts/pre-push @@ -4,37 +4,4 @@ set -eo pipefail -if [ -f Cargo.toml ]; then - # Run precommit checks. - dir=$(dirname "${BASH_SOURCE[0]}") - "$dir/pre-commit" - - # Ensure that the public API works. - cargo test --doc - - # Ensure that no_std build succeeds. - # Build for a target that has no std available. - if command -v rustup > /dev/null; then - rustup target add thumbv7m-none-eabi - (cd tests/ensure_no_std; cargo build -r --target thumbv7m-none-eabi) - fi - - # Ensure that we can build with only std. - cargo build --example regorus --no-default-features --features std - - # Ensure that we can build with all features. - cargo build --all-features - - # Ensure that all tests pass. - cargo test - cargo test --test aci - cargo test --test kata - - # Ensure that all tests pass with extensions - cargo test --features rego-extensions - cargo test --test aci --features rego-extensions - cargo test --test kata --features rego-extensions - - # Ensure that OPA conformance tests don't regress. - cargo test --features opa-testutil,serde_json/arbitrary_precision,rego-extensions --test opa -- $(tr '\n' ' ' < tests/opa.passing) -fi +cargo xtask pre-push diff --git a/src/number.rs b/src/number.rs index cfdde840..35e5b3bc 100644 --- a/src/number.rs +++ b/src/number.rs @@ -20,7 +20,7 @@ use core::str::FromStr; use anyhow::{anyhow, bail, Result}; use num_bigint::BigInt as NumBigInt; -#[cfg(not(feature = "std"))] +#[allow(unused)] use num_traits::float::FloatCore; use num_traits::{One, Signed, ToPrimitive, Zero}; diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml index d0e335bb..405c7a27 100644 --- a/xtask/Cargo.toml +++ b/xtask/Cargo.toml @@ -10,3 +10,4 @@ clap = { version = "4.5", features = ["derive"] } regex = "1.11" toml_edit = "0.22" semver = "1.0" +zip = { version = "0.6", default-features = false, features = ["deflate"] } diff --git a/xtask/src/main.rs b/xtask/src/main.rs index 10805b56..671230a9 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -6,7 +6,14 @@ mod tasks; use anyhow::Result; use clap::{Parser, Subcommand}; -use tasks::{BindingsCommand, UpdateDepsCommand}; +use tasks::{ + BindingsCommand, BuildAllBindingsCommand, BuildFfiCommand, BuildJavaCommand, BuildNugetCommand, + BuildPythonCommand, BuildWasmCommand, CiDebugCommand, CiReleaseCommand, ClippyCommand, + FmtCommand, PrecommitCommand, PrepushCommand, TestAllBindingsCommand, TestCCommand, + TestCNoStdCommand, TestCppCommand, TestCsharpCommand, TestFfiCommand, TestGoCommand, + TestJavaCommand, TestMuslCommand, TestNoStdCommand, TestPythonCommand, TestRubyCommand, + TestWasmCommand, UpdateDepsCommand, +}; #[derive(Parser)] #[command(author, version, about, propagate_version = true)] @@ -19,6 +26,59 @@ struct Cli { enum Commands { /// Bump binding manifests to match the main regorus crate version Bindings(BindingsCommand), + /// Build the regorus FFI crate for selected targets + #[command(name = "build-ffi", alias = "ffi")] + Ffi(BuildFfiCommand), + /// Run the release-focused CI workflow locally + CiRelease(CiReleaseCommand), + /// Run the debug-focused CI workflow locally + CiDebug(CiDebugCommand), + /// Format the workspace and all binding crates + Fmt(FmtCommand), + /// Run clippy across the workspace and all binding crates + Clippy(ClippyCommand), + /// Run the repository pre-commit validation sequence + PreCommit(PrecommitCommand), + /// Run the repository pre-push validation sequence + PrePush(PrepushCommand), + /// Build every binding artefact with opinionated defaults + BuildAllBindings(BuildAllBindingsCommand), + /// Build the Java bindings via Maven + BuildJava(BuildJavaCommand), + /// Execute the Java binding test suite + TestJava(TestJavaCommand), + /// Build the Python wheels via maturin + BuildPython(BuildPythonCommand), + /// Execute the Python binding tests + TestPython(TestPythonCommand), + /// Build the WASM bindings via wasm-pack + BuildWasm(BuildWasmCommand), + /// Execute the WASM binding smoke tests + TestWasm(TestWasmCommand), + /// Execute the FFI binding test suite + TestFfi(TestFfiCommand), + /// Execute the Go binding smoke tests + TestGo(TestGoCommand), + /// Execute all binding smoke tests + TestAllBindings(TestAllBindingsCommand), + /// Execute the MUSL cross-compilation test matrix + TestMusl(TestMuslCommand), + /// Build the ensure_no_std harness for the embedded target + TestNoStd(TestNoStdCommand), + /// Configure, build, and run the C binding sample + TestC(TestCCommand), + /// Configure, build, and run the C++ binding sample + TestCpp(TestCppCommand), + /// Configure, build, and run the no_std C binding sample + #[command(name = "test-c-no-std", alias = "test-c-nostd")] + TestCNoStd(TestCNoStdCommand), + /// Execute the Ruby binding smoke tests + TestRuby(TestRubyCommand), + /// Build and validate the C# binding via its NuGet package + TestCsharp(TestCsharpCommand), + /// Build the Regorus C# NuGet package from local artefacts + #[command(name = "build-csharp", alias = "build-nuget", alias = "nuget")] + BuildCsharp(BuildNugetCommand), /// Update dependencies across all workspace Cargo.lock files UpdateDeps(UpdateDepsCommand), } @@ -28,6 +88,31 @@ fn main() -> Result<()> { match cli.command { Commands::Bindings(cmd) => cmd.run()?, + Commands::Ffi(cmd) => cmd.run()?, + Commands::CiRelease(cmd) => cmd.run()?, + Commands::CiDebug(cmd) => cmd.run()?, + Commands::Fmt(cmd) => cmd.run()?, + Commands::Clippy(cmd) => cmd.run()?, + Commands::PreCommit(cmd) => cmd.run()?, + Commands::PrePush(cmd) => cmd.run()?, + Commands::BuildAllBindings(cmd) => cmd.run()?, + Commands::BuildJava(cmd) => cmd.run()?, + Commands::TestJava(cmd) => cmd.run()?, + Commands::BuildPython(cmd) => cmd.run()?, + Commands::TestPython(cmd) => cmd.run()?, + Commands::BuildWasm(cmd) => cmd.run()?, + Commands::TestWasm(cmd) => cmd.run()?, + Commands::TestFfi(cmd) => cmd.run()?, + Commands::TestGo(cmd) => cmd.run()?, + Commands::TestAllBindings(cmd) => cmd.run()?, + Commands::TestMusl(cmd) => cmd.run()?, + Commands::TestNoStd(cmd) => cmd.run()?, + Commands::TestC(cmd) => cmd.run()?, + Commands::TestCpp(cmd) => cmd.run()?, + Commands::TestCNoStd(cmd) => cmd.run()?, + Commands::TestRuby(cmd) => cmd.run()?, + Commands::TestCsharp(cmd) => cmd.run()?, + Commands::BuildCsharp(cmd) => cmd.run()?, Commands::UpdateDeps(cmd) => cmd.run()?, } diff --git a/xtask/src/tasks/bindings/all.rs b/xtask/src/tasks/bindings/all.rs new file mode 100644 index 00000000..bceeb94d --- /dev/null +++ b/xtask/src/tasks/bindings/all.rs @@ -0,0 +1,139 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use anyhow::Result; +use clap::Args; + +use super::c::{TestCCommand, TestCNoStdCommand}; +use super::cpp::TestCppCommand; +use super::csharp::{build_nuget_package, BuildNugetConfig, TestCsharpCommand}; +use super::ffi; +use super::java::{BuildJavaCommand, TestJavaCommand}; +use super::python::{BuildPythonCommand, TestPythonCommand}; +use super::wasm::{BuildWasmCommand, TestWasmCommand}; +use crate::tasks::util::workspace_root; + +/// Builds every published binding with opinionated defaults. +#[derive(Args, Default)] +pub struct BuildAllBindingsCommand { + /// Optimise artefacts where supported (defaults to debug builds). + #[arg(long)] + pub release: bool, +} + +impl BuildAllBindingsCommand { + pub fn run(&self) -> Result<()> { + let workspace = workspace_root(); + let release = self.release; + + let targets = ffi::resolve_targets(Vec::new())?; + ffi::build_targets(&workspace, &targets, release)?; + + let ffi_dir = workspace.join("bindings/ffi/target"); + let nuget_result = build_nuget_package(&BuildNugetConfig { + targets: Vec::new(), + release, + artifacts_dir: Some(ffi_dir.clone()), + enforce_artifacts: false, + })?; + + if nuget_result.packages.is_empty() { + println!("NuGet packaging completed but no archives were produced."); + } else { + for package in &nuget_result.packages { + println!("NuGet package ready at {}", package.display()); + } + } + + BuildJavaCommand { skip_tests: true }.run()?; + BuildPythonCommand { + release, + target: None, + target_dir: None, + frozen: false, + } + .run()?; + BuildWasmCommand { + release, + target: "nodejs".to_string(), + out_dir: None, + } + .run()?; + + println!("Completed building all bindings."); + Ok(()) + } +} + +/// Executes the smoke tests for all bindings in sequence. +#[derive(Args, Default)] +pub struct TestAllBindingsCommand { + /// Optimise artefacts where supported (defaults to debug builds). + #[arg(long)] + pub release: bool, + + /// Python executable leveraged by the binding tests. + #[arg(long, value_name = "EXE", default_value = "python3")] + pub python: String, + + /// Node.js executable leveraged by the WASM test. + #[arg(long, value_name = "EXE", default_value = "node")] + pub node: String, +} + +impl TestAllBindingsCommand { + pub fn run(&self) -> Result<()> { + TestCCommand { + release: self.release, + frozen: false, + skip_ffi: false, + } + .run()?; + TestCppCommand { + release: self.release, + frozen: false, + skip_ffi: true, + } + .run()?; + TestCNoStdCommand { + release: self.release, + frozen: false, + skip_ffi: true, + } + .run()?; + TestJavaCommand { + release: self.release, + frozen: false, + } + .run()?; + + TestPythonCommand { + release: self.release, + target: None, + python: self.python.clone(), + } + .run()?; + + TestWasmCommand { + release: self.release, + target: "nodejs".to_string(), + out_dir: None, + node: self.node.clone(), + frozen: false, + skip_build: false, + } + .run()?; + + TestCsharpCommand { + targets: Vec::new(), + release: self.release, + artifacts_dir: None, + enforce_artifacts: false, + force_nuget: false, + } + .run()?; + + println!("Completed testing all bindings."); + Ok(()) + } +} diff --git a/xtask/src/tasks/bindings/c.rs b/xtask/src/tasks/bindings/c.rs new file mode 100644 index 00000000..21a45696 --- /dev/null +++ b/xtask/src/tasks/bindings/c.rs @@ -0,0 +1,134 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use std::fs; +use std::path::{Path, PathBuf}; +use std::process::Command; + +use crate::tasks::util::{run_cargo_step, run_command, workspace_root}; +use anyhow::{anyhow, Context, Result}; +use clap::Args; + +#[derive(Args, Default)] +pub struct TestCCommand { + /// Build the FFI crate in release mode before exercising the binding. + #[arg(long)] + pub release: bool, + + /// Pass --frozen to the preparatory cargo invocations. + #[arg(long)] + pub frozen: bool, + + /// Reuse previously built FFI artefacts instead of rebuilding. + #[arg(long)] + pub skip_ffi: bool, +} + +#[derive(Args, Default)] +pub struct TestCNoStdCommand { + /// Build the FFI crate in release mode before exercising the binding. + #[arg(long)] + pub release: bool, + + /// Pass --frozen to the preparatory cargo invocations. + #[arg(long)] + pub frozen: bool, + + /// Reuse previously built FFI artefacts instead of rebuilding. + #[arg(long)] + pub skip_ffi: bool, +} + +impl TestCCommand { + pub fn run(&self) -> Result<()> { + if !self.skip_ffi { + prepare_ffi_artifacts(self.release, self.frozen)?; + } + run_binding("bindings/c", "regorus_test", self.release) + } +} + +impl TestCNoStdCommand { + pub fn run(&self) -> Result<()> { + if !self.skip_ffi { + prepare_ffi_artifacts(self.release, self.frozen)?; + } + run_binding("bindings/c-nostd", "regorus_test", self.release) + } +} + +pub(super) fn run_binding(relative_dir: &str, binary_name: &str, release: bool) -> Result<()> { + let workspace = workspace_root(); + let source_dir = workspace.join(relative_dir); + let build_dir = source_dir.join("build"); + fs::create_dir_all(&build_dir).with_context(|| { + format!( + "failed to create build directory at {}", + build_dir.display() + ) + })?; + + let build_type = if release { "Release" } else { "Debug" }; + let mut configure = Command::new("cmake"); + configure.arg("-S").arg(&source_dir); + configure.arg("-B").arg(&build_dir); + configure.arg(format!("-DCMAKE_BUILD_TYPE={build_type}")); + run_command(configure, &format!("cmake configure ({relative_dir})"))?; + + let mut build = Command::new("cmake"); + build.arg("--build").arg(&build_dir); + build.arg("--config").arg(build_type); + run_command(build, &format!("cmake build ({relative_dir})"))?; + + let executable = locate_executable(&build_dir, binary_name)?; + let mut run = Command::new(&executable); + run.current_dir(&build_dir); + run_command(run, &format!("{binary_name} ({relative_dir})"))?; + + Ok(()) +} + +fn locate_executable(build_dir: &Path, binary_name: &str) -> Result { + let mut candidates = Vec::new(); + if cfg!(windows) { + let exe = format!("{binary_name}.exe"); + candidates.push(build_dir.join(&exe)); + candidates.push(build_dir.join("Release").join(&exe)); + candidates.push(build_dir.join("Debug").join(&exe)); + } else { + candidates.push(build_dir.join(binary_name)); + candidates.push(build_dir.join("Release").join(binary_name)); + candidates.push(build_dir.join("Debug").join(binary_name)); + } + + for candidate in candidates { + if candidate.exists() { + return Ok(candidate); + } + } + + Err(anyhow!( + "failed to locate built executable '{}' under {}", + binary_name, + build_dir.display() + )) +} + +pub(super) fn prepare_ffi_artifacts(release: bool, frozen: bool) -> Result<()> { + let workspace = workspace_root(); + let ffi_dir = workspace.join("bindings/ffi"); + + let mut build_args = vec!["build", "--locked"]; + if release { + build_args.push("--release"); + } + if frozen { + build_args.push("--frozen"); + } + let build_label = if release { + "cargo build --release (bindings/ffi)" + } else { + "cargo build (bindings/ffi)" + }; + run_cargo_step(&ffi_dir, build_label, build_args) +} diff --git a/xtask/src/tasks/bindings/cpp.rs b/xtask/src/tasks/bindings/cpp.rs new file mode 100644 index 00000000..4708ab7c --- /dev/null +++ b/xtask/src/tasks/bindings/cpp.rs @@ -0,0 +1,31 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use anyhow::Result; +use clap::Args; + +use super::c::{prepare_ffi_artifacts, run_binding}; + +#[derive(Args, Default)] +pub struct TestCppCommand { + /// Build the FFI crate in release mode before exercising the binding. + #[arg(long)] + pub release: bool, + + /// Pass --frozen to the preparatory cargo invocations. + #[arg(long)] + pub frozen: bool, + + /// Reuse previously built FFI artefacts instead of rebuilding. + #[arg(long)] + pub skip_ffi: bool, +} + +impl TestCppCommand { + pub fn run(&self) -> Result<()> { + if !self.skip_ffi { + prepare_ffi_artifacts(self.release, self.frozen)?; + } + run_binding("bindings/cpp", "regorus_test", self.release) + } +} diff --git a/xtask/src/tasks/bindings/csharp.rs b/xtask/src/tasks/bindings/csharp.rs new file mode 100644 index 00000000..b0066532 --- /dev/null +++ b/xtask/src/tasks/bindings/csharp.rs @@ -0,0 +1,382 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use std::fs::{self, File}; +use std::path::{Path, PathBuf}; +use std::process::Command; + +use anyhow::{anyhow, Context, Result}; +use clap::Args; + +use super::ffi; +use crate::tasks::util::{dotnet_host_arch, run_command, workspace_root}; +use zip::read::ZipArchive; + +/// Builds the Regorus C# NuGet package from local sources. +#[derive(Args, Clone)] +pub struct BuildNugetCommand { + /// Build the Rust FFI artefacts for the provided target triple (defaults to the host). + #[arg(long = "target", value_name = "TRIPLE")] + pub targets: Vec, + + /// Build the package in release mode (defaults to debug). + #[arg(long)] + pub release: bool, + + /// Override the directory that contains compiled regorus FFI artefacts. + #[arg(long, value_name = "PATH")] + pub artifacts_dir: Option, + + /// Require all platform artefacts to exist before packing. + #[arg(long)] + pub enforce_artifacts: bool, +} + +/// Parsed build options shared across tasks that need a NuGet package. +#[derive(Clone, Debug)] +pub struct BuildNugetConfig { + pub targets: Vec, + pub release: bool, + pub artifacts_dir: Option, + pub enforce_artifacts: bool, +} + +/// Result of a NuGet build, including generated artefacts. +#[derive(Debug)] +pub struct BuildNugetResult { + pub package_dir: PathBuf, + pub packages: Vec, +} + +/// Builds (or rebuilds) the C# NuGet package with the supplied configuration. +pub fn build_nuget_package(config: &BuildNugetConfig) -> Result { + let workspace_root = workspace_root(); + let configuration = if config.release { "Release" } else { "Debug" }; + let profile = ffi::profile_dir(config.release); + + let base_artifacts_dir = if let Some(dir) = &config.artifacts_dir { + if !config.targets.is_empty() { + println!("Skipping FFI build for specified targets because --artifacts-dir is set."); + } + dir.clone() + } else { + let targets = ffi::resolve_targets(config.targets.clone())?; + ffi::build_targets(&workspace_root, &targets, config.release)?; + + workspace_root.join("bindings/ffi/target") + }; + + let artifacts_dir = base_artifacts_dir.canonicalize().with_context(|| { + format!( + "failed to canonicalize FFI artefacts directory at {}", + base_artifacts_dir.display() + ) + })?; + + let package_dir = invoke_dotnet_pack( + &workspace_root, + &artifacts_dir, + configuration, + &profile, + !config.enforce_artifacts, + )?; + + let packages = find_packages(&package_dir)?; + + Ok(BuildNugetResult { + package_dir, + packages, + }) +} + +/// Returns all NuGet packages currently present in the given directory. +pub fn find_packages(package_dir: &Path) -> Result> { + if !package_dir.exists() { + return Ok(Vec::new()); + } + + let mut packages = Vec::new(); + let entries = fs::read_dir(package_dir).with_context(|| { + format!( + "failed to enumerate NuGet artefacts under {}", + package_dir.display() + ) + })?; + + for entry in entries { + let entry = entry?; + let path = entry.path(); + if path + .extension() + .and_then(|ext| ext.to_str()) + .map_or(false, |ext| ext.eq_ignore_ascii_case("nupkg")) + { + packages.push(path); + } + } + + packages.sort(); + Ok(packages) +} +fn invoke_dotnet_pack( + root: &Path, + artifacts_dir: &Path, + configuration: &str, + profile: &str, + ignore_missing: bool, +) -> Result { + let project_dir = root.join("bindings/csharp/Regorus"); + let artifacts_dir_str = artifacts_dir + .to_str() + .ok_or_else(|| anyhow!("artefact directory path contains invalid UTF-8"))?; + + let profile_arg = format!("/p:RegorusFFIArtifactsProfile={}", profile); + let dir_arg = format!("/p:RegorusFFIArtifactsDir={}", artifacts_dir_str); + + let mut restore = Command::new("dotnet"); + restore.current_dir(&project_dir); + restore.arg("restore"); + run_command(restore, "dotnet restore")?; + + let mut build = Command::new("dotnet"); + build.current_dir(&project_dir); + build.arg("build"); + build.arg("--no-restore"); + build.arg("-c"); + build.arg(configuration); + build.arg("--verbosity"); + build.arg("minimal"); + build.arg(&dir_arg); + build.arg(&profile_arg); + if ignore_missing { + build.arg("/p:IgnoreMissingArtifacts=true"); + } + run_command(build, "dotnet build")?; + + let mut pack = Command::new("dotnet"); + pack.current_dir(&project_dir); + pack.arg("pack"); + pack.arg("--no-build"); + pack.arg("-c"); + pack.arg(configuration); + pack.arg(&dir_arg); + pack.arg(&profile_arg); + if ignore_missing { + pack.arg("/p:IgnoreMissingArtifacts=true"); + } + run_command(pack, "dotnet pack")?; + + Ok(project_dir.join("bin").join(configuration)) +} + +impl BuildNugetCommand { + /// Entry point executed by the xtask harness. + pub fn run(&self) -> Result<()> { + let config = self.to_config(); + let result = build_nuget_package(&config)?; + + println!( + "NuGet package(s) available under {}", + result.package_dir.display() + ); + + if result.packages.is_empty() { + println!("No NuGet packages were produced; check earlier log output."); + } else { + print_package_listing(&result.packages)?; + } + + Ok(()) + } + + pub fn to_config(&self) -> BuildNugetConfig { + BuildNugetConfig { + targets: self.targets.clone(), + release: self.release, + artifacts_dir: self.artifacts_dir.clone(), + enforce_artifacts: self.enforce_artifacts, + } + } +} + +fn print_package_listing(packages: &[PathBuf]) -> Result<()> { + for package in packages { + println!("Contents of {}:", package.display()); + let file = + File::open(package).with_context(|| format!("failed to open {}", package.display()))?; + let mut archive = ZipArchive::new(file) + .with_context(|| format!("failed to read zip archive from {}", package.display()))?; + + let mut entries = Vec::new(); + for index in 0..archive.len() { + let file = archive.by_index(index).with_context(|| { + format!("failed to access entry {index} in {}", package.display()) + })?; + entries.push(file.name().to_string()); + } + + entries.sort(); + for entry in entries { + println!(" {}", entry); + } + } + + Ok(()) +} + +/// Builds (if required) and tests the C# bindings against the packaged NuGet. +#[derive(Args, Clone)] +pub struct TestCsharpCommand { + /// Build the Rust FFI artefacts for the provided target triple (defaults to the host). + #[arg(long = "target", value_name = "TRIPLE")] + pub targets: Vec, + + /// Build and pack using the Release configuration (defaults to Debug). + #[arg(long)] + pub release: bool, + + /// Override the directory that contains compiled regorus FFI artefacts. + #[arg(long, value_name = "PATH")] + pub artifacts_dir: Option, + + /// Require all platform artefacts to exist before packing. + #[arg(long)] + pub enforce_artifacts: bool, + + /// Always rebuild the NuGet package instead of reusing an existing archive. + #[arg(long)] + pub force_nuget: bool, +} + +impl TestCsharpCommand { + pub fn run(&self) -> Result<()> { + let workspace = workspace_root(); + let configuration = if self.release { "Release" } else { "Debug" }; + let mut package_dir = workspace + .join("bindings/csharp/Regorus/bin") + .join(configuration); + + let build_config = BuildNugetConfig { + targets: self.targets.clone(), + release: self.release, + artifacts_dir: self.artifacts_dir.clone(), + enforce_artifacts: self.enforce_artifacts, + }; + + let mut packages = find_packages(&package_dir)?; + if self.force_nuget || packages.is_empty() { + println!( + "{} NuGet package(s); invoking build...", + if self.force_nuget { + "Forcing rebuild of" + } else { + "Missing" + } + ); + let build = build_nuget_package(&build_config)?; + package_dir = build.package_dir; + packages = build.packages; + } else { + println!( + "Reusing existing NuGet package(s) under {}.", + package_dir.display() + ); + } + + if packages.is_empty() { + return Err(anyhow!( + "No NuGet packages are available under {} after build", + package_dir.display() + )); + } + + println!("Using NuGet package(s):"); + for package in &packages { + println!(" {}", package.display()); + } + + run_regorus_tests(&workspace, configuration, &package_dir)?; + + Ok(()) + } +} + +fn run_regorus_tests(workspace: &Path, configuration: &str, package_dir: &Path) -> Result<()> { + let nuget_source = package_dir + .to_str() + .ok_or_else(|| anyhow!("NuGet directory path contains invalid UTF-8"))?; + let source_property = format!("/p:RestoreAdditionalProjectSources={}", nuget_source); + + let regorus_tests = workspace.join("bindings/csharp/Regorus.Tests"); + restore_with_source(®orus_tests, &source_property, "Regorus.Tests")?; + + let mut test = Command::new("dotnet"); + test.current_dir(®orus_tests); + test.arg("test"); + test.arg("--no-restore"); + test.arg("-c"); + test.arg(configuration); + test.arg("--arch"); + test.arg(dotnet_host_arch()); + run_command(test, "dotnet test (Regorus.Tests)")?; + + let test_app = workspace.join("bindings/csharp/TestApp"); + restore_with_source(&test_app, &source_property, "TestApp")?; + let mut build = Command::new("dotnet"); + build.current_dir(&test_app); + build.arg("build"); + build.arg("--no-restore"); + build.arg("-c"); + build.arg(configuration); + build.arg("--arch"); + build.arg(dotnet_host_arch()); + run_command(build, "dotnet build (TestApp)")?; + + let mut run = Command::new("dotnet"); + run.current_dir(&test_app); + run.arg("run"); + run.arg("--no-build"); + run.arg("--framework"); + run.arg("net8.0"); + run.arg("-c"); + run.arg(configuration); + run.arg("--arch"); + run.arg(dotnet_host_arch()); + run_command(run, "dotnet run (TestApp)")?; + + let target_example = workspace.join("bindings/csharp/TargetExampleApp"); + restore_with_source(&target_example, &source_property, "TargetExampleApp")?; + let mut build_example = Command::new("dotnet"); + build_example.current_dir(&target_example); + build_example.arg("build"); + build_example.arg("--no-restore"); + build_example.arg("-c"); + build_example.arg(configuration); + build_example.arg("--arch"); + build_example.arg(dotnet_host_arch()); + run_command(build_example, "dotnet build (TargetExampleApp)")?; + + let mut run_example = Command::new("dotnet"); + run_example.current_dir(&target_example); + run_example.arg("run"); + run_example.arg("--no-build"); + run_example.arg("--framework"); + run_example.arg("net8.0"); + run_example.arg("-c"); + run_example.arg(configuration); + run_example.arg("--arch"); + run_example.arg(dotnet_host_arch()); + run_command(run_example, "dotnet run (TargetExampleApp)")?; + + Ok(()) +} + +fn restore_with_source(project_dir: &Path, source_property: &str, label: &str) -> Result<()> { + let mut restore = Command::new("dotnet"); + restore.current_dir(project_dir); + restore.arg("restore"); + restore.arg("--arch"); + restore.arg(dotnet_host_arch()); + restore.arg(source_property); + run_command(restore, &format!("dotnet restore ({label})")) +} diff --git a/xtask/src/tasks/bindings/ffi.rs b/xtask/src/tasks/bindings/ffi.rs new file mode 100644 index 00000000..f09531df --- /dev/null +++ b/xtask/src/tasks/bindings/ffi.rs @@ -0,0 +1,172 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use std::ffi::OsString; +use std::path::{Path, PathBuf}; +use std::process::Command; + +use anyhow::{anyhow, bail, Context, Result}; +use clap::Args; + +use crate::tasks::util::{dedup, run_cargo_step, workspace_root}; + +/// Builds the regorus FFI crate for the selected targets. +#[derive(Args)] +pub struct BuildFfiCommand { + /// Target triples to compile (defaults to the current host when omitted). + #[arg(long = "target", value_name = "TRIPLE")] + targets: Vec, + + /// Build in release mode instead of debug. + #[arg(long)] + release: bool, +} + +impl BuildFfiCommand { + pub fn run(&self) -> Result<()> { + let workspace_root = workspace_root(); + let targets = resolve_targets(self.targets.clone())?; + let profile = profile_dir(self.release); + + build_targets(&workspace_root, &targets, self.release)?; + + let base = workspace_root.join("bindings/ffi/target"); + if targets.len() == 1 { + println!( + "Built FFI artefacts at {}", + artifact_path(&base, targets[0].as_str(), profile).display() + ); + } else { + println!("Built FFI artefacts:"); + for triple in &targets { + println!( + " {} -> {}", + triple, + artifact_path(&base, triple, profile).display() + ); + } + } + + Ok(()) + } +} + +/// Resolves the list of target triples to compile, defaulting to the host. +pub fn resolve_targets(mut targets: Vec) -> Result> { + if targets.is_empty() { + targets.push(detect_host_triple()?); + } + + dedup(&mut targets); + Ok(targets) +} + +/// Compiles the FFI crate for the supplied target triples. +pub fn build_targets(root: &Path, targets: &[String], release: bool) -> Result<()> { + for target in targets { + cargo_build(root, target, release)?; + } + Ok(()) +} + +/// Returns the cargo profile directory associated with the release flag. +pub fn profile_dir(release: bool) -> &'static str { + if release { + "release" + } else { + "debug" + } +} + +pub fn detect_host_triple() -> Result { + let output = Command::new("rustc") + .arg("-Vv") + .output() + .context("failed to invoke rustc")?; + + if !output.status.success() { + bail!( + "rustc -Vv failed: {}", + String::from_utf8_lossy(&output.stderr) + ); + } + + let stdout = String::from_utf8(output.stdout).context("rustc output was not valid UTF-8")?; + for line in stdout.lines() { + if let Some(rest) = line.strip_prefix("host: ") { + return Ok(rest.trim().to_string()); + } + } + + Err(anyhow!("failed to detect host target triple")) +} + +fn cargo_build(root: &Path, target: &str, release: bool) -> Result<()> { + let dir = root.join("bindings/ffi"); + let mut args = vec![ + OsString::from("build"), + OsString::from("--locked"), + OsString::from("--target"), + OsString::from(target), + ]; + if release { + args.push(OsString::from("--release")); + } + + let title = format!("cargo build (ffi:{target})"); + run_cargo_step(&dir, &title, args) +} + +fn artifact_path(base: &Path, target: &str, profile: &str) -> PathBuf { + base.join(target).join(profile) +} + +/// Executes the FFI binding test suite. +#[derive(Args, Default)] +pub struct TestFfiCommand { + /// Build the FFI crate in release mode prior to testing. + #[arg(long)] + pub release: bool, + + /// Pass --frozen to all cargo invocations. + #[arg(long)] + pub frozen: bool, +} + +impl TestFfiCommand { + pub fn run(&self) -> Result<()> { + let workspace = workspace_root(); + let ffi_dir = workspace.join("bindings/ffi"); + + let mut build_args = vec![OsString::from("build"), OsString::from("--locked")]; + if self.release { + build_args.push(OsString::from("--release")); + } + if self.frozen { + build_args.push(OsString::from("--frozen")); + } + let build_label = if self.release { + "cargo build --release (bindings/ffi)" + } else { + "cargo build (bindings/ffi)" + }; + run_cargo_step(&ffi_dir, build_label, build_args)?; + + let mut test_args = vec![OsString::from("test"), OsString::from("--locked")]; + if self.release { + test_args.push(OsString::from("--release")); + } + if self.frozen { + test_args.push(OsString::from("--frozen")); + } + test_args.push(OsString::from("--features")); + test_args.push(OsString::from("contention_checks")); + run_cargo_step( + &ffi_dir, + "cargo test --features contention_checks (bindings/ffi)", + test_args, + )?; + + Ok(()) + } +} diff --git a/xtask/src/tasks/bindings/go.rs b/xtask/src/tasks/bindings/go.rs new file mode 100644 index 00000000..9efccac2 --- /dev/null +++ b/xtask/src/tasks/bindings/go.rs @@ -0,0 +1,74 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use std::path::{Path, PathBuf}; +use std::process::Command; + +use anyhow::{anyhow, Result}; +use clap::Args; + +use super::c::prepare_ffi_artifacts; +use crate::tasks::util::{add_library_search_path, run_command, workspace_root}; + +/// Builds the Go binding sample and runs its smoke test. +#[derive(Args, Default)] +pub struct TestGoCommand { + /// Build the FFI crate in release mode before exercising the Go sample. + #[arg(long)] + pub release: bool, + + /// Skip rebuilding the FFI artefacts and reuse existing outputs. + #[arg(long)] + pub skip_ffi: bool, + + /// Pass --frozen to the preparatory cargo invocations. + #[arg(long)] + pub frozen: bool, +} + +impl TestGoCommand { + pub fn run(&self) -> Result<()> { + let workspace = workspace_root(); + let ffi_dir = workspace.join("bindings/ffi"); + let go_dir = workspace.join("bindings/go"); + let profile = if self.release { "release" } else { "debug" }; + + if !self.skip_ffi { + prepare_ffi_artifacts(self.release, self.frozen)?; + } + + run_go_command(&go_dir, ["mod", "tidy"], "go mod tidy (bindings/go)")?; + run_go_command(&go_dir, ["build"], "go build (bindings/go)")?; + + let binary = go_test_binary(&go_dir); + if !binary.exists() { + return Err(anyhow!( + "expected Go test binary at {} after build", + binary.display() + )); + } + + let lib_dir = ffi_dir.join("target").join(profile); + let mut run = Command::new(&binary); + run.current_dir(&go_dir); + add_library_search_path(&mut run, &lib_dir); + run_command(run, "regorus_test (bindings/go)") + } +} + +fn run_go_command(dir: &Path, args: [&str; N], label: &str) -> Result<()> { + let mut cmd = Command::new("go"); + cmd.current_dir(dir); + for arg in args { + cmd.arg(arg); + } + run_command(cmd, label) +} + +fn go_test_binary(dir: &Path) -> PathBuf { + if cfg!(windows) { + dir.join("regorus_test.exe") + } else { + dir.join("regorus_test") + } +} diff --git a/xtask/src/tasks/bindings/java.rs b/xtask/src/tasks/bindings/java.rs new file mode 100644 index 00000000..ee15fd31 --- /dev/null +++ b/xtask/src/tasks/bindings/java.rs @@ -0,0 +1,134 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use std::fs; +use std::path::{Path, PathBuf}; +use std::process::Command; + +use anyhow::{anyhow, Context, Result}; +use clap::Args; + +use crate::tasks::util::{path_separator, run_cargo_step, run_command, workspace_root}; + +#[derive(Args, Default)] +pub struct BuildJavaCommand { + /// Skip the Maven test phase while packaging. + #[arg(long)] + pub skip_tests: bool, +} + +impl BuildJavaCommand { + pub fn run(&self) -> Result<()> { + let workspace = workspace_root(); + let java_dir = workspace.join("bindings/java"); + + run_command(self.into_command(&java_dir)?, "mvn package (bindings/java)") + } + + fn into_command(&self, java_dir: &Path) -> Result { + let mut mvn = Command::new("mvn"); + mvn.current_dir(java_dir); + mvn.arg("--batch-mode"); + mvn.arg("--no-transfer-progress"); + mvn.arg("package"); + if self.skip_tests { + mvn.arg("-DskipTests"); + } + Ok(mvn) + } +} + +#[derive(Args, Default)] +pub struct TestJavaCommand { + /// Build the JNI artefacts in release mode before testing. + #[arg(long)] + pub release: bool, + + /// Propagate --frozen to cargo invocations. + #[arg(long)] + pub frozen: bool, +} + +impl TestJavaCommand { + pub fn run(&self) -> Result<()> { + let workspace = workspace_root(); + let java_dir = workspace.join("bindings/java"); + + build_java_crate(&workspace, self.release, self.frozen)?; + run_command( + BuildJavaCommand { skip_tests: false }.into_command(&java_dir)?, + "mvn package (bindings/java)", + )?; + run_java_integration(&java_dir, self.release) + } +} +fn build_java_crate(workspace: &Path, release: bool, frozen: bool) -> Result<()> { + let mut args = vec!["build"]; + if release { + args.push("--release"); + } + if frozen { + args.push("--frozen"); + } + args.push("--manifest-path"); + args.push("bindings/java/Cargo.toml"); + args.push("--locked"); + run_cargo_step(workspace, "cargo build (bindings/java)", args) +} + +fn run_java_integration(java_dir: &Path, release: bool) -> Result<()> { + let jar = locate_java_jar(java_dir)?; + let separator = path_separator(); + let classpath = format!("{}{}.", jar.display(), separator); + + let mut javac = Command::new("javac"); + javac.current_dir(java_dir); + javac.arg("-cp"); + javac.arg(&classpath); + javac.arg("Test.java"); + run_command(javac, "javac Test.java (bindings/java)")?; + + let profile = if release { "release" } else { "debug" }; + let lib_path = java_dir.join("target").join(profile); + let mut java = Command::new("java"); + java.current_dir(java_dir); + java.arg(format!("-Djava.library.path={}", lib_path.display())); + java.arg("-cp"); + java.arg(&classpath); + java.arg("Test"); + run_command(java, "java Test (bindings/java)") +} + +fn locate_java_jar(java_dir: &Path) -> Result { + let target_dir = java_dir.join("target"); + let entries = fs::read_dir(&target_dir).with_context(|| { + format!( + "failed to enumerate built JARs under {}", + target_dir.display() + ) + })?; + + let mut candidates = Vec::new(); + for entry in entries { + let entry = entry?; + let path = entry.path(); + if !path.is_file() { + continue; + } + let Some(file_name) = path.file_name().and_then(|name| name.to_str()) else { + continue; + }; + if !file_name.starts_with("regorus-java-") || !file_name.ends_with(".jar") { + continue; + } + if file_name.contains("-sources") || file_name.contains("-javadoc") { + continue; + } + candidates.push(path); + } + + candidates.sort(); + candidates + .pop() + .ok_or_else(|| anyhow!("no regorus-java jar found under {}", target_dir.display())) +} diff --git a/xtask/src/tasks/bindings/mod.rs b/xtask/src/tasks/bindings/mod.rs new file mode 100644 index 00000000..0782af30 --- /dev/null +++ b/xtask/src/tasks/bindings/mod.rs @@ -0,0 +1,26 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +pub mod all; +pub mod c; +pub mod cpp; +pub mod csharp; +pub mod ffi; +pub mod go; +pub mod java; +pub mod python; +pub mod ruby; +pub mod version; +pub mod wasm; + +pub use all::{BuildAllBindingsCommand, TestAllBindingsCommand}; +pub use c::{TestCCommand, TestCNoStdCommand}; +pub use cpp::TestCppCommand; +pub use csharp::{BuildNugetCommand, TestCsharpCommand}; +pub use ffi::{BuildFfiCommand, TestFfiCommand}; +pub use go::TestGoCommand; +pub use java::{BuildJavaCommand, TestJavaCommand}; +pub use python::{BuildPythonCommand, TestPythonCommand}; +pub use ruby::TestRubyCommand; +pub use version::BindingsCommand; +pub use wasm::{BuildWasmCommand, TestWasmCommand}; diff --git a/xtask/src/tasks/bindings/python.rs b/xtask/src/tasks/bindings/python.rs new file mode 100644 index 00000000..59eb2431 --- /dev/null +++ b/xtask/src/tasks/bindings/python.rs @@ -0,0 +1,283 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use std::ffi::OsString; +use std::fs; +use std::path::{Path, PathBuf}; +use std::process::Command; + +use anyhow::{anyhow, bail, Context, Result}; +use clap::Args; + +use crate::tasks::util::{path_separator, run_cargo_step, run_command, workspace_root}; + +const MIN_PYTHON_VERSION: (u32, u32) = (3, 10); + +#[derive(Args, Default)] +pub struct BuildPythonCommand { + /// Optimise the wheel artefacts (defaults to debug builds). + #[arg(long)] + pub release: bool, + + /// Cross-compile for the provided Rust target triple. + #[arg(long, value_name = "TRIPLE")] + pub target: Option, + + /// Override the directory that receives built wheel files. + #[arg(long, value_name = "PATH")] + pub target_dir: Option, + + /// Propagate --frozen to cargo invocations prior to packaging. + #[arg(long)] + pub frozen: bool, +} + +impl BuildPythonCommand { + pub fn run(&self) -> Result<()> { + let workspace = workspace_root(); + let python_dir = workspace.join("bindings/python"); + + build_python_crate( + &python_dir, + self.release, + self.target.as_deref(), + self.frozen, + )?; + + let wheel_dir = self + .target_dir + .clone() + .unwrap_or_else(|| python_dir.join("wheels")); + fs::create_dir_all(&wheel_dir).with_context(|| { + format!( + "failed to create Python wheel directory at {}", + wheel_dir.display() + ) + })?; + + let mut maturin = Command::new("maturin"); + maturin.current_dir(&python_dir); + maturin.arg("build"); + if self.release { + maturin.arg("--release"); + } + if let Some(target) = &self.target { + maturin.arg("--target"); + maturin.arg(target); + } + maturin.arg("--target-dir"); + maturin.arg(&wheel_dir); + run_command(maturin, "maturin build (bindings/python)") + } +} + +#[derive(Args, Default)] +pub struct TestPythonCommand { + /// Optimise the extension module used for testing (defaults to debug builds). + #[arg(long)] + pub release: bool, + + /// Cross-compile for the provided Rust target triple. + #[arg(long, value_name = "TRIPLE")] + pub target: Option, + + /// Python interpreter used to run the sample and tests. + #[arg(long, value_name = "EXE", default_value = "python3")] + pub python: String, +} + +impl TestPythonCommand { + pub fn run(&self) -> Result<()> { + let workspace = workspace_root(); + let python_dir = workspace.join("bindings/python"); + + let (venv_python, venv_dir) = ensure_virtual_env(&python_dir, &self.python)?; + + install_testing_dependencies(&venv_python)?; + + install_local_package(&python_dir, self.release, self.target.as_deref(), &venv_dir)?; + + let mut sample = Command::new(&venv_python); + sample.current_dir(&python_dir); + sample.arg("test.py"); + let sample_label = format!("{} test.py (bindings/python)", venv_python.display()); + run_command(sample, &sample_label)?; + + let mut pytest = Command::new(&venv_python); + pytest.current_dir(&python_dir); + pytest.arg("-m"); + pytest.arg("pytest"); + pytest.arg("test_extensions.py"); + let pytest_label = format!( + "{} -m pytest test_extensions.py (bindings/python)", + venv_python.display() + ); + run_command(pytest, &pytest_label) + } +} + +fn install_testing_dependencies(venv_python: &Path) -> Result<()> { + let mut install = Command::new(venv_python); + install.arg("-m"); + install.arg("pip"); + install.arg("install"); + install.arg("pytest"); + let label = format!( + "{} -m pip install pytest (bindings/python)", + venv_python.display() + ); + run_command(install, &label) +} + +fn install_local_package( + python_dir: &Path, + release: bool, + target: Option<&str>, + venv_dir: &Path, +) -> Result<()> { + let mut maturin = Command::new("maturin"); + maturin.current_dir(python_dir); + maturin.arg("develop"); + if release { + maturin.arg("--release"); + } + if let Some(target) = target { + maturin.arg("--target"); + maturin.arg(target); + } + maturin.env("VIRTUAL_ENV", venv_dir); + let bin_dir = venv_bin_dir(venv_dir); + let mut path_value = bin_dir.clone().into_os_string(); + if let Some(existing) = std::env::var_os("PATH") { + if !existing.is_empty() { + path_value.push(path_separator()); + path_value.push(existing); + } + } + maturin.env("PATH", path_value); + run_command(maturin, "maturin develop (bindings/python)") +} + +fn build_python_crate( + python_dir: &Path, + release: bool, + target: Option<&str>, + frozen: bool, +) -> Result<()> { + let mut args = Vec::new(); + args.push(OsString::from("build")); + if release { + args.push(OsString::from("--release")); + } + if let Some(target) = target { + args.push(OsString::from("--target")); + args.push(OsString::from(target)); + } + if frozen { + args.push(OsString::from("--frozen")); + } + args.push(OsString::from("--locked")); + run_cargo_step(python_dir, "cargo build (bindings/python)", args) +} + +fn ensure_virtual_env(python_dir: &Path, python: &str) -> Result<(PathBuf, PathBuf)> { + ensure_python_version(std::ffi::OsStr::new(python), python)?; + + let venv_dir = python_dir.join(".venv"); + if venv_dir.exists() { + let existing_python = venv_bin_dir(&venv_dir).join(venv_python_name()); + if existing_python.exists() + && ensure_python_version( + existing_python.as_os_str(), + &existing_python.display().to_string(), + ) + .is_ok() + { + return Ok((existing_python, venv_dir)); + } + + fs::remove_dir_all(&venv_dir).with_context(|| { + format!( + "failed to remove incompatible virtual environment at {}", + venv_dir.display() + ) + })?; + } + + let mut create = Command::new(python); + create.current_dir(python_dir); + create.arg("-m"); + create.arg("venv"); + create.arg(".venv"); + let label = format!("{} -m venv .venv (bindings/python)", python); + run_command(create, &label)?; + + let python_path = venv_bin_dir(&venv_dir).join(venv_python_name()); + ensure_python_version(python_path.as_os_str(), &python_path.display().to_string())?; + + Ok((python_path, venv_dir)) +} + +fn venv_bin_dir(venv_dir: &Path) -> PathBuf { + if cfg!(windows) { + venv_dir.join("Scripts") + } else { + venv_dir.join("bin") + } +} + +fn venv_python_name() -> &'static str { + if cfg!(windows) { + "python.exe" + } else { + "python" + } +} + +fn ensure_python_version(executable: &std::ffi::OsStr, label: &str) -> Result<()> { + let (major, minor) = query_python_version(executable, label)?; + if (major, minor) < MIN_PYTHON_VERSION { + bail!( + "Python interpreter {} reports version {}.{}; the bindings require Python >= {}.{}. Use --python to provide a compatible interpreter.", + label, + major, + minor, + MIN_PYTHON_VERSION.0, + MIN_PYTHON_VERSION.1 + ); + } + Ok(()) +} + +fn query_python_version(executable: &std::ffi::OsStr, label: &str) -> Result<(u32, u32)> { + let output = Command::new(executable) + .arg("-c") + .arg("import sys; print(f'{sys.version_info[0]}.{sys.version_info[1]}')") + .output() + .with_context(|| format!("failed to query Python version from {}", label))?; + + if !output.status.success() { + bail!( + "{} -c 'import sys; ...' exited with status {}", + label, + output.status + ); + } + + let stdout = String::from_utf8(output.stdout) + .with_context(|| format!("failed to decode Python version output from {}", label))?; + let trimmed = stdout.trim(); + let mut parts = trimmed.split('.'); + let major = parts + .next() + .ok_or_else(|| anyhow!("missing major version in '{}'", trimmed))? + .parse::() + .with_context(|| format!("failed to parse major version from '{}'", trimmed))?; + let minor = parts + .next() + .ok_or_else(|| anyhow!("missing minor version in '{}'", trimmed))? + .parse::() + .with_context(|| format!("failed to parse minor version from '{}'", trimmed))?; + + Ok((major, minor)) +} diff --git a/xtask/src/tasks/bindings/ruby.rs b/xtask/src/tasks/bindings/ruby.rs new file mode 100644 index 00000000..86e7bc1d --- /dev/null +++ b/xtask/src/tasks/bindings/ruby.rs @@ -0,0 +1,72 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use std::ffi::OsString; +use std::process::Command; + +use anyhow::Result; +use clap::Args; + +use crate::tasks::util::{run_cargo_step, run_command, workspace_root}; + +/// Runs the Ruby binding smoke tests. +#[derive(Args, Default)] +pub struct TestRubyCommand { + /// Skip installing the bundler gem before running tests. + #[arg(long)] + pub skip_bundler_install: bool, + + /// Skip the Rust clippy pass prior to executing the Ruby test suite. + #[arg(long)] + pub skip_clippy: bool, + + /// Propagate --frozen to cargo invocations ahead of the Ruby test suite. + #[arg(long)] + pub frozen: bool, +} + +impl TestRubyCommand { + pub fn run(&self) -> Result<()> { + let workspace = workspace_root(); + let ruby_dir = workspace.join("bindings/ruby"); + + if !self.skip_bundler_install { + let mut gem = Command::new("gem"); + gem.current_dir(&ruby_dir); + gem.arg("install"); + gem.arg("bundler"); + gem.arg("--user-install"); + run_command(gem, "gem install bundler (bindings/ruby)")?; + } + + let mut bundle_install = Command::new("bundle"); + bundle_install.current_dir(&ruby_dir); + bundle_install.arg("install"); + run_command(bundle_install, "bundle install (bindings/ruby)")?; + + if !self.skip_clippy { + let mut clippy_args = vec![ + OsString::from("clippy"), + OsString::from("--all-targets"), + OsString::from("--no-deps"), + ]; + if self.frozen { + clippy_args.insert(1, OsString::from("--frozen")); + } + clippy_args.push(OsString::from("--")); + clippy_args.push(OsString::from("-Dwarnings")); + + run_cargo_step( + &ruby_dir, + "cargo clippy --all-targets --no-deps -- -Dwarnings (bindings/ruby)", + clippy_args, + )?; + } + + let mut rake = Command::new("bundle"); + rake.current_dir(&ruby_dir); + rake.arg("exec"); + rake.arg("rake"); + run_command(rake, "bundle exec rake (bindings/ruby)") + } +} diff --git a/xtask/src/tasks/bindings.rs b/xtask/src/tasks/bindings/version.rs similarity index 100% rename from xtask/src/tasks/bindings.rs rename to xtask/src/tasks/bindings/version.rs diff --git a/xtask/src/tasks/bindings/wasm.rs b/xtask/src/tasks/bindings/wasm.rs new file mode 100644 index 00000000..1b282b5e --- /dev/null +++ b/xtask/src/tasks/bindings/wasm.rs @@ -0,0 +1,115 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use std::path::{Path, PathBuf}; +use std::process::Command; + +use anyhow::Result; +use clap::Args; + +use crate::tasks::util::{run_command, workspace_root}; + +#[derive(Args, Default)] +pub struct BuildWasmCommand { + /// Optimise the generated WASM artefacts (defaults to debug builds). + #[arg(long)] + pub release: bool, + + /// Target environment passed to wasm-pack (defaults to nodejs). + #[arg(long, value_name = "TARGET", default_value = "nodejs")] + pub target: String, + + /// Override the directory that receives wasm-pack outputs. + #[arg(long, value_name = "PATH")] + pub out_dir: Option, +} + +impl BuildWasmCommand { + pub fn run(&self) -> Result<()> { + let workspace = workspace_root(); + let wasm_dir = workspace.join("bindings/wasm"); + build_wasm( + &wasm_dir, + self.release, + &self.target, + self.out_dir.as_deref(), + ) + } +} + +#[derive(Args, Default)] +pub struct TestWasmCommand { + /// Optimise the generated WASM artefacts (defaults to debug builds). + #[arg(long)] + pub release: bool, + + /// Target environment passed to wasm-pack (defaults to nodejs). + #[arg(long, value_name = "TARGET", default_value = "nodejs")] + pub target: String, + + /// Override the directory that receives wasm-pack outputs. + #[arg(long, value_name = "PATH")] + pub out_dir: Option, + + /// Node.js executable used for the sample script. + #[arg(long, value_name = "EXE", default_value = "node")] + pub node: String, + + /// Accepted for compatibility with CI; ignored because wasm-pack controls builds. + #[arg(long)] + pub frozen: bool, + + /// Skip rebuilding the wasm artefacts before running the sample. + #[arg(long)] + pub skip_build: bool, +} + +impl TestWasmCommand { + pub fn run(&self) -> Result<()> { + let workspace = workspace_root(); + let wasm_dir = workspace.join("bindings/wasm"); + + if !self.skip_build { + build_wasm( + &wasm_dir, + self.release, + &self.target, + self.out_dir.as_deref(), + )?; + } + + run_wasm_tests(&wasm_dir, self.release)?; + + let mut node = Command::new(&self.node); + node.current_dir(&wasm_dir); + node.arg("test.js"); + let label = format!("{} test.js (bindings/wasm)", self.node); + run_command(node, &label) + } +} + +fn build_wasm(wasm_dir: &Path, release: bool, target: &str, out_dir: Option<&Path>) -> Result<()> { + let mut pack = Command::new("wasm-pack"); + pack.current_dir(wasm_dir); + pack.arg("build"); + pack.arg("--target"); + pack.arg(target); + if release { + pack.arg("--release"); + } + if let Some(out_dir) = out_dir { + pack.arg("--out-dir"); + pack.arg(out_dir); + } + run_command(pack, "wasm-pack build (bindings/wasm)") +} +fn run_wasm_tests(wasm_dir: &Path, release: bool) -> Result<()> { + let mut test = Command::new("wasm-pack"); + test.current_dir(wasm_dir); + test.arg("test"); + test.arg("--node"); + if release { + test.arg("--release"); + } + run_command(test, "wasm-pack test --node (bindings/wasm)") +} diff --git a/xtask/src/tasks/ci/mod.rs b/xtask/src/tasks/ci/mod.rs new file mode 100644 index 00000000..7250a0db --- /dev/null +++ b/xtask/src/tasks/ci/mod.rs @@ -0,0 +1,366 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use std::ffi::OsString; +use std::path::Path; + +use anyhow::Result; +use clap::Args; + +use crate::tasks::util::{ + opa_passing_arguments, run_cargo_step as util_run_cargo_step, workspace_root, +}; + +pub mod musl; + +pub use musl::TestMuslCommand; + +const DEFAULT_OPA_FEATURES: &str = "opa-testutil,serde_json/arbitrary_precision"; + +/// Mirrors the release-focused GitHub workflow locally. +#[derive(Args, Default)] +pub struct CiReleaseCommand { + /// Propagate --frozen to cargo invocations (match CI by passing --frozen). + #[arg(long)] + pub frozen: bool, + + /// Enable the supplied feature list across build and test steps (comma separated). + #[arg(long, value_delimiter = ',', value_name = "FEATURE")] + pub features: Vec, + + /// Override the feature set passed to the OPA tests. + #[arg(long, value_name = "FEATURES")] + pub opa_features: Option, + + /// Skip the `cargo build --all-features` phase. + #[arg(long)] + pub skip_all_features_build: bool, + + /// Skip the `cargo test --no-default-features` phase. + #[arg(long)] + pub skip_no_default_features_tests: bool, + + /// Skip the Azure Policy feature tests. + #[arg(long)] + pub skip_azure_policy: bool, + + /// Skip the Azure RBAC feature tests. + #[arg(long)] + pub skip_azure_rbac: bool, +} + +/// Mirrors the debug-focused GitHub workflow locally. +#[derive(Args, Default)] +pub struct CiDebugCommand { + /// Propagate --frozen to cargo invocations (match CI by passing --frozen). + #[arg(long)] + pub frozen: bool, +} + +impl CiReleaseCommand { + pub fn run(&self) -> Result<()> { + let opa_features = self + .opa_features + .clone() + .unwrap_or_else(|| DEFAULT_OPA_FEATURES.to_string()); + + run_ci_suite(CiSuiteConfig { + release: true, + frozen: self.frozen, + include_fmt: true, + include_run_example: true, + include_azure_policy: !self.skip_azure_policy, + include_azure_rbac: !self.skip_azure_rbac, + include_all_features_build: !self.skip_all_features_build, + include_no_default_features_tests: !self.skip_no_default_features_tests, + base_features: self.features.clone(), + opa_features, + }) + } +} + +impl CiDebugCommand { + pub fn run(&self) -> Result<()> { + run_ci_suite(CiSuiteConfig { + release: false, + frozen: self.frozen, + include_fmt: false, + include_run_example: false, + include_azure_policy: false, + include_azure_rbac: true, + include_all_features_build: true, + include_no_default_features_tests: true, + base_features: Vec::new(), + opa_features: DEFAULT_OPA_FEATURES.to_string(), + }) + } +} + +struct CiSuiteConfig { + release: bool, + frozen: bool, + include_fmt: bool, + include_run_example: bool, + include_azure_policy: bool, + include_azure_rbac: bool, + include_all_features_build: bool, + include_no_default_features_tests: bool, + base_features: Vec, + opa_features: String, +} + +fn run_ci_suite(config: CiSuiteConfig) -> Result<()> { + let workspace = workspace_root(); + let joined_features = join_features(&config.base_features); + + if config.include_fmt { + run_fmt(&workspace)?; + } + + if config.include_all_features_build { + run_ci_cargo_step( + &workspace, + "build", + config.release, + config.frozen, + None, + &["--all-features"], + "cargo build --all-features (ci)", + )?; + } + + run_ci_cargo_step( + &workspace, + "build", + config.release, + config.frozen, + joined_features.as_deref(), + &[], + "cargo build (ci)", + )?; + + if config.include_no_default_features_tests { + run_ci_cargo_step( + &workspace, + "test", + config.release, + config.frozen, + None, + &["--no-default-features"], + "cargo test --no-default-features (ci)", + )?; + } + + let example_features = example_features(&config.base_features); + let example_label = format!( + "cargo build --example regorus --no-default-features --features {} (ci)", + example_features + ); + run_ci_cargo_step( + &workspace, + "build", + config.release, + config.frozen, + Some(&example_features), + &["--example", "regorus", "--no-default-features"], + &example_label, + )?; + + run_ci_cargo_step( + &workspace, + "test", + config.release, + config.frozen, + joined_features.as_deref(), + &["--doc"], + "cargo test --doc (ci)", + )?; + + run_ci_cargo_step( + &workspace, + "test", + config.release, + config.frozen, + joined_features.as_deref(), + &[], + "cargo test (ci)", + )?; + + if config.include_run_example { + run_example( + &workspace, + config.release, + config.frozen, + joined_features.as_deref(), + )?; + } + + run_named_test( + &workspace, + config.release, + config.frozen, + joined_features.as_deref(), + "aci", + )?; + + run_named_test( + &workspace, + config.release, + config.frozen, + joined_features.as_deref(), + "kata", + )?; + + run_opa_tests( + &workspace, + config.release, + config.frozen, + &config.opa_features, + )?; + + if config.include_azure_policy { + run_ci_cargo_step( + &workspace, + "test", + config.release, + config.frozen, + None, + &["--features", "azure_policy"], + "cargo test --features azure_policy (ci)", + )?; + } + + if config.include_azure_rbac { + run_ci_cargo_step( + &workspace, + "test", + config.release, + config.frozen, + None, + &["--features", "azure-rbac"], + "cargo test --features azure-rbac (ci)", + )?; + } + + Ok(()) +} + +fn join_features(features: &[String]) -> Option { + if features.is_empty() { + None + } else { + Some(features.join(",")) + } +} + +fn example_features(base: &[String]) -> String { + let mut features = Vec::with_capacity(base.len() + 1); + features.push(String::from("std")); + features.extend(base.iter().cloned()); + features.join(",") +} + +fn run_fmt(workspace: &Path) -> Result<()> { + util_run_cargo_step( + workspace, + "cargo xtask fmt --check", + ["xtask", "fmt", "--check"], + ) +} + +fn run_ci_cargo_step( + workspace: &Path, + subcommand: &str, + release: bool, + frozen: bool, + features: Option<&str>, + extra: &[&str], + label: &str, +) -> Result<()> { + let mut args = base_cargo_args(subcommand, release, frozen, features); + for arg in extra { + args.push(OsString::from(*arg)); + } + util_run_cargo_step(workspace, label, args) +} + +fn run_named_test( + workspace: &Path, + release: bool, + frozen: bool, + features: Option<&str>, + name: &str, +) -> Result<()> { + let label = format!("cargo test --test {} (ci)", name); + run_ci_cargo_step( + workspace, + "test", + release, + frozen, + features, + &["--test", name], + &label, + ) +} + +fn run_example( + workspace: &Path, + release: bool, + frozen: bool, + features: Option<&str>, +) -> Result<()> { + let mut args = base_cargo_args("run", release, frozen, features); + args.extend( + [ + "--example", + "regorus", + "--", + "eval", + "-d", + "examples/server/allowed_server.rego", + "-i", + "examples/server/input.json", + "data.example", + ] + .into_iter() + .map(OsString::from), + ); + + util_run_cargo_step(workspace, "cargo run --example regorus (ci)", args) +} + +fn run_opa_tests(workspace: &Path, release: bool, frozen: bool, features: &str) -> Result<()> { + let tests = opa_passing_arguments(workspace)?; + let mut args = base_cargo_args("test", release, frozen, Some(features)); + args.push(OsString::from("--test")); + args.push(OsString::from("opa")); + args.push(OsString::from("--")); + for entry in tests { + args.push(OsString::from(entry)); + } + + let label = format!("cargo test --test opa --features {} (ci)", features); + util_run_cargo_step(workspace, &label, args) +} + +fn base_cargo_args( + subcommand: &str, + release: bool, + frozen: bool, + features: Option<&str>, +) -> Vec { + let mut args = vec![OsString::from(subcommand)]; + if release { + args.push(OsString::from("--release")); + } + if frozen { + args.push(OsString::from("--frozen")); + } + if let Some(features) = features { + if !features.is_empty() { + args.push(OsString::from("--features")); + args.push(OsString::from(features)); + } + } + args +} diff --git a/xtask/src/tasks/ci/musl.rs b/xtask/src/tasks/ci/musl.rs new file mode 100644 index 00000000..24f9aa6d --- /dev/null +++ b/xtask/src/tasks/ci/musl.rs @@ -0,0 +1,150 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use std::ffi::OsString; +use std::path::Path; +use std::process::{Command, Stdio}; + +use anyhow::{anyhow, Result}; +use clap::Args; + +use crate::tasks::util::{ + opa_passing_arguments, run_cargo_step as util_run_cargo_step, workspace_root, +}; + +/// Exercises the MUSL build and test matrix used in CI. +#[derive(Args, Default)] +pub struct TestMuslCommand { + /// Target triple to compile and test against. + #[arg(long, default_value = "x86_64-unknown-linux-musl")] + pub target: String, + + /// Compile artefacts in release mode. + #[arg(long)] + pub release: bool, + + /// Propagate --frozen to all cargo invocations. + #[arg(long)] + pub frozen: bool, + + /// Feature list passed to the OPA conformance tests. + #[arg(long, default_value = "opa-testutil,serde_json/arbitrary_precision")] + pub opa_features: String, +} + +impl TestMuslCommand { + pub fn run(&self) -> Result<()> { + ensure_musl_gcc()?; + + let workspace = workspace_root(); + + run_build_all_targets(&workspace, &self.target, self.release, self.frozen)?; + run_cargo_test(&workspace, &self.target, self.release, self.frozen, &[])?; + run_named_test(&workspace, &self.target, self.release, self.frozen, "aci")?; + run_named_test(&workspace, &self.target, self.release, self.frozen, "kata")?; + run_opa_tests( + &workspace, + &self.target, + self.release, + self.frozen, + &self.opa_features, + ) + } +} + +fn ensure_musl_gcc() -> Result<()> { + let status = Command::new("musl-gcc") + .arg("--version") + .stdout(Stdio::null()) + .stderr(Stdio::null()) + .status(); + + match status { + Ok(result) if result.success() => Ok(()), + _ => Err(anyhow!( + "musl-gcc is required but was not found in PATH. Install musl-tools to continue." + )), + } +} +fn run_build_all_targets( + workspace: &Path, + target: &str, + release: bool, + frozen: bool, +) -> Result<()> { + let mut args = cargo_target_args("build", target, release, frozen); + args.push(OsString::from("--all-targets")); + util_run_cargo_step(workspace, "cargo build --all-targets (musl)", args) +} + +fn run_cargo_test( + workspace: &Path, + target: &str, + release: bool, + frozen: bool, + extra: &[&str], +) -> Result<()> { + let mut args = cargo_target_args("test", target, release, frozen); + for arg in extra { + args.push(OsString::from(*arg)); + } + let mut label = String::from("cargo test (musl)"); + if release { + label.push_str(" --release"); + } + if !extra.is_empty() { + label.push(' '); + label.push_str(&extra.join(" ")); + } + util_run_cargo_step(workspace, &label, args) +} + +fn run_named_test( + workspace: &Path, + target: &str, + release: bool, + frozen: bool, + name: &str, +) -> Result<()> { + run_cargo_test(workspace, target, release, frozen, &["--test", name]) +} + +fn run_opa_tests( + workspace: &Path, + target: &str, + release: bool, + frozen: bool, + features: &str, +) -> Result<()> { + let tests = opa_passing_arguments(workspace)?; + let mut args = cargo_target_args("test", target, release, frozen); + args.push(OsString::from("--features")); + args.push(OsString::from(features)); + args.push(OsString::from("--test")); + args.push(OsString::from("opa")); + args.push(OsString::from("--")); + for entry in tests { + args.push(OsString::from(entry)); + } + + let mut label = format!("cargo test --test opa --features {} (musl)", features); + if release { + label.push_str(" --release"); + } + + util_run_cargo_step(workspace, &label, args) +} + +fn cargo_target_args(subcommand: &str, target: &str, release: bool, frozen: bool) -> Vec { + let mut args = vec![OsString::from(subcommand)]; + if release { + args.push(OsString::from("--release")); + } + if frozen { + args.push(OsString::from("--frozen")); + } + args.push(OsString::from("--locked")); + args.push(OsString::from("--target")); + args.push(OsString::from(target)); + args +} diff --git a/xtask/src/tasks/dev/clippy.rs b/xtask/src/tasks/dev/clippy.rs new file mode 100644 index 00000000..a16ac624 --- /dev/null +++ b/xtask/src/tasks/dev/clippy.rs @@ -0,0 +1,141 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use std::fs::File; +use std::path::{Path, PathBuf}; +use std::process::{Command, Stdio}; + +use anyhow::{Context, Result}; +use clap::Args; + +use crate::tasks::util::{run_cargo_step, run_command, workspace_root}; + +const BINDING_MANIFESTS: &[&str] = &[ + "bindings/ffi/Cargo.toml", + "bindings/java/Cargo.toml", + "bindings/python/Cargo.toml", + "bindings/wasm/Cargo.toml", + // TODO: Reenable this once Ruby binding is actively maintained + //"bindings/ruby/Cargo.toml", +]; + +/// Runs Clippy across the workspace and binding crates with CI-equivalent checks. +#[derive(Args, Default)] +pub struct ClippyCommand { + /// Emit SARIF output to the supplied path (relative to the workspace by default). + #[arg(long, value_name = "PATH")] + pub sarif: Option, +} + +impl ClippyCommand { + pub fn run(&self) -> Result<()> { + let workspace = workspace_root(); + + match self.sarif.as_ref() { + Some(sarif) => run_clippy_with_sarif(&workspace, sarif)?, + None => { + run_cargo_step( + &workspace, + "cargo clippy --all-targets --all-features -- -Dwarnings", + [ + "clippy", + "--all-targets", + "--all-features", + "--", + "-Dwarnings", + ], + )?; + } + } + + run_cargo_step( + &workspace, + "cargo clippy --no-default-features -- -Dwarnings", + ["clippy", "--no-default-features", "--", "-Dwarnings"], + )?; + + for manifest in BINDING_MANIFESTS { + let label = + format!("cargo clippy --manifest-path {manifest} --all-targets -- -Dwarnings"); + run_cargo_step( + &workspace, + &label, + [ + "clippy", + "--manifest-path", + manifest, + "--all-targets", + "--", + "-Dwarnings", + ], + )?; + } + + Ok(()) + } +} + +fn run_clippy_with_sarif(workspace: &Path, sarif: &Path) -> Result<()> { + let sarif_path = if sarif.is_absolute() { + sarif.to_path_buf() + } else { + workspace.join(sarif) + }; + + if let Some(parent) = sarif_path.parent() { + std::fs::create_dir_all(parent) + .with_context(|| format!("failed to create SARIF directory at {}", parent.display()))?; + } + + let xtask_target = workspace.join("target").join("xtask"); + std::fs::create_dir_all(&xtask_target).with_context(|| { + format!( + "failed to ensure xtask scratch directory at {}", + xtask_target.display() + ) + })?; + let json_path = xtask_target.join("clippy.json"); + + let json_file = File::create(&json_path).with_context(|| { + format!( + "failed to create intermediate clippy output at {}", + json_path.display() + ) + })?; + + let mut cargo = Command::new("cargo"); + cargo.current_dir(workspace); + cargo.arg("clippy"); + cargo.arg("--all-targets"); + cargo.arg("--all-features"); + cargo.arg("--message-format=json"); + cargo.arg("--"); + cargo.arg("-Dwarnings"); + cargo.stdout(Stdio::from(json_file)); + run_command( + cargo, + "cargo clippy --all-targets --all-features --message-format=json -- -Dwarnings", + )?; + + let json_input = File::open(&json_path).with_context(|| { + format!( + "failed to reopen clippy JSON output at {}", + json_path.display() + ) + })?; + let sarif_file = File::create(&sarif_path) + .with_context(|| format!("failed to create SARIF output at {}", sarif_path.display()))?; + + let mut sarif_cmd = Command::new("clippy-sarif"); + sarif_cmd.stdin(Stdio::from(json_input)); + sarif_cmd.stdout(Stdio::from(sarif_file)); + run_command(sarif_cmd, "clippy-sarif")?; + + let mut fmt = Command::new("sarif-fmt"); + fmt.arg(&sarif_path); + run_command(fmt, "sarif-fmt")?; + + println!("SARIF report written to {}", sarif_path.display()); + + Ok(()) +} diff --git a/xtask/src/tasks/dev/fmt.rs b/xtask/src/tasks/dev/fmt.rs new file mode 100644 index 00000000..7a70f024 --- /dev/null +++ b/xtask/src/tasks/dev/fmt.rs @@ -0,0 +1,56 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use anyhow::Result; +use clap::Args; + +use crate::tasks::util::{run_cargo_step, workspace_root}; + +const BINDING_MANIFESTS: &[&str] = &[ + "bindings/ffi/Cargo.toml", + "bindings/java/Cargo.toml", + "bindings/python/Cargo.toml", + "bindings/wasm/Cargo.toml", + "bindings/ruby/Cargo.toml", +]; + +/// Formats every Rust crate in the repository, including binding workspaces. +#[derive(Args, Default)] +pub struct FmtCommand { + /// Fail instead of writing edits when formatting differs + #[arg(long)] + check: bool, +} + +impl FmtCommand { + pub fn run(&self) -> Result<()> { + let workspace = workspace_root(); + + let mut fmt_args = vec!["fmt", "--all"]; + if self.check { + fmt_args.extend(["--", "--check"]); + } + + let fmt_label = if self.check { + "cargo fmt --all -- --check" + } else { + "cargo fmt --all" + }; + + run_cargo_step(&workspace, fmt_label, fmt_args)?; + + for manifest in BINDING_MANIFESTS { + let mut args = vec!["fmt", "--manifest-path", *manifest]; + let label = if self.check { + args.extend(["--", "--check"]); + format!("cargo fmt --manifest-path {manifest} -- --check") + } else { + format!("cargo fmt --manifest-path {manifest}") + }; + + run_cargo_step(&workspace, &label, args)?; + } + + Ok(()) + } +} diff --git a/xtask/src/tasks/dev/mod.rs b/xtask/src/tasks/dev/mod.rs new file mode 100644 index 00000000..85b12ecd --- /dev/null +++ b/xtask/src/tasks/dev/mod.rs @@ -0,0 +1,12 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +pub mod clippy; +pub mod fmt; +pub mod precommit; +pub mod prepush; + +pub use clippy::ClippyCommand; +pub use fmt::FmtCommand; +pub use precommit::PrecommitCommand; +pub use prepush::PrepushCommand; diff --git a/xtask/src/tasks/dev/precommit.rs b/xtask/src/tasks/dev/precommit.rs new file mode 100644 index 00000000..5171033e --- /dev/null +++ b/xtask/src/tasks/dev/precommit.rs @@ -0,0 +1,90 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use std::collections::HashSet; +use std::io::Write; +use std::process::{Command, Stdio}; + +use anyhow::{anyhow, Context, Result}; +use clap::Args; + +use crate::tasks::util::{log_step, run_cargo_step, workspace_root}; + +/// Runs the repository's pre-commit validation sequence. +#[derive(Args, Default)] +pub struct PrecommitCommand; + +impl PrecommitCommand { + pub fn run(&self) -> Result<()> { + let workspace = workspace_root(); + + log_step("cargo build --all-targets"); + run_cargo_step( + &workspace, + "cargo build --all-targets", + ["build", "--all-targets"], + ) + .with_context(|| "pre-commit: cargo build --all-targets failed".to_string())?; + + run_cargo_step( + &workspace, + "cargo xtask fmt --check", + ["xtask", "fmt", "--check"], + ) + .with_context(|| "pre-commit: cargo xtask fmt --check failed".to_string())?; + + run_cargo_step(&workspace, "cargo xtask clippy", ["xtask", "clippy"]) + .with_context(|| "pre-commit: cargo xtask clippy failed".to_string())?; + + log_step("git status --short"); + verify_clean_status(&workspace)?; + + Ok(()) + } +} + +fn verify_clean_status(root: &std::path::Path) -> Result<()> { + let mut git = Command::new("git"); + git.current_dir(root); + git.arg("status"); + git.arg("-s"); + git.stdout(Stdio::piped()); + + let output = git + .output() + .with_context(|| format!("failed to inspect git status in {}", root.display()))?; + + if !output.status.success() { + return Err(anyhow!( + "git status -s exited with status {}", + output.status + )); + } + + let stdout = String::from_utf8(output.stdout) + .with_context(|| "git status output was not UTF-8".to_string())?; + + let interesting: HashSet<&str> = ["MM", "??", "AM", " M"].into_iter().collect(); + let mut flagged = Vec::new(); + for line in stdout.lines() { + if line.len() >= 2 { + let status = &line[..2]; + if interesting.contains(status) { + flagged.push(line.to_string()); + } + } + } + + if flagged.is_empty() { + return Ok(()); + } + + let mut stderr = std::io::stderr(); + writeln!(stderr, "\nUnstaged changes found:").ok(); + for entry in &flagged { + writeln!(stderr, "{}", entry).ok(); + } + writeln!(stderr, "Stage them and try again").ok(); + + Err(anyhow!("repository contains unstaged changes")) +} diff --git a/xtask/src/tasks/dev/prepush.rs b/xtask/src/tasks/dev/prepush.rs new file mode 100644 index 00000000..669e5979 --- /dev/null +++ b/xtask/src/tasks/dev/prepush.rs @@ -0,0 +1,149 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use std::path::Path; +use std::process::{Command, Stdio}; + +use anyhow::{Context, Result}; +use clap::Args; + +use super::precommit::PrecommitCommand; +use crate::tasks::util::{ + log_step, opa_passing_arguments, run_cargo_step, run_command, workspace_root, +}; + +/// Runs the repository's pre-push validation sequence. +#[derive(Args, Default)] +pub struct PrepushCommand; + +impl PrepushCommand { + pub fn run(&self) -> Result<()> { + let workspace = workspace_root(); + + log_step("pre-commit checks"); + PrecommitCommand::default() + .run() + .with_context(|| "pre-push: pre-commit sequence failed".to_string())?; + + run_cargo_step(&workspace, "cargo test --doc", ["test", "--doc"]) + .with_context(|| "pre-push: cargo test --doc failed".to_string())?; + + if rustup_available() { + log_step("rustup target add thumbv7m-none-eabi"); + let mut rustup = Command::new("rustup"); + rustup.arg("target"); + rustup.arg("add"); + rustup.arg("thumbv7m-none-eabi"); + run_command(rustup, "rustup target add thumbv7m-none-eabi") + .with_context(|| "pre-push: rustup target add failed".to_string())?; + + run_cargo_step( + &workspace, + "cargo xtask test-no-std", + ["xtask", "test-no-std"], + ) + .with_context(|| "pre-push: cargo xtask test-no-std failed".to_string())?; + } + + run_cargo_step( + &workspace, + "cargo build --example regorus --no-default-features --features std", + [ + "build", + "--example", + "regorus", + "--no-default-features", + "--features", + "std", + ], + ) + .with_context(|| { + "pre-push: cargo build --example regorus --no-default-features --features std failed" + .to_string() + })?; + + run_cargo_step( + &workspace, + "cargo build --all-features", + ["build", "--all-features"], + ) + .with_context(|| "pre-push: cargo build --all-features failed".to_string())?; + + run_cargo_step(&workspace, "cargo test", ["test"]) + .with_context(|| "pre-push: cargo test failed".to_string())?; + run_cargo_step( + &workspace, + "cargo test --test aci", + ["test", "--test", "aci"], + ) + .with_context(|| "pre-push: cargo test --test aci failed".to_string())?; + run_cargo_step( + &workspace, + "cargo test --test kata", + ["test", "--test", "kata"], + ) + .with_context(|| "pre-push: cargo test --test kata failed".to_string())?; + + run_cargo_step( + &workspace, + "cargo test --features rego-extensions", + ["test", "--features", "rego-extensions"], + ) + .with_context(|| "pre-push: cargo test --features rego-extensions failed".to_string())?; + run_cargo_step( + &workspace, + "cargo test --test aci --features rego-extensions", + ["test", "--test", "aci", "--features", "rego-extensions"], + ) + .with_context(|| { + "pre-push: cargo test --test aci --features rego-extensions failed".to_string() + })?; + run_cargo_step( + &workspace, + "cargo test --test kata --features rego-extensions", + ["test", "--test", "kata", "--features", "rego-extensions"], + ) + .with_context(|| { + "pre-push: cargo test --test kata --features rego-extensions failed".to_string() + })?; + + log_step("cargo test --features opa-testutil,serde_json/arbitrary_precision,rego-extensions --test opa"); + run_opa_conformance(&workspace).with_context(|| { + "pre-push: cargo test --features opa-testutil,serde_json/arbitrary_precision,rego-extensions --test opa failed" + .to_string() + })?; + + Ok(()) + } +} + +fn run_opa_conformance(root: &Path) -> Result<()> { + let tests = opa_passing_arguments(root)?; + let mut cmd = Command::new("cargo"); + cmd.current_dir(root); + cmd.arg("test"); + cmd.arg("--features"); + cmd.arg("opa-testutil,serde_json/arbitrary_precision,rego-extensions"); + cmd.arg("--test"); + cmd.arg("opa"); + cmd.arg("--"); + for entry in tests { + cmd.arg(entry); + } + + run_command( + cmd, + "cargo test --features opa-testutil,serde_json/arbitrary_precision,rego-extensions --test opa", + ) +} + +fn rustup_available() -> bool { + let mut probe = Command::new("rustup"); + probe.arg("--version"); + probe.stdout(Stdio::null()); + probe.stderr(Stdio::null()); + probe + .status() + .map(|status| status.success()) + .unwrap_or(false) +} diff --git a/xtask/src/tasks/mod.rs b/xtask/src/tasks/mod.rs index 6f8a66e8..44fc8a33 100644 --- a/xtask/src/tasks/mod.rs +++ b/xtask/src/tasks/mod.rs @@ -2,7 +2,19 @@ // Licensed under the MIT License. pub mod bindings; +pub mod ci; +pub mod dev; +pub mod no_std; pub mod update_deps; +mod util; -pub use bindings::BindingsCommand; +pub use bindings::{ + BindingsCommand, BuildAllBindingsCommand, BuildFfiCommand, BuildJavaCommand, BuildNugetCommand, + BuildPythonCommand, BuildWasmCommand, TestAllBindingsCommand, TestCCommand, TestCNoStdCommand, + TestCppCommand, TestCsharpCommand, TestFfiCommand, TestGoCommand, TestJavaCommand, + TestPythonCommand, TestRubyCommand, TestWasmCommand, +}; +pub use ci::{CiDebugCommand, CiReleaseCommand, TestMuslCommand}; +pub use dev::{ClippyCommand, FmtCommand, PrecommitCommand, PrepushCommand}; +pub use no_std::TestNoStdCommand; pub use update_deps::UpdateDepsCommand; diff --git a/xtask/src/tasks/no_std.rs b/xtask/src/tasks/no_std.rs new file mode 100644 index 00000000..6e88b5fc --- /dev/null +++ b/xtask/src/tasks/no_std.rs @@ -0,0 +1,46 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use std::ffi::OsString; + +use anyhow::Result; +use clap::Args; + +use crate::tasks::util::{run_cargo_step, workspace_root}; + +/// Builds the ensure_no_std harness for an embedded target. +#[derive(Args, Default)] +pub struct TestNoStdCommand { + /// Target triple to compile (defaults to thumbv7m-none-eabi). + #[arg(long, default_value = "thumbv7m-none-eabi")] + pub target: String, + + /// Compile artefacts in release mode. + #[arg(long)] + pub release: bool, + + /// Propagate --frozen to the cargo invocations. + #[arg(long)] + pub frozen: bool, +} + +impl TestNoStdCommand { + pub fn run(&self) -> Result<()> { + let workspace = workspace_root(); + let project_dir = workspace.join("tests/ensure_no_std"); + + let mut args = vec![ + OsString::from("build"), + OsString::from("--target"), + OsString::from(&self.target), + ]; + if self.release { + args.push(OsString::from("--release")); + } + if self.frozen { + args.push(OsString::from("--frozen")); + } + run_cargo_step(&project_dir, "cargo build (tests/ensure_no_std)", args)?; + Ok(()) + } +} diff --git a/xtask/src/tasks/util.rs b/xtask/src/tasks/util.rs new file mode 100644 index 00000000..74881318 --- /dev/null +++ b/xtask/src/tasks/util.rs @@ -0,0 +1,151 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use std::ffi::{OsStr, OsString}; +use std::fs; +use std::path::{Path, PathBuf}; +use std::process::Command; + +use anyhow::{bail, Context, Result}; + +/// Returns the root of the workspace that hosts the xtask crate. +pub fn workspace_root() -> PathBuf { + PathBuf::from(env!("CARGO_MANIFEST_DIR")) + .parent() + .expect("xtask resides in workspace root") + .to_path_buf() +} + +/// Runs a command, surfaces the command line, and maps a failing exit status to an error. +pub fn run_command(mut command: Command, label: &str) -> Result<()> { + let display = format_command(&command); + println!("$ {}", display); + + let status = command + .status() + .with_context(|| format!("failed to spawn {}", label))?; + if !status.success() { + bail!("{} failed with status {}", label, status); + } + + Ok(()) +} + +/// Prints a simple section header to highlight the upcoming action. +pub fn log_step(title: &str) { + println!("\n=== {} ===", title); +} + +/// Logs a section heading and executes a cargo subcommand. +pub fn run_cargo_step(workspace: &Path, title: &str, args: I) -> Result<()> +where + I: IntoIterator, + S: AsRef, +{ + run_cargo_in_step(workspace, title, args) +} + +/// Logs a section heading and executes a cargo subcommand within the provided directory. +pub fn run_cargo_in_step(directory: &Path, title: &str, args: I) -> Result<()> +where + I: IntoIterator, + S: AsRef, +{ + let heading = format!("Running {}", title); + log_step(&heading); + let mut command = Command::new("cargo"); + command.current_dir(directory); + command.args(args); + run_command(command, title) +} + +/// Formats a command with its arguments for diagnostic output. +pub fn format_command(command: &Command) -> String { + let mut parts = Vec::new(); + parts.push(command.get_program().to_string_lossy().into_owned()); + for arg in command.get_args() { + parts.push(arg.to_string_lossy().into_owned()); + } + parts.join(" ") +} + +/// Deduplicates the provided vector while preserving the original order. +pub fn dedup(values: &mut Vec) { + let mut unique = Vec::new(); + for value in values.drain(..) { + if unique.iter().any(|existing| existing == &value) { + continue; + } + unique.push(value); + } + *values = unique; +} + +/// Returns the platform-specific path list separator character. +pub fn path_separator() -> &'static str { + if cfg!(windows) { + ";" + } else { + ":" + } +} + +/// Returns the environment variable used for dynamic library lookup. +pub fn library_search_env_var() -> &'static str { + if cfg!(windows) { + "PATH" + } else if cfg!(target_os = "macos") { + "DYLD_LIBRARY_PATH" + } else { + "LD_LIBRARY_PATH" + } +} + +/// Ensures the provided directory is prepended to the requested environment variable. +pub fn prepend_env_path(command: &mut Command, key: &str, dir: &Path) { + let mut value = OsString::new(); + value.push(dir); + + if let Some(existing) = std::env::var_os(key) { + if !existing.is_empty() { + value.push(path_separator()); + value.push(existing); + } + } + + command.env(key, value); +} + +/// Prepends the supplied directory to the standard dynamic library search path. +pub fn add_library_search_path(command: &mut Command, dir: &Path) { + let key = library_search_env_var(); + prepend_env_path(command, key, dir); +} + +/// Returns the host architecture string expected by dotnet CLI switches. +pub fn dotnet_host_arch() -> &'static str { + match std::env::consts::ARCH { + "aarch64" => "arm64", + "x86_64" => "x64", + "arm" => "arm", + "x86" => "x86", + _ => "x64", + } +} + +/// Loads the list of passing OPA tests from the repository. +pub fn opa_passing_arguments(root: &Path) -> Result> { + let listing = root.join("tests/opa.passing"); + let contents = fs::read_to_string(&listing).with_context(|| { + format!( + "failed to read list of passing OPA tests from {}", + listing.display() + ) + })?; + + Ok(contents + .split_whitespace() + .filter(|entry| !entry.is_empty()) + .map(|entry| entry.to_string()) + .collect()) +}