Consolidate Dependabot, fix #595 (mimalloc + indexmap), add feature-matrix CI (#627)

* build: consolidate dependabot cargo entries and add commit prefixes

Consolidate all 9 separate cargo ecosystem entries into a single entry
using the 'directories' key. This ensures Dependabot creates one PR per
dependency update across the root workspace and all bindings, preventing
version skew that caused build failures.

Also add semantic commit-message prefixes to all ecosystem entries:
- build(deps) for cargo, gomod, maven, nuget, pip, bundler
- ci(deps) for github-actions

Rename the cargo group to 'rust-dependencies' and the github-actions
group to 'github-actions' for clarity.

Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>

* fix: remove mimalloc from default features, fix indexmap/std propagation

Address #595: the vendored mimalloc allocator should not be imposed on
library consumers. Remove allocator-memory-limits and mimalloc from the
full-opa feature so that users of regorus as a library can choose their
own global allocator.

Bindings (ffi, java, python, ruby) that ship as standalone artifacts
continue to opt in to regorus/allocator-memory-limits explicitly so they
retain the performant allocator.

Also propagate indexmap/std via the std feature (using the indexmap?/std
weak-dependency syntax) so that users enabling std + rvm without default
features no longer hit 'IndexMap takes 3 generic arguments' errors.

Closes #595

Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>

* ci: add feature-combination checks to PR CI and weekly matrix

PR CI (xtask): add cargo check for 5 non-default feature combos in
run_ci_suite(). These run on every PR and catch compile failures from
feature-gating issues (e.g. #595) with near-zero overhead.

Weekly workflow: new feature-matrix.yml runs cargo build + cargo test
across 9 feature combinations every Monday. Uses a GitHub Actions matrix
with fail-fast: false so all combos are tested even if one fails.

Combinations tested weekly:
- std,arc (minimal library)
- std,arc,rvm (common library usage)
- std,arc,full-opa (full-opa without mimalloc)
- std,arc,full-opa,allocator-memory-limits (binding-style)
- std,arc,rvm,regex,time,semver,cache (cherry-picked builtins)
- std,arc,rvm,coverage,cache (observability)
- std,arc,full-opa,azure_policy (Azure Policy)
- std,arc,full-opa,azure-rbac (Azure RBAC)
- arc,opa-no-std (no_std codepath)

Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>

* fix: gate benchmark memory-limit calls behind allocator-memory-limits feature

The set_global_memory_limit function is only available when the
allocator-memory-limits feature is enabled. After removing mimalloc
from the default feature set, the rvm_benchmark failed to compile.

Add #[cfg(feature = "allocator-memory-limits")] guards around the
call sites and the MEMORY_LIMIT_BYTES constant.

Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>

---------

Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
This commit is contained in:
Anand Krishnamoorthi
2026-03-24 11:54:01 -05:00
committed by GitHub
parent 83891d7782
commit 86088d2049
8 changed files with 167 additions and 81 deletions

104
.github/dependabot.yml vendored
View File

@@ -7,12 +7,26 @@
version: 2
updates:
# All Rust/Cargo directories are grouped into a single entry so that
# when a dependency is updated, Dependabot creates ONE PR that bumps it
# across the root workspace AND every binding, preventing version skew.
- package-ecosystem: "cargo"
directory: "/" # Location of package manifests
directories:
- "/"
- "/bindings/ffi"
- "/bindings/java"
- "/bindings/python"
- "/bindings/ruby"
- "/bindings/ruby/ext/regorusrb"
- "/bindings/wasm"
- "/tests/ensure_no_std"
- "/xtask"
schedule:
interval: "weekly"
commit-message:
prefix: "build(deps)"
groups:
per-dependency:
rust-dependencies:
patterns:
- "*"
# Ignore vendored mimalloc crates; updates are managed manually.
@@ -20,82 +34,12 @@ updates:
- dependency-name: "regorus-mimalloc"
- dependency-name: "regorus-mimalloc-sys"
- package-ecosystem: "cargo"
directory: "/bindings/ffi"
schedule:
interval: "weekly"
groups:
per-dependency:
patterns:
- "*"
- package-ecosystem: "cargo"
directory: "/bindings/java"
schedule:
interval: "weekly"
groups:
per-dependency:
patterns:
- "*"
- package-ecosystem: "cargo"
directory: "/bindings/python"
schedule:
interval: "weekly"
groups:
per-dependency:
patterns:
- "*"
- package-ecosystem: "cargo"
directory: "/bindings/ruby"
schedule:
interval: "weekly"
groups:
per-dependency:
patterns:
- "*"
- package-ecosystem: "cargo"
directory: "/bindings/ruby/ext/regorusrb"
schedule:
interval: "weekly"
groups:
per-dependency:
patterns:
- "*"
- package-ecosystem: "cargo"
directory: "/bindings/wasm"
schedule:
interval: "weekly"
groups:
per-dependency:
patterns:
- "*"
- package-ecosystem: "cargo"
directory: "/tests/ensure_no_std"
schedule:
interval: "weekly"
groups:
per-dependency:
patterns:
- "*"
- package-ecosystem: "cargo"
directory: "/xtask"
schedule:
interval: "weekly"
groups:
per-dependency:
patterns:
- "*"
- package-ecosystem: "gomod"
directory: "/bindings/go"
schedule:
interval: "weekly"
commit-message:
prefix: "build(deps)"
groups:
per-dependency:
patterns:
@@ -105,6 +49,8 @@ updates:
directory: "/bindings/java"
schedule:
interval: "weekly"
commit-message:
prefix: "build(deps)"
groups:
per-dependency:
patterns:
@@ -114,6 +60,8 @@ updates:
directory: "/bindings/csharp"
schedule:
interval: "weekly"
commit-message:
prefix: "build(deps)"
groups:
per-dependency:
patterns:
@@ -123,6 +71,8 @@ updates:
directory: "/bindings/python"
schedule:
interval: "weekly"
commit-message:
prefix: "build(deps)"
groups:
per-dependency:
patterns:
@@ -132,6 +82,8 @@ updates:
directory: "/bindings/ruby"
schedule:
interval: "weekly"
commit-message:
prefix: "build(deps)"
groups:
per-dependency:
patterns:
@@ -141,7 +93,9 @@ updates:
directory: "/"
schedule:
interval: "weekly"
commit-message:
prefix: "ci(deps)"
groups:
per-dependency:
github-actions:
patterns:
- "*"

82
.github/workflows/feature-matrix.yml vendored Normal file
View File

@@ -0,0 +1,82 @@
# Copyright (c) Microsoft Corporation. All rights reserved.
#
# Thorough weekly test of non-default feature combinations.
# Catches regressions from dependency updates and feature-gating issues
# that the fast PR CI checks (cargo check only) would miss at runtime.
name: tests/feature-matrix
on:
workflow_dispatch:
schedule:
# Run at 3:42 AM UTC every Saturday.
- cron: "42 3 * * 6"
env:
CARGO_TERM_COLOR: always
jobs:
feature-matrix:
name: ${{ matrix.name }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
include:
# Bare minimum: validates that the core interpreter works
# without any builtins or optional subsystems.
- name: minimal (std + arc)
features: std,arc
# Common library usage pattern (issue #595): consumer enables
# std + arc + rvm and relies on indexmap/std propagation.
- name: library (std + arc + rvm)
features: std,arc,rvm
# New default after removing mimalloc from full-opa.
# Ensures all builtins compile without the allocator.
- name: full-opa (no mimalloc)
features: std,arc,full-opa
# Binding-style usage: full-opa with the vendored allocator.
# Mirrors how ffi/java/python/ruby bindings are built.
- name: full-opa + allocator
features: std,arc,full-opa,allocator-memory-limits
# Selective builtins without full-opa: validates that popular
# features can be cherry-picked independently.
- name: cherry-picked builtins
features: std,arc,rvm,regex,time,semver,cache
# Observability features only: coverage + cache without the
# heavier builtins (regex, time, etc.).
- name: observability
features: std,arc,rvm,coverage,cache
# Azure Policy adds jsonschema + dashmap; test it compiles
# and runs on top of full-opa.
- name: azure-policy
features: std,arc,full-opa,azure_policy
# Azure RBAC adds regex + time + net on top of full-opa.
- name: azure-rbac
features: std,arc,full-opa,azure-rbac
# no_std with the OPA-compatible feature set: exercises the
# spin_no_std codepath and absence of std-only dependencies.
- name: no_std
features: arc,opa-no-std
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- 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-features
- name: Fetch dependencies
run: cargo fetch --locked
- name: Build
run: cargo build --no-default-features --features "${{ matrix.features }}" --frozen
- name: Test
run: cargo test --no-default-features --features "${{ matrix.features }}" --frozen

View File

@@ -43,7 +43,7 @@ cache = ["dep:lru"]
rvm = ["dep:postcard", "dep:indexmap"]
semver = ["dep:semver"]
allocator-memory-limits = ["std", "mimalloc", "mimalloc/allocator-memory-limits"]
std = ["rand/std", "rand/std_rng", "serde_json/std", "msvc_spectre_libs", "dep:parking_lot" ]
std = ["rand/std", "rand/std_rng", "serde_json/std", "indexmap?/std", "msvc_spectre_libs", "dep:parking_lot" ]
time = ["dep:chrono", "dep:chrono-tz"]
uuid = ["dep:uuid"]
urlquery = ["dep:url"]
@@ -57,8 +57,6 @@ full-opa = [
"hex",
"http",
"jsonschema",
"allocator-memory-limits",
"mimalloc",
"net",
"opa-runtime",
"regex",

View File

@@ -57,6 +57,7 @@ use regorus::{Engine, Rc, Value};
// hot path (memory_check, execution_timer_tick, instruction-limit compare).
// ---------------------------------------------------------------------------
#[cfg(feature = "allocator-memory-limits")]
const MEMORY_LIMIT_BYTES: u64 = 256 * 1024 * 1024;
const TIME_LIMIT: Duration = Duration::from_secs(30);
const TIMER_CHECK_INTERVAL: NonZeroU32 = NonZeroU32::new(16).unwrap();
@@ -360,6 +361,7 @@ fn compile_all_programs() -> Vec<BenchmarkProgram> {
/// Apply or remove production-style limits based on a boolean flag.
fn configure_limits(vm: &mut RegoVM, limits: bool) {
if limits {
#[cfg(feature = "allocator-memory-limits")]
regorus::set_global_memory_limit(Some(MEMORY_LIMIT_BYTES));
vm.set_execution_timer_config(Some(ExecutionTimerConfig {
limit: TIME_LIMIT,
@@ -367,6 +369,7 @@ fn configure_limits(vm: &mut RegoVM, limits: bool) {
}));
vm.set_max_instructions(INSTRUCTION_LIMIT);
} else {
#[cfg(feature = "allocator-memory-limits")]
regorus::set_global_memory_limit(None);
vm.set_execution_timer_config(None);
vm.set_max_instructions(usize::MAX);

View File

@@ -14,7 +14,7 @@ keywords = ["interpreter", "opa", "policy-as-code", "rego"]
crate-type = ["cdylib"]
[features]
default = ["ast", "cache", "coverage", "regorus/std", "regorus/full-opa"]
default = ["ast", "cache", "coverage", "regorus/std", "regorus/full-opa", "regorus/allocator-memory-limits"]
coverage = ["regorus/coverage"]
ast = ["regorus/ast"]
cache = ["regorus/cache"]

View File

@@ -15,7 +15,7 @@ keywords = ["interpreter", "opa", "policy-as-code", "rego"]
crate-type = ["cdylib"]
[features]
default = ["ast", "cache", "coverage", "regorus/std", "regorus/full-opa"]
default = ["ast", "cache", "coverage", "regorus/std", "regorus/full-opa", "regorus/allocator-memory-limits"]
ast = ["regorus/ast"]
cache = ["regorus/cache"]
coverage = ["regorus/coverage"]

View File

@@ -11,7 +11,7 @@ crate-type = ["cdylib"]
path = "src/lib.rs"
[features]
default = ["ast", "cache", "coverage", "regorus/std", "regorus/full-opa"]
default = ["ast", "cache", "coverage", "regorus/std", "regorus/full-opa", "regorus/allocator-memory-limits"]
ast = ["regorus/ast"]
cache = ["regorus/cache"]
coverage = ["regorus/coverage"]

View File

@@ -242,6 +242,10 @@ fn run_ci_suite(config: CiSuiteConfig) -> Result<()> {
)?;
}
// Verify that important feature subsets compile correctly.
// These catch issues like #595 where non-default combinations fail.
check_feature_combinations(&workspace, config.release, config.frozen)?;
Ok(())
}
@@ -364,3 +368,48 @@ fn base_cargo_args(
}
args
}
/// Verify that various feature subsets compile.
///
/// Library consumers may pick non-default feature combinations. Running
/// `cargo check` for each combination is fast and catches regressions like
/// issue #595 (indexmap/std not propagated) early.
///
/// The weekly `feature-matrix.yml` workflow runs a superset of these with
/// full `cargo test`; these PR checks are intentionally `cargo check` only
/// to keep CI fast.
fn check_feature_combinations(workspace: &Path, release: bool, frozen: bool) -> Result<()> {
let combos: &[&str] = &[
// Issue #595: library consumer with std + arc + rvm but no full-opa.
// Validates indexmap/std propagation via the weak-dep syntax.
"std,arc,rvm",
// full-opa without mimalloc: the new default after removing the
// vendored allocator from full-opa. All builtins, no allocator.
"std,arc,full-opa",
// Binding-style: full-opa plus the explicit allocator opt-in.
// Mirrors how ffi/java/python/ruby crates are configured.
"std,arc,full-opa,allocator-memory-limits",
// no_std codepath: exercises spin_no_std + absence of std deps.
"arc,opa-no-std",
// Cherry-picked builtins: popular features without full-opa to
// ensure individual feature gates compose correctly.
"std,arc,rvm,coverage,cache,regex,time",
];
for features in combos {
let label = format!(
"cargo check --no-default-features --features {} (ci)",
features
);
run_ci_cargo_step(
workspace,
"check",
release,
frozen,
Some(features),
&["--no-default-features"],
&label,
)?;
}
Ok(())
}