Add unavailable package dialog

This commit is contained in:
Victor Fuentes 2022-11-03 17:52:21 -04:00
parent ceb00abc4c
commit b25fd4e7b9
No known key found for this signature in database
GPG Key ID: 0A88B68D6A9ACAE0
9 changed files with 759 additions and 220 deletions

10
Cargo.lock generated
View File

@ -1592,9 +1592,9 @@ dependencies = [
[[package]]
name = "native-tls"
version = "0.2.10"
version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd7e2f3618557f980e0b17e8856252eee3c97fa12c54dff0ca290fb6266ca4a9"
checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e"
dependencies = [
"lazy_static",
"libc",
@ -1617,7 +1617,7 @@ checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54"
[[package]]
name = "nix-data"
version = "0.0.2"
source = "git+https://github.com/snowflakelinux/nix-data#33479e595f14142f15fb6ec29cc585352c2c7703"
source = "git+https://github.com/snowflakelinux/nix-data#315d3efee6860cdc48e514ac1fa288196312a4f4"
dependencies = [
"anyhow",
"csv",
@ -1742,9 +1742,9 @@ dependencies = [
[[package]]
name = "num_cpus"
version = "1.13.1"
version = "1.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1"
checksum = "f6058e64324c71e02bc2b150e4f3bc8286db6c83092132ffa3f6b1eab0f9def5"
dependencies = [
"hermit-abi",
"libc",

View File

@ -20,7 +20,7 @@ pkgs.stdenv.mkDerivation rec {
cargoDeps = pkgs.rustPlatform.fetchCargoTarball {
inherit src;
name = "${pname}-${version}";
hash = "sha256-CRw/T0TAgW+81C3966PAEpDHJotSbOHkAqhotY1sozM=";
hash = "sha256-yChtK86/onSI2JQaQkBwwVzijGTEyc6vQbCUmgWb3u8=";
};
nativeBuildInputs = with pkgs; [

View File

@ -1,7 +1,7 @@
use clap::{self, FromArgMatches, Subcommand};
use std::{
error::Error,
fs::{File, self},
fs::{self, File},
io::{self, Read, Write},
process::Command,
};
@ -23,6 +23,12 @@ enum SubCommands {
/// Whether to rebuild the system after updating channels
#[arg(short, long)]
rebuild: bool,
/// Update file
#[arg(short, long)]
update: bool,
/// Write stdin to file in path output
#[arg(short, long)]
output: String,
/// Run `nixos-rebuild` with the given arguments
arguments: Vec<String>,
},
@ -33,6 +39,12 @@ enum SubCommands {
/// Path to the flake file
#[arg(short, long)]
flakepath: String,
/// Update file
#[arg(short, long)]
update: bool,
/// Write stdin to file in path output
#[arg(short, long)]
output: String,
/// Run `nixos-rebuild` with the given arguments
arguments: Vec<String>,
},
@ -81,7 +93,18 @@ fn main() {
std::process::exit(1);
}
},
SubCommands::Channel { rebuild: dorebuild, arguments } => {
SubCommands::Channel {
rebuild: dorebuild,
update,
output,
arguments,
} => {
if update {
if let Err(e) = write_file(&output) {
eprintln!("{}", e);
std::process::exit(1);
}
}
match channel() {
Ok(_) => {
if dorebuild {
@ -93,14 +116,26 @@ fn main() {
}
}
}
},
}
Err(err) => {
eprintln!("{}", err);
std::process::exit(1);
}
}
},
SubCommands::Flake { rebuild: dorebuild, flakepath, arguments } => {
}
SubCommands::Flake {
rebuild: dorebuild,
flakepath,
update,
output,
arguments,
} => {
if update {
if let Err(e) = write_file(&output) {
eprintln!("{}", e);
std::process::exit(1);
}
}
match flake(&flakepath) {
Ok(_) => {
if dorebuild {
@ -112,7 +147,7 @@ fn main() {
}
}
}
},
}
Err(err) => {
eprintln!("{}", err);
std::process::exit(1);
@ -132,9 +167,7 @@ fn write_file(path: &str) -> Result<(), Box<dyn Error>> {
}
fn rebuild(args: Vec<String>) -> Result<(), Box<dyn Error>> {
let mut cmd = Command::new("nixos-rebuild")
.args(args)
.spawn()?;
let mut cmd = Command::new("nixos-rebuild").args(args).spawn()?;
let x = cmd.wait()?;
if x.success() {
Ok(())
@ -148,9 +181,7 @@ fn rebuild(args: Vec<String>) -> Result<(), Box<dyn Error>> {
}
fn channel() -> Result<(), Box<dyn Error>> {
let mut cmd = Command::new("nix-channel")
.arg("--update")
.spawn()?;
let mut cmd = Command::new("nix-channel").arg("--update").spawn()?;
let x = cmd.wait()?;
if x.success() {
Ok(())
@ -179,4 +210,4 @@ fn flake(path: &str) -> Result<(), Box<dyn Error>> {
"nix flake failed",
)))
}
}
}

View File

@ -135,6 +135,7 @@ impl Worker for InstallAsyncHandler {
.arg("profile")
.arg("install")
.arg(format!("nixpkgs#{}", work.pkg))
.arg("--impure")
.kill_on_drop(true)
.stdout(Stdio::piped())
.stderr(Stdio::piped())

View File

@ -10,6 +10,7 @@ pub mod preferencespage;
pub mod rebuild;
pub mod screenshotfactory;
pub mod searchpage;
pub mod unavailabledialog;
pub mod updatepage;
pub mod updateworker;
pub mod welcome;

264
src/ui/unavailabledialog.rs Normal file
View File

@ -0,0 +1,264 @@
use std::path::Path;
use gtk::pango;
use log::*;
use relm4::{*, prelude::*, factory::*};
use adw::prelude::*;
use crate::{APPINFO, ui::{window::REBUILD_BROKER, rebuild::RebuildMsg}};
use super::updatepage::{UpdatePageMsg, UpdateType};
#[derive(Debug)]
pub struct UnavailableDialogModel {
hidden: bool,
unavailableuseritems: FactoryVecDeque<UnavailableItemModel>,
unavailablesysitems: FactoryVecDeque<UnavailableItemModel>,
updatetype: UpdateType,
}
#[derive(Debug)]
pub enum UnavailableDialogMsg {
Show(Vec<UnavailableItemModel>, Vec<UnavailableItemModel>, UpdateType),
Close,
Continue,
}
#[relm4::component(pub)]
impl SimpleComponent for UnavailableDialogModel {
type Init = gtk::Window;
type Input = UnavailableDialogMsg;
type Output = UpdatePageMsg;
type Widgets = UnavailableDialogWidgets;
view! {
dialog = adw::MessageDialog {
#[watch]
set_visible: !model.hidden,
set_transient_for: Some(&parent_window),
set_modal: true,
set_heading: Some("Some packages are unavailable!"),
set_body: "If you continue this update, some packages will be removed",
#[wrap(Some)]
set_extra_child = &gtk::Box {
set_orientation: gtk::Orientation::Vertical,
set_spacing: 20,
adw::PreferencesGroup {
#[watch]
set_visible: !model.unavailableuseritems.is_empty(),
set_title: "User Packages",
#[local_ref]
unavailableuserlist -> gtk::ListBox {
add_css_class: "boxed-list",
set_selection_mode: gtk::SelectionMode::None,
},
},
adw::PreferencesGroup {
#[watch]
set_visible: !model.unavailablesysitems.is_empty(),
set_title: "System Packages",
#[local_ref]
unavailablesyslist -> gtk::ListBox {
add_css_class: "boxed-list",
set_selection_mode: gtk::SelectionMode::None
},
}
},
add_response: ("cancel", "Cancel"),
add_response: ("continue", "Continue"),
set_response_appearance: ("continue", adw::ResponseAppearance::Destructive),
connect_close_request => |_| {
gtk::Inhibit(true)
}
}
}
fn init(
parent_window: Self::Init,
root: &Self::Root,
sender: ComponentSender<Self>,
) -> ComponentParts<Self> {
let model = UnavailableDialogModel {
unavailableuseritems: FactoryVecDeque::new(gtk::ListBox::new(), sender.input_sender()),
unavailablesysitems: FactoryVecDeque::new(gtk::ListBox::new(), sender.input_sender()),
updatetype: UpdateType::All,
hidden: true,
};
let unavailableuserlist = model.unavailableuseritems.widget();
let unavailablesyslist = model.unavailablesysitems.widget();
let widgets = view_output!();
widgets.dialog.connect_response(None, move |_, resp| {
match resp {
"cancel" => {
REBUILD_BROKER.send(RebuildMsg::Close);
debug!("Response: cancel")
},
"continue" => {
sender.input(UnavailableDialogMsg::Continue);
debug!("Response: continue")
},
_ => unreachable!(),
}
});
ComponentParts { model, widgets }
}
fn update(&mut self, msg: Self::Input, sender: ComponentSender<Self>) {
match msg {
UnavailableDialogMsg::Show(useritems, sysitems, updatetype) => {
self.updatetype = updatetype;
let mut unavailableuseritems_guard = self.unavailableuseritems.guard();
unavailableuseritems_guard.clear();
for item in useritems {
unavailableuseritems_guard.push_back(item);
}
let mut unavailablesysitems_guard = self.unavailablesysitems.guard();
for item in sysitems {
unavailablesysitems_guard.push_back(item);
}
self.hidden = false;
}
UnavailableDialogMsg::Close => {
info!("UpdateDialogMsg::Close");
let mut unavailableuseritems_guard = self.unavailableuseritems.guard();
let mut unavailablesysitems_guard = self.unavailablesysitems.guard();
unavailableuseritems_guard.clear();
unavailablesysitems_guard.clear();
self.hidden = true;
}
UnavailableDialogMsg::Continue => {
match self.updatetype {
UpdateType::User => {
sender.output(UpdatePageMsg::UpdateAllUserRm(self.unavailableuseritems.iter().map(|x| x.pkg.to_string()).collect()));
}
UpdateType::System => {
sender.output(UpdatePageMsg::UpdateSystemRm(self.unavailablesysitems.iter().map(|x| x.pkg.to_string()).collect()));
}
UpdateType::All => {
sender.output(UpdatePageMsg::UpdateAllRm(self.unavailableuseritems.iter().map(|x| x.pkg.to_string()).collect(), self.unavailablesysitems.iter().map(|x| x.pkg.to_string()).collect()));
}
}
sender.input(UnavailableDialogMsg::Close)
}
}
}
}
#[derive(Default, Debug, PartialEq, Eq)]
pub struct UnavailableItemModel {
pub name: String,
pub pkg: String,
pub pname: String,
pub icon: Option<String>,
pub message: String,
}
#[derive(Debug)]
pub enum UnavailableItemMsg {}
#[relm4::factory(pub)]
impl FactoryComponent for UnavailableItemModel {
type CommandOutput = ();
type Init = UnavailableItemModel;
type Input = ();
type Output = UnavailableItemMsg;
type Widgets = UnavailableItemWidgets;
type ParentWidget = adw::gtk::ListBox;
type ParentInput = UnavailableDialogMsg;
view! {
adw::PreferencesRow {
set_activatable: false,
#[wrap(Some)]
set_child = &gtk::Box {
set_orientation: gtk::Orientation::Horizontal,
set_hexpand: true,
set_spacing: 10,
set_margin_all: 10,
adw::Bin {
set_valign: gtk::Align::Center,
#[wrap(Some)]
set_child = if self.icon.is_some() {
gtk::Image {
add_css_class: "icon-dropshadow",
set_halign: gtk::Align::Start,
set_from_file: {
if let Some(i) = &self.icon {
let iconpath = format!("{}/icons/nixos/128x128/{}", APPINFO, i);
let iconpath64 = format!("{}/icons/nixos/64x64/{}", APPINFO, i);
if Path::new(&iconpath).is_file() {
Some(iconpath)
} else if Path::new(&iconpath64).is_file() {
Some(iconpath64)
} else {
None
}
} else {
None
}
},
set_pixel_size: 64,
}
} else {
gtk::Image {
add_css_class: "icon-dropshadow",
set_halign: gtk::Align::Start,
set_icon_name: Some("package-x-generic"),
set_pixel_size: 64,
}
}
},
gtk::Box {
set_orientation: gtk::Orientation::Horizontal,
set_halign: gtk::Align::Fill,
set_valign: gtk::Align::Center,
set_hexpand: true,
set_spacing: 20,
gtk::Box {
set_orientation: gtk::Orientation::Vertical,
set_halign: gtk::Align::Fill,
set_valign: gtk::Align::Center,
set_spacing: 2,
gtk::Label {
set_halign: gtk::Align::Start,
set_label: self.name.as_str(),
set_ellipsize: pango::EllipsizeMode::End,
set_lines: 1,
set_wrap: true,
set_max_width_chars: 0,
},
gtk::Label {
set_halign: gtk::Align::Start,
add_css_class: "dim-label",
add_css_class: "caption",
set_label: self.pkg.as_str(),
set_ellipsize: pango::EllipsizeMode::End,
set_lines: 1,
set_wrap: true,
set_max_width_chars: 0,
},
},
gtk::Label {
set_halign: gtk::Align::Center,
set_hexpand: true,
set_label: self.message.as_str(),
set_wrap: true,
}
}
}
}
}
fn init_model(
init: Self::Init,
_index: &DynamicIndex,
_sender: FactoryComponentSender<Self>,
) -> Self {
init
}
}

View File

@ -1,12 +1,14 @@
use crate::APPINFO;
use crate::{APPINFO, ui::unavailabledialog::UnavailableDialogModel};
use super::{pkgpage::InstallType, window::*, updateworker::{UpdateAsyncHandler, UpdateAsyncHandlerMsg, UpdateAsyncHandlerInit}, rebuild::RebuildMsg};
use adw::prelude::*;
use nix_data::config::configfile::NixDataConfig;
use relm4::{factory::*, gtk::pango, *};
use std::{path::Path, convert::identity};
use std::{path::Path, convert::identity, collections::HashMap};
use log::*;
pub static UNAVAILABLE_BROKER: MessageBroker<UnavailableDialogModel> = MessageBroker::new();
#[tracker::track]
#[derive(Debug)]
pub struct UpdatePageModel {
@ -21,6 +23,8 @@ pub struct UpdatePageModel {
systype: SystemPkgs,
usertype: UserPkgs,
updatetracker: u8,
#[tracker::no_eq]
unavailabledialog: Controller<UnavailableDialogModel>,
}
#[derive(Debug)]
@ -30,15 +34,25 @@ pub enum UpdatePageMsg {
Update(Vec<UpdateItem>, Vec<UpdateItem>),
OpenRow(usize, InstallType),
UpdateSystem,
UpdateSystemRm(Vec<String>),
UpdateAllUser,
UpdateAllUserRm(Vec<String>),
UpdateUser(String),
// UpdateChannels,
// UpdateSystemAndChannels,
UpdateAll,
UpdateAllRm(Vec<String>, Vec<String>),
DoneWorking,
FailedWorking,
}
#[derive(Debug)]
pub enum UpdateType {
System,
User,
All,
}
pub struct UpdatePageInit {
pub window: gtk::Window,
pub systype: SystemPkgs,
@ -84,101 +98,6 @@ impl SimpleComponent for UpdatePageModel {
}
}
},
// gtk::Box {
// set_orientation: gtk::Orientation::Horizontal,
// set_hexpand: true,
// #[watch]
// set_visible: model.channelupdate.is_some(),
// gtk::Label {
// set_halign: gtk::Align::Start,
// add_css_class: "title-4",
// set_label: "Channels",
// },
// },
// gtk::ListBox {
// set_valign: gtk::Align::Start,
// add_css_class: "boxed-list",
// set_selection_mode: gtk::SelectionMode::None,
// #[watch]
// set_visible: model.channelupdate.is_some(),
// adw::PreferencesRow {
// set_activatable: false,
// set_can_focus: false,
// #[wrap(Some)]
// set_child = &gtk::Box {
// set_orientation: gtk::Orientation::Horizontal,
// set_hexpand: true,
// set_spacing: 10,
// set_margin_all: 10,
// adw::Bin {
// set_valign: gtk::Align::Center,
// gtk::Image {
// add_css_class: "icon-dropshadow",
// set_halign: gtk::Align::Start,
// set_icon_name: Some("application-x-addon"),
// set_pixel_size: 64,
// }
// },
// gtk::Box {
// set_orientation: gtk::Orientation::Vertical,
// set_halign: gtk::Align::Fill,
// set_valign: gtk::Align::Center,
// set_hexpand: true,
// set_spacing: 2,
// gtk::Label {
// set_halign: gtk::Align::Start,
// set_label: "nixos",
// set_ellipsize: pango::EllipsizeMode::End,
// set_lines: 1,
// set_wrap: true,
// set_max_width_chars: 0,
// },
// gtk::Label {
// set_halign: gtk::Align::Start,
// add_css_class: "dim-label",
// add_css_class: "caption",
// set_label: {
// &(if let Some((old, new)) = &model.channelupdate {
// format!("{} → {}", old, new)
// } else {
// String::default()
// })
// },
// set_visible: model.channelupdate.is_some(),
// set_ellipsize: pango::EllipsizeMode::End,
// set_lines: 1,
// set_wrap: true,
// set_max_width_chars: 0,
// },
// },
// gtk::Box {
// set_orientation: gtk::Orientation::Vertical,
// set_spacing: 5,
// set_halign: gtk::Align::End,
// set_valign: gtk::Align::Center,
// gtk::Button {
// add_css_class: "suggested-action",
// set_valign: gtk::Align::Center,
// set_halign: gtk::Align::End,
// set_label: "Update channel and system",
// set_can_focus: false,
// connect_clicked[sender] => move |_| {
// sender.input(UpdatePageMsg::UpdateSystemAndChannels);
// }
// },
// gtk::Button {
// set_valign: gtk::Align::Center,
// set_halign: gtk::Align::End,
// set_label: "Update channel only",
// set_can_focus: false,
// connect_clicked[sender] => move |_| {
// sender.input(UpdatePageMsg::UpdateChannels);
// }
// },
// }
// }
// }
// },
gtk::Box {
set_orientation: gtk::Orientation::Horizontal,
set_hexpand: true,
@ -283,6 +202,10 @@ impl SimpleComponent for UpdatePageModel {
.detach_worker(UpdateAsyncHandlerInit { syspkgs: initparams.systype.clone(), userpkgs: initparams.usertype.clone() })
.forward(sender.input_sender(), identity);
let unavailabledialog = UnavailableDialogModel::builder()
.launch_with_broker(initparams.window.clone(), &UNAVAILABLE_BROKER)
.forward(sender.input_sender(), identity);
let config = initparams.config;
updateworker.emit(UpdateAsyncHandlerMsg::UpdateConfig(config.clone()));
@ -295,6 +218,7 @@ impl SimpleComponent for UpdatePageModel {
config,
systype: initparams.systype,
usertype: initparams.usertype,
unavailabledialog,
tracker: 0,
};
@ -322,13 +246,6 @@ impl SimpleComponent for UpdatePageModel {
info!("UpdatePageMsg::Update");
debug!("UPDATEUSERLIST: {:?}", updateuserlist);
debug!("UPDATESYSTEMLIST: {:?}", updatesystemlist);
// self.channelupdate = match nix_data::cache::channel::uptodate() {
// Ok(x) => {
// x
// },
// Err(_) => None,
// };
// debug!("CHANNELUPDATE: {:?}", self.channelupdate);
self.update_updatetracker(|_| ());
let mut updateuserlist_guard = self.updateuserlist.guard();
updateuserlist_guard.clear();
@ -340,16 +257,6 @@ impl SimpleComponent for UpdatePageModel {
for updatesystem in updatesystemlist {
updatesystemlist_guard.push_back(updatesystem);
}
updatesystemlist_guard.push_back(UpdateItem {
pkg: None,
name: "NixOS".to_string(),
pname: "nixos".to_string(),
summary: None,
icon: None,
pkgtype: InstallType::System,
verfrom: None,
verto: None,
});
}
UpdatePageMsg::OpenRow(row, pkgtype) => match pkgtype {
InstallType::User => {
@ -369,17 +276,33 @@ impl SimpleComponent for UpdatePageModel {
}
}
},
// UpdatePageMsg::UpdateChannels => {
// self.updatedialog.emit(UpdateDialogMsg::Show(String::from("Updating channels...")));
// self.updateworker.emit(UpdateAsyncHandlerMsg::UpdateChannels);
// }
// UpdatePageMsg::UpdateSystemAndChannels => {
// self.updatedialog.emit(UpdateDialogMsg::Show(String::from("Updating system and channels...")));
// self.updateworker.emit(UpdateAsyncHandlerMsg::UpdateChannelsAndSystem);
// }
UpdatePageMsg::UpdateSystem => {
let systype = self.systype.clone();
let systemconfig = self.config.systemconfig.clone();
let workersender = self.updateworker.sender().clone();
let output = sender.output_sender().clone();
REBUILD_BROKER.send(RebuildMsg::Show);
self.updateworker.emit(UpdateAsyncHandlerMsg::UpdateSystem);
relm4::spawn(async move {
let uninstallsys = match systype {
SystemPkgs::Legacy => {
nix_data::cache::channel::unavailablepkgs(&[&systemconfig.unwrap()]).await.unwrap_or_default()
}
SystemPkgs::Flake => {
nix_data::cache::flakes::unavailablepkgs(&[&systemconfig.unwrap()]).await.unwrap_or_default()
}
_ => HashMap::new(),
};
if uninstallsys.is_empty() {
workersender.send(UpdateAsyncHandlerMsg::UpdateSystem);
} else {
warn!("Uninstalling unavailable packages: {:?}", uninstallsys);
output.send(AppMsg::GetUnavailableItems(HashMap::new(), uninstallsys, UpdateType::System));
}
});
}
UpdatePageMsg::UpdateSystemRm(pkgs) => {
info!("UpdatePageMsg::UpdateSystemRm({:?})", pkgs);
self.updateworker.emit(UpdateAsyncHandlerMsg::UpdateSystemRemove(pkgs));
}
UpdatePageMsg::UpdateUser(pkg) => {
info!("UPDATE USER PKG: {}", pkg);
@ -387,11 +310,62 @@ impl SimpleComponent for UpdatePageModel {
}
UpdatePageMsg::UpdateAllUser => {
REBUILD_BROKER.send(RebuildMsg::Show);
self.updateworker.emit(UpdateAsyncHandlerMsg::UpdateUserPkgs);
if self.usertype == UserPkgs::Profile {
let workersender = self.updateworker.sender().clone();
let output = sender.output_sender().clone();
relm4::spawn(async move {
let uninstalluser = nix_data::cache::profile::unavailablepkgs().await.unwrap_or_default();
if uninstalluser.is_empty() {
workersender.send(UpdateAsyncHandlerMsg::UpdateUserPkgs);
} else {
warn!("Uninstalling unavailable packages: {:?}", uninstalluser);
output.send(AppMsg::GetUnavailableItems(uninstalluser, HashMap::new(), UpdateType::User));
}
});
} else {
self.updateworker.emit(UpdateAsyncHandlerMsg::UpdateUserPkgs);
}
}
UpdatePageMsg::UpdateAllUserRm(pkgs) => {
info!("UpdatePageMsg::UpdateAllUserRm({:?})", pkgs);
self.updateworker.emit(UpdateAsyncHandlerMsg::UpdateUserPkgsRemove(pkgs));
}
UpdatePageMsg::UpdateAll => {
info!("UpdatePageMsg::UpdateAll");
let systype = self.systype.clone();
let usertype = self.usertype.clone();
let systemconfig = self.config.systemconfig.clone();
let workersender = self.updateworker.sender().clone();
let output = sender.output_sender().clone();
REBUILD_BROKER.send(RebuildMsg::Show);
self.updateworker.emit(UpdateAsyncHandlerMsg::UpdateAll);
relm4::spawn(async move {
let uninstallsys = match systype {
SystemPkgs::Legacy => {
nix_data::cache::channel::unavailablepkgs(&[&systemconfig.unwrap()]).await.unwrap_or_default()
}
SystemPkgs::Flake => {
nix_data::cache::flakes::unavailablepkgs(&[&systemconfig.unwrap()]).await.unwrap_or_default()
}
_ => HashMap::new(),
};
let uninstalluser = if usertype == UserPkgs::Profile {
nix_data::cache::profile::unavailablepkgs().await.unwrap_or_default()
} else {
HashMap::new()
};
if uninstallsys.is_empty() && uninstalluser.is_empty() {
workersender.send(UpdateAsyncHandlerMsg::UpdateAll);
} else {
warn!("Uninstalling unavailable user packages: {:?}", uninstalluser);
warn!("Uninstalling unavailable system packages: {:?}", uninstallsys);
output.send(AppMsg::GetUnavailableItems(uninstalluser, uninstallsys, UpdateType::All));
}
});
}
UpdatePageMsg::UpdateAllRm(userpkgs, syspkgs) => {
info!("UpdatePageMsg::UpdateAllRm({:?}, {:?})", userpkgs, syspkgs);
self.updateworker.emit(UpdateAsyncHandlerMsg::UpdateAllRemove(userpkgs, syspkgs));
}
UpdatePageMsg::DoneWorking => {
REBUILD_BROKER.send(RebuildMsg::FinishSuccess);

View File

@ -1,9 +1,9 @@
use anyhow::{anyhow, Result};
use log::*;
use nix_data::config::configfile::NixDataConfig;
use relm4::*;
use std::{path::Path, process::Stdio};
use tokio::io::AsyncBufReadExt;
use anyhow::Result;
use log::*;
use std::{fs, path::Path, process::Stdio};
use tokio::io::{AsyncBufReadExt, AsyncWriteExt};
use crate::ui::{rebuild::RebuildMsg, window::REBUILD_BROKER};
@ -17,7 +17,7 @@ use super::{
pub struct UpdateAsyncHandler {
#[tracker::no_eq]
process: Option<JoinHandle<()>>,
systemconfig: Option<String>,
systemconfig: String,
flakeargs: Option<String>,
syspkgs: SystemPkgs,
userpkgs: UserPkgs,
@ -30,13 +30,15 @@ pub enum UpdateAsyncHandlerMsg {
// UpdateChannels,
// UpdateChannelsAndSystem,
UpdateSystem,
UpdateSystemRemove(Vec<String>),
RebuildSystem,
UpdateUserPkgs,
UpdateUserPkgsRemove(Vec<String>),
UpdateAll,
UpdateAllRemove(Vec<String>, Vec<String>),
}
enum NscCmd {
@ -58,7 +60,7 @@ impl Worker for UpdateAsyncHandler {
fn init(params: Self::Init, _sender: relm4::ComponentSender<Self>) -> Self {
Self {
process: None,
systemconfig: None,
systemconfig: String::new(),
flakeargs: None,
syspkgs: params.syspkgs,
userpkgs: params.userpkgs,
@ -69,7 +71,7 @@ impl Worker for UpdateAsyncHandler {
fn update(&mut self, msg: Self::Input, sender: ComponentSender<Self>) {
match msg {
UpdateAsyncHandlerMsg::UpdateConfig(config) => {
self.systemconfig = config.systemconfig;
self.systemconfig = config.systemconfig.unwrap_or_default();
self.flakeargs = if let Some(flake) = config.flake {
if let Some(flakearg) = config.flakearg {
Some(format!("{}#{}", flake, flakearg))
@ -84,46 +86,30 @@ impl Worker for UpdateAsyncHandler {
self.syspkgs = syspkgs;
self.userpkgs = userpkgs;
}
// UpdateAsyncHandlerMsg::UpdateChannels => {
// let systemconfig = self.systemconfig.clone();
// let flakeargs = self.flakeargs.clone();
// let syspkgs = self.syspkgs.clone();
// relm4::spawn(async move {
// let result = runcmd(NscCmd::Channel, systemconfig, flakeargs, syspkgs).await;
// match result {
// Ok(true) => {
// sender.output(UpdatePageMsg::DoneWorking);
// }
// _ => {
// warn!("UPDATE CHANNEL FAILED");
// sender.output(UpdatePageMsg::FailedWorking);
// }
// }
// });
// }
// UpdateAsyncHandlerMsg::UpdateChannelsAndSystem => {
// let systenconfig = self.systemconfig.clone();
// let flakeargs = self.flakeargs.clone();
// let syspkgs = self.syspkgs.clone();
// relm4::spawn(async move {
// let result = runcmd(NscCmd::All, systenconfig, flakeargs, syspkgs).await;
// match result {
// Ok(true) => {
// sender.output(UpdatePageMsg::DoneWorking);
// }
// _ => {
// warn!("UPDATE CHANNEL AND SYSTEM FAILED");
// sender.output(UpdatePageMsg::FailedWorking);
// }
// }
// });
// }
UpdateAsyncHandlerMsg::UpdateSystem => {
let systemconfig = self.systemconfig.clone();
let flakeargs = self.flakeargs.clone();
let syspkgs = self.syspkgs.clone();
relm4::spawn(async move {
let result = runcmd(NscCmd::All, systemconfig, flakeargs, syspkgs).await;
let result = runcmd(NscCmd::All, systemconfig, flakeargs, syspkgs, None).await;
match result {
Ok(true) => {
sender.output(UpdatePageMsg::DoneWorking);
}
_ => {
warn!("UPDATE SYSTEM FAILED");
sender.output(UpdatePageMsg::FailedWorking);
}
}
});
}
UpdateAsyncHandlerMsg::UpdateSystemRemove(pkgs) => {
let systemconfig = self.systemconfig.clone();
let flakeargs = self.flakeargs.clone();
let syspkgs = self.syspkgs.clone();
relm4::spawn(async move {
let result =
runcmd(NscCmd::All, systemconfig, flakeargs, syspkgs, Some(pkgs)).await;
match result {
Ok(true) => {
sender.output(UpdatePageMsg::DoneWorking);
@ -141,8 +127,12 @@ impl Worker for UpdateAsyncHandler {
let syspkgs = self.syspkgs.clone();
relm4::spawn(async move {
let result = match syspkgs {
SystemPkgs::Legacy => runcmd(NscCmd::Rebuild, systemconfig, flakeargs, syspkgs).await,
SystemPkgs::Flake => runcmd(NscCmd::All, systemconfig, flakeargs, syspkgs).await,
SystemPkgs::Legacy => {
runcmd(NscCmd::Rebuild, systemconfig, flakeargs, syspkgs, None).await
}
SystemPkgs::Flake => {
runcmd(NscCmd::All, systemconfig, flakeargs, syspkgs, None).await
}
SystemPkgs::None => Ok(true),
};
match result {
@ -161,7 +151,25 @@ impl Worker for UpdateAsyncHandler {
relm4::spawn(async move {
let result = match userpkgs {
UserPkgs::Env => updateenv().await,
UserPkgs::Profile => updateprofile().await,
UserPkgs::Profile => updateprofile(None).await,
};
match result {
Ok(true) => {
sender.output(UpdatePageMsg::DoneWorking);
}
_ => {
warn!("UPDATE USER FAILED");
sender.output(UpdatePageMsg::FailedWorking);
}
}
});
}
UpdateAsyncHandlerMsg::UpdateUserPkgsRemove(pkgs) => {
let userpkgs = self.userpkgs.clone();
relm4::spawn(async move {
let result = match userpkgs {
UserPkgs::Env => updateenv().await,
UserPkgs::Profile => updateprofile(Some(pkgs)).await,
};
match result {
Ok(true) => {
@ -180,12 +188,41 @@ impl Worker for UpdateAsyncHandler {
let syspkgs = self.syspkgs.clone();
let userpkgs = self.userpkgs.clone();
relm4::spawn(async move {
let result = runcmd(NscCmd::All, systemconfig, flakeargs, syspkgs).await;
let result = runcmd(NscCmd::All, systemconfig, flakeargs, syspkgs, None).await;
match result {
Ok(true) => {
match match userpkgs {
UserPkgs::Env => updateenv().await,
UserPkgs::Profile => updateprofile().await,
UserPkgs::Profile => updateprofile(None).await,
} {
Ok(true) => {
sender.output(UpdatePageMsg::DoneWorking);
}
_ => {
warn!("UPDATE ALL FAILED");
sender.output(UpdatePageMsg::FailedWorking);
}
}
}
_ => {
warn!("UPDATE ALL FAILED");
sender.output(UpdatePageMsg::FailedWorking);
}
}
});
}
UpdateAsyncHandlerMsg::UpdateAllRemove(userrmpkgs, sysrmpkgs) => {
let systemconfig = self.systemconfig.clone();
let flakeargs = self.flakeargs.clone();
let syspkgs = self.syspkgs.clone();
let userpkgs = self.userpkgs.clone();
relm4::spawn(async move {
let result = runcmd(NscCmd::All, systemconfig, flakeargs, syspkgs, Some(sysrmpkgs)).await;
match result {
Ok(true) => {
match match userpkgs {
UserPkgs::Env => updateenv().await,
UserPkgs::Profile => updateprofile(Some(userrmpkgs)).await,
} {
Ok(true) => {
sender.output(UpdatePageMsg::DoneWorking);
@ -209,10 +246,12 @@ impl Worker for UpdateAsyncHandler {
async fn runcmd(
cmd: NscCmd,
_systemconfig: Option<String>,
systemconfig: String,
flakeargs: Option<String>,
syspkgs: SystemPkgs,
rmpkgs: Option<Vec<String>>,
) -> Result<bool> {
let f = fs::read_to_string(&systemconfig)?;
let exe = match std::env::current_exe() {
Ok(mut e) => {
e.pop(); // root/bin
@ -231,7 +270,12 @@ async fn runcmd(
};
let flakepathsplit = flakeargs.clone().unwrap_or_default().to_string();
let flakepath = flakepathsplit.split('#').collect::<Vec<&str>>().first().cloned().unwrap_or_default();
let flakepath = flakepathsplit
.split('#')
.collect::<Vec<&str>>()
.first()
.cloned()
.unwrap_or_default();
let rebuildargs = if let Some(x) = flakeargs {
let mut v = vec![String::from("--flake")];
@ -260,27 +304,92 @@ async fn runcmd(
.stderr(Stdio::piped())
.spawn()?,
NscCmd::All => match syspkgs {
SystemPkgs::Legacy => tokio::process::Command::new("pkexec")
.arg(&exe)
.arg("channel")
.arg("--rebuild")
.arg("--")
.arg("switch")
.args(&rebuildargs)
.stderr(Stdio::piped())
.spawn()?,
SystemPkgs::Flake => tokio::process::Command::new("pkexec")
.arg(&exe)
.arg("flake")
.arg("--rebuild")
.arg("--flakepath")
.arg(flakepath)
.arg("--")
.arg("switch")
.arg("--impure")
.args(&rebuildargs)
.stderr(Stdio::piped())
.spawn()?,
SystemPkgs::Legacy => {
if let Some(rmpkgs) = rmpkgs {
let newconfig =
match nix_editor::write::rmarr(&f, "environment.systemPackages", rmpkgs) {
Ok(x) => x,
Err(_) => {
return Err(anyhow!("Failed to write configuration.nix"));
}
};
let mut cmd = tokio::process::Command::new("pkexec")
.arg(&exe)
.arg("channel")
.arg("--rebuild")
.arg("--update")
.arg("--output")
.arg(&systemconfig)
.arg("--")
.arg("switch")
.args(&rebuildargs)
.stderr(Stdio::piped())
.stdin(Stdio::piped())
.spawn()?;
cmd.stdin
.take()
.unwrap()
.write_all(newconfig.as_bytes())
.await?;
cmd
} else {
tokio::process::Command::new("pkexec")
.arg(&exe)
.arg("channel")
.arg("--rebuild")
.arg("--")
.arg("switch")
.args(&rebuildargs)
.stderr(Stdio::piped())
.spawn()?
}
}
SystemPkgs::Flake => {
if let Some(rmpkgs) = rmpkgs {
let newconfig =
match nix_editor::write::rmarr(&f, "environment.systemPackages", rmpkgs) {
Ok(x) => x,
Err(_) => {
return Err(anyhow!("Failed to write configuration.nix"));
}
};
let mut cmd = tokio::process::Command::new("pkexec")
.arg(&exe)
.arg("flake")
.arg("--rebuild")
.arg("--flakepath")
.arg(&flakepath)
.arg("--update")
.arg("--output")
.arg(&systemconfig)
.arg("--")
.arg("switch")
.arg("--impure")
.args(&rebuildargs)
.stderr(Stdio::piped())
.stdin(Stdio::piped())
.spawn()?;
cmd.stdin
.take()
.unwrap()
.write_all(newconfig.as_bytes())
.await?;
cmd
} else {
tokio::process::Command::new("pkexec")
.arg(&exe)
.arg("flake")
.arg("--rebuild")
.arg("--flakepath")
.arg(&flakepath)
.arg("--")
.arg("switch")
.arg("--impure")
.args(&rebuildargs)
.stderr(Stdio::piped())
.spawn()?
}
}
SystemPkgs::None => return Ok(true),
},
};
@ -321,7 +430,38 @@ async fn updateenv() -> Result<bool> {
}
}
async fn updateprofile() -> Result<bool> {
async fn updateprofile(rmpkgs: Option<Vec<String>>) -> Result<bool> {
if let Some(rmpkgs) = rmpkgs {
if !rmpkgs.is_empty() {
let mut cmd = tokio::process::Command::new("nix")
.arg("profile")
.arg("remove")
.args(
&rmpkgs
.iter()
.map(|x| format!(
"legacyPackages.x86_64-linux.{}",
x
))
.collect::<Vec<String>>()
)
// Allow updating potential unfree packages
.arg("--impure")
.stderr(Stdio::piped())
.spawn()?;
let stderr = cmd.stderr.take().unwrap();
let reader = tokio::io::BufReader::new(stderr);
let mut lines = reader.lines();
while let Ok(Some(line)) = lines.next_line().await {
REBUILD_BROKER.send(RebuildMsg::UpdateText(line.to_string()));
trace!("CAUGHT NIX PROFILE LINE: {}", line);
}
cmd.wait().await?;
}
}
let mut cmd = tokio::process::Command::new("nix")
.arg("profile")
.arg("upgrade")
@ -344,4 +484,4 @@ async fn updateprofile() -> Result<bool> {
} else {
Ok(false)
}
}
}

View File

@ -4,7 +4,7 @@ use crate::{
config::{editconfig, getconfig},
packages::{AppData, LicenseEnum, PkgMaintainer, Platform},
},
ui::{installedpage::InstalledItem, pkgpage::PkgPageInit, welcome::WelcomeMsg, rebuild::RebuildMsg},
ui::{installedpage::InstalledItem, pkgpage::PkgPageInit, welcome::WelcomeMsg, rebuild::RebuildMsg, updatepage::UNAVAILABLE_BROKER, unavailabledialog::UnavailableDialogMsg},
APPINFO,
};
use adw::prelude::*;
@ -30,9 +30,9 @@ use super::{
pkgtile::PkgTile,
preferencespage::{PreferencesPageModel, PreferencesPageMsg},
searchpage::{SearchItem, SearchPageModel, SearchPageMsg},
updatepage::{UpdateItem, UpdatePageInit, UpdatePageModel, UpdatePageMsg},
updatepage::{UpdateItem, UpdatePageInit, UpdatePageModel, UpdatePageMsg, UpdateType},
welcome::WelcomeModel,
windowloading::{LoadErrorModel, LoadErrorMsg, WindowAsyncHandler, WindowAsyncHandlerMsg}, rebuild::RebuildModel,
windowloading::{LoadErrorModel, LoadErrorMsg, WindowAsyncHandler, WindowAsyncHandlerMsg}, rebuild::RebuildModel, unavailabledialog::UnavailableItemModel,
};
pub static REBUILD_BROKER: MessageBroker<RebuildModel> = MessageBroker::new();
@ -152,6 +152,7 @@ pub enum AppMsg {
LoadCategory(PkgCategory),
UpdateRecPkgs(Vec<String>),
SetDarkMode(bool),
GetUnavailableItems(HashMap<String, String>, HashMap<String, String>, UpdateType),
}
#[derive(Debug, Clone, PartialEq, Eq)]
@ -1825,6 +1826,133 @@ FROM pkgs JOIN meta ON (pkgs.attribute = meta.attribute) WHERE pkgs.attribute =
let scheme = if dark { "Adwaita-dark" } else { "Adwaita" };
self.rebuild.emit(RebuildMsg::SetScheme(scheme.to_string()));
}
AppMsg::GetUnavailableItems(userpkgs, syspkgs, updatetype) => {
info!("AppMsg::GetUnavailableItems");
let appdata: HashMap<String, AppData> = self
.appdata
.iter()
.filter_map(|(k, v)| {
if syspkgs.contains_key(k) || userpkgs.contains_key(k) {
Some((k.to_string(), v.clone()))
} else {
None
}
})
.collect();
let poolref = self.pkgdb.clone();
relm4::spawn(async move {
let mut unavailableuser = vec![];
let mut unavailablesys = vec![];
if let Ok(pool) = &SqlitePool::connect(&format!("sqlite://{}", poolref)).await {
let mut sortuserpkgs = userpkgs.into_iter().collect::<Vec<_>>();
sortuserpkgs.sort();
for (pkg, msg) in sortuserpkgs {
if let Some(data) = appdata.get(&pkg) {
let pname: Result<(String,), sqlx::Error> =
sqlx::query_as("SELECT pname FROM pkgs WHERE attribute = $1")
.bind(&pkg)
.fetch_one(pool)
.await;
if let Ok(pname) = pname {
unavailableuser.push(UnavailableItemModel {
pkg: pkg.to_string(),
name: if let Some(name) = &data.name {
name.get("C").unwrap_or(&pname.0).to_string()
} else {
pname.0.to_string()
},
pname: pname.0.to_string(),
icon: data
.icon
.as_ref()
.and_then(|x| x.cached.as_ref())
.map(|x| x[0].name.clone()),
message: msg
})
} else {
unavailableuser.push(UnavailableItemModel {
pkg: pkg.to_string(),
name: if let Some(name) = &data.name {
name.get("C").unwrap_or(&pkg).to_string()
} else {
pkg.to_string()
},
pname: String::new(),
icon: data
.icon
.as_ref()
.and_then(|x| x.cached.as_ref())
.map(|x| x[0].name.clone()),
message: msg
})
}
} else {
unavailableuser.push(UnavailableItemModel {
pkg: pkg.to_string(),
name: pkg.to_string(),
pname: String::new(),
icon: None,
message: msg
})
}
}
let mut sortsyspkgs = syspkgs.into_iter().collect::<Vec<_>>();
sortsyspkgs.sort();
for (pkg, msg) in sortsyspkgs {
if let Some(data) = appdata.get(&pkg) {
let pname: Result<(String,), sqlx::Error> =
sqlx::query_as("SELECT pname FROM pkgs WHERE attribute = $1")
.bind(&pkg)
.fetch_one(pool)
.await;
if let Ok(pname) = pname {
unavailablesys.push(UnavailableItemModel {
pkg: pkg.to_string(),
name: if let Some(name) = &data.name {
name.get("C").unwrap_or(&pname.0).to_string()
} else {
pname.0.to_string()
},
pname: pname.0.to_string(),
icon: data
.icon
.as_ref()
.and_then(|x| x.cached.as_ref())
.map(|x| x[0].name.clone()),
message: msg
})
} else {
unavailablesys.push(UnavailableItemModel {
pkg: pkg.to_string(),
name: if let Some(name) = &data.name {
name.get("C").unwrap_or(&pkg).to_string()
} else {
pkg.to_string()
},
pname: String::new(),
icon: data
.icon
.as_ref()
.and_then(|x| x.cached.as_ref())
.map(|x| x[0].name.clone()),
message: msg
})
}
} else {
unavailablesys.push(UnavailableItemModel {
pkg: pkg.to_string(),
name: pkg.to_string(),
pname: String::new(),
icon: None,
message: msg
})
}
}
}
UNAVAILABLE_BROKER.send(UnavailableDialogMsg::Show(unavailableuser, unavailablesys, updatetype));
});
}
}
}