fix: duplicate packages in installation
Signed-off-by: sisungo <[email protected]>
This commit is contained in:
@@ -26,6 +26,7 @@ tar = "0.4"
|
|||||||
tempfile = "3"
|
tempfile = "3"
|
||||||
thiserror = "2"
|
thiserror = "2"
|
||||||
toml = "1"
|
toml = "1"
|
||||||
|
itertools = "0.15"
|
||||||
ureq = { version = "3", features = ["socks-proxy"] }
|
ureq = { version = "3", features = ["socks-proxy"] }
|
||||||
url = { version = "2", features = ["serde"] }
|
url = { version = "2", features = ["serde"] }
|
||||||
nix = { version = "0.31", features = ["fs"] }
|
nix = { version = "0.31", features = ["fs"] }
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ path = "src/main.rs"
|
|||||||
anyhow = "1"
|
anyhow = "1"
|
||||||
clap = { version = "4", features = ["derive"] }
|
clap = { version = "4", features = ["derive"] }
|
||||||
console = "0.16"
|
console = "0.16"
|
||||||
|
nix = { version = "0.31", features = ["sched"] }
|
||||||
|
itertools = "0.15"
|
||||||
packie = { path = "../" }
|
packie = { path = "../" }
|
||||||
rust-i18n = "4"
|
rust-i18n = "4"
|
||||||
indicatif = "0.18"
|
indicatif = "0.18"
|
||||||
|
|||||||
+2
-2
@@ -14,11 +14,11 @@ pub fn yesno(prompt: &str) -> bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn print_list(indent: u32, list: impl Iterator<Item = impl AsRef<str>>) {
|
pub fn print_list(indent: u32, list: impl Iterator<Item = impl AsRef<str>>) {
|
||||||
let (_, column) = Term::stdout().size();
|
let (row, _) = Term::stdout().size();
|
||||||
let mut line_used: usize = 0;
|
let mut line_used: usize = 0;
|
||||||
for i in list {
|
for i in list {
|
||||||
let mut i = i.as_ref().to_string();
|
let mut i = i.as_ref().to_string();
|
||||||
if line_used >= column as _ {
|
if line_used >= row as _ {
|
||||||
line_used = 0;
|
line_used = 0;
|
||||||
println!();
|
println!();
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-4
@@ -1,8 +1,8 @@
|
|||||||
|
use crate::common::format_size;
|
||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use packie::{PackieBuilder, package::PkgSpec};
|
use packie::{PackieBuilder, package::PkgSpec};
|
||||||
|
use rust_i18n::t;
|
||||||
use crate::common::format_size;
|
|
||||||
|
|
||||||
#[derive(Debug, Parser)]
|
#[derive(Debug, Parser)]
|
||||||
pub struct Cli {
|
pub struct Cli {
|
||||||
@@ -13,10 +13,10 @@ pub fn main(cli: Cli) -> anyhow::Result<()> {
|
|||||||
let mut packie = PackieBuilder::new().readonly(true).build()?;
|
let mut packie = PackieBuilder::new().readonly(true).build()?;
|
||||||
let mut found = packie.search_installation(&cli.pkgspec)?;
|
let mut found = packie.search_installation(&cli.pkgspec)?;
|
||||||
if found.len() == 0 {
|
if found.len() == 0 {
|
||||||
return Err(anyhow!("no package found"));
|
return Err(anyhow!("{}", t!("info.no_package_found")));
|
||||||
}
|
}
|
||||||
if found.len() != 1 {
|
if found.len() != 1 {
|
||||||
return Err(anyhow!("not unique package spec"));
|
return Err(anyhow!("{}", t!("info.pkgspec_not_unique")));
|
||||||
}
|
}
|
||||||
let found = found.remove(0);
|
let found = found.remove(0);
|
||||||
|
|
||||||
|
|||||||
+5
-1
@@ -1,6 +1,7 @@
|
|||||||
use crate::common::{format_size, print_list, yesno};
|
use crate::common::{format_size, print_list, yesno};
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use indicatif::ProgressBar;
|
use indicatif::ProgressBar;
|
||||||
|
use itertools::Itertools;
|
||||||
use packie::{
|
use packie::{
|
||||||
PackieBuilder, install::InstallOptions, package::PkgSpec, repo::DownloadPackageEvent,
|
PackieBuilder, install::InstallOptions, package::PkgSpec, repo::DownloadPackageEvent,
|
||||||
};
|
};
|
||||||
@@ -30,7 +31,10 @@ pub fn main(cli: Cli) -> anyhow::Result<()> {
|
|||||||
for i in cli.items {
|
for i in cli.items {
|
||||||
packages.append(&mut packie.calculate_depgraph(i.parse::<PkgSpec>()?)?);
|
packages.append(&mut packie.calculate_depgraph(i.parse::<PkgSpec>()?)?);
|
||||||
}
|
}
|
||||||
packages.dedup_by_key(|x| x.repo_pkg.pkg_manifest.pkg_ident());
|
let packages: Vec<_> = packages
|
||||||
|
.into_iter()
|
||||||
|
.unique_by(|x| x.repo_pkg.pkg_manifest.pkg_ident())
|
||||||
|
.collect();
|
||||||
|
|
||||||
if packages.is_empty() {
|
if packages.is_empty() {
|
||||||
eprintln!("No packages to install.");
|
eprintln!("No packages to install.");
|
||||||
|
|||||||
+10
-1
@@ -74,7 +74,16 @@ fn main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_family = "unix")]
|
#[cfg(target_os = "linux")]
|
||||||
|
fn chroot(new_root: &Path) -> std::io::Result<()> {
|
||||||
|
nix::sched::unshare(nix::sched::CloneFlags::CLONE_NEWUSER)?;
|
||||||
|
nix::sched::unshare(nix::sched::CloneFlags::CLONE_NEWNS)?;
|
||||||
|
std::os::unix::fs::chroot(new_root)?;
|
||||||
|
std::env::set_current_dir("/")?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(all(target_family = "unix", not(target_os = "linux")))]
|
||||||
fn chroot(new_root: &Path) -> std::io::Result<()> {
|
fn chroot(new_root: &Path) -> std::io::Result<()> {
|
||||||
std::os::unix::fs::chroot(new_root)?;
|
std::os::unix::fs::chroot(new_root)?;
|
||||||
std::env::set_current_dir("/")?;
|
std::env::set_current_dir("/")?;
|
||||||
|
|||||||
+31
-4
@@ -1,18 +1,45 @@
|
|||||||
|
use crate::common::{print_list, yesno};
|
||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use packie::{PackieBuilder, package::PkgSpec};
|
use packie::{PackieBuilder, package::PkgSpec};
|
||||||
|
use rust_i18n::t;
|
||||||
|
|
||||||
#[derive(Debug, Parser)]
|
#[derive(Debug, Parser)]
|
||||||
pub struct Cli {
|
pub struct Cli {
|
||||||
pkgspecs: Vec<PkgSpec>,
|
#[arg(short, long)]
|
||||||
|
yes: bool,
|
||||||
|
|
||||||
|
items: Vec<PkgSpec>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn main(cli: Cli) -> anyhow::Result<()> {
|
pub fn main(cli: Cli) -> anyhow::Result<()> {
|
||||||
let mut packie = PackieBuilder::new().build()?;
|
let mut packie = PackieBuilder::new().build()?;
|
||||||
for pkgspec in cli.pkgspecs {
|
let mut packages = Vec::with_capacity(cli.items.len());
|
||||||
|
for pkgspec in cli.items {
|
||||||
|
let mut found = packie.search_installation(&pkgspec)?;
|
||||||
|
if found.is_empty() {
|
||||||
|
return Err(anyhow!("{}", t!("remove.package_not_found")));
|
||||||
|
}
|
||||||
|
if found.len() != 1 {
|
||||||
|
return Err(anyhow!("{}", t!("remove.pkgspec_not_unique")));
|
||||||
|
}
|
||||||
|
packages.push(found.remove(0));
|
||||||
|
}
|
||||||
|
println!("{}", t!("remove.prompt_to_remove"));
|
||||||
|
print_list(4, packages.iter().map(|x| x.install_pkgspec.to_string()));
|
||||||
|
if cli.yes {
|
||||||
|
println!("y");
|
||||||
|
} else if !yesno(&t!("remove.confirm_removal")) {
|
||||||
|
println!("{}", t!("remove.aborting"));
|
||||||
|
}
|
||||||
|
for i in packages {
|
||||||
|
println!(
|
||||||
|
"{}",
|
||||||
|
t!("remove.action_hint", package = i.pkg_manifest.pkg_ident())
|
||||||
|
);
|
||||||
packie
|
packie
|
||||||
.remove(&pkgspec)
|
.remove(&i.pkg_manifest.pkg_ident().into())
|
||||||
.map_err(|err| anyhow!("failed to remove {pkgspec}: {err}"))?;
|
.map_err(|error| anyhow!("{}", t!("remove.action_failure", error = error)))?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,3 +17,30 @@ install.confirm_installation:
|
|||||||
install.aborting:
|
install.aborting:
|
||||||
en: "Aborting."
|
en: "Aborting."
|
||||||
zh-CN: "中止。"
|
zh-CN: "中止。"
|
||||||
|
info.no_package_found:
|
||||||
|
en: "The specified package was not found."
|
||||||
|
zh-CN: "找不到指定的包。"
|
||||||
|
info.pkgspec_not_unique:
|
||||||
|
en: "The specified package spec was not unique."
|
||||||
|
zh-CN: "指定的包规范匹配了多个包。"
|
||||||
|
remove.package_not_found:
|
||||||
|
en: "The specified package was not found."
|
||||||
|
zh-CN: "找不到指定的包。"
|
||||||
|
remove.pkgspec_not_unique:
|
||||||
|
en: "The specified package spec was not unique."
|
||||||
|
zh-CN: "指定的包规范匹配了多个包。"
|
||||||
|
remove.action_failure:
|
||||||
|
en: "Failed to remove the specified package: %{error}"
|
||||||
|
zh-CN: "无法移除指定的包:%{error}"
|
||||||
|
remove.prompt_to_remove:
|
||||||
|
en: "The following packages are to be removed:"
|
||||||
|
zh-CN: "将要移除下面的包:"
|
||||||
|
remove.confirm_removal:
|
||||||
|
en: "Confirm to remove?"
|
||||||
|
zh-CN: "确认移除?"
|
||||||
|
remove.aborting:
|
||||||
|
en: "Aborting."
|
||||||
|
zh-CN: "中止。"
|
||||||
|
remove.action_hint:
|
||||||
|
en: "Removing package %{package} ..."
|
||||||
|
zh-CN: "正在移除包 %{package} ..."
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ use std::path::Path;
|
|||||||
|
|
||||||
#[derive(Debug, Default, Clone)]
|
#[derive(Debug, Default, Clone)]
|
||||||
pub struct InstallOptions {
|
pub struct InstallOptions {
|
||||||
|
/// Package spec to record for this installation.
|
||||||
pub install_pkgspec: Option<PkgSpec>,
|
pub install_pkgspec: Option<PkgSpec>,
|
||||||
}
|
}
|
||||||
impl InstallOptions {
|
impl InstallOptions {
|
||||||
|
|||||||
+5
-2
@@ -2,6 +2,7 @@ use crate::{
|
|||||||
package::{AbsPkgIdent, PkgIdent, PkgManifest, PkgSpec},
|
package::{AbsPkgIdent, PkgIdent, PkgManifest, PkgSpec},
|
||||||
version::VersionFilter,
|
version::VersionFilter,
|
||||||
};
|
};
|
||||||
|
use itertools::Itertools;
|
||||||
use rusqlite::{OpenFlags, Row, params};
|
use rusqlite::{OpenFlags, Row, params};
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
@@ -27,8 +28,10 @@ impl super::Packie {
|
|||||||
for (_, provider) in abs {
|
for (_, provider) in abs {
|
||||||
real.push(self.local_db.select_installation_by_ident(&provider)?);
|
real.push(self.local_db.select_installation_by_ident(&provider)?);
|
||||||
}
|
}
|
||||||
real.dedup_by_key(|x| x.pkg_manifest.pkg_ident());
|
Ok(real
|
||||||
Ok(real)
|
.into_iter()
|
||||||
|
.unique_by(|x| x.pkg_manifest.pkg_ident())
|
||||||
|
.collect())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+4
-2
@@ -4,6 +4,7 @@ mod serve;
|
|||||||
#[doc(inline)]
|
#[doc(inline)]
|
||||||
pub use download::DownloadError;
|
pub use download::DownloadError;
|
||||||
|
|
||||||
|
use itertools::Itertools;
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
#[doc(inline)]
|
#[doc(inline)]
|
||||||
pub use serve::RepoServeDir;
|
pub use serve::RepoServeDir;
|
||||||
@@ -389,8 +390,9 @@ impl QuerySession {
|
|||||||
all.push((name.into(), real));
|
all.push((name.into(), real));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
all.dedup_by_key(|(repo, pkg)| (repo.to_string(), pkg.pkg_manifest.pkg_ident()));
|
all.into_iter()
|
||||||
all
|
.unique_by(|(repo, pkg)| (repo.to_string(), pkg.pkg_manifest.pkg_ident()))
|
||||||
|
.collect()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user