Crate prox

Crate prox 

Source
Expand description

Rusty development process manager like foreman, but better!

WIP: This project is a work-in-progress but it’s basically working. The API is subject to change. I plan to add improvements as I use it/as needed, including a possible TUI interface.

§Motivation

I wanted something to run multiple processes in my project like Ruby’s foreman (back in the day) but without the drawbacks of:

  • the complexity/sluggishness of wrapping every binary in a Docker container and maintaining a docker-compose.yaml
  • cargo bacon has tedious config and is meant for one-off cargo check/cargo test, not running multiple procs in parallel like docker or foreman
  • cargo watch is abandonware (why not transfer the name to someone else?)
  • just is great, but requires manual watchexec, etc.
  • something like foreman but a bit smarter for restarts/crash handling, almost like docker
  • shell scripts to combine logs and manage the procs + awk + control-C are a pain!

§Installation

cargo install prox

(Or use as a library in your dev-dependencies and create your own wrapper – see examples/basic_usage.rs)

§Usage

§Binary Usage:

Create a prox.toml (or .yaml or .json) file in your workspace/crate root:

[config]
readiness_fallback_timeout = 15

[[procs]]
name = "api"
command = "cargo"
args = ["run", "--bin", "api"]
working_dir = "api"
readiness_pattern = "Server listening"
# In a workspace, you can watch from the workspace root:
watch = ["Cargo.toml"]
# ... and/or relative to `working_dir`:
watch_rel = ["src"]
env_clear = true
env = { PORT = "3000", RUST_LOG = "debug" }

[[procs]]
name = "worker"
command = "cargo"
args = ["run", "--bin", "worker"]
working_dir = "tests/fixtures"
env_clear = true

Then just run prox!

§Library Usage:

Add prox to your [dev-dependencies] and create your own bin wrapper, e.g. dev. See examples/basic_usage.rs for an example.

§Features:

  • Run several commands in parallel
  • Colored prefixes per process
  • File watching with automatic restart (global + per-proc watch paths)
  • Readiness pattern or fallback timeout
  • Debounced output idle detection
  • Clean shutdown & optional cleanup commands
  • Config via Rust builders or TOML / YAML / JSON files

Events you can receive (via setup_event_rx()):

  • AllStarted when every proc is ready
  • Started, Restarted per process
  • Exited, StartFailed on failure
  • Idle after no output for the debounce period
  • SigIntReceived on Ctrl-C (if enabled)

Runtime control (optional): send ProxSignal::Start, Restart, Shutdown through the signal_rx channel you provide.

Environment:

  • Per process vars: env (clears first if env_clear = true).

Colors:

  • Auto-assigned from config.colors unless a proc sets color.

Readiness:

  • If readiness_pattern set, logs are scanned case-insensitively.
  • Otherwise, after readiness_fallback_timeout the proc is assumed running.

IMPORTANT! Not a production supervisor! For local development only.

§Example:

examples/basic_usage.rs

use owo_colors::AnsiColors;
use prox::{Config, Proc, Prox};
use std::time::Duration;
use std::{collections::HashMap, path::PathBuf};

fn main() -> anyhow::Result<()> {
    // Example: Simple process manager with multiple services
    let mut manager = Prox::builder()
        .config(
            Config::builder()
                .readiness_fallback_timeout(Duration::from_secs(15))
                .build(),
        )
        .procs(
            [
                Proc::builder()
                    .name("api".into())
                    .command("cargo".into())
                    .args(vec!["run".into(), "--bin".into(), "api".into()])
                    .working_dir(PathBuf::from("./api"))
                    .readiness_pattern("Server listening".into())
                    .watch(vec![
                        PathBuf::from("./api/src"),
                        PathBuf::from("./shared/src"),
                    ])
                    .env(HashMap::from_iter([("LOG_LEVEL".into(), "debug".into())]))
                    .color(AnsiColors::BrightGreen)
                    .build(),
                Proc::builder()
                    .name("database".into())
                    .command("docker".into())
                    .args(["run".into()].into())
                    .env(
                        [
                            ("POSTGRES_DB".into(), "myapp".into()),
                            ("POSTGRES_PASSWORD".into(), "password".into()),
                        ]
                        .into(),
                    )
                    .readiness_pattern("database system is ready to accept connections".into())
                    .build(),
                Proc::builder()
                    .name("frontend".into())
                    .command("npm".into())
                    .args(vec!["run".into(), "dev".into()])
                    .working_dir(PathBuf::from("./frontend"))
                    .readiness_pattern("Local:".into())
                    .watch(vec![PathBuf::from("./frontend/src")])
                    .color(AnsiColors::BrightYellow)
                    .build(),
            ]
            .into(),
        )
        .build();

    println!("Starting development environment...");

    // Start all processes - this will block until Ctrl+C or a process exits
    manager.start()?;

    Ok(())
}

Modules§

logging
Logging utilities and macros

Macros§

debug
Log to the prox logger at debug level
error
Log to the prox logger at error level
info
Log to the prox logger at info level
warn
Log to the prox logger at warn level

Structs§

Config
Configuration for the process manager
Proc
A single process configuration for prox
Prox
Process manager that handles multiple processes

Enums§

ProcStatus
Status of a managed process
ProxEvent
Events emitted by the process manager (Prox)
ProxSignal
Signals that can be sent to the process manager (Prox)

Constants§

ALL
All bright and normal ANSI colors except black
BRIGHT
All bright ANSI colors except black
NORMAL
All ANSI colors black