Skip to content
Open
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
23 changes: 23 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,29 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]

### Fixed
- **Rootfs and initramfs no longer reinstall on every run.** `avocado sdk
install` wiped and rebuilt both sysroots from scratch on every invocation,
even with nothing changed. Removal detection compared the lockfile against
*config-declared* packages only, so the per-kernel packages the install
auto-appends (`packagegroup-avocado-{rootfs,initramfs}-modules-<kver>` and
`kernel-image-<kver>`) read as "removed" from the second run onward and
forced a clean reinstall. It now compares against the effective set —
config packages plus those auto-appends.
As a second-order effect of that false positive, the same path dropped those
packages' version pins from the in-memory lockfile, so dnf resolved the
kernel image and module packagegroup to newest-available instead of the
locked NVR. `avocado.lock` looked pinned on disk but the pin never applied;
it now binds.
- **Unchanged sysroots are skipped outright.** The rootfs and initramfs install
stamps were written but never read, so each run still paid a kernel
repoquery, a dnf transaction, and a lockfile rewrite. Both stamps are now
read (batched into one container invocation) and a current stamp short-
circuits the install. The stamp hash covers the effective package set,
`sdk.repo_url`, `sdk.repo_release`, `sdk.disable_weak_dependencies`, and a
digest of the sysroot's lockfile pins, so a snapshot bump, a feed switch, or
an `avocado unlock` all invalidate it. `avocado {rootfs,initramfs} clean` now
removes the install stamp along with the sysroot, and `--no-stamps` still
forces a full reinstall.
- **`--connect-sign` guidance.** The deploy help text and the Level 2 setup
messages now reference `avocado connect trust promote-root --key <KEY>` with
its required `--key` option, matching the CLI reference documentation.
Expand Down
44 changes: 19 additions & 25 deletions src/commands/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -175,10 +175,7 @@ impl BuildCommand {
// task list. This gives the user a clear "run avocado install" message
// instead of failing mid-build inside a TUI.
if !self.no_stamps {
use crate::utils::stamps::{
generate_batch_read_stamps_script, resolve_required_stamps, validate_stamps_batch,
StampCommand, StampComponent,
};
use crate::utils::stamps::{resolve_required_stamps, StampCommand, StampComponent};

let container_image = config.get_sdk_image().ok_or_else(|| {
anyhow::anyhow!("No container image specified in config under 'sdk.image'")
Expand All @@ -192,27 +189,24 @@ impl BuildCommand {
let required =
resolve_required_stamps(StampCommand::Install, StampComponent::Runtime, None, &[]);

let batch_script = generate_batch_read_stamps_script(&required);
let run_config = crate::utils::container::RunConfig {
container_image: container_image.clone(),
target: target.clone(),
command: batch_script,
verbose: false,
source_environment: true,
interactive: false,
repo_url: config.get_sdk_repo_url(),
repo_release: config.get_sdk_repo_release(),
container_args: self.container_args.clone(),
sdk_arch: self.sdk_arch.clone(),
runs_on: self.runs_on.clone(),
nfs_port: self.nfs_port,
..Default::default()
};

let output = container_helper
.run_in_container_with_output(run_config)
.await?;
let validation = validate_stamps_batch(&required, output.as_deref().unwrap_or(""), &[]);
let validation = crate::utils::prerequisites::read_stamps_batch(
&required,
&container_helper,
crate::utils::container::RunConfig {
container_image: container_image.clone(),
target: target.clone(),
repo_url: config.get_sdk_repo_url(),
repo_release: config.get_sdk_repo_release(),
container_args: self.container_args.clone(),
sdk_arch: self.sdk_arch.clone(),
runs_on: self.runs_on.clone(),
nfs_port: self.nfs_port,
..Default::default()
},
None,
)
.await?
.validate(&required, &[]);

if !validation.is_satisfied() {
let error =
Expand Down
44 changes: 19 additions & 25 deletions src/commands/ext/checkout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,8 @@ use tokio::process::Command as AsyncCommand;
use crate::utils::config::{ComposedConfig, Config};
use crate::utils::container::{RunConfig, SdkContainer};
use crate::utils::output::{print_error, print_info, print_success, OutputLevel};
use crate::utils::stamps::{
generate_batch_read_stamps_script, validate_stamps_batch, StampRequirement,
};
use crate::utils::prerequisites::read_stamps_batch;
use crate::utils::stamps::StampRequirement;
use crate::utils::target::resolve_target_required;
use crate::utils::volume::VolumeManager;

Expand Down Expand Up @@ -115,28 +114,23 @@ impl ExtCheckoutCommand {
StampRequirement::ext_install(&self.extension),
];

let batch_script = generate_batch_read_stamps_script(&requirements);
let run_config = RunConfig {
container_image: container_image.to_string(),
target: target.clone(),
command: batch_script,
verbose: false,
source_environment: true,
interactive: false,
repo_url: config.get_sdk_repo_url(),
repo_release: config.get_sdk_repo_release(),
container_args: config.merge_sdk_container_args(None),
sdk_arch: self.sdk_arch.clone(),
env_vars: self.runtime_env_vars(),
..Default::default()
};

let output = container_helper
.run_in_container_with_output(run_config)
.await?;

let validation =
validate_stamps_batch(&requirements, output.as_deref().unwrap_or(""), &[]);
let validation = read_stamps_batch(
&requirements,
&container_helper,
RunConfig {
container_image: container_image.to_string(),
target: target.clone(),
repo_url: config.get_sdk_repo_url(),
repo_release: config.get_sdk_repo_release(),
container_args: config.merge_sdk_container_args(None),
sdk_arch: self.sdk_arch.clone(),
env_vars: self.runtime_env_vars(),
..Default::default()
},
None,
)
.await?
.validate(&requirements, &[]);

if !validation.is_satisfied() {
validation
Expand Down
45 changes: 20 additions & 25 deletions src/commands/ext/clean.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,8 @@ use super::find_ext_in_mapping;
use crate::utils::config::{ComposedConfig, Config, ExtensionLocation};
use crate::utils::container::{RunConfig, SdkContainer};
use crate::utils::output::{print_error, print_info, print_success, OutputLevel};
use crate::utils::stamps::{
generate_batch_read_stamps_script, validate_stamps_batch, StampRequirement,
};
use crate::utils::prerequisites::read_stamps_batch;
use crate::utils::stamps::StampRequirement;
use crate::utils::target::resolve_target_required;

pub struct ExtCleanCommand {
Expand Down Expand Up @@ -214,28 +213,24 @@ impl ExtCleanCommand {

// Validate SDK is installed before running clean scripts
let requirements = vec![StampRequirement::sdk_install()];
let batch_script = generate_batch_read_stamps_script(&requirements);
let run_config = RunConfig {
container_image: container_image.to_string(),
target: target.to_string(),
command: batch_script,
verbose: false,
source_environment: true,
interactive: false,
repo_url: repo_url.clone(),
repo_release: repo_release.clone(),
container_args: merged_container_args.clone(),
dnf_args: self.dnf_args.clone(),
sdk_arch: self.sdk_arch.clone(),
env_vars: self.runtime_env_vars(),
..Default::default()
};

let output = container_helper
.run_in_container_with_output(run_config)
.await?;

let validation = validate_stamps_batch(&requirements, output.as_deref().unwrap_or(""), &[]);
let validation = read_stamps_batch(
&requirements,
&container_helper,
RunConfig {
container_image: container_image.to_string(),
target: target.to_string(),
repo_url: repo_url.clone(),
repo_release: repo_release.clone(),
container_args: merged_container_args.clone(),
dnf_args: self.dnf_args.clone(),
sdk_arch: self.sdk_arch.clone(),
env_vars: self.runtime_env_vars(),
..Default::default()
},
None,
)
.await?
.validate(&requirements, &[]);

if !validation.is_satisfied() {
validation
Expand Down
31 changes: 29 additions & 2 deletions src/commands/initramfs/install.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,16 @@ use std::sync::Arc;

use crate::utils::{
config::{ComposedConfig, Config},
container::SdkContainer,
container::{RunConfig, SdkContainer},
lockfile::{LockFile, SysrootType},
output::{print_error, OutputLevel},
runs_on::RunsOnContext,
target::validate_and_log_target,
};

use crate::commands::rootfs::install::{install_sysroot, SysrootInstallParams};
use crate::commands::rootfs::install::{
install_sysroot, read_sysroot_install_stamp, SysrootInstallParams,
};

/// Implementation of the 'initramfs install' command.
pub struct InitramfsInstallCommand {
Expand Down Expand Up @@ -128,6 +130,24 @@ impl InitramfsInstallCommand {
.unwrap_or(std::path::Path::new("."));
let mut lock_file = LockFile::load(src_dir)?;

let prefetched_stamp = read_sysroot_install_stamp(
&SysrootType::Initramfs,
self.no_stamps,
&container_helper,
RunConfig {
container_image: container_image.to_string(),
target: target.to_string(),
verbose: self.verbose,
repo_url: repo_url.clone(),
repo_release: repo_release.clone(),
container_args: merged_container_args.clone(),
sdk_arch: self.sdk_arch.clone(),
..Default::default()
},
runs_on_context.as_ref(),
)
.await?;

let result = install_sysroot(&mut SysrootInstallParams {
sysroot_type: SysrootType::Initramfs,
config,
Expand All @@ -147,10 +167,17 @@ impl InitramfsInstallCommand {
sdk_arch: self.sdk_arch.as_ref(),
no_stamps: self.no_stamps,
parsed: Some(&composed.merged_value),
prefetched_stamp,
tui_context: None,
})
.await;

// Persist the lockfile the install updated — `install_sysroot` leaves
// saving to its caller.
if result.is_ok() {
lock_file.save(src_dir)?;
}
Comment on lines +175 to +179

if let Some(ref mut context) = runs_on_context {
if let Err(e) = context.teardown().await {
print_error(
Expand Down
8 changes: 7 additions & 1 deletion src/commands/rootfs/clean.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,14 @@ use crate::utils::{
};

/// Generate the shell command to clean a sysroot (rootfs or initramfs).
///
/// Removes the install stamp along with the sysroot. Without this, `rootfs
/// clean && sdk install` would see a current stamp against an empty sysroot
/// and skip the reinstall (see the short-circuit in
/// [`install_sysroot`](super::install::install_sysroot)) — mirroring how
/// `ext clean` drops `.stamps/ext/<name>`.
pub fn clean_sysroot_command(sysroot_dir: &str) -> String {
format!(r#"rm -rf "$AVOCADO_PREFIX/{sysroot_dir}""#)
format!(r#"rm -rf "$AVOCADO_PREFIX/{sysroot_dir}" "$AVOCADO_PREFIX/.stamps/{sysroot_dir}""#)
}

/// Implementation of the 'rootfs clean' command.
Expand Down
Loading
Loading