From 5b9d870b464053ec3cdf6bccca25b2a01cd21e74 Mon Sep 17 00:00:00 2001 From: qpismont Date: Fri, 31 Jul 2026 20:18:29 +0000 Subject: [PATCH] Dockerfile field must be present --- crates/devcontainer-rs/src/lib.rs | 79 +++++++++++-------------------- 1 file changed, 27 insertions(+), 52 deletions(-) diff --git a/crates/devcontainer-rs/src/lib.rs b/crates/devcontainer-rs/src/lib.rs index 44c8bc3..1dd6d69 100644 --- a/crates/devcontainer-rs/src/lib.rs +++ b/crates/devcontainer-rs/src/lib.rs @@ -8,7 +8,7 @@ use serde::Deserialize; #[derive(Debug, Deserialize)] pub struct DevContainerBuildSchema { #[serde(default)] - pub dockerfile: Option, + pub dockerfile: String, #[serde(default)] pub args: HashMap, } @@ -34,7 +34,6 @@ pub struct DevContainerSchema { #[derive(Debug)] pub struct DevContainer { - /// Absolute or relative path to the Dockerfile/Containerfile to build. pub container_file_path: PathBuf, pub name: Option, pub build_args: HashMap, @@ -73,10 +72,7 @@ impl TryFrom<(DevContainerSchema, PathBuf)> for DevContainer { .parent() .ok_or_else(|| ParseError::InvalidDevContainerPath(devcontainer_path.clone()))?; - let container_file_path = match schema.build.dockerfile.as_deref() { - Some(file) => base_dir.join(file), - None => first_existing_container_file(base_dir), - }; + let container_file_path = base_dir.join(schema.build.dockerfile); if !container_file_path.is_file() { return Err(ParseError::ContainerFileNotFound(container_file_path)); @@ -94,14 +90,6 @@ impl TryFrom<(DevContainerSchema, PathBuf)> for DevContainer { } } -fn first_existing_container_file(base_dir: &Path) -> PathBuf { - ["Dockerfile", "Containerfile"] - .iter() - .map(|filename| base_dir.join(filename)) - .find(|path| path.is_file()) - .unwrap_or_else(|| base_dir.join("Dockerfile")) -} - pub async fn parse(path: impl AsRef) -> Result { let path = path.as_ref().to_path_buf(); let contents = tokio::fs::read_to_string(&path) @@ -126,50 +114,37 @@ mod tests { use super::*; use std::fs; - #[test] - fn resolves_configured_containerfile_relative_to_devcontainer_file() { - let dir = tempfile::tempdir().unwrap(); - let devcontainer_path = dir.path().join("devcontainer.json"); - let containerfile_path = dir.path().join("Containerfile"); - fs::write(&containerfile_path, "FROM alpine\n").unwrap(); - - let schema = DevContainerSchema { - name: Some("test".into()), - build: DevContainerBuildSchema { - dockerfile: Some("Containerfile".into()), - args: HashMap::new(), - }, - workspace_folder: None, - container_env: HashMap::new(), - post_create_command: None, - post_start_command: None, - }; - - let config = DevContainer::try_from((schema, devcontainer_path)).unwrap(); - assert_eq!(config.container_file_path, containerfile_path); - } - - #[test] - fn falls_back_to_dockerfile_before_containerfile() { + #[tokio::test] + async fn parses_devcontainer_file() { let dir = tempfile::tempdir().unwrap(); let devcontainer_path = dir.path().join("devcontainer.json"); let dockerfile_path = dir.path().join("Dockerfile"); + fs::write(&dockerfile_path, "FROM alpine\n").unwrap(); - fs::write(dir.path().join("Containerfile"), "FROM busybox\n").unwrap(); + fs::write( + &devcontainer_path, + r#"{ + "name": "test", + "build": { + "dockerfile": "Dockerfile", + "args": { + "VERSION": "1" + } + }, + "workspaceFolder": "/workspace", + "containerEnv": { + "RUST_LOG": "debug" + } + }"#, + ) + .unwrap(); - let schema = DevContainerSchema { - name: None, - build: DevContainerBuildSchema { - dockerfile: None, - args: HashMap::new(), - }, - workspace_folder: None, - container_env: HashMap::new(), - post_create_command: None, - post_start_command: None, - }; + let config = parse(&devcontainer_path).await.unwrap(); - let config = DevContainer::try_from((schema, devcontainer_path)).unwrap(); + assert_eq!(config.name.as_deref(), Some("test")); assert_eq!(config.container_file_path, dockerfile_path); + assert_eq!(config.build_args.get("VERSION").unwrap(), "1"); + assert_eq!(config.container_env.get("RUST_LOG").unwrap(), "debug"); + assert_eq!(config.workspace_folder.as_deref(), Some("/workspace")); } }