Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 19 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "avocado-cli"
version = "0.19.1"
version = "0.20.0"
edition = "2021"
description = "Command line interface for Avocado."
authors = ["Avocado"]
Expand All @@ -24,6 +24,7 @@ serde_yaml = "0.9"
anyhow = "1.0"
clap = { version = "4.0", features = ["derive"] }
serde_json = "1.0"
serde_jcs = "0.1"
tokio = { version = "1.0", features = [
"macros",
"rt-multi-thread",
Expand Down
16 changes: 8 additions & 8 deletions src/commands/ext/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -616,9 +616,9 @@ fi
# Copy overlay directory to extension sysroot (opaque mode)
if [ -d "/opt/src/{}" ]; then
echo "Copying overlay directory '{}' to extension sysroot (opaque mode)"
# Use cp -r to replace directory contents completely
cp -r /opt/src/{}/* "$AVOCADO_EXT_SYSROOTS/{}/"
# Fix ownership to root:root for copied overlay files only
# Use cp -a to replace directory contents completely while preserving permissions
cp -a /opt/src/{}/* "$AVOCADO_EXT_SYSROOTS/{}/"
# Fix ownership to root:root for copied overlay files only (permissions are preserved)
echo "Setting ownership to root:root for overlay files"
find "/opt/src/{}" -mindepth 1 | while IFS= read -r srcpath; do
relpath="$(echo "$srcpath" | sed "s|^/opt/src/{}||" | sed "s|^/||")"
Expand Down Expand Up @@ -789,9 +789,9 @@ fi
# Copy overlay directory to extension sysroot (opaque mode)
if [ -d "/opt/src/{}" ]; then
echo "Copying overlay directory '{}' to extension sysroot (opaque mode)"
# Use cp -r to replace directory contents completely
cp -r /opt/src/{}/* "$AVOCADO_EXT_SYSROOTS/{}/"
# Fix ownership to root:root for copied overlay files only
# Use cp -a to replace directory contents completely while preserving permissions
cp -a /opt/src/{}/* "$AVOCADO_EXT_SYSROOTS/{}/"
# Fix ownership to root:root for copied overlay files only (permissions are preserved)
echo "Setting ownership to root:root for overlay files"
find "/opt/src/{}" -mindepth 1 | while IFS= read -r srcpath; do
relpath="$(echo "$srcpath" | sed "s|^/opt/src/{}||" | sed "s|^/||")"
Expand Down Expand Up @@ -1970,7 +1970,7 @@ mod tests {
assert!(script.contains(
"echo \"Copying overlay directory 'peridio' to extension sysroot (opaque mode)\""
));
assert!(script.contains("cp -r /opt/src/peridio/* \"$AVOCADO_EXT_SYSROOTS/opaque-ext/\""));
assert!(script.contains("cp -a /opt/src/peridio/* \"$AVOCADO_EXT_SYSROOTS/opaque-ext/\""));
assert!(script.contains("echo \"Setting ownership to root:root for overlay files\""));
assert!(script.contains("find \"/opt/src/peridio\" -mindepth 1"));
assert!(script.contains("echo \"Error: Overlay directory 'peridio' not found in source\""));
Expand Down Expand Up @@ -2011,7 +2011,7 @@ mod tests {
assert!(script.contains(
"echo \"Copying overlay directory 'peridio' to extension sysroot (opaque mode)\""
));
assert!(script.contains("cp -r /opt/src/peridio/* \"$AVOCADO_EXT_SYSROOTS/opaque-ext/\""));
assert!(script.contains("cp -a /opt/src/peridio/* \"$AVOCADO_EXT_SYSROOTS/opaque-ext/\""));
assert!(script.contains("echo \"Setting ownership to root:root for overlay files\""));
assert!(script.contains("find \"/opt/src/peridio\" -mindepth 1"));
assert!(script.contains("echo \"Error: Overlay directory 'peridio' not found in source\""));
Expand Down
84 changes: 73 additions & 11 deletions src/commands/ext/install.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
use anyhow::{Context, Result};
use std::path::{Path, PathBuf};

use crate::utils::config::{Config, ExtensionLocation};
use crate::utils::container::{RunConfig, SdkContainer};
use crate::utils::lockfile::{build_package_spec_with_lock, LockFile, SysrootType};
use crate::utils::output::{print_debug, print_error, print_info, print_success, OutputLevel};
use crate::utils::stamps::{
compute_ext_input_hash, generate_write_stamp_script, Stamp, StampOutputs,
Expand Down Expand Up @@ -171,9 +173,27 @@ impl ExtInstallCommand {
let target = resolve_target_required(self.target.as_deref(), config)?;

// Use the container helper to run the setup commands
let container_helper = SdkContainer::new();
let container_helper = SdkContainer::new().verbose(self.verbose);
let total = extensions_to_install.len();

// Load lock file for reproducible builds
let src_dir = config
.get_resolved_src_dir(&self.config_path)
.unwrap_or_else(|| {
PathBuf::from(&self.config_path)
.parent()
.unwrap_or(std::path::Path::new("."))
.to_path_buf()
});
let mut lock_file = LockFile::load(&src_dir).with_context(|| "Failed to load lock file")?;

if self.verbose && !lock_file.is_empty() {
print_info(
"Using existing lock file for version pinning.",
OutputLevel::Normal,
);
}

// Install each extension
for (index, (ext_name, ext_location)) in extensions_to_install.iter().enumerate() {
if self.verbose {
Expand Down Expand Up @@ -210,6 +230,8 @@ impl ExtInstallCommand {
repo_release.as_ref(),
&merged_container_args,
config.get_sdk_disable_weak_dependencies(),
&mut lock_file,
&src_dir,
)
.await?
{
Expand Down Expand Up @@ -271,6 +293,8 @@ impl ExtInstallCommand {
repo_release: Option<&String>,
merged_container_args: &Option<Vec<String>>,
disable_weak_dependencies: bool,
lock_file: &mut LockFile,
src_dir: &Path,
) -> Result<bool> {
// Create the commands to check and set up the directory structure
let check_command = format!("[ -d $AVOCADO_EXT_SYSROOTS/{extension} ]");
Expand Down Expand Up @@ -332,9 +356,12 @@ impl ExtInstallCommand {
// Install dependencies if they exist
let dependencies = ext_config.as_ref().and_then(|ec| ec.get("dependencies"));

let sysroot = SysrootType::Extension(extension.to_string());

if let Some(serde_yaml::Value::Mapping(deps_map)) = dependencies {
// Build list of packages to install and handle extension dependencies
let mut packages = Vec::new();
let mut package_names = Vec::new();
let mut extension_dependencies = Vec::new();

for (package_name_val, version_spec) in deps_map {
Expand All @@ -349,11 +376,15 @@ impl ExtInstallCommand {
// Simple string version: "package: version" or "package: '*'"
// These are always package repository dependencies
serde_yaml::Value::String(version) => {
if version == "*" {
packages.push(package_name.to_string());
} else {
packages.push(format!("{package_name}-{version}"));
}
let package_spec = build_package_spec_with_lock(
lock_file,
target,
&sysroot,
package_name,
version,
);
packages.push(package_spec);
package_names.push(package_name.to_string());
}
// Object/mapping value: need to check what type of dependency
serde_yaml::Value::Mapping(spec_map) => {
Expand Down Expand Up @@ -410,11 +441,15 @@ impl ExtInstallCommand {
// Check for explicit version in object format
// Format: { version: "1.0.0" }
if let Some(serde_yaml::Value::String(version)) = spec_map.get("version") {
if version == "*" {
packages.push(package_name.to_string());
} else {
packages.push(format!("{package_name}-{version}"));
}
let package_spec = build_package_spec_with_lock(
lock_file,
target,
&sysroot,
package_name,
version,
);
packages.push(package_spec);
package_names.push(package_name.to_string());
}
// If it's a mapping without compile, ext, or version keys, skip it
// (unknown format)
Expand Down Expand Up @@ -511,6 +546,33 @@ $DNF_SDK_HOST \
);
return Ok(false);
}

// Query installed versions and update lock file
if !package_names.is_empty() {
let installed_versions = container_helper
.query_installed_packages(
&sysroot,
&package_names,
container_image,
target,
repo_url.cloned(),
repo_release.cloned(),
merged_container_args.clone(),
)
.await?;

if !installed_versions.is_empty() {
lock_file.update_sysroot_versions(target, &sysroot, installed_versions);
if self.verbose {
print_info(
&format!("Updated lock file with extension '{extension}' package versions."),
OutputLevel::Normal,
);
}
// Save lock file immediately after extension install
lock_file.save(src_dir)?;
}
}
} else if self.verbose {
print_debug(
&format!("No valid dependencies found for extension '{extension}'."),
Expand Down
Loading