fix: use SHA-256 for container fingerprints instead of DefaultHasher
DefaultHasher is not stable across Rust compiler versions or binary rebuilds, causing unnecessary container recreations on every app update. Replace with SHA-256 for deterministic, cross-build-stable fingerprints. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
1
app/src-tauri/Cargo.lock
generated
1
app/src-tauri/Cargo.lock
generated
@@ -4681,6 +4681,7 @@ dependencies = [
|
|||||||
"reqwest 0.12.28",
|
"reqwest 0.12.28",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
"sha2",
|
||||||
"tar",
|
"tar",
|
||||||
"tauri",
|
"tauri",
|
||||||
"tauri-build",
|
"tauri-build",
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ fern = { version = "0.7", features = ["date-based"] }
|
|||||||
tar = "0.4"
|
tar = "0.4"
|
||||||
reqwest = { version = "0.12", default-features = false, features = ["json", "rustls-tls"] }
|
reqwest = { version = "0.12", default-features = false, features = ["json", "rustls-tls"] }
|
||||||
iana-time-zone = "0.1"
|
iana-time-zone = "0.1"
|
||||||
|
sha2 = "0.10"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
tauri-build = { version = "2", features = [] }
|
tauri-build = { version = "2", features = [] }
|
||||||
|
|||||||
@@ -5,8 +5,7 @@ use bollard::container::{
|
|||||||
use bollard::image::{CommitContainerOptions, RemoveImageOptions};
|
use bollard::image::{CommitContainerOptions, RemoveImageOptions};
|
||||||
use bollard::models::{ContainerSummary, HostConfig, Mount, MountTypeEnum, PortBinding};
|
use bollard::models::{ContainerSummary, HostConfig, Mount, MountTypeEnum, PortBinding};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::collections::hash_map::DefaultHasher;
|
use sha2::{Sha256, Digest};
|
||||||
use std::hash::{Hash, Hasher};
|
|
||||||
|
|
||||||
use super::client::get_docker;
|
use super::client::get_docker;
|
||||||
use crate::models::{AuthMode, BedrockAuthMethod, ContainerInfo, EnvVar, GlobalAwsSettings, McpServer, McpTransportType, PortMapping, Project, ProjectPath};
|
use crate::models::{AuthMode, BedrockAuthMethod, ContainerInfo, EnvVar, GlobalAwsSettings, McpServer, McpTransportType, PortMapping, Project, ProjectPath};
|
||||||
@@ -129,20 +128,28 @@ fn merge_claude_instructions(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Hash a string with SHA-256 and return the hex digest.
|
||||||
|
fn sha256_hex(input: &str) -> String {
|
||||||
|
let mut hasher = Sha256::new();
|
||||||
|
hasher.update(input.as_bytes());
|
||||||
|
format!("{:x}", hasher.finalize())
|
||||||
|
}
|
||||||
|
|
||||||
/// Compute a fingerprint for the Bedrock configuration so we can detect changes.
|
/// Compute a fingerprint for the Bedrock configuration so we can detect changes.
|
||||||
fn compute_bedrock_fingerprint(project: &Project) -> String {
|
fn compute_bedrock_fingerprint(project: &Project) -> String {
|
||||||
if let Some(ref bedrock) = project.bedrock_config {
|
if let Some(ref bedrock) = project.bedrock_config {
|
||||||
let mut hasher = DefaultHasher::new();
|
let parts = vec![
|
||||||
format!("{:?}", bedrock.auth_method).hash(&mut hasher);
|
format!("{:?}", bedrock.auth_method),
|
||||||
bedrock.aws_region.hash(&mut hasher);
|
bedrock.aws_region.clone(),
|
||||||
bedrock.aws_access_key_id.hash(&mut hasher);
|
bedrock.aws_access_key_id.as_deref().unwrap_or("").to_string(),
|
||||||
bedrock.aws_secret_access_key.hash(&mut hasher);
|
bedrock.aws_secret_access_key.as_deref().unwrap_or("").to_string(),
|
||||||
bedrock.aws_session_token.hash(&mut hasher);
|
bedrock.aws_session_token.as_deref().unwrap_or("").to_string(),
|
||||||
bedrock.aws_profile.hash(&mut hasher);
|
bedrock.aws_profile.as_deref().unwrap_or("").to_string(),
|
||||||
bedrock.aws_bearer_token.hash(&mut hasher);
|
bedrock.aws_bearer_token.as_deref().unwrap_or("").to_string(),
|
||||||
bedrock.model_id.hash(&mut hasher);
|
bedrock.model_id.as_deref().unwrap_or("").to_string(),
|
||||||
bedrock.disable_prompt_caching.hash(&mut hasher);
|
format!("{}", bedrock.disable_prompt_caching),
|
||||||
format!("{:x}", hasher.finish())
|
];
|
||||||
|
sha256_hex(&parts.join("|"))
|
||||||
} else {
|
} else {
|
||||||
String::new()
|
String::new()
|
||||||
}
|
}
|
||||||
@@ -157,9 +164,7 @@ fn compute_paths_fingerprint(paths: &[ProjectPath]) -> String {
|
|||||||
.collect();
|
.collect();
|
||||||
parts.sort();
|
parts.sort();
|
||||||
let joined = parts.join(",");
|
let joined = parts.join(",");
|
||||||
let mut hasher = DefaultHasher::new();
|
sha256_hex(&joined)
|
||||||
joined.hash(&mut hasher);
|
|
||||||
format!("{:x}", hasher.finish())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Compute a fingerprint for port mappings so we can detect changes.
|
/// Compute a fingerprint for port mappings so we can detect changes.
|
||||||
@@ -171,9 +176,7 @@ fn compute_ports_fingerprint(port_mappings: &[PortMapping]) -> String {
|
|||||||
.collect();
|
.collect();
|
||||||
parts.sort();
|
parts.sort();
|
||||||
let joined = parts.join(",");
|
let joined = parts.join(",");
|
||||||
let mut hasher = DefaultHasher::new();
|
sha256_hex(&joined)
|
||||||
joined.hash(&mut hasher);
|
|
||||||
format!("{:x}", hasher.finish())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Build the JSON value for MCP servers config to be injected into ~/.claude.json.
|
/// Build the JSON value for MCP servers config to be injected into ~/.claude.json.
|
||||||
@@ -250,9 +253,7 @@ fn compute_mcp_fingerprint(servers: &[McpServer]) -> String {
|
|||||||
return String::new();
|
return String::new();
|
||||||
}
|
}
|
||||||
let json = build_mcp_servers_json(servers);
|
let json = build_mcp_servers_json(servers);
|
||||||
let mut hasher = DefaultHasher::new();
|
sha256_hex(&json)
|
||||||
json.hash(&mut hasher);
|
|
||||||
format!("{:x}", hasher.finish())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn find_existing_container(project: &Project) -> Result<Option<String>, String> {
|
pub async fn find_existing_container(project: &Project) -> Result<Option<String>, String> {
|
||||||
|
|||||||
Reference in New Issue
Block a user