feat(xtask): consolidate CI workflows onto xtask helpers (#542)

- split the xtask crate into structured modules for
  - bindings
  - ci
  - dev
  - util
  - no-std
- Adding commands for
  - ci-release/ci-debug
  - MUSL/no-std
  - per- binding language smoke tests
  - developer tasks (fmt, clippy, pre-commit, pre-push)
- refresh Cargo manifests/locks, binding readmes, and shared FFI helpers so every binding reuses the same preparation steps
- refactor GitHub Actions (release/debug, extensions, CodeQL, clippy, bindings) to call the new xtask commands
- Use rust-cache in ci workflows (microsoft qdk also does this)
- extend README with a contributor workflow section describing how xtask mirrors CI expectations
- update pre-commit and pre-push hooks to use the xtask dev commands

WORKAROUND:
When dotnet is run from an xtask, codeql tracer intercepts it an routes to a nonexistent binary.
Therefore in codeql workflow, xtask is not used for c# and instead dotnet is directly invoked.
Tracked by #545

closes #475

Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
This commit is contained in:
Anand Krishnamoorthi
2026-01-27 07:42:21 +05:30
committed by GitHub
parent 2b1434b3ac
commit e68e852ee3
56 changed files with 3478 additions and 608 deletions

View File

@@ -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
cargo xtask test-c --release --frozen
cargo xtask test-cpp --release --frozen --skip-ffi
cargo xtask test-c-no-std --release --frozen --skip-ffi
# 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
- 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 .
run: cargo xtask test-go --release --frozen
- name: Build C# bindings
- 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

View File

@@ -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"

View File

@@ -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

View File

@@ -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 }}/

View File

@@ -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

View File

@@ -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

View File

@@ -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 binding via xtask
run: cargo xtask test-c --release --frozen
- name: Test c-nostd binding
run: |
mkdir bindings/c-nostd/build
cd bindings/c-nostd/build
cmake -G Ninja ..
ninja
./regorus_test
- name: Test C (no-std) binding via xtask
run: cargo xtask test-c-nostd --release --frozen --skip-ffi
- 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-cpp --release --frozen --skip-ffi

View File

@@ -11,7 +11,7 @@ on:
- cron: "0 8 * * *"
env:
VersionSuffix: ${{ github.run_id }}
VersionSuffix: ${{ github.event_name == 'workflow_dispatch' && 'manualtrigger' || null }}
jobs:
build-ffi:
@@ -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: 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 Regorus binding formatting
run: cargo fmt --check
working-directory: ./bindings/ffi
- name: Fetch FFI crate dependencies
run: cargo fetch --locked --manifest-path bindings/ffi/Cargo.toml --target ${{ matrix.runtime.target }}
- name: Check Clippy linting for Regorus binding
run: cargo clippy --frozen -- -D warnings
working-directory: ./bindings/ffi
- 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 /p:VersionSuffix=${{ env.VersionSuffix }}
working-directory: ./bindings/csharp/Regorus
- name: Pack
run: dotnet pack /p:RegorusFFIArtifactsDir=./tmp/bindings/ffi/target /p:VersionSuffix=${{ env.VersionSuffix }}
working-directory: ./bindings/csharp/Regorus
- name: Build Regorus nuget via xtask
run: cargo xtask build-csharp --release --clean --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,46 +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/
path: ./bindings/csharp/Regorus/bin/Release
- name: Restore Regorus.Tests
run: dotnet restore /p:UseLocalRegorus=false /p:VersionSuffix=${{ env.VersionSuffix }} /p:RestoreAdditionalProjectSources=../regorus-nuget
working-directory: ./bindings/csharp/Regorus.Tests
- name: Display regorus nuget
run: ls -R ./bindings/csharp/Regorus/bin/Release
- name: Run Regorus.Tests
run: dotnet test --no-restore /p:UseLocalRegorus=false /p:VersionSuffix=${{ env.VersionSuffix }}
working-directory: ./bindings/csharp/Regorus.Tests
- name: Run C# tests via xtask
run: cargo xtask test-csharp --release --clean --nuget-dir bindings/csharp/Regorus/bin/Release
- name: Restore TestApp
run: dotnet restore /p:VersionSuffix=${{ env.VersionSuffix }} /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:VersionSuffix=${{ env.VersionSuffix }} /p:RestoreAdditionalProjectSources=../regorus-nuget
working-directory: ./bindings/csharp/TargetExampleApp
- name: Build TargetExampleApp
run: dotnet build --no-restore
working-directory: ./bindings/csharp/TargetExampleApp
- name: Run TargetExampleApp
run: dotnet run --no-build --framework net8.0
working-directory: ./bindings/csharp/TargetExampleApp

View File

@@ -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

View File

@@ -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

View File

@@ -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: Fetch Java crate dependencies
run: cargo fetch --locked --manifest-path bindings/java/Cargo.toml
- 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: 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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

158
Cargo.lock generated
View File

@@ -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"
@@ -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.52"
version = "1.2.54"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd4932aefd12402b36c60956a4fe0035421f544799057659ff86f923657aada3"
checksum = "6354c81bbfd62d9cfa9cb3c773c2b7b2a3a482d569de977fd0e961f6e7c00583"
dependencies = [
"find-msvc-tools",
"shlex",
@@ -277,8 +289,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a0b5487afeab2deb2ff4e03a807ad1a03ac532ff5a2cee5d86884440c7f7671"
dependencies = [
"heck",
"proc-macro2 1.0.105",
"quote 1.0.43",
"proc-macro2 1.0.106",
"quote 1.0.44",
"syn 2.0.114",
]
@@ -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"
@@ -392,8 +413,8 @@ version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
dependencies = [
"proc-macro2 1.0.105",
"quote 1.0.43",
"proc-macro2 1.0.106",
"quote 1.0.44",
"syn 2.0.114",
]
@@ -439,9 +460,19 @@ dependencies = [
[[package]]
name = "find-msvc-tools"
version = "0.1.7"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f449e6c6c08c865631d4890cfacf252b3d396c9bcc83adb6623cdb02a8336c41"
checksum = "8591b0bcc8a98a64310a2fae1bb3e9b8564dd10e381e6e28010fde8e8e8568db"
[[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"
@@ -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.105"
version = "1.0.106"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "535d180e0ecab6268a3e718bb9fd44db66bbbc256257165fc699dadf70d16fe7"
checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
dependencies = [
"unicode-ident",
]
@@ -1064,11 +1105,11 @@ dependencies = [
[[package]]
name = "quote"
version = "1.0.43"
version = "1.0.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc74d9a594b72ae6656596548f56f667211f8a97b3d4c3d467150794690dc40a"
checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4"
dependencies = [
"proc-macro2 1.0.105",
"proc-macro2 1.0.106",
]
[[package]]
@@ -1150,8 +1191,8 @@ version = "1.0.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da"
dependencies = [
"proc-macro2 1.0.105",
"quote 1.0.43",
"proc-macro2 1.0.106",
"quote 1.0.44",
"syn 2.0.114",
]
@@ -1293,8 +1334,8 @@ version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
dependencies = [
"proc-macro2 1.0.105",
"quote 1.0.43",
"proc-macro2 1.0.106",
"quote 1.0.44",
"syn 2.0.114",
]
@@ -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"
@@ -1377,8 +1424,8 @@ version = "2.0.114"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4d107df263a3013ef9b1879b0df87d706ff80f65a86ea879bd9c31f9b307c2a"
dependencies = [
"proc-macro2 1.0.105",
"quote 1.0.43",
"proc-macro2 1.0.106",
"quote 1.0.44",
"unicode-ident",
]
@@ -1388,8 +1435,8 @@ version = "0.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2"
dependencies = [
"proc-macro2 1.0.105",
"quote 1.0.43",
"proc-macro2 1.0.106",
"quote 1.0.44",
"syn 2.0.114",
]
@@ -1407,21 +1454,21 @@ dependencies = [
[[package]]
name = "thiserror"
version = "2.0.17"
version = "2.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8"
checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "2.0.17"
version = "2.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913"
checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5"
dependencies = [
"proc-macro2 1.0.105",
"quote 1.0.43",
"proc-macro2 1.0.106",
"quote 1.0.44",
"syn 2.0.114",
]
@@ -1564,9 +1611,9 @@ dependencies = [
[[package]]
name = "wasip2"
version = "1.0.1+wasi-0.2.4"
version = "1.0.2+wasi-0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7"
checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5"
dependencies = [
"wit-bindgen",
]
@@ -1590,7 +1637,7 @@ version = "0.2.108"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "008b239d9c740232e71bd39e8ef6429d27097518b6b30bdf9086833bd5b6d608"
dependencies = [
"quote 1.0.43",
"quote 1.0.44",
"wasm-bindgen-macro-support",
]
@@ -1601,8 +1648,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5256bae2d58f54820e6490f9839c49780dff84c65aeab9e772f15d5f0e913a55"
dependencies = [
"bumpalo",
"proc-macro2 1.0.105",
"quote 1.0.43",
"proc-macro2 1.0.106",
"quote 1.0.44",
"syn 2.0.114",
"wasm-bindgen-shared",
]
@@ -1676,8 +1723,8 @@ version = "0.60.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf"
dependencies = [
"proc-macro2 1.0.105",
"quote 1.0.43",
"proc-macro2 1.0.106",
"quote 1.0.44",
"syn 2.0.114",
]
@@ -1687,8 +1734,8 @@ version = "0.59.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358"
dependencies = [
"proc-macro2 1.0.105",
"quote 1.0.43",
"proc-macro2 1.0.106",
"quote 1.0.44",
"syn 2.0.114",
]
@@ -1736,9 +1783,9 @@ dependencies = [
[[package]]
name = "wit-bindgen"
version = "0.46.0"
version = "0.51.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59"
checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5"
[[package]]
name = "writeable"
@@ -1755,6 +1802,7 @@ dependencies = [
"regex",
"semver",
"toml_edit",
"zip",
]
[[package]]
@@ -1774,8 +1822,8 @@ version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d"
dependencies = [
"proc-macro2 1.0.105",
"quote 1.0.43",
"proc-macro2 1.0.106",
"quote 1.0.44",
"syn 2.0.114",
"synstructure",
]
@@ -1795,8 +1843,8 @@ version = "0.8.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c7962b26b0a8685668b671ee4b54d007a67d4eaf05fda79ac0ecf41e32270f1"
dependencies = [
"proc-macro2 1.0.105",
"quote 1.0.43",
"proc-macro2 1.0.106",
"quote 1.0.44",
"syn 2.0.114",
]
@@ -1815,8 +1863,8 @@ version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502"
dependencies = [
"proc-macro2 1.0.105",
"quote 1.0.43",
"proc-macro2 1.0.106",
"quote 1.0.44",
"syn 2.0.114",
"synstructure",
]
@@ -1849,13 +1897,25 @@ version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3"
dependencies = [
"proc-macro2 1.0.105",
"quote 1.0.43",
"proc-macro2 1.0.106",
"quote 1.0.44",
"syn 2.0.114",
]
[[package]]
name = "zmij"
version = "1.0.14"
name = "zip"
version = "0.6.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd8f3f50b848df28f887acb68e41201b5aea6bc8a8dacc00fb40635ff9a72fea"
checksum = "760394e246e4c28189f19d488c058bf16f564016aefac5d32bb1f3b51d5e9261"
dependencies = [
"byteorder",
"crc32fast",
"crossbeam-utils",
"flate2",
]
[[package]]
name = "zmij"
version = "1.0.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dfcd145825aace48cff44a8844de64bf75feec3080e0aa5cdbde72961ae51a65"

View File

@@ -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)

View File

@@ -29,6 +29,7 @@ Once the workflow run completes, the generated Nuget can be downloaded by follow
## Local
<<<<<<< HEAD
TODO
## Memory Usage Safeguards
@@ -60,3 +61,10 @@ Regorus.MemoryLimits.SetThreadFlushThresholdOverride(null);
```
See `bindings/csharp/Regorus.Tests/RegorusTests.cs` for scenario coverage and `bindings/csharp/TargetExampleApp/Program.cs` for end-to-end usage.
=======
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 <triple>` (repeatable) to cross-compile, or `--release` to produce optimised artefacts. Results land under `bindings/ffi/target/<triple>/<profile>`.
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 <path>` 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`.
>>>>>>> 380a27c (feat(xtask): consolidate CI workflows onto xtask helpers)

View File

@@ -12,11 +12,6 @@
<PropertyGroup>
<!-- If the environment variable is set (such as in a Github Action run), append the suffix to the version number -->
<RegorusPackageVersionSuffix Condition="'$(VersionSuffix)' != ''">-$(VersionSuffix)</RegorusPackageVersionSuffix>
<UseLocalRegorus Condition="'$(UseLocalRegorus)' == ''">true</UseLocalRegorus>
</PropertyGroup>
<PropertyGroup Condition="'$(UseLocalRegorus)' == 'true'">
<DefineConstants>$(DefineConstants);REGORUS_FFI_TEST_HOOKS</DefineConstants>
</PropertyGroup>
<ItemGroup>
@@ -27,13 +22,7 @@
<PackageReference Include="MSTest" Version="3.8.2" />
</ItemGroup>
<ItemGroup Condition="'$(UseLocalRegorus)' == 'true'">
<ProjectReference Include="../Regorus/Regorus.csproj">
<AdditionalProperties>EnableRegorusTestHooks=true</AdditionalProperties>
</ProjectReference>
</ItemGroup>
<ItemGroup Condition="'$(UseLocalRegorus)' != 'true'">
<ItemGroup>
<PackageReference Include="Regorus" Version="0.8.0$(RegorusPackageVersionSuffix)" />
</ItemGroup>
</Project>

View File

@@ -13,6 +13,10 @@
<PackageReadmeFile>README.md</PackageReadmeFile>
</PropertyGroup>
<PropertyGroup>
<RegorusFFIArtifactsProfile Condition="'$(RegorusFFIArtifactsProfile)' == ''">release</RegorusFFIArtifactsProfile>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="System.Text.Json" Version="8.0.5" />
</ItemGroup>
@@ -27,7 +31,7 @@
by the publishing pipeline.
For each target triple, `Pack` expects the regorus ffi shared library
to be found in $(RegorusFFIArtifactsDir)/<target-triple>/release.
to be found in $(RegorusFFIArtifactsDir)/<target-triple>/$(RegorusFFIArtifactsProfile).
If $(IgnoreMissingArtifacts) is not set, ensure that the binaries for officially supported platforms exists.
-->
@@ -35,27 +39,27 @@
<Error Text="RegorusFFIArtifactsDir must be supplied." Condition="$(RegorusFFIArtifactsDir) == ''" />
<!-- Ensure that the binaries for officially supported platforms exists. -->
<Error Text="$(RegorusFFIArtifactsDir)/x86_64-pc-windows-msvc/release/regorus_ffi.dll missing."
Condition="!Exists('$(RegorusFFIArtifactsDir)/x86_64-pc-windows-msvc/release/regorus_ffi.dll')" />
<Error Text="$(RegorusFFIArtifactsDir)/x86_64-pc-windows-msvc/release/regorus_ffi.pdb missing."
Condition="!Exists('$(RegorusFFIArtifactsDir)/x86_64-pc-windows-msvc/release/regorus_ffi.pdb')" />
<Error Text="$(RegorusFFIArtifactsDir)/x86_64-pc-windows-msvc/$(RegorusFFIArtifactsProfile)/regorus_ffi.dll missing."
Condition="!Exists('$(RegorusFFIArtifactsDir)/x86_64-pc-windows-msvc/$(RegorusFFIArtifactsProfile)/regorus_ffi.dll')" />
<Error Text="$(RegorusFFIArtifactsDir)/x86_64-pc-windows-msvc/$(RegorusFFIArtifactsProfile)/regorus_ffi.pdb missing."
Condition="!Exists('$(RegorusFFIArtifactsDir)/x86_64-pc-windows-msvc/$(RegorusFFIArtifactsProfile)/regorus_ffi.pdb')" />
<Error Text="$(RegorusFFIArtifactsDir)/x86_64-unknown-linux-gnu/release/libregorus_ffi.so missing."
Condition="!Exists('$(RegorusFFIArtifactsDir)/x86_64-unknown-linux-gnu/release/libregorus_ffi.so')" />
<Error Text="$(RegorusFFIArtifactsDir)/x86_64-unknown-linux-gnu/$(RegorusFFIArtifactsProfile)/libregorus_ffi.so missing."
Condition="!Exists('$(RegorusFFIArtifactsDir)/x86_64-unknown-linux-gnu/$(RegorusFFIArtifactsProfile)/libregorus_ffi.so')" />
</Target>
<ItemGroup>
<None Include="docs/README.md" Pack="true" PackagePath="/" />
<!-- Copy each binary to expected location within the package -->
<None Include="$(RegorusFFIArtifactsDir)/x86_64-pc-windows-msvc/release/*.dll" Pack="true" PackagePath="runtimes/win-x64/native/" />
<None Include="$(RegorusFFIArtifactsDir)/x86_64-pc-windows-msvc/release/*.pdb" Pack="true" PackagePath="runtimes/win-x64/native/" />
<None Include="$(RegorusFFIArtifactsDir)/x86_64-pc-windows-msvc/$(RegorusFFIArtifactsProfile)/*.dll" Pack="true" PackagePath="runtimes/win-x64/native/" />
<None Include="$(RegorusFFIArtifactsDir)/x86_64-pc-windows-msvc/$(RegorusFFIArtifactsProfile)/*.pdb" Pack="true" PackagePath="runtimes/win-x64/native/" />
<None Include="$(RegorusFFIArtifactsDir)/aarch64-pc-windows-msvc/release/*.dll" Pack="true" PackagePath="runtimes/win-arm64/native/" />
<None Include="$(RegorusFFIArtifactsDir)/aarch64-pc-windows-msvc/release/*.pdb" Pack="true" PackagePath="runtimes/win-arm64/native/" />
<None Include="$(RegorusFFIArtifactsDir)/aarch64-pc-windows-msvc/$(RegorusFFIArtifactsProfile)/*.dll" Pack="true" PackagePath="runtimes/win-arm64/native/" />
<None Include="$(RegorusFFIArtifactsDir)/aarch64-pc-windows-msvc/$(RegorusFFIArtifactsProfile)/*.pdb" Pack="true" PackagePath="runtimes/win-arm64/native/" />
<None Include="$(RegorusFFIArtifactsDir)/x86_64-unknown-linux-gnu/release/lib*.so" Pack="true" PackagePath="runtimes/linux-x64/native/" />
<None Include="$(RegorusFFIArtifactsDir)/x86_64-unknown-linux-gnu/$(RegorusFFIArtifactsProfile)/lib*.so" Pack="true" PackagePath="runtimes/linux-x64/native/" />
<None Include="$(RegorusFFIArtifactsDir)/aarch64-apple-darwin/release/lib*.dylib" Pack="true" PackagePath="runtimes/osx-arm64/native/" />
<None Include="$(RegorusFFIArtifactsDir)/aarch64-apple-darwin/$(RegorusFFIArtifactsProfile)/lib*.dylib" Pack="true" PackagePath="runtimes/osx-arm64/native/" />
</ItemGroup>
</Project>

View File

@@ -172,9 +172,9 @@ dependencies = [
[[package]]
name = "cc"
version = "1.2.52"
version = "1.2.54"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd4932aefd12402b36c60956a4fe0035421f544799057659ff86f923657aada3"
checksum = "6354c81bbfd62d9cfa9cb3c773c2b7b2a3a482d569de977fd0e961f6e7c00583"
dependencies = [
"find-msvc-tools",
"shlex",
@@ -329,9 +329,9 @@ checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
[[package]]
name = "find-msvc-tools"
version = "0.1.7"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f449e6c6c08c865631d4890cfacf252b3d396c9bcc83adb6623cdb02a8336c41"
checksum = "8591b0bcc8a98a64310a2fae1bb3e9b8564dd10e381e6e28010fde8e8e8568db"
[[package]]
name = "fluent-uri"
@@ -827,18 +827,18 @@ dependencies = [
[[package]]
name = "proc-macro2"
version = "1.0.105"
version = "1.0.106"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "535d180e0ecab6268a3e718bb9fd44db66bbbc256257165fc699dadf70d16fe7"
checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.43"
version = "1.0.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc74d9a594b72ae6656596548f56f667211f8a97b3d4c3d467150794690dc40a"
checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4"
dependencies = [
"proc-macro2",
]
@@ -1158,18 +1158,18 @@ dependencies = [
[[package]]
name = "thiserror"
version = "2.0.17"
version = "2.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8"
checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "2.0.17"
version = "2.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913"
checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5"
dependencies = [
"proc-macro2",
"quote",
@@ -1300,9 +1300,9 @@ checksum = "5c3082ca00d5a5ef149bb8b555a72ae84c9c59f7250f013ac822ac2e49b19c64"
[[package]]
name = "wasip2"
version = "1.0.1+wasi-0.2.4"
version = "1.0.2+wasi-0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7"
checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5"
dependencies = [
"wit-bindgen",
]
@@ -1431,9 +1431,9 @@ dependencies = [
[[package]]
name = "wit-bindgen"
version = "0.46.0"
version = "0.51.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59"
checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5"
[[package]]
name = "writeable"
@@ -1540,6 +1540,6 @@ dependencies = [
[[package]]
name = "zmij"
version = "1.0.14"
version = "1.0.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd8f3f50b848df28f887acb68e41201b5aea6bc8a8dacc00fb40635ff9a72fea"
checksum = "dfcd145825aace48cff44a8844de64bf75feec3080e0aa5cdbde72961ae51a65"

View File

@@ -109,9 +109,9 @@ checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3"
[[package]]
name = "cc"
version = "1.2.52"
version = "1.2.54"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd4932aefd12402b36c60956a4fe0035421f544799057659ff86f923657aada3"
checksum = "6354c81bbfd62d9cfa9cb3c773c2b7b2a3a482d569de977fd0e961f6e7c00583"
dependencies = [
"find-msvc-tools",
"shlex",
@@ -213,9 +213,9 @@ dependencies = [
[[package]]
name = "find-msvc-tools"
version = "0.1.7"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f449e6c6c08c865631d4890cfacf252b3d396c9bcc83adb6623cdb02a8336c41"
checksum = "8591b0bcc8a98a64310a2fae1bb3e9b8564dd10e381e6e28010fde8e8e8568db"
[[package]]
name = "fluent-uri"
@@ -703,18 +703,18 @@ dependencies = [
[[package]]
name = "proc-macro2"
version = "1.0.105"
version = "1.0.106"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "535d180e0ecab6268a3e718bb9fd44db66bbbc256257165fc699dadf70d16fe7"
checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.43"
version = "1.0.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc74d9a594b72ae6656596548f56f667211f8a97b3d4c3d467150794690dc40a"
checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4"
dependencies = [
"proc-macro2",
]
@@ -848,7 +848,7 @@ dependencies = [
"serde",
"serde_json",
"serde_yaml",
"thiserror 2.0.17",
"thiserror 2.0.18",
"url",
"uuid",
]
@@ -1009,11 +1009,11 @@ dependencies = [
[[package]]
name = "thiserror"
version = "2.0.17"
version = "2.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8"
checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4"
dependencies = [
"thiserror-impl 2.0.17",
"thiserror-impl 2.0.18",
]
[[package]]
@@ -1029,9 +1029,9 @@ dependencies = [
[[package]]
name = "thiserror-impl"
version = "2.0.17"
version = "2.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913"
checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5"
dependencies = [
"proc-macro2",
"quote",
@@ -1125,9 +1125,9 @@ dependencies = [
[[package]]
name = "wasip2"
version = "1.0.1+wasi-0.2.4"
version = "1.0.2+wasi-0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7"
checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5"
dependencies = [
"wit-bindgen",
]
@@ -1322,9 +1322,9 @@ checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
[[package]]
name = "wit-bindgen"
version = "0.46.0"
version = "0.51.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59"
checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5"
[[package]]
name = "writeable"
@@ -1431,6 +1431,6 @@ dependencies = [
[[package]]
name = "zmij"
version = "1.0.14"
version = "1.0.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd8f3f50b848df28f887acb68e41201b5aea6bc8a8dacc00fb40635ff9a72fea"
checksum = "dfcd145825aace48cff44a8844de64bf75feec3080e0aa5cdbde72961ae51a65"

View File

@@ -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:

View File

@@ -103,9 +103,9 @@ checksum = "175812e0be2bccb6abe50bb8d566126198344f707e304f45c648fd8f2cc0365e"
[[package]]
name = "cc"
version = "1.2.52"
version = "1.2.54"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd4932aefd12402b36c60956a4fe0035421f544799057659ff86f923657aada3"
checksum = "6354c81bbfd62d9cfa9cb3c773c2b7b2a3a482d569de977fd0e961f6e7c00583"
dependencies = [
"find-msvc-tools",
"shlex",
@@ -191,9 +191,9 @@ dependencies = [
[[package]]
name = "find-msvc-tools"
version = "0.1.7"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f449e6c6c08c865631d4890cfacf252b3d396c9bcc83adb6623cdb02a8336c41"
checksum = "8591b0bcc8a98a64310a2fae1bb3e9b8564dd10e381e6e28010fde8e8e8568db"
[[package]]
name = "fluent-uri"
@@ -698,9 +698,9 @@ dependencies = [
[[package]]
name = "proc-macro2"
version = "1.0.105"
version = "1.0.106"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "535d180e0ecab6268a3e718bb9fd44db66bbbc256257165fc699dadf70d16fe7"
checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
dependencies = [
"unicode-ident",
]
@@ -771,9 +771,9 @@ dependencies = [
[[package]]
name = "quote"
version = "1.0.43"
version = "1.0.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc74d9a594b72ae6656596548f56f667211f8a97b3d4c3d467150794690dc40a"
checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4"
dependencies = [
"proc-macro2",
]
@@ -1057,18 +1057,18 @@ checksum = "b1dd07eb858a2067e2f3c7155d54e929265c264e6f37efe3ee7a8d1b5a1dd0ba"
[[package]]
name = "thiserror"
version = "2.0.17"
version = "2.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8"
checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "2.0.17"
version = "2.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913"
checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5"
dependencies = [
"proc-macro2",
"quote",
@@ -1158,9 +1158,9 @@ checksum = "5c3082ca00d5a5ef149bb8b555a72ae84c9c59f7250f013ac822ac2e49b19c64"
[[package]]
name = "wasip2"
version = "1.0.1+wasi-0.2.4"
version = "1.0.2+wasi-0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7"
checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5"
dependencies = [
"wit-bindgen",
]
@@ -1271,9 +1271,9 @@ dependencies = [
[[package]]
name = "wit-bindgen"
version = "0.46.0"
version = "0.51.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59"
checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5"
[[package]]
name = "writeable"
@@ -1380,6 +1380,6 @@ dependencies = [
[[package]]
name = "zmij"
version = "1.0.14"
version = "1.0.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd8f3f50b848df28f887acb68e41201b5aea6bc8a8dacc00fb40635ff9a72fea"
checksum = "dfcd145825aace48cff44a8844de64bf75feec3080e0aa5cdbde72961ae51a65"

View File

@@ -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

152
bindings/ruby/Cargo.lock generated
View File

@@ -111,9 +111,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"
@@ -123,9 +123,9 @@ checksum = "175812e0be2bccb6abe50bb8d566126198344f707e304f45c648fd8f2cc0365e"
[[package]]
name = "cc"
version = "1.2.47"
version = "1.2.54"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd405d82c84ff7f35739f175f67d8b9fb7687a0e84ccdc78bd3568839827cf07"
checksum = "6354c81bbfd62d9cfa9cb3c773c2b7b2a3a482d569de977fd0e961f6e7c00583"
dependencies = [
"find-msvc-tools",
"shlex",
@@ -148,9 +148,9 @@ checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
[[package]]
name = "chrono"
version = "0.4.42"
version = "0.4.43"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2"
checksum = "fac4744fb15ae8337dc853fee7fb3f4e48c0fbaa23d0afe49c447b4fab126118"
dependencies = [
"iana-time-zone",
"js-sys",
@@ -188,9 +188,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"
@@ -237,9 +237,9 @@ dependencies = [
[[package]]
name = "find-msvc-tools"
version = "0.1.5"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844"
checksum = "8591b0bcc8a98a64310a2fae1bb3e9b8564dd10e381e6e28010fde8e8e8568db"
[[package]]
name = "fluent-uri"
@@ -379,9 +379,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",
@@ -393,9 +393,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"
@@ -435,9 +435,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",
@@ -460,15 +460,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.85"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b011eec8cc36da2aab2d5cff675ec18454fad408585853910a202391cf9f8e65"
checksum = "8c942ebf8e95485ca0d52d97da7c5a2c387d0e7f0ba4c35e93bfcaee045955b3"
dependencies = [
"once_cell",
"wasm-bindgen",
@@ -514,9 +514,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
[[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 = "libloading"
@@ -545,9 +545,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 = "magnus"
@@ -580,7 +580,7 @@ checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273"
[[package]]
name = "mimalloc"
version = "2.1.2"
version = "2.2.6"
dependencies = [
"mimalloc-sys",
]
@@ -775,18 +775,18 @@ dependencies = [
[[package]]
name = "proc-macro2"
version = "1.0.103"
version = "1.0.106"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8"
checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.42"
version = "1.0.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f"
checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4"
dependencies = [
"proc-macro2",
]
@@ -819,27 +819,27 @@ dependencies = [
[[package]]
name = "rand_core"
version = "0.9.3"
version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38"
checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c"
dependencies = [
"getrandom",
]
[[package]]
name = "rb-sys"
version = "0.9.117"
version = "0.9.124"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f900d1ce4629a2ebffaf5de74bd8f9c1188d4c5ed406df02f97e22f77a006f44"
checksum = "c85c4188462601e2aa1469def389c17228566f82ea72f137ed096f21591bc489"
dependencies = [
"rb-sys-build",
]
[[package]]
name = "rb-sys-build"
version = "0.9.117"
version = "0.9.124"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef1e9c857028f631056bcd6d88cec390c751e343ce2223ddb26d23eb4a151d59"
checksum = "568068db4102230882e6d4ae8de6632e224ca75fe5970f6e026a04e91ed635d3"
dependencies = [
"bindgen",
"lazy_static",
@@ -950,7 +950,6 @@ dependencies = [
"serde",
"serde_json",
"serde_yaml",
"smol_str",
"thiserror",
"url",
"uuid",
@@ -980,9 +979,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"
@@ -1034,15 +1033,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]]
@@ -1071,9 +1070,9 @@ dependencies = [
[[package]]
name = "shell-words"
version = "1.1.0"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde"
checksum = "dc6fe69c597f9c37bfeeeeeb33da3530379845f10be461a66d16d03eca2ded77"
[[package]]
name = "shlex"
@@ -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"
@@ -1110,9 +1100,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",
@@ -1138,18 +1128,18 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
[[package]]
name = "thiserror"
version = "2.0.17"
version = "2.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8"
checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "2.0.17"
version = "2.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913"
checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5"
dependencies = [
"proc-macro2",
"quote",
@@ -1180,9 +1170,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",
@@ -1198,9 +1188,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",
@@ -1233,18 +1223,18 @@ checksum = "5c3082ca00d5a5ef149bb8b555a72ae84c9c59f7250f013ac822ac2e49b19c64"
[[package]]
name = "wasip2"
version = "1.0.1+wasi-0.2.4"
version = "1.0.2+wasi-0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7"
checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5"
dependencies = [
"wit-bindgen",
]
[[package]]
name = "wasm-bindgen"
version = "0.2.105"
version = "0.2.108"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da95793dfc411fbbd93f5be7715b0578ec61fe87cb1a42b12eb625caa5c5ea60"
checksum = "64024a30ec1e37399cf85a7ffefebdb72205ca1c972291c51512360d90bd8566"
dependencies = [
"cfg-if",
"once_cell",
@@ -1255,9 +1245,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.105"
version = "0.2.108"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04264334509e04a7bf8690f2384ef5265f05143a4bff3889ab7a3269adab59c2"
checksum = "008b239d9c740232e71bd39e8ef6429d27097518b6b30bdf9086833bd5b6d608"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
@@ -1265,9 +1255,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.105"
version = "0.2.108"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "420bc339d9f322e562942d52e115d57e950d12d88983a14c79b86859ee6c7ebc"
checksum = "5256bae2d58f54820e6490f9839c49780dff84c65aeab9e772f15d5f0e913a55"
dependencies = [
"bumpalo",
"proc-macro2",
@@ -1278,9 +1268,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.105"
version = "0.2.108"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "76f218a38c84bcb33c25ec7059b07847d465ce0e0a76b995e134a45adcb6af76"
checksum = "1f01b580c9ac74c8d8f0c0e4afb04eeef2acf145458e52c03845ee9cd23e3d12"
dependencies = [
"unicode-ident",
]
@@ -1346,9 +1336,9 @@ dependencies = [
[[package]]
name = "wit-bindgen"
version = "0.46.0"
version = "0.51.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59"
checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5"
[[package]]
name = "writeable"
@@ -1381,18 +1371,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",
@@ -1452,3 +1442,9 @@ dependencies = [
"quote",
"syn",
]
[[package]]
name = "zmij"
version = "1.0.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dfcd145825aace48cff44a8844de64bf75feec3080e0aa5cdbde72961ae51a65"

View File

@@ -120,9 +120,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
[[package]]
name = "cc"
version = "1.2.52"
version = "1.2.54"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd4932aefd12402b36c60956a4fe0035421f544799057659ff86f923657aada3"
checksum = "6354c81bbfd62d9cfa9cb3c773c2b7b2a3a482d569de977fd0e961f6e7c00583"
dependencies = [
"find-msvc-tools",
"shlex",
@@ -208,9 +208,9 @@ dependencies = [
[[package]]
name = "find-msvc-tools"
version = "0.1.7"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f449e6c6c08c865631d4890cfacf252b3d396c9bcc83adb6623cdb02a8336c41"
checksum = "8591b0bcc8a98a64310a2fae1bb3e9b8564dd10e381e6e28010fde8e8e8568db"
[[package]]
name = "fluent-uri"
@@ -746,18 +746,18 @@ dependencies = [
[[package]]
name = "proc-macro2"
version = "1.0.105"
version = "1.0.106"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "535d180e0ecab6268a3e718bb9fd44db66bbbc256257165fc699dadf70d16fe7"
checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.43"
version = "1.0.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc74d9a594b72ae6656596548f56f667211f8a97b3d4c3d467150794690dc40a"
checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4"
dependencies = [
"proc-macro2",
]
@@ -1051,18 +1051,18 @@ dependencies = [
[[package]]
name = "thiserror"
version = "2.0.17"
version = "2.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8"
checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "2.0.17"
version = "2.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913"
checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5"
dependencies = [
"proc-macro2",
"quote",
@@ -1162,9 +1162,9 @@ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
[[package]]
name = "wasip2"
version = "1.0.1+wasi-0.2.4"
version = "1.0.2+wasi-0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7"
checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5"
dependencies = [
"wit-bindgen",
]
@@ -1356,9 +1356,9 @@ dependencies = [
[[package]]
name = "wit-bindgen"
version = "0.46.0"
version = "0.51.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59"
checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5"
[[package]]
name = "writeable"
@@ -1465,6 +1465,6 @@ dependencies = [
[[package]]
name = "zmij"
version = "1.0.14"
version = "1.0.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd8f3f50b848df28f887acb68e41201b5aea6bc8a8dacc00fb40635ff9a72fea"
checksum = "dfcd145825aace48cff44a8844de64bf75feec3080e0aa5cdbde72961ae51a65"

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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"] }

View File

@@ -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()?,
}

View File

@@ -0,0 +1,142 @@
// 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,
clean: false,
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,
clean: false,
artifacts_dir: None,
enforce_artifacts: false,
force_nuget: false,
nuget_dir: None,
}
.run()?;
println!("Completed testing all bindings.");
Ok(())
}
}

View File

@@ -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<PathBuf> {
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)
}

View File

@@ -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)
}
}

View File

@@ -0,0 +1,484 @@
// 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<String>,
/// Build the package in release mode (defaults to debug).
#[arg(long)]
pub release: bool,
/// Remove existing build artefacts before compiling.
#[arg(long)]
pub clean: bool,
/// Override the directory that contains compiled regorus FFI artefacts.
#[arg(long, value_name = "PATH")]
pub artifacts_dir: Option<PathBuf>,
/// 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<String>,
pub release: bool,
pub clean: bool,
pub artifacts_dir: Option<PathBuf>,
pub enforce_artifacts: bool,
}
/// Result of a NuGet build, including generated artefacts.
#[derive(Debug)]
pub struct BuildNugetResult {
pub package_dir: PathBuf,
pub packages: Vec<PathBuf>,
}
/// Builds (or rebuilds) the C# NuGet package with the supplied configuration.
pub fn build_nuget_package(config: &BuildNugetConfig) -> Result<BuildNugetResult> {
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,
config.clean,
)?;
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<Vec<PathBuf>> {
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,
clean: bool,
) -> Result<PathBuf> {
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);
if clean {
clean_msbuild_project(&project_dir, configuration)?;
let artefact_root = project_dir.join("bin").join(configuration);
if artefact_root.exists() {
fs::remove_dir_all(&artefact_root).with_context(|| {
format!(
"failed to remove existing NuGet artefacts from {}",
artefact_root.display()
)
})?;
}
}
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,
clean: self.clean,
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<String>,
/// Build and pack using the Release configuration (defaults to Debug).
#[arg(long)]
pub release: bool,
/// Remove existing build artefacts before running the command.
#[arg(long)]
pub clean: bool,
/// Override the directory that contains compiled regorus FFI artefacts.
#[arg(long, value_name = "PATH")]
pub artifacts_dir: Option<PathBuf>,
/// 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,
/// Restore and test using Regorus NuGet artefacts located at DIR. Defaults to bindings/csharp/Regorus/bin/<configuration>.
#[arg(long = "nuget-dir", value_name = "DIR")]
pub nuget_dir: Option<PathBuf>,
}
impl TestCsharpCommand {
pub fn run(&self) -> Result<()> {
let workspace = workspace_root();
let configuration = if self.release { "Release" } else { "Debug" };
if self.nuget_dir.is_some() && self.force_nuget {
return Err(anyhow!(
"--force-nuget cannot be combined with --nuget-dir; build outputs always land in the default directory"
));
}
let mut package_dir = if let Some(dir) = &self.nuget_dir {
let path = PathBuf::from(dir);
if path.is_absolute() {
path
} else {
workspace.join(path)
}
} else {
workspace
.join("bindings/csharp/Regorus/bin")
.join(configuration)
};
let build_config = BuildNugetConfig {
targets: self.targets.clone(),
release: self.release,
clean: self.clean,
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() && self.nuget_dir.is_none()) {
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 {}; supply --nuget-dir with a populated directory or omit it to let xtask build the package",
package_dir.display()
));
}
println!("Using NuGet package(s):");
for package in &packages {
println!(" {}", package.display());
}
run_regorus_tests(&workspace, configuration, &package_dir, self.clean)?;
Ok(())
}
}
fn run_regorus_tests(
workspace: &Path,
configuration: &str,
package_dir: &Path,
clean: bool,
) -> Result<()> {
let nuget_source = package_dir
.to_str()
.ok_or_else(|| anyhow!("NuGet directory path contains invalid UTF-8"))?;
let properties = vec![format!(
"/p:RestoreAdditionalProjectSources={}",
nuget_source
)];
let property_args: Vec<&str> = properties.iter().map(|value| value.as_str()).collect();
let regorus_tests = workspace.join("bindings/csharp/Regorus.Tests");
if clean {
clean_msbuild_project(&regorus_tests, configuration)?;
}
restore_with_source(&regorus_tests, &property_args, "Regorus.Tests")?;
let mut test = Command::new("dotnet");
test.current_dir(&regorus_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");
if clean {
clean_msbuild_project(&test_app, configuration)?;
}
restore_with_source(&test_app, &property_args, "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");
if clean {
clean_msbuild_project(&target_example, configuration)?;
}
restore_with_source(&target_example, &property_args, "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, properties: &[&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());
for property in properties {
restore.arg(property);
}
run_command(restore, &format!("dotnet restore ({label})"))
}
fn clean_msbuild_project(project_dir: &Path, configuration: &str) -> Result<()> {
if !project_dir.exists() {
return Ok(());
}
let mut clean = Command::new("dotnet");
clean.current_dir(project_dir);
clean.arg("clean");
clean.arg("-c");
clean.arg(configuration);
clean.arg("--verbosity");
clean.arg("minimal");
run_command(clean, "dotnet clean")?;
let bin_dir = project_dir.join("bin");
if bin_dir.exists() {
fs::remove_dir_all(&bin_dir).with_context(|| {
format!(
"failed to remove bin directory at {} while cleaning",
bin_dir.display()
)
})?;
}
let obj_dir = project_dir.join("obj");
if obj_dir.exists() {
fs::remove_dir_all(&obj_dir).with_context(|| {
format!(
"failed to remove obj directory at {} while cleaning",
obj_dir.display()
)
})?;
}
Ok(())
}

View File

@@ -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<String>,
/// 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<String>) -> Result<Vec<String>> {
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<String> {
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(())
}
}

View File

@@ -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<const N: usize>(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")
}
}

View File

@@ -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<Command> {
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<PathBuf> {
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()))
}

View File

@@ -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};

View File

@@ -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<String>,
/// Override the directory that receives built wheel files.
#[arg(long, value_name = "PATH")]
pub target_dir: Option<PathBuf>,
/// 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<String>,
/// 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::<u32>()
.with_context(|| format!("failed to parse major version from '{}'", trimmed))?;
let minor = parts
.next()
.ok_or_else(|| anyhow!("missing minor version in '{}'", trimmed))?
.parse::<u32>()
.with_context(|| format!("failed to parse minor version from '{}'", trimmed))?;
Ok((major, minor))
}

View File

@@ -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)")
}
}

View File

@@ -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<PathBuf>,
}
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<PathBuf>,
/// 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)")
}

366
xtask/src/tasks/ci/mod.rs Normal file
View File

@@ -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<String>,
/// Override the feature set passed to the OPA tests.
#[arg(long, value_name = "FEATURES")]
pub opa_features: Option<String>,
/// 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<String>,
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<String> {
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<OsString> {
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
}

150
xtask/src/tasks/ci/musl.rs Normal file
View File

@@ -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<OsString> {
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
}

View File

@@ -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<PathBuf>,
}
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(())
}

View File

@@ -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(())
}
}

View File

@@ -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;

View File

@@ -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"))
}

View File

@@ -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)
}

View File

@@ -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;

46
xtask/src/tasks/no_std.rs Normal file
View File

@@ -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(())
}
}

151
xtask/src/tasks/util.rs Normal file
View File

@@ -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<I, S>(workspace: &Path, title: &str, args: I) -> Result<()>
where
I: IntoIterator<Item = S>,
S: AsRef<OsStr>,
{
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<I, S>(directory: &Path, title: &str, args: I) -> Result<()>
where
I: IntoIterator<Item = S>,
S: AsRef<OsStr>,
{
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<String>) {
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<Vec<String>> {
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())
}