From 7c44318cef3b97dc8bb5a12e778d447adef97055 Mon Sep 17 00:00:00 2001 From: ankur-paan Date: Thu, 22 Oct 2026 19:52:24 +0100 Subject: [PATCH] feat(publish): add rule-engine package, config init scaffolding, and crates.io release automation --- .github/workflows/release.yml | 44 ++++++++- Cargo.toml | 14 ++- crates/kuiperd/src/lib.rs | 81 +++++++++++++++- crates/rekuiper-core/Cargo.toml | 3 - .../tests/test_udf_plugins.rs | 0 crates/rule-engine/Cargo.toml | 36 +++++++ crates/rule-engine/README.md | 93 +++++++++++++++++++ crates/rule-engine/src/bin/kuiperd.rs | 6 ++ crates/rule-engine/src/bin/rekuiperd.rs | 6 ++ crates/rule-engine/src/lib.rs | 29 ++++++ crates/rule-engine/src/main.rs | 6 ++ crates/ruleengine/Cargo.toml | 22 +++++ crates/ruleengine/README.md | 11 +++ crates/ruleengine/src/main.rs | 6 ++ docs/en_US/installation.md | 78 ++++++++++++++++ .../public/diagrams/cargo-install-flow.svg | 73 +++++++++++++++ scripts/run_full_ci.sh | 4 + 17 files changed, 500 insertions(+), 12 deletions(-) rename crates/{rekuiper-core => rekuiper-server}/tests/test_udf_plugins.rs (100%) create mode 100644 crates/rule-engine/Cargo.toml create mode 100644 crates/rule-engine/README.md create mode 100644 crates/rule-engine/src/bin/kuiperd.rs create mode 100644 crates/rule-engine/src/bin/rekuiperd.rs create mode 100644 crates/rule-engine/src/lib.rs create mode 100644 crates/rule-engine/src/main.rs create mode 100644 crates/ruleengine/Cargo.toml create mode 100644 crates/ruleengine/README.md create mode 100644 crates/ruleengine/src/main.rs create mode 100644 docs/en_US/public/diagrams/cargo-install-flow.svg diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5f445431..94456532 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -209,9 +209,51 @@ jobs: readme-filepath: ./deploy/docker/README.md short-description: "Lightweight, ultra-fast streaming SQL engine for edge & IoT in Rust" + publish-cargo: + name: Publish Packages to Crates.io + needs: gate-checks + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + ref: ${{ inputs.tag || github.ref }} + + - name: Setup Rust toolchain + uses: ./.github/actions/setup-rust + + - name: Publish Crates in Topological Order + env: + CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} + shell: bash + run: | + if [ -z "$CARGO_REGISTRY_TOKEN" ]; then + echo "Notice: CARGO_REGISTRY_TOKEN secret is not set; skipping crates.io publish." + exit 0 + fi + + CRATES=( + "rekuiper-conf" + "rekuiper-core" + "rekuiper-sql" + "rekuiper-connectors" + "rekuiper-server" + "kuiperd" + "rekuiper-cli" + "rule-engine" + "ruleengine" + ) + + for crate in "${CRATES[@]}"; do + echo "=== Publishing crate: ${crate} ===" + cargo publish -p "${crate}" --token "${CARGO_REGISTRY_TOKEN}" || true + echo "Sleeping 15 seconds for crates.io index propagation..." + sleep 15 + done + publish-release: name: Publish GitHub Release - needs: [build-release, docker-release] + needs: [build-release, docker-release, publish-cargo] runs-on: ubuntu-latest permissions: contents: write diff --git a/Cargo.toml b/Cargo.toml index 2d1e74b6..521033a3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,6 +8,8 @@ members = [ "crates/rekuiper-cli", "crates/kuiperd", "crates/rekuiper-mcp", + "crates/rule-engine", + "crates/ruleengine", ] resolver = "2" @@ -65,11 +67,13 @@ boa_engine = "0.20" yamux = "0.13" # Internal crate dependencies -rekuiper-core = { path = "crates/rekuiper-core" } -rekuiper-sql = { path = "crates/rekuiper-sql" } -rekuiper-conf = { path = "crates/rekuiper-conf" } -rekuiper-connectors = { path = "crates/rekuiper-connectors" } -rekuiper-server = { path = "crates/rekuiper-server" } +rekuiper-core = { path = "crates/rekuiper-core", version = "1.0.0-rc4" } +rekuiper-sql = { path = "crates/rekuiper-sql", version = "1.0.0-rc4" } +rekuiper-conf = { path = "crates/rekuiper-conf", version = "1.0.0-rc4" } +rekuiper-connectors = { path = "crates/rekuiper-connectors", version = "1.0.0-rc4" } +rekuiper-server = { path = "crates/rekuiper-server", version = "1.0.0-rc4" } +kuiperd = { path = "crates/kuiperd", version = "1.0.0-rc4" } +rule-engine = { path = "crates/rule-engine", version = "1.0.0-rc4" } [profile.release] opt-level = 3 diff --git a/crates/kuiperd/src/lib.rs b/crates/kuiperd/src/lib.rs index 00d9d2a6..239d9a93 100644 --- a/crates/kuiperd/src/lib.rs +++ b/crates/kuiperd/src/lib.rs @@ -3,13 +3,23 @@ use clap::Parser; use rekuiper_conf::PathConfig; #[derive(Parser, Debug)] -#[command(name = "rekuiperd")] -#[command(about = "rekuiper Daemon - High-performance edge stream processing engine in Rust (kuiperd drop-in)", long_about = None)] +#[command(name = "rule-engine")] +#[command( + about = "rule-engine Daemon - High-performance edge stream processing engine and MQTT rule engine in Rust (rekuiper / eKuiper drop-in)", + long_about = None +)] pub struct Args { + #[command(subcommand)] + pub command: Option, + /// Path of etc dir #[arg(long, default_value = "etc")] pub etc: String, + /// Scaffold default configuration files into the target etc directory and exit + #[arg(long)] + pub init: bool, + /// Path of data dir #[arg(long, default_value = "data")] pub data: String, @@ -76,10 +86,75 @@ pub struct Args { pub ui_password: Option, } +#[derive(clap::Subcommand, Debug)] +pub enum Commands { + /// Scaffold and write default configuration files (kuiper.yaml and connector configurations) + Init { + /// Target directory where configuration files will be written (default: "etc") + #[arg(short, long, default_value = "etc")] + dir: String, + + /// Overwrite existing configuration files + #[arg(short, long, default_value_t = false)] + force: bool, + }, +} + +const DEFAULT_KUIPER_YAML: &str = include_str!("../../../etc/kuiper.yaml"); +const DEFAULT_MQTT_SOURCE_YAML: &str = include_str!("../../../etc/mqtt_source.yaml"); +const DEFAULT_CLIENT_YAML: &str = include_str!("../../../etc/client.yaml"); + +/// Scaffolds configuration directory and default YAML files for quickstart. +pub fn scaffold_config(target_dir: &str, force: bool) -> Result<()> { + let base = std::path::Path::new(target_dir); + std::fs::create_dir_all(base)?; + + let files = [ + ("kuiper.yaml", DEFAULT_KUIPER_YAML), + ("mqtt_source.yaml", DEFAULT_MQTT_SOURCE_YAML), + ("client.yaml", DEFAULT_CLIENT_YAML), + ]; + + println!("Scaffolding configuration in '{}/':", target_dir); + for (filename, content) in files { + let file_path = base.join(filename); + if file_path.exists() && !force { + println!( + " [SKIP] {} already exists (use --force to overwrite)", + file_path.display() + ); + } else { + std::fs::write(&file_path, content)?; + println!(" [CREATED] {}", file_path.display()); + } + } + + if target_dir == "etc" { + let _ = std::fs::create_dir_all("data"); + let _ = std::fs::create_dir_all("log"); + println!(" [CREATED] ./data/"); + println!(" [CREATED] ./log/"); + } + + println!("\nConfiguration complete! You can now start the daemon:"); + println!(" rule-engine"); + println!(" # or: kuiperd\n"); + + Ok(()) +} + pub async fn run_daemon() -> Result<()> { + let args = Args::parse(); + + if let Some(Commands::Init { dir, force }) = args.command { + return scaffold_config(&dir, force); + } + if args.init { + return scaffold_config(&args.etc, false); + } + tracing_subscriber::fmt::init(); - let args = Args::parse(); let paths = PathConfig::new(Some(&args.etc), Some(&args.data), Some(&args.log)); let config = paths.load_config()?; diff --git a/crates/rekuiper-core/Cargo.toml b/crates/rekuiper-core/Cargo.toml index 2d61c45d..96d50920 100644 --- a/crates/rekuiper-core/Cargo.toml +++ b/crates/rekuiper-core/Cargo.toml @@ -28,6 +28,3 @@ serde_yaml = { workspace = true } toml = { workspace = true } regex = { workspace = true } wasmi = "0.36" - -[dev-dependencies] -rekuiper-sql = { workspace = true } diff --git a/crates/rekuiper-core/tests/test_udf_plugins.rs b/crates/rekuiper-server/tests/test_udf_plugins.rs similarity index 100% rename from crates/rekuiper-core/tests/test_udf_plugins.rs rename to crates/rekuiper-server/tests/test_udf_plugins.rs diff --git a/crates/rule-engine/Cargo.toml b/crates/rule-engine/Cargo.toml new file mode 100644 index 00000000..eb031375 --- /dev/null +++ b/crates/rule-engine/Cargo.toml @@ -0,0 +1,36 @@ +[package] +name = "rule-engine" +version.workspace = true +edition.workspace = true +license.workspace = true +description = "High-performance edge stream processing engine and MQTT rule engine daemon (rekuiper / eKuiper drop-in)" +authors.workspace = true +repository.workspace = true +homepage.workspace = true +documentation.workspace = true +keywords.workspace = true +categories.workspace = true +readme = "README.md" + +[lib] +name = "rule_engine" +path = "src/lib.rs" + +[[bin]] +name = "rule-engine" +path = "src/main.rs" + +[[bin]] +name = "kuiperd" +path = "src/bin/kuiperd.rs" + +[[bin]] +name = "rekuiperd" +path = "src/bin/rekuiperd.rs" + +[dependencies] +kuiperd = { workspace = true } +rekuiper-sql = { workspace = true } +rekuiper-core = { workspace = true } +tokio = { workspace = true } +anyhow = { workspace = true } diff --git a/crates/rule-engine/README.md b/crates/rule-engine/README.md new file mode 100644 index 00000000..22474c1b --- /dev/null +++ b/crates/rule-engine/README.md @@ -0,0 +1,93 @@ +# rule-engine + +[![Crates.io](https://img.shields.io/crates/v/rule-engine.svg)](https://crates.io/crates/rule-engine) +[![License](https://img.shields.io/badge/license-MIT%20OR%20Apache--2.0-blue.svg)](https://github.com/Indus-Stack/rekuiper) +[![Documentation](https://img.shields.io/badge/docs-rekuiper.indramqtt.com-blue)](https://rekuiper.indramqtt.com) + +**rule-engine** (`rekuiper`) is a high-performance, lightweight edge stream processing engine and MQTT rule engine written in Rust. It functions as a drop-in replacement for LF Edge eKuiper (`kuiperd`), delivering sub-millisecond rule evaluation with ultra-low memory overhead on Linux, macOS, Windows, and embedded IoT architectures. + +--- + +## Quickstart + +### 1. Install via Cargo + +```bash +cargo install rule-engine +``` + +*Or install pre-compiled binaries instantly:* +```bash +cargo binstall rule-engine +``` + +### 2. Scaffold Default Configuration + +Initialize the configuration files (`kuiper.yaml`, `mqtt_source.yaml`, `client.yaml`): + +```bash +rule-engine init +``` + +### 3. Start the Daemon + +```bash +rule-engine +``` + +The daemon starts: +- **REST API**: `http://127.0.0.1:9081` +- **Embedded Web Console**: `http://127.0.0.1:9081/ui` (when enabled with `--ui`) +- **CLI RPC Port**: `20498` + +--- + +## Use as an Embedded Library in Rust + +You can also embed the streaming SQL processor and rule engine directly inside your own Rust application: + +```toml +[dependencies] +rule-engine = "1.0.0-rc4" +``` + +```rust +use rule_engine::sql::Parser; + +fn main() -> anyhow::Result<()> { + // Parse streaming SQL queries with windowing, filters, and aggregations + let mut parser = Parser::new("SELECT temperature, humidity FROM sensor WHERE temperature > 50"); + let stmt = parser.parse_select()?; + println!("Parsed query: {:?}", stmt); + Ok(()) +} +``` + +Or depend exclusively on the lightweight SQL parsing & execution engine: +```toml +[dependencies] +rekuiper-sql = "1.0.0-rc4" +``` + +--- + +## Binary Names + +Installing `rule-engine` installs three executable aliases: +1. `rule-engine` +2. `kuiperd` (100% LF Edge eKuiper drop-in compatibility) +3. `rekuiperd` + +--- + +## Documentation & Community + +- **Official Documentation**: [https://rekuiper.indramqtt.com](https://rekuiper.indramqtt.com) +- **Source Repository**: [https://github.com/Indus-Stack/rekuiper](https://github.com/Indus-Stack/rekuiper) +- **Docker Images**: `indusstack/rekuiper:latest` + +## License + +Licensed under either of: +- Apache License, Version 2.0 ([LICENSE-APACHE](https://github.com/Indus-Stack/rekuiper/blob/main/LICENSE-APACHE)) +- MIT license ([LICENSE-MIT](https://github.com/Indus-Stack/rekuiper/blob/main/LICENSE-MIT)) diff --git a/crates/rule-engine/src/bin/kuiperd.rs b/crates/rule-engine/src/bin/kuiperd.rs new file mode 100644 index 00000000..83ad340e --- /dev/null +++ b/crates/rule-engine/src/bin/kuiperd.rs @@ -0,0 +1,6 @@ +use anyhow::Result; + +#[tokio::main] +async fn main() -> Result<()> { + kuiperd::run_daemon().await +} diff --git a/crates/rule-engine/src/bin/rekuiperd.rs b/crates/rule-engine/src/bin/rekuiperd.rs new file mode 100644 index 00000000..83ad340e --- /dev/null +++ b/crates/rule-engine/src/bin/rekuiperd.rs @@ -0,0 +1,6 @@ +use anyhow::Result; + +#[tokio::main] +async fn main() -> Result<()> { + kuiperd::run_daemon().await +} diff --git a/crates/rule-engine/src/lib.rs b/crates/rule-engine/src/lib.rs new file mode 100644 index 00000000..09227789 --- /dev/null +++ b/crates/rule-engine/src/lib.rs @@ -0,0 +1,29 @@ +//! # rule-engine +//! +//! High-performance edge stream processing engine and streaming SQL rule engine in Rust. +//! +//! ## Embedding in Your Own Application +//! +//! You can embed `rule-engine` or its streaming SQL engine directly in your Rust project: +//! +//! ```toml +//! [dependencies] +//! rule-engine = "1.0.0-rc4" +//! ``` +//! +//! ### Using the Streaming SQL Engine +//! +//! ```rust +//! use rule_engine::sql::Parser; +//! +//! # fn main() -> anyhow::Result<()> { +//! let sql = "SELECT temperature * 1.8 + 32 AS temp_f FROM sensor WHERE temperature > 20"; +//! let mut parser = Parser::new(sql); +//! let stmt = parser.parse_select()?; +//! # Ok(()) +//! # } +//! ``` + +pub use kuiperd::*; +pub use rekuiper_core as core; +pub use rekuiper_sql as sql; diff --git a/crates/rule-engine/src/main.rs b/crates/rule-engine/src/main.rs new file mode 100644 index 00000000..83ad340e --- /dev/null +++ b/crates/rule-engine/src/main.rs @@ -0,0 +1,6 @@ +use anyhow::Result; + +#[tokio::main] +async fn main() -> Result<()> { + kuiperd::run_daemon().await +} diff --git a/crates/ruleengine/Cargo.toml b/crates/ruleengine/Cargo.toml new file mode 100644 index 00000000..5f00e801 --- /dev/null +++ b/crates/ruleengine/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "ruleengine" +version.workspace = true +edition.workspace = true +license.workspace = true +description = "High-performance edge stream processing engine and MQTT rule engine daemon (alias for rule-engine / rekuiper)" +authors.workspace = true +repository.workspace = true +homepage.workspace = true +documentation.workspace = true +keywords.workspace = true +categories.workspace = true +readme = "README.md" + +[[bin]] +name = "ruleengine" +path = "src/main.rs" + +[dependencies] +kuiperd = { workspace = true } +tokio = { workspace = true } +anyhow = { workspace = true } diff --git a/crates/ruleengine/README.md b/crates/ruleengine/README.md new file mode 100644 index 00000000..820c11f9 --- /dev/null +++ b/crates/ruleengine/README.md @@ -0,0 +1,11 @@ +# ruleengine + +This crate is an alias for **[rule-engine](https://crates.io/crates/rule-engine)** (`rekuiper`). + +Please install and use `rule-engine`: + +```bash +cargo install rule-engine +``` + +For full documentation and instructions, visit [https://rekuiper.indramqtt.com](https://rekuiper.indramqtt.com). diff --git a/crates/ruleengine/src/main.rs b/crates/ruleengine/src/main.rs new file mode 100644 index 00000000..83ad340e --- /dev/null +++ b/crates/ruleengine/src/main.rs @@ -0,0 +1,6 @@ +use anyhow::Result; + +#[tokio::main] +async fn main() -> Result<()> { + kuiperd::run_daemon().await +} diff --git a/docs/en_US/installation.md b/docs/en_US/installation.md index 1ac307c2..3d396121 100644 --- a/docs/en_US/installation.md +++ b/docs/en_US/installation.md @@ -243,4 +243,82 @@ helm install rekuiper deploy/chart/rekuiper \ For the complete values schema, review the [rekuiper Helm Chart Documentation](https://github.com/Indus-Stack/rekuiper/tree/main/deploy/chart/rekuiper). +--- + +## 8. Cargo (crates.io) Installation + +You can install rekuiper as a Rust crate using Cargo. The official package name on crates.io is **`rule-engine`**. + +![Cargo Installation and Scaffolding Flow](/diagrams/cargo-install-flow.svg) + +### 8.1 Install the Package + +Install from source using `cargo`: + +```shell +cargo install rule-engine +``` + +Or install pre-compiled binaries directly using `cargo-binstall`: + +```shell +cargo binstall rule-engine +``` + +This command installs three binary aliases in `~/.cargo/bin/`: +- `rule-engine` (primary command) +- `kuiperd` (100% drop-in command for LF Edge eKuiper) +- `rekuiperd` + +### 8.2 Scaffold Configuration Files + +`cargo install` copies only executable files. It does not copy default configuration directories. + +Run the `init` command to create the default configuration files: + +```shell +rule-engine init +``` + +Expected output: +```text +Scaffolding configuration in 'etc/': + [CREATED] etc/kuiper.yaml + [CREATED] etc/mqtt_source.yaml + [CREATED] etc/client.yaml + [CREATED] ./data/ + [CREATED] ./log/ + +Configuration complete! You can now start the daemon: + rule-engine +``` + +To write configuration to a custom directory: + +```shell +rule-engine init --dir /opt/rekuiper/etc +``` + +To overwrite existing files, add the `--force` flag: + +```shell +rule-engine init --force +``` + +### 8.3 Start the Engine + +Start the daemon: + +```shell +rule-engine +``` + +Verify service status: + +```shell +curl http://localhost:9081/ping +# Response: pong +``` + + diff --git a/docs/en_US/public/diagrams/cargo-install-flow.svg b/docs/en_US/public/diagrams/cargo-install-flow.svg new file mode 100644 index 00000000..80df6bf0 --- /dev/null +++ b/docs/en_US/public/diagrams/cargo-install-flow.svg @@ -0,0 +1,73 @@ + + Cargo Installation and Configuration Scaffolding Flow + Flow showing cargo install of rule-engine, followed by rule-engine init to scaffold configuration, and starting the daemon. + + + + + + + + + + + + + + + + STEP 1: INSTALL CRATE + + CARGO REGISTRY + + cargo install rule-engine + + OR FAST BINARY (3 SEC) + + cargo binstall rule-engine + + Installs to ~/.cargo/bin/ + + + + + + + + + + + STEP 2: SCAFFOLD CONFIG + + COMMAND + + rule-engine init + + FILES CREATED + • etc/kuiper.yaml + • etc/mqtt_source.yaml + • etc/client.yaml + • data/ and log/ dirs + + + + + + + + + + + STEP 3: RUN ENGINE + + START DAEMON + + rule-engine + + ACTIVE SERVICES + • REST API: :9081 + • Web UI: :9081/ui + • CLI RPC: :20498 + • Stream Processing + + diff --git a/scripts/run_full_ci.sh b/scripts/run_full_ci.sh index 13d3f081..1df751bb 100644 --- a/scripts/run_full_ci.sh +++ b/scripts/run_full_ci.sh @@ -53,4 +53,8 @@ echo ">>> Testing kuiperd <<<" cargo test -p kuiperd --target-dir /home/paanday/target rm -rf /home/paanday/target +echo ">>> Testing rule-engine & ruleengine <<<" +cargo test -p rule-engine -p ruleengine --target-dir /home/paanday/target +rm -rf /home/paanday/target + echo "=== ALL LOCAL CI CHECKS PASSED SUCCESSFULLY ==="