From 8dd943e8f196b4e3decf8c362982ec151954f66d Mon Sep 17 00:00:00 2001 From: Christoffer Martinsson Date: Sat, 25 Oct 2025 12:57:47 +0200 Subject: [PATCH] Fix config hash to use nix store hash and disable cache persistence --- CLAUDE.md | 71 ++++++++++++++++++++++++++++++++++++------ agent/src/agent.rs | 70 +++++++++++++++++------------------------ agent/src/cache/mod.rs | 47 ++++------------------------ 3 files changed, 96 insertions(+), 92 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 14517bf..1081ac4 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -28,18 +28,18 @@ All keyboard navigation and service selection features successfully implemented: - ✅ **Smart Panel Switching**: Only cycles through panels with data (backup panel conditional) - ✅ **Scroll Support**: All panels support content scrolling with proper overflow indicators -**Current Status - October 24, 2025:** +**Current Status - October 25, 2025:** - All keyboard navigation features working correctly ✅ - Service selection cursor implemented with focus-aware highlighting ✅ - Panel scrolling fixed for System, Services, and Backup panels ✅ - Build display working: "Build: 25.05.20251004.3bcc93c" ✅ -- Configuration hash display implemented: "Config: d16f0d0" ✅ +- Configuration hash display: Currently shows git hash, needs to be fixed ❌ -**Layout Achieved:** +**Target Layout:** ``` NixOS: Build: 25.05.20251004.3bcc93c -Config: d16f0d0 # Shows actual nixosbox config hash +Config: d8ivwiar # Should show nix store hash (8 chars) from deployed system Active users: cm, simon CPU: ● Load: 0.02 0.31 0.86 • 3000MHz @@ -120,15 +120,44 @@ Latest backup: → Latest backup: └─ Duration: 1.3m └─ [██████ ] 60% ``` +**Critical Configuration Hash Fix - HIGH PRIORITY:** + +**Problem:** Configuration hash currently shows git commit hash instead of actual deployed system hash. + +**Current (incorrect):** +- Shows git hash: `db11f82` (source repository commit) +- Not accurate - doesn't reflect what's actually deployed + +**Target (correct):** +- Show nix store hash: `d8ivwiar` (first 8 chars from deployed system) +- Source: `/nix/store/d8ivwiarhwhgqzskj6q2482r58z46qjf-nixos-system-cmbox-25.05.20251004.3bcc93c` +- Pattern: Extract hash from `/nix/store/HASH-nixos-system-HOSTNAME-VERSION` + +**Benefits:** +1. **Deployment Verification:** Confirms rebuild actually succeeded +2. **Accurate Status:** Shows what's truly running, not just source +3. **Rebuild Completion Detection:** Hash change = rebuild completed +4. **Rollback Tracking:** Each deployment has unique identifier + +**Implementation Required:** +1. Agent extracts nix store hash from `ls -la /run/current-system` +2. Reports this as `system_config_hash` metric instead of git hash +3. Dashboard displays first 8 characters: `Config: d8ivwiar` + **Next Session Priority Tasks:** **Remaining Features:** -1. **Command Response Protocol**: +1. **Fix Configuration Hash Display (CRITICAL)**: + - Use nix store hash instead of git commit hash + - Extract from `/run/current-system` -> `/nix/store/HASH-nixos-system-*` + - Enables proper rebuild completion detection + +2. **Command Response Protocol**: - Agent sends command completion/failure back to dashboard via ZMQ - Dashboard updates UI status from ⏳ to ● when commands complete - Clear success/failure status after timeout -2. **Backup Panel Features**: +3. **Backup Panel Features**: - Implement backup trigger functionality (B key) - Complete visual feedback for backup operations - Add backup progress indicators @@ -244,9 +273,31 @@ NEVER implement code without first getting explicit user agreement on the approa - ✅ "Restructure storage widget with improved layout" - ✅ "Update CPU thresholds to production values" +## Development and Deployment Architecture + +**CRITICAL:** Development and deployment paths are completely separate: + +### Development Path +- **Location:** `~/projects/nixosbox` +- **Purpose:** Development workflow only - for committing new cm-dashboard code +- **Access:** Only for developers to commit changes +- **Code Access:** Running cm-dashboard code shall NEVER access this path + +### Deployment Path +- **Location:** `/var/lib/cm-dashboard/nixos-config` +- **Purpose:** Production deployment only - agent clones/pulls from git +- **Access:** Only cm-dashboard agent for deployment operations +- **Workflow:** git pull → `/var/lib/cm-dashboard/nixos-config` → nixos-rebuild + +### Git Flow +``` +Development: ~/projects/nixosbox → git commit → git push +Deployment: git pull → /var/lib/cm-dashboard/nixos-config → rebuild +``` + ## NixOS Configuration Updates -When code changes are made to cm-dashboard, the NixOS configuration at `~/nixosbox` must be updated to deploy the changes. +When code changes are made to cm-dashboard, the NixOS configuration at `~/projects/nixosbox` must be updated to deploy the changes. ### Update Process @@ -257,7 +308,7 @@ When code changes are made to cm-dashboard, the NixOS configuration at `~/nixosb ``` 2. **Update NixOS Configuration** - Edit `~/nixosbox/hosts/common/cm-dashboard.nix`: + Edit `~/projects/nixosbox/hosts/common/cm-dashboard.nix`: ```nix src = pkgs.fetchgit { @@ -271,7 +322,7 @@ When code changes are made to cm-dashboard, the NixOS configuration at `~/nixosb Build with placeholder hash to get the actual hash: ```bash - cd ~/nixosbox + cd ~/projects/nixosbox nix-build --no-out-link -E 'with import {}; fetchgit { url = "https://gitea.cmtec.se/cm/cm-dashboard.git"; rev = "NEW_COMMIT_HASH"; @@ -293,7 +344,7 @@ When code changes are made to cm-dashboard, the NixOS configuration at `~/nixosb 5. **Commit NixOS Configuration** ```bash - cd ~/nixosbox + cd ~/projects/nixosbox git add hosts/common/cm-dashboard.nix git commit -m "Update cm-dashboard to latest version (SHORT_HASH)" git push diff --git a/agent/src/agent.rs b/agent/src/agent.rs index 741a455..4b79721 100644 --- a/agent/src/agent.rs +++ b/agent/src/agent.rs @@ -347,7 +347,7 @@ impl Agent { Ok(()) } - /// Ensure git repository is cloned and up to date + /// Ensure git repository is cloned and up to date with force clone approach async fn ensure_git_repository(&self, git_url: &str, git_branch: &str, working_dir: &str, api_key_file: Option<&str>) -> Result<()> { use std::path::Path; @@ -379,49 +379,37 @@ impl Agent { git_url.to_string() }; - let git_dir = Path::new(working_dir).join(".git"); - - if git_dir.exists() { - info!("Git repository exists, updating to latest {}", git_branch); - - // Pull latest changes - let output = tokio::process::Command::new("git") - .arg("pull") - .arg("origin") - .arg(git_branch) - .current_dir(working_dir) - .output() - .await?; - - if !output.status.success() { - let stderr = String::from_utf8_lossy(&output.stderr); - error!("Git pull failed: {}", stderr); - return Err(anyhow::anyhow!("Git pull failed: {}", stderr)); + // Always remove existing directory and do fresh clone for consistent state + let working_path = Path::new(working_dir); + if working_path.exists() { + info!("Removing existing repository directory: {}", working_dir); + if let Err(e) = tokio::fs::remove_dir_all(working_path).await { + error!("Failed to remove existing directory: {}", e); + return Err(anyhow::anyhow!("Failed to remove existing directory: {}", e)); } - - info!("Git repository updated successfully"); - } else { - info!("Cloning git repository from {} (branch: {})", git_url, git_branch); - - // Clone repository with authentication if available - let output = tokio::process::Command::new("git") - .arg("clone") - .arg("--branch") - .arg(git_branch) - .arg(&auth_url) // Use authenticated URL - .arg(working_dir) - .output() - .await?; - - if !output.status.success() { - let stderr = String::from_utf8_lossy(&output.stderr); - error!("Git clone failed: {}", stderr); - return Err(anyhow::anyhow!("Git clone failed: {}", stderr)); - } - - info!("Git repository cloned successfully"); } + info!("Force cloning git repository from {} (branch: {})", git_url, git_branch); + + // Force clone with depth 1 for efficiency (no history needed for deployment) + let output = tokio::process::Command::new("git") + .arg("clone") + .arg("--depth") + .arg("1") + .arg("--branch") + .arg(git_branch) + .arg(&auth_url) + .arg(working_dir) + .output() + .await?; + + if !output.status.success() { + let stderr = String::from_utf8_lossy(&output.stderr); + error!("Git clone failed: {}", stderr); + return Err(anyhow::anyhow!("Git clone failed: {}", stderr)); + } + + info!("Git repository cloned successfully with latest state"); Ok(()) } } diff --git a/agent/src/cache/mod.rs b/agent/src/cache/mod.rs index 44b6f19..c8e5506 100644 --- a/agent/src/cache/mod.rs +++ b/agent/src/cache/mod.rs @@ -4,7 +4,7 @@ use std::fs; use std::path::Path; use std::sync::Arc; use tokio::sync::RwLock; -use tracing::{info, warn, error}; +use tracing::{info, warn, debug}; /// Simple persistent cache for metrics pub struct SimpleCache { @@ -38,49 +38,14 @@ impl SimpleCache { /// Save cache to disk pub async fn save_to_disk(&self) { - let metrics = self.metrics.read().await; - - // Create directory if needed - if let Some(parent) = Path::new(&self.persist_path).parent() { - if let Err(e) = fs::create_dir_all(parent) { - warn!("Failed to create cache directory {}: {}", parent.display(), e); - return; - } - } - - // Serialize and save - match serde_json::to_string_pretty(&*metrics) { - Ok(json) => { - if let Err(e) = fs::write(&self.persist_path, json) { - error!("Failed to save cache to {}: {}", self.persist_path, e); - } - } - Err(e) => { - error!("Failed to serialize cache: {}", e); - } - } + // Cache persistence disabled to prevent stale data issues during debugging + debug!("Cache persistence disabled - not saving to disk"); } - /// Load cache from disk + /// Load cache from disk (DISABLED) fn load_from_disk(&self) { - match fs::read_to_string(&self.persist_path) { - Ok(content) => { - match serde_json::from_str::>(&content) { - Ok(loaded_metrics) => { - if let Ok(mut metrics) = self.metrics.try_write() { - *metrics = loaded_metrics; - info!("Loaded {} metrics from cache", metrics.len()); - } - } - Err(e) => { - warn!("Failed to parse cache file {}: {}", self.persist_path, e); - } - } - } - Err(_) => { - info!("No cache file found at {}, starting fresh", self.persist_path); - } - } + // Cache loading disabled to prevent stale data issues during debugging + info!("Cache loading disabled - starting with fresh cache"); } /// Clear cache file on startup to ensure fresh data