diff --git a/crates/dev_container/src/devcontainer_manifest.rs b/crates/dev_container/src/devcontainer_manifest.rs index be3e2f1d5dfc0ef8fca82f4678e9c34b7e6e7d89..9927a639542619464f559a5853071433b20b4823 100644 --- a/crates/dev_container/src/devcontainer_manifest.rs +++ b/crates/dev_container/src/devcontainer_manifest.rs @@ -2108,9 +2108,9 @@ pub(crate) async fn read_devcontainer_configuration( environment: HashMap, ) -> Result { let docker = if context.use_podman { - Docker::new("podman") + Docker::new("podman").await } else { - Docker::new("docker") + Docker::new("docker").await }; let mut dev_container = DevContainerManifest::new( context, @@ -2132,9 +2132,9 @@ pub(crate) async fn spawn_dev_container( local_project_path: &Path, ) -> Result { let docker = if context.use_podman { - Docker::new("podman") + Docker::new("podman").await } else { - Docker::new("docker") + Docker::new("docker").await }; let mut devcontainer_manifest = DevContainerManifest::new( context, @@ -4784,12 +4784,14 @@ FROM docker.io/hexpm/elixir:1.21-erlang-28.4.1-debian-trixie-20260316-slim AS de pub(crate) struct FakeDocker { exec_commands_recorded: Mutex>, podman: bool, + has_buildx: bool, } impl FakeDocker { pub(crate) fn new() -> Self { Self { podman: false, + has_buildx: true, exec_commands_recorded: Mutex::new(Vec::new()), } } @@ -5029,7 +5031,7 @@ FROM docker.io/hexpm/elixir:1.21-erlang-28.4.1-debian-trixie-20260316-slim AS de })) } fn supports_compose_buildkit(&self) -> bool { - !self.podman + !self.podman && self.has_buildx } fn docker_cli(&self) -> String { if self.podman { diff --git a/crates/dev_container/src/docker.rs b/crates/dev_container/src/docker.rs index 7931923b4219e33fa56e8fb2fb6b97c1ea89a750..faa2e7e364b00f05b6da248852bda2ea3d18cc37 100644 --- a/crates/dev_container/src/docker.rs +++ b/crates/dev_container/src/docker.rs @@ -175,6 +175,7 @@ pub(crate) struct DockerComposeConfig { pub(crate) struct Docker { docker_cli: String, + has_buildx: bool, } impl DockerInspect { @@ -184,9 +185,24 @@ impl DockerInspect { } impl Docker { - pub(crate) fn new(docker_cli: &str) -> Self { + pub(crate) async fn new(docker_cli: &str) -> Self { + let has_buildx = if docker_cli == "podman" { + false + } else { + let output = Command::new(docker_cli) + .args(["buildx", "version"]) + .output() + .await; + output.map(|o| o.status.success()).unwrap_or(false) + }; + if !has_buildx && docker_cli != "podman" { + log::info!( + "docker buildx not found; dev container builds will use the scratch-image fallback" + ); + } Self { docker_cli: docker_cli.to_string(), + has_buildx, } } @@ -372,7 +388,7 @@ impl DockerClient for Docker { } fn supports_compose_buildkit(&self) -> bool { - !self.is_podman() + self.has_buildx } } @@ -595,7 +611,10 @@ mod test { #[test] fn should_create_docker_inspect_command() { - let docker = Docker::new("docker"); + let docker = Docker { + docker_cli: "docker".to_string(), + has_buildx: false, + }; let given_id = "given_docker_id"; let command = docker.create_docker_inspect(given_id);