From b8b2225449fb22647300fac20704264008b2d0b9 Mon Sep 17 00:00:00 2001 From: Wildan M Date: Thu, 19 Mar 2026 15:30:54 +0700 Subject: [PATCH] Implement push update diff --- src/bin/repo.rs | 26 +++++++++++------- src/cook/fs.rs | 5 ++++ src/cook/package.rs | 47 ++++++++++++++++++++++++++++++- src/lib.rs | 67 +++++++++++++++++++++++++++++++++++++++++++++ src/recipe.rs | 11 +++----- 5 files changed, 138 insertions(+), 18 deletions(-) diff --git a/src/bin/repo.rs b/src/bin/repo.rs index 09e3e36eb..130efdb18 100644 --- a/src/bin/repo.rs +++ b/src/bin/repo.rs @@ -5,13 +5,13 @@ use cookbook::cook::cook_build::{build, get_stage_dirs, remove_stage_dir}; use cookbook::cook::fetch::{fetch, fetch_offline}; use cookbook::cook::fs::{create_target_dir, run_command}; use cookbook::cook::ident; -use cookbook::cook::package::package; +use cookbook::cook::package::{package, package_handle_push}; use cookbook::cook::pty::{PtyOut, UnixSlavePty, flush_pty, setup_pty}; use cookbook::cook::script::KILL_ALL_PID; use cookbook::cook::tree::{self, WalkTreeEntry}; use cookbook::log_to_pty; use cookbook::recipe::{CookRecipe, recipes_flatten_package_names, recipes_mark_as_deps}; -use pkg::PackageName; +use pkg::{PackageName, PackageState}; use ratatui::Terminal; use ratatui::layout::{Constraint, Direction, Layout, Position, Rect}; use ratatui::prelude::TermionBackend; @@ -798,15 +798,17 @@ fn handle_push(recipes: &Vec, config: &CliConfig) -> anyhow::Result< _is_last: bool, entry: &WalkTreeEntry| -> anyhow::Result<()> { - let public_path = "build/id_ed25519.pub.toml"; let r = match entry { WalkTreeEntry::Built(archive_path, _) => { - let sysroot_dir = PUSH_SYSROOT_DIR.get().unwrap(); - pkgar::extract(public_path, archive_path.as_path(), sysroot_dir).context(format!( - "failed to install '{}' in '{}'", - archive_path.display(), - sysroot_dir.display(), - )) + let install_path = PUSH_SYSROOT_DIR.get().unwrap(); + let mut state = + PackageState::from_sysroot(install_path).map_err(|e| anyhow!("{e:?}"))?; + let r = package_handle_push(&mut state, archive_path, &install_path, false) + .map_err(|e| anyhow!("{e:?}")); + if matches!(r, Ok(false)) { + state.to_sysroot(install_path)?; + } + r } WalkTreeEntry::NotBuilt => Err(anyhow!( "Package {} has not been built", @@ -817,7 +819,11 @@ fn handle_push(recipes: &Vec, config: &CliConfig) -> anyhow::Result< } }; match r { - Ok(()) => { + Ok(true) => { + print_cached(&CliCommand::Push, Some(package_name)); + Ok(()) + } + Ok(false) => { print_success(&CliCommand::Push, Some(package_name)); Ok(()) } diff --git a/src/cook/fs.rs b/src/cook/fs.rs index 3d81d77de..fc7135d2e 100644 --- a/src/cook/fs.rs +++ b/src/cook/fs.rs @@ -12,6 +12,7 @@ use walkdir::{DirEntry, WalkDir}; use crate::{ config::translate_mirror, cook::pty::{PtyOut, spawn_to_pipe}, + wrap_io_err, }; //TODO: pub(crate) for all of these functions @@ -314,6 +315,10 @@ pub fn download_wget(url: &str, dest: &PathBuf, logger: &PtyOut) -> Result<(), S Ok(()) } +pub fn read_to_string(path: &Path) -> crate::Result { + fs::read_to_string(path).map_err(wrap_io_err!(path, "Reading file to string")) +} + /// get commit rev and return if it's detached or not pub fn get_git_head_rev(dir: &PathBuf) -> Result<(String, bool), String> { let git_head = dir.join(".git/HEAD"); diff --git a/src/cook/package.rs b/src/cook/package.rs index e63a92413..ba8a177f4 100644 --- a/src/cook/package.rs +++ b/src/cook/package.rs @@ -3,7 +3,7 @@ use std::{ path::{Path, PathBuf}, }; -use pkg::{Package, PackageName, PackagePrefix}; +use pkg::{InstallState, Package, PackageName, PackagePrefix, PackageState}; use pkgar::ext::PackageSrcExt; use pkgar_core::HeaderFlags; @@ -252,3 +252,48 @@ fn get_package_name_inner(name: &str, package: Option<&str>) -> String { } prefix_name } + +pub fn package_handle_push( + state: &mut PackageState, + archive_path: &Path, + sysroot_dir: &Path, + reinstall: bool, +) -> crate::Result { + let archive_toml = archive_path.with_extension("toml"); + let pkey_path = "build/id_ed25519.pub.toml"; + let pkg_toml = Package::from_file(&archive_toml)?; + match state.installed.get(&pkg_toml.name) { + Some(s) if !reinstall && pkg_toml.blake3 == s.blake3 => Ok(true), + Some(s) => { + // "local" is what remote name from installer is hardcoded into + let remote_name = "local".to_string(); + + let install_state = + InstallState::from_package(&pkg_toml, remote_name, s.manual, s.dependents.clone()); + + // TODO: use pkgar::replace unless forced reinstall + pkgar::extract(pkey_path, &archive_path, sysroot_dir)?; + + state.installed.insert(pkg_toml.name.clone(), install_state); + + Ok(false) + } + None => { + // "local" is what remote name from installer is hardcoded into + let remote_name = "local".to_string(); + + // TODO: Handle manual & depedents + let install_state = + InstallState::from_package(&pkg_toml, remote_name, true, BTreeSet::new()); + + pkgar::extract(pkey_path, &archive_path, sysroot_dir)?; + + // TODO: Inject dependencies + // TODO: Check if we need to inject remote key + + state.installed.insert(pkg_toml.name.clone(), install_state); + + Ok(false) + } + } +} diff --git a/src/lib.rs b/src/lib.rs index bcb4b2dea..7e3676a37 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -15,3 +15,70 @@ pub const REMOTE_PKG_SOURCE: &str = "https://static.redox-os.org/pkg"; pub fn is_redox() -> bool { cfg!(target_os = "redox") } + +// Errors + +use std::io; +use std::path::PathBuf; + +#[derive(Debug)] +pub enum Error { + Io { + source: io::Error, + path: Option, + context: &'static str, + }, + Package(pkg::PackageError), + Pkgar(pkgar::Error), + Other(String), +} + +macro_rules! wrap_io_err { + ($context:expr) => { + |source| crate::Error::Io { + source, + path: None, + context: $context, + } + }; + ($path:expr, $context:expr) => { + |source| crate::Error::Io { + source, + path: Some($path.to_path_buf()), + context: $context, + } + }; +} + +impl From for Error { + fn from(value: String) -> Self { + Error::Other(value) + } +} + +impl From for Error { + fn from(value: pkg::PackageError) -> Self { + Error::Package(value) + } +} + +impl From for Error { + fn from(value: pkgar::Error) -> Self { + match value { + pkgar::Error::Io { + source, + path, + context, + } => Error::Io { + source, + path, + context, + }, + _ => Error::Pkgar(value), + } + } +} + +pub(crate) type Result = std::result::Result; + +pub(crate) use wrap_io_err; diff --git a/src/recipe.rs b/src/recipe.rs index 97bbc06c9..03dafaea3 100644 --- a/src/recipe.rs +++ b/src/recipe.rs @@ -7,10 +7,7 @@ use std::{ use pkg::{PackageError, PackageName, recipes}; use regex::Regex; -use serde::{ - Deserialize, Serialize, - de::{Error as DeErrorT, value::Error as DeError}, -}; +use serde::{Deserialize, Serialize}; use crate::{WALK_DEPTH, cook::package as cook_package}; @@ -198,9 +195,9 @@ impl Recipe { return Err(PackageError::FileMissing(file.clone())); } let toml = fs::read_to_string(&file) - .map_err(|err| PackageError::Parse(DeError::custom(err), Some(file.clone())))?; - let recipe: Recipe = toml::from_str(&toml) - .map_err(|err| PackageError::Parse(DeError::custom(err), Some(file.clone())))?; + .map_err(|err| PackageError::FileError(err.raw_os_error(), file.clone()))?; + let recipe: Recipe = + toml::from_str(&toml).map_err(|err| PackageError::Parse(err, Some(file.clone())))?; Ok(recipe) }