huskies: merge 1152 story Set HUSKIES_GATEWAY_URL on every sled container so 1136's relay actually spawns

This commit is contained in:
dave
2026-05-19 17:55:37 +00:00
parent 398726a14a
commit 9a5b6f4d92
4 changed files with 164 additions and 1 deletions
@@ -756,6 +756,7 @@ async fn handle_adopt_project(
&git_user_name,
&git_user_email,
Some(&credentials_file),
&resolve_gateway_url(),
);
docker_args.push("-v".into());
@@ -1142,6 +1143,7 @@ pub async fn handle_new_project(
&git_user_name,
&git_user_email,
Some(&credentials_file),
&resolve_gateway_url(),
);
// HTTPS push token: passed as env vars consumed by the entrypoint credential helper.
@@ -1330,11 +1332,15 @@ pub async fn build_project_image(
/// Without this the server defaults to `127.0.0.1` inside the container —
/// reachable only from within the container itself, not via `docker -p`.
///
/// When `gateway_url` is non-empty, `-e HUSKIES_GATEWAY_URL=<url>` is added so
/// the sled's relay task connects back to the gateway and forwards CRDT events.
///
/// When `credentials_path` is `Some`, the file is bind-mounted read-only at
/// `/run/claude-credentials-src` so the container entrypoint can copy it into
/// `/home/huskies/.claude/.credentials.json` with mode 0600. Mounting to an
/// intermediate path (rather than directly to the destination) ensures the
/// huskies user owns the copy regardless of the host user's UID.
#[allow(clippy::too_many_arguments)]
pub(crate) fn project_docker_run_args(
container_name: &str,
port: u16,
@@ -1343,6 +1349,7 @@ pub(crate) fn project_docker_run_args(
git_user_name: &str,
git_user_email: &str,
credentials_path: Option<&std::path::Path>,
gateway_url: &str,
) -> Vec<String> {
let mut args = vec![
"run".into(),
@@ -1364,6 +1371,10 @@ pub(crate) fn project_docker_run_args(
"-e".into(),
format!("GIT_USER_EMAIL={git_user_email}"),
];
if !gateway_url.is_empty() {
args.push("-e".into());
args.push(format!("HUSKIES_GATEWAY_URL={gateway_url}"));
}
if let Some(creds) = credentials_path {
args.push("-v".into());
args.push(format!(
@@ -1374,6 +1385,16 @@ pub(crate) fn project_docker_run_args(
args
}
/// Resolve the gateway URL to inject into project sled containers.
///
/// Reads `HUSKIES_GATEWAY_URL` from the environment first; falls back to
/// `http://host.docker.internal:3000` so containers launched by the gateway
/// can always relay events back without explicit configuration.
pub(crate) fn resolve_gateway_url() -> String {
std::env::var("HUSKIES_GATEWAY_URL")
.unwrap_or_else(|_| "http://host.docker.internal:3000".to_string())
}
/// Convert a failed `docker run` stderr into an actionable chat message.
///
/// When Docker cannot find the image locally it prints `Unable to find image`.
@@ -1792,8 +1813,8 @@ mod tests {
"Test User",
"test@example.com",
None,
"http://host.docker.internal:3000",
);
// Find "-e" followed by "HUSKIES_HOST=0.0.0.0"
let pairs: Vec<_> = args.windows(2).collect();
assert!(
pairs
@@ -1807,6 +1828,31 @@ mod tests {
.any(|w| w[0] == "-e" && w[1] == "HUSKIES_PORT=3001"),
"expected -e HUSKIES_PORT=3001 in docker args, got: {args:?}"
);
assert!(
pairs
.iter()
.any(|w| w[0] == "-e"
&& w[1] == "HUSKIES_GATEWAY_URL=http://host.docker.internal:3000"),
"expected -e HUSKIES_GATEWAY_URL=http://host.docker.internal:3000 in docker args, got: {args:?}"
);
}
#[test]
fn project_docker_args_no_gateway_url_when_empty() {
let args = project_docker_run_args(
"huskies-myapp",
3100,
2200,
"ssh-ed25519 AAAA...",
"Test User",
"test@example.com",
None,
"",
);
assert!(
!args.iter().any(|a| a.contains("HUSKIES_GATEWAY_URL")),
"expected no HUSKIES_GATEWAY_URL when gateway_url is empty, got: {args:?}"
);
}
#[test]
@@ -1820,6 +1866,7 @@ mod tests {
"Test User",
"test@example.com",
Some(creds),
"",
);
let pairs: Vec<_> = args.windows(2).collect();
assert!(
@@ -1839,6 +1886,7 @@ mod tests {
"Test User",
"test@example.com",
None,
"",
);
assert!(
!args.iter().any(|a| a.contains("claude-credentials-src")),