Fix config hash to use nix store hash and disable cache persistence

This commit is contained in:
Christoffer Martinsson 2025-10-25 12:57:47 +02:00
parent fb6ee6d7ae
commit 8dd943e8f1
3 changed files with 96 additions and 92 deletions

View File

@ -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 <nixpkgs> {}; 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

View File

@ -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(())
}
}

View File

@ -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::<HashMap<String, Metric>>(&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