huskies: merge 1108 story Chat bootstrap Phase 3: SSH-remote editor access into the project container (any editor)

This commit is contained in:
dave
2026-05-16 23:32:33 +00:00
parent efafe44db1
commit 59302b465d
6 changed files with 217 additions and 10 deletions
+51
View File
@@ -26,6 +26,13 @@ pub struct ProjectEntry {
/// `[sled_tokens]` table for projects that set this field.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub auth_token: Option<String>,
/// Host-local port for SSH access into the project container.
///
/// Set by `new project` (story 1108). The container's SSH server is bound
/// to `127.0.0.1:<ssh_port>:22` so the user can connect with
/// `ssh huskies@127.0.0.1 -p <ssh_port> -i ~/.huskies/<name>/id_ed25519`.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub ssh_port: Option<u16>,
}
impl ProjectEntry {
@@ -36,6 +43,7 @@ impl ProjectEntry {
Self {
url: Some(url.into()),
auth_token: None,
ssh_port: None,
}
}
@@ -205,6 +213,7 @@ auth_token = "secret"
ProjectEntry {
url: None,
auth_token: Some("secret".into()),
ssh_port: None,
},
);
let config = GatewayConfig {
@@ -238,6 +247,7 @@ auth_token = "secret"
ProjectEntry {
url: None,
auth_token: Some("tok".into()),
ssh_port: None,
},
);
assert_eq!(validate_project_exists(&projects, "ws").unwrap(), "");
@@ -256,6 +266,7 @@ auth_token = "secret"
let e = ProjectEntry {
url: None,
auth_token: Some("tok".into()),
ssh_port: None,
};
assert!(!e.has_url());
}
@@ -297,6 +308,7 @@ auth_token = "secret"
let entry = ProjectEntry {
url: Some("http://a:3001".into()),
auth_token: Some("mysecret".into()),
ssh_port: None,
};
let mut projects = BTreeMap::new();
projects.insert("myproj".into(), entry);
@@ -315,4 +327,43 @@ auth_token = "secret"
Some("mysecret")
);
}
#[test]
fn roundtrip_project_entry_with_ssh_port() {
let entry = ProjectEntry {
url: Some("http://127.0.0.1:3101".into()),
auth_token: None,
ssh_port: Some(2201),
};
let mut projects = BTreeMap::new();
projects.insert("myproj".into(), entry);
let config = GatewayConfig {
projects,
sled_tokens: BTreeMap::new(),
};
let toml_str = toml::to_string_pretty(&config).unwrap();
let parsed: GatewayConfig = toml::from_str(&toml_str).unwrap();
assert_eq!(parsed.projects["myproj"].ssh_port, Some(2201));
// ssh_port must appear in the serialised TOML.
assert!(
toml_str.contains("ssh_port = 2201"),
"ssh_port missing from TOML: {toml_str}"
);
}
#[test]
fn ssh_port_none_is_omitted_from_toml() {
let entry = ProjectEntry::with_url("http://127.0.0.1:3101");
let mut projects = BTreeMap::new();
projects.insert("p".into(), entry);
let config = GatewayConfig {
projects,
sled_tokens: BTreeMap::new(),
};
let toml_str = toml::to_string_pretty(&config).unwrap();
assert!(
!toml_str.contains("ssh_port"),
"ssh_port should be omitted when None: {toml_str}"
);
}
}