mirror of
https://github.com/ruvnet/RuView
synced 2026-07-27 18:11:43 +00:00
fix: Docker entrypoint arg handling + configurable model directory
Fixes #384: docker run with --source/--tick-ms flags now works correctly. Fixes #399: model files in mounted volumes are now discoverable via MODELS_DIR env var. Root cause (issue #384): The Dockerfile used ENTRYPOINT ["/bin/sh", "-c"] with a shell-form CMD. When users passed flags like `--source wifi --tick-ms 500` as docker run arguments, Docker replaced CMD entirely, resulting in `/bin/sh -c "--source wifi --tick-ms 500"` which executes `--source` as a shell command → `--source: not found`. Root cause (issue #399): Model directory was hardcoded to the relative path `data/models`. When Docker users mounted models to `/app/models/`, the scan looked in the wrong place. Changes: 1. docker/docker-entrypoint.sh (new): - Proper entrypoint script that handles both env-var-based defaults and user-passed CLI flags - No arguments → starts server with CSI_SOURCE env var as --source - Flag arguments (start with -) → prepends /app/sensing-server + defaults, appends user flags (clap last-wins allows overrides) - Non-flag first arg → exec passthrough (e.g., /bin/sh for debugging) - Sets --bind-addr 0.0.0.0 (was 127.0.0.1 which blocks container access) 2. docker/Dockerfile.rust: - Switch from ENTRYPOINT ["/bin/sh", "-c"] to exec-form entrypoint - Add MODELS_DIR env var (default: data/models) - COPY the entrypoint script into the image 3. docker/docker-compose.yml: - Remove shell-form command (entrypoint handles defaults) - Add MODELS_DIR env var 4. model_manager.rs + main.rs: - Replace hardcoded `data/models` path with `effective_models_dir()` / `models_dir()` that reads MODELS_DIR env var at runtime - Docker users can now: docker run -v /host/models:/app/models -e MODELS_DIR=/app/models 5. tests/test_docker_entrypoint.sh (new, 17 tests): - Default CSI_SOURCE substitution (6 assertions) - Custom CSI_SOURCE propagation - User-passed flag arguments (--source, --tick-ms, --model) - Unset CSI_SOURCE defaults to auto - Explicit command passthrough - MODELS_DIR env var propagation
This commit is contained in:
@@ -2797,7 +2797,7 @@ async fn delete_model(
|
||||
if safe_id.is_empty() || safe_id != id {
|
||||
return Json(serde_json::json!({ "error": "invalid model id", "success": false }));
|
||||
}
|
||||
let path = PathBuf::from("data/models").join(format!("{}.rvf", safe_id));
|
||||
let path = effective_models_dir().join(format!("{}.rvf", safe_id));
|
||||
if path.exists() {
|
||||
if let Err(e) = std::fs::remove_file(&path) {
|
||||
warn!("Failed to delete model file {:?}: {}", path, e);
|
||||
@@ -2842,9 +2842,18 @@ async fn activate_lora_profile(
|
||||
Json(serde_json::json!({ "success": true, "profile": profile }))
|
||||
}
|
||||
|
||||
/// Scan `data/models/` for `.rvf` files and return metadata.
|
||||
/// Return the effective models directory, respecting the `MODELS_DIR`
|
||||
/// environment variable. Defaults to `data/models`.
|
||||
fn effective_models_dir() -> PathBuf {
|
||||
PathBuf::from(
|
||||
std::env::var("MODELS_DIR").unwrap_or_else(|_| "data/models".to_string()),
|
||||
)
|
||||
}
|
||||
|
||||
/// Scan the models directory for `.rvf` files and return metadata.
|
||||
/// Respects the `MODELS_DIR` environment variable.
|
||||
fn scan_model_files() -> Vec<serde_json::Value> {
|
||||
let dir = PathBuf::from("data/models");
|
||||
let dir = effective_models_dir();
|
||||
let mut models = Vec::new();
|
||||
if let Ok(entries) = std::fs::read_dir(&dir) {
|
||||
for entry in entries.flatten() {
|
||||
@@ -2874,9 +2883,10 @@ fn scan_model_files() -> Vec<serde_json::Value> {
|
||||
models
|
||||
}
|
||||
|
||||
/// Scan `data/models/` for `.lora.json` LoRA profile files.
|
||||
/// Scan the models directory for `.lora.json` LoRA profile files.
|
||||
/// Respects the `MODELS_DIR` environment variable.
|
||||
fn scan_lora_profiles() -> Vec<serde_json::Value> {
|
||||
let dir = PathBuf::from("data/models");
|
||||
let dir = effective_models_dir();
|
||||
let mut profiles = Vec::new();
|
||||
if let Ok(entries) = std::fs::read_dir(&dir) {
|
||||
for entry in entries.flatten() {
|
||||
@@ -4604,7 +4614,8 @@ async fn main() {
|
||||
}
|
||||
|
||||
// Ensure data directories exist for models and recordings
|
||||
let _ = std::fs::create_dir_all("data/models");
|
||||
let models_dir = effective_models_dir();
|
||||
let _ = std::fs::create_dir_all(&models_dir);
|
||||
let _ = std::fs::create_dir_all("data/recordings");
|
||||
|
||||
// Discover model and recording files on startup
|
||||
|
||||
+15
-4
@@ -30,8 +30,19 @@ use crate::rvf_container::RvfReader;
|
||||
|
||||
// ── Models data directory ────────────────────────────────────────────────────
|
||||
|
||||
/// Base directory for RVF model files.
|
||||
pub const MODELS_DIR: &str = "data/models";
|
||||
/// Default base directory for RVF model files.
|
||||
///
|
||||
/// Overridden at runtime by the `MODELS_DIR` environment variable so that
|
||||
/// Docker users can point to a mounted volume without rebuilding:
|
||||
/// docker run -v /path/to/models:/app/models -e MODELS_DIR=/app/models ...
|
||||
pub const MODELS_DIR_DEFAULT: &str = "data/models";
|
||||
|
||||
/// Return the effective models directory, respecting `MODELS_DIR` env var.
|
||||
pub fn models_dir() -> PathBuf {
|
||||
PathBuf::from(
|
||||
std::env::var("MODELS_DIR").unwrap_or_else(|_| MODELS_DIR_DEFAULT.to_string()),
|
||||
)
|
||||
}
|
||||
|
||||
// ── Types ────────────────────────────────────────────────────────────────────
|
||||
|
||||
@@ -110,7 +121,7 @@ pub type AppState = Arc<RwLock<super::AppStateInner>>;
|
||||
|
||||
/// Scan the models directory and build `ModelInfo` for each `.rvf` file.
|
||||
async fn scan_models() -> Vec<ModelInfo> {
|
||||
let dir = PathBuf::from(MODELS_DIR);
|
||||
let dir = models_dir();
|
||||
let mut models = Vec::new();
|
||||
|
||||
let mut entries = match tokio::fs::read_dir(&dir).await {
|
||||
@@ -204,7 +215,7 @@ async fn scan_models() -> Vec<ModelInfo> {
|
||||
|
||||
/// Load a model from disk by ID and return its `LoadedModelState`.
|
||||
fn load_model_from_disk(model_id: &str) -> Result<LoadedModelState, String> {
|
||||
let file_path = PathBuf::from(MODELS_DIR).join(format!("{model_id}.rvf"));
|
||||
let file_path = models_dir().join(format!("{model_id}.rvf"));
|
||||
let reader = RvfReader::from_file(&file_path)?;
|
||||
|
||||
let manifest = reader.manifest().unwrap_or_default();
|
||||
|
||||
Reference in New Issue
Block a user