diff --git a/configs/advantech/icam-540.yaml b/configs/advantech/icam-540.yaml deleted file mode 100644 index a047a14..0000000 --- a/configs/advantech/icam-540.yaml +++ /dev/null @@ -1,83 +0,0 @@ -supported_targets: -- icam-540 -default_target: icam-540 -src_dir: ../../ -distro: - channel: apollo-edge - version: 0.1.0 -runtimes: - dev: - extensions: - - avocado-ext-dev - - avocado-bsp-{{ avocado.target }} - - config - - app - packages: - avocado-img-bootfiles: '*' - avocado-img-rootfs: '*' - avocado-img-initramfs: '*' - icam-540: - packages: {} -extensions: - avocado-ext-dev: - source: - type: package - version: '*' - avocado-bsp-{{ avocado.target }}: - source: - type: package - version: '*' - app: - types: - - sysext - - confext - version: 0.1.0 - packages: - i2c-tools: '*' - pylon: '*' - pylon-dev: '*' - v4l-utils: '*' - opencv: '*' - gstreamer1.0: '*' - gstreamer1.0-plugins-base-videoconvertscale: '*' - gstreamer1.0-plugins-good-jpeg: '*' - gstreamer1.0-plugins-good-rtp: '*' - gstreamer1.0-plugins-good-udp: '*' - gstreamer1.0-plugins-good-multipart: '*' - gstreamer1.0-plugins-base-tcp: '*' - gstreamer1.0-plugins-bad-mpegtsmux: '*' - gstreamer1.0-plugins-nvvidconv: '*' - gstreamer1.0-plugins-nvjpeg: '*' - gst-plugin-pylon: '*' - usbutils: '*' - udev-hwdb: '*' - config: - types: - - confext - version: 0.1.0 - users: - root: - password: '' -sdk: - image: docker.io/avocadolinux/sdk:{{ config.distro.channel }} - packages: - avocado-sdk-toolchain: "{{ config.distro.version }}" - icam-540: - packages: - nativesdk-util-linux-mount: '*' - nativesdk-tegraflash-tools: '*' - container_args: - - --network=host - - -v - - /dev:/dev - - -v - - /sys:/sys:ro - - --privileged -provision_profiles: - tegraflash: - container_args: - - -v - - /dev:/dev - - -v - - /sys:/sys:ro - - --privileged diff --git a/configs/default.yaml b/configs/default.yaml index 7894fb9..25674ff 100644 --- a/configs/default.yaml +++ b/configs/default.yaml @@ -25,9 +25,7 @@ runtimes: - config - app packages: - avocado-img-bootfiles: "*" - avocado-img-rootfs: "*" - avocado-img-initramfs: "*" + avocado-runtime: "{{ avocado.distro.version }}" ## ## Extensions @@ -57,14 +55,14 @@ extensions: - confext version: "0.1.0" - # Install application dependencies - # packages: -#curl = "*" -#iperf3 = "*" + # Install additional application dependencies + #packages: + #curl: "*" + #iperf3: "*" -# Generated default config extension -# Use or modify this to configure "real" user accounts (passwd, shadow, group) -# or configure other system services + # Generated default config extension + # Use or modify this to configure "real" user accounts (passwd, shadow, group) + # or configure other system services config: types: - confext @@ -82,34 +80,11 @@ extensions: sdk: image: "docker.io/avocadolinux/sdk:{{ config.distro.channel }}" + container_args: + - --privileged + - --network=host + - -v /dev:/dev + - -v /sys:/sys + packages: avocado-sdk-toolchain: "{{ config.distro.version }}" - - {target}: - container_args: - - --network=host - -## -## Provisioning -## - -# When provisioning using usb or sd provisioning profiles, set extra sdk -# container arguments to allow access to these devices - -provision_profiles: - usb: - container_args: - - -v - - /dev:/dev - - -v - - /sys:/sys:ro - - --privileged - - sd: - container_args: - - -v - - /dev:/dev - - -v - - /sys:/sys:ro - - --privileged - diff --git a/configs/nvidia/jetson-orin-nano-devkit.yaml b/configs/nvidia/jetson-orin-nano-devkit.yaml deleted file mode 100644 index 350e072..0000000 --- a/configs/nvidia/jetson-orin-nano-devkit.yaml +++ /dev/null @@ -1,59 +0,0 @@ -default_target: jetson-orin-nano-devkit -supported_targets: -- jetson-orin-nano-devkit -distro: - channel: apollo-edge - version: 0.1.0 -runtimes: - dev: - extensions: - - avocado-bsp-{{ avocado.target }} - - config - - app - packages: - avocado-img-bootfiles: '*' - avocado-img-rootfs: '*' - avocado-img-initramfs: '*' - jetson-orin-nano-devkit: - packages: {} -extensions: - avocado-bsp-{{ avocado.target }}: - source: - type: package - version: '*' - app: - types: - - sysext - - confext - version: 0.1.0 - packages: {} - config: - types: - - confext - version: 0.1.0 - users: - root: - password: '' -sdk: - image: docker.io/avocadolinux/sdk:{{ config.distro.channel }} - packages: - avocado-sdk-toolchain: "{{ config.distro.version }}" - jetson-orin-nano-devkit: - container_args: - - --network=host - - -v - - /dev:/dev - - -v - - /sys:/sys:ro - - --privileged - packages: - nativesdk-util-linux-mount: '*' - nativesdk-tegraflash-tools: '*' -provision_profiles: - tegraflash: - container_args: - - -v - - /dev:/dev - - -v - - /sys:/sys:ro - - --privileged diff --git a/configs/raspberry-pi/raspberrypi-4-model-b.yaml b/configs/raspberry-pi/raspberrypi-4-model-b.yaml deleted file mode 100644 index d7ac2a9..0000000 --- a/configs/raspberry-pi/raspberrypi-4-model-b.yaml +++ /dev/null @@ -1,66 +0,0 @@ -default_target: raspberrypi4 -supported_targets: -- raspberrypi4 -distro: - channel: apollo-edge - version: 0.1.0 -runtimes: - dev: - extensions: - - avocado-ext-dev - - avocado-ext-sshd-dev - - avocado-bsp-{{ avocado.target }} - - config - - app - packages: - avocado-img-bootfiles: '*' - avocado-img-rootfs: '*' - avocado-img-initramfs: '*' -extensions: - avocado-ext-dev: - source: - type: package - version: '*' - avocado-ext-sshd-dev: - source: - type: package - version: '*' - avocado-bsp-{{ avocado.target }}: - source: - type: package - version: '*' - app: - types: - - sysext - - confext - version: 0.1.0 - packages: {} - config: - types: - - confext - version: 0.1.0 - users: - root: - password: '' -sdk: - image: docker.io/avocadolinux/sdk:{{ config.distro.channel }} - packages: - avocado-sdk-toolchain: "{{ config.distro.version }}" - raspberrypi4: - container_args: - - --network=host -provision_profiles: - img: - container_args: - - -v - - /dev:/dev - - -v - - /sys:/sys:ro - - --privileged - sd: - container_args: - - -v - - /dev:/dev - - -v - - /sys:/sys:ro - - --privileged diff --git a/configs/raspberry-pi/raspberrypi-5.yaml b/configs/raspberry-pi/raspberrypi-5.yaml deleted file mode 100644 index 7e17640..0000000 --- a/configs/raspberry-pi/raspberrypi-5.yaml +++ /dev/null @@ -1,66 +0,0 @@ -default_target: raspberrypi5 -supported_targets: -- raspberrypi5 -distro: - channel: apollo-edge - version: 0.1.0 -runtimes: - dev: - extensions: - - avocado-ext-dev - - avocado-ext-sshd-dev - - avocado-bsp-{{ avocado.target }} - - config - - app - packages: - avocado-img-bootfiles: '*' - avocado-img-rootfs: '*' - avocado-img-initramfs: '*' -extensions: - avocado-ext-dev: - source: - type: package - version: '*' - avocado-ext-sshd-dev: - source: - type: package - version: '*' - avocado-bsp-{{ avocado.target }}: - source: - type: package - version: '*' - app: - types: - - sysext - - confext - version: 0.1.0 - packages: {} - config: - types: - - confext - version: 0.1.0 - users: - root: - password: '' -sdk: - image: docker.io/avocadolinux/sdk:{{ config.distro.channel }} - packages: - avocado-sdk-toolchain: "{{ config.distro.version }}" - raspberrypi5: - container_args: - - --network=host -provision_profiles: - img: - container_args: - - -v - - /dev:/dev - - -v - - /sys:/sys:ro - - --privileged - sd: - container_args: - - -v - - /dev:/dev - - -v - - /sys:/sys:ro - - --privileged diff --git a/configs/seeed/reterminal-dm.yaml b/configs/seeed/reterminal-dm.yaml deleted file mode 100644 index e522e1b..0000000 --- a/configs/seeed/reterminal-dm.yaml +++ /dev/null @@ -1,66 +0,0 @@ -default_target: reterminal-dm -supported_targets: -- reterminal-dm -distro: - channel: apollo-edge - version: 0.1.0 -runtimes: - dev: - extensions: - - avocado-ext-dev - - avocado-ext-sshd-dev - - avocado-bsp-{{ avocado.target }} - - config - - app - packages: - avocado-img-bootfiles: '*' - avocado-img-rootfs: '*' - avocado-img-initramfs: '*' -extensions: - avocado-ext-dev: - source: - type: package - version: '*' - avocado-ext-sshd-dev: - source: - type: package - version: '*' - avocado-bsp-{{ avocado.target }}: - source: - type: package - version: '*' - app: - types: - - sysext - - confext - version: 0.1.0 - packages: {} - config: - types: - - confext - version: 0.1.0 - users: - root: - password: '' -sdk: - image: docker.io/avocadolinux/sdk:{{ config.distro.channel }} - packages: - avocado-sdk-toolchain: "{{ config.distro.version }}" - reterminal-dm: - container_args: - - --network=host -provision_profiles: - usb: - container_args: - - -v - - /dev:/dev - - -v - - /sys:/sys:ro - - --privileged - sd: - container_args: - - -v - - /dev:/dev - - -v - - /sys:/sys:ro - - --privileged diff --git a/configs/seeed/reterminal.yaml b/configs/seeed/reterminal.yaml deleted file mode 100644 index a01cdd7..0000000 --- a/configs/seeed/reterminal.yaml +++ /dev/null @@ -1,66 +0,0 @@ -default_target: reterminal -supported_targets: -- reterminal -distro: - channel: apollo-edge - version: 0.1.0 -runtimes: - dev: - extensions: - - avocado-ext-dev - - avocado-ext-sshd-dev - - avocado-bsp-{{ avocado.target }} - - config - - app - packages: - avocado-img-bootfiles: '*' - avocado-img-rootfs: '*' - avocado-img-initramfs: '*' -extensions: - avocado-ext-dev: - source: - type: package - version: '*' - avocado-ext-sshd-dev: - source: - type: package - version: '*' - avocado-bsp-{{ avocado.target }}: - source: - type: package - version: '*' - app: - types: - - sysext - - confext - version: 0.1.0 - packages: {} - config: - types: - - confext - version: 0.1.0 - users: - root: - password: '' -sdk: - image: docker.io/avocadolinux/sdk:{{ config.distro.channel }} - packages: - avocado-sdk-toolchain: "{{ config.distro.version }}" - reterminal: - container_args: - - --network=host -provision_profiles: - usb: - container_args: - - -v - - /dev:/dev - - -v - - /sys:/sys:ro - - --privileged - sd: - container_args: - - -v - - /dev:/dev - - -v - - /sys:/sys:ro - - --privileged diff --git a/src/commands/build.rs b/src/commands/build.rs index 6a2e203..6c9193c 100644 --- a/src/commands/build.rs +++ b/src/commands/build.rs @@ -1662,7 +1662,8 @@ echo "Successfully created image for versioned extension '$EXT_NAME-$EXT_VERSION .and_then(|ext| ext.get(ext_name)) { // Check if this local extension has dependencies - if let Some(dependencies) = ext_config.get("packages").and_then(|d| d.as_mapping()) { + let packages_value = ext_config.get("packages"); + if let Some(dependencies) = packages_value.and_then(|d| d.as_mapping()) { for (_dep_name, dep_spec) in dependencies { // Check for extension dependency if let Some(nested_ext_name) = @@ -1702,6 +1703,24 @@ echo "Successfully created image for versioned extension '$EXT_NAME-$EXT_VERSION } } } + } else if let Some(deps_value) = packages_value { + // packages field exists but is not a YAML mapping — detect common syntax mistakes + if !deps_value.is_null() { + let value_str = serde_yaml::to_string(deps_value) + .unwrap_or_else(|_| format!("{deps_value:?}")); + let hint = if value_str.contains('=') { + "\n\nIt looks like '=' was used instead of ':'. YAML uses ':' for key-value pairs.\n\ + Example:\n packages:\n curl: \"*\"\n iperf3: \"*\"" + } else { + "\n\nExpected a YAML mapping (key: value pairs).\n\ + Example:\n packages:\n curl: \"*\"\n iperf3: \"*\"" + }; + return Err(anyhow::anyhow!( + "Invalid 'packages' format in extension '{ext_name}': \ + expected a mapping but got: {}{hint}", + value_str.trim() + )); + } } } diff --git a/src/commands/ext/install.rs b/src/commands/ext/install.rs index f4feade..a5b6787 100644 --- a/src/commands/ext/install.rs +++ b/src/commands/ext/install.rs @@ -697,6 +697,24 @@ $DNF_SDK_HOST \ OutputLevel::Normal, ); } + } else if let Some(deps_value) = dependencies { + // packages field exists but is not a YAML mapping — detect common syntax mistakes + if !deps_value.is_null() { + let value_str = + serde_yaml::to_string(deps_value).unwrap_or_else(|_| format!("{deps_value:?}")); + let hint = if value_str.contains('=') { + "\n\nIt looks like '=' was used instead of ':'. YAML uses ':' for key-value pairs.\n\ + Example:\n packages:\n curl: \"*\"\n iperf3: \"*\"" + } else { + "\n\nExpected a YAML mapping (key: value pairs).\n\ + Example:\n packages:\n curl: \"*\"\n iperf3: \"*\"" + }; + return Err(anyhow::anyhow!( + "Invalid 'packages' format in extension '{extension}': \ + expected a mapping but got: {}{hint}", + value_str.trim() + )); + } } else if self.verbose { print_debug( &format!("No dependencies defined for extension '{extension}'."), diff --git a/src/commands/init.rs b/src/commands/init.rs index 55a01c0..aa23f64 100644 --- a/src/commands/init.rs +++ b/src/commands/init.rs @@ -150,30 +150,11 @@ impl InitCommand { /// # Returns /// * The configuration template content as a string fn load_config_template(target: &str) -> String { - // Try to load YAML config first, fall back to default with target substitution - let yaml_content = match target { - "reterminal" => Some(include_str!("../../configs/seeed/reterminal.yaml")), - "reterminal-dm" => Some(include_str!("../../configs/seeed/reterminal-dm.yaml")), - "jetson-orin-nano-devkit" => Some(include_str!( - "../../configs/nvidia/jetson-orin-nano-devkit.yaml" - )), - "raspberrypi4" => Some(include_str!( - "../../configs/raspberry-pi/raspberrypi-4-model-b.yaml" - )), - "raspberrypi5" => Some(include_str!( - "../../configs/raspberry-pi/raspberrypi-5.yaml" - )), - "icam-540" => Some(include_str!("../../configs/advantech/icam-540.yaml")), - _ => None, - }; - - if let Some(content) = yaml_content { - content.to_string() - } else { - // Use default YAML template and substitute the target - let default_template = include_str!("../../configs/default.yaml"); - default_template.replace("{target}", target) - } + // All targets start from the default config template. + // To add target-specific config additions in the future, create a YAML file at + // configs/additions/.yaml and add an include_str! match arm here. + let default_template = include_str!("../../configs/default.yaml"); + default_template.replace("{target}", target) } /// Checks if a reference exists in the repository. @@ -1052,9 +1033,7 @@ mod tests { assert!(content.contains("runtimes:")); assert!(content.contains("dev:")); assert!(content.contains("packages:")); - assert!(content.contains("avocado-img-bootfiles:")); - assert!(content.contains("avocado-img-rootfs:")); - assert!(content.contains("avocado-img-initramfs:")); + assert!(content.contains("avocado-runtime:")); assert!(content.contains("avocado-ext-dev:")); assert!(content.contains("type: package")); assert!( @@ -1220,45 +1199,18 @@ mod tests { let default_template = include_str!("../../configs/default.yaml").replace("{target}", "test-target"); validate_ext_versions(&default_template, "default.yaml"); - - // Test reterminal - let reterminal = include_str!("../../configs/seeed/reterminal.yaml"); - validate_ext_versions(reterminal, "reterminal.yaml"); - - // Test reterminal-dm - let reterminal_dm = include_str!("../../configs/seeed/reterminal-dm.yaml"); - validate_ext_versions(reterminal_dm, "reterminal-dm.yaml"); - - // Test jetson-orin-nano-devkit - let jetson = include_str!("../../configs/nvidia/jetson-orin-nano-devkit.yaml"); - validate_ext_versions(jetson, "jetson-orin-nano-devkit.yaml"); - - // Test raspberrypi4 - let rpi4 = include_str!("../../configs/raspberry-pi/raspberrypi-4-model-b.yaml"); - validate_ext_versions(rpi4, "raspberrypi-4-model-b.yaml"); - - // Test raspberrypi5 - let rpi5 = include_str!("../../configs/raspberry-pi/raspberrypi-5.yaml"); - validate_ext_versions(rpi5, "raspberrypi-5.yaml"); - - // Test icam-540 - let icam = include_str!("../../configs/advantech/icam-540.yaml"); - validate_ext_versions(icam, "icam-540.yaml"); } #[tokio::test] async fn test_generated_configs_have_ext_versions() { - // Test that configs generated for all supported targets have versions + // Test that configs generated for various targets have versions. + // All targets use the default template with target substitution. let targets = vec![ "qemux86-64", "qemuarm64", - "reterminal", - "reterminal-dm", - "jetson-orin-nano-devkit", "raspberrypi4", - "raspberrypi5", - "icam-540", - "custom-target", // This uses the default template + "jetson-orin-nano-devkit", + "custom-target", ]; for target in targets {