fix: duplicate packages in installation

Signed-off-by: sisungo <[email protected]>
This commit is contained in:
2026-06-26 18:12:50 +08:00
parent 4f0f8e9320
commit 8a029d2213
11 changed files with 92 additions and 16 deletions
+1
View File
@@ -26,6 +26,7 @@ tar = "0.4"
tempfile = "3"
thiserror = "2"
toml = "1"
itertools = "0.15"
ureq = { version = "3", features = ["socks-proxy"] }
url = { version = "2", features = ["serde"] }
nix = { version = "0.31", features = ["fs"] }
+2
View File
@@ -11,6 +11,8 @@ path = "src/main.rs"
anyhow = "1"
clap = { version = "4", features = ["derive"] }
console = "0.16"
nix = { version = "0.31", features = ["sched"] }
itertools = "0.15"
packie = { path = "../" }
rust-i18n = "4"
indicatif = "0.18"
+2 -2
View File
@@ -14,11 +14,11 @@ pub fn yesno(prompt: &str) -> bool {
}
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;
for i in list {
let mut i = i.as_ref().to_string();
if line_used >= column as _ {
if line_used >= row as _ {
line_used = 0;
println!();
}
+4 -4
View File
@@ -1,8 +1,8 @@
use crate::common::format_size;
use anyhow::anyhow;
use clap::Parser;
use packie::{PackieBuilder, package::PkgSpec};
use crate::common::format_size;
use rust_i18n::t;
#[derive(Debug, Parser)]
pub struct Cli {
@@ -13,10 +13,10 @@ pub fn main(cli: Cli) -> anyhow::Result<()> {
let mut packie = PackieBuilder::new().readonly(true).build()?;
let mut found = packie.search_installation(&cli.pkgspec)?;
if found.len() == 0 {
return Err(anyhow!("no package found"));
return Err(anyhow!("{}", t!("info.no_package_found")));
}
if found.len() != 1 {
return Err(anyhow!("not unique package spec"));
return Err(anyhow!("{}", t!("info.pkgspec_not_unique")));
}
let found = found.remove(0);
+5 -1
View File
@@ -1,6 +1,7 @@
use crate::common::{format_size, print_list, yesno};
use clap::Parser;
use indicatif::ProgressBar;
use itertools::Itertools;
use packie::{
PackieBuilder, install::InstallOptions, package::PkgSpec, repo::DownloadPackageEvent,
};
@@ -30,7 +31,10 @@ pub fn main(cli: Cli) -> anyhow::Result<()> {
for i in cli.items {
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() {
eprintln!("No packages to install.");
+10 -1
View File
@@ -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<()> {
std::os::unix::fs::chroot(new_root)?;
std::env::set_current_dir("/")?;
+31 -4
View File
@@ -1,18 +1,45 @@
use crate::common::{print_list, yesno};
use anyhow::anyhow;
use clap::Parser;
use packie::{PackieBuilder, package::PkgSpec};
use rust_i18n::t;
#[derive(Debug, Parser)]
pub struct Cli {
pkgspecs: Vec<PkgSpec>,
#[arg(short, long)]
yes: bool,
items: Vec<PkgSpec>,
}
pub fn main(cli: Cli) -> anyhow::Result<()> {
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
.remove(&pkgspec)
.map_err(|err| anyhow!("failed to remove {pkgspec}: {err}"))?;
.remove(&i.pkg_manifest.pkg_ident().into())
.map_err(|error| anyhow!("{}", t!("remove.action_failure", error = error)))?;
}
Ok(())
}
+27
View File
@@ -17,3 +17,30 @@ install.confirm_installation:
install.aborting:
en: "Aborting."
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} ..."
+1
View File
@@ -8,6 +8,7 @@ use std::path::Path;
#[derive(Debug, Default, Clone)]
pub struct InstallOptions {
/// Package spec to record for this installation.
pub install_pkgspec: Option<PkgSpec>,
}
impl InstallOptions {
+5 -2
View File
@@ -2,6 +2,7 @@ use crate::{
package::{AbsPkgIdent, PkgIdent, PkgManifest, PkgSpec},
version::VersionFilter,
};
use itertools::Itertools;
use rusqlite::{OpenFlags, Row, params};
use std::path::{Path, PathBuf};
@@ -27,8 +28,10 @@ impl super::Packie {
for (_, provider) in abs {
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
View File
@@ -4,6 +4,7 @@ mod serve;
#[doc(inline)]
pub use download::DownloadError;
use itertools::Itertools;
use rustc_hash::FxHashMap;
#[doc(inline)]
pub use serve::RepoServeDir;
@@ -389,8 +390,9 @@ impl QuerySession {
all.push((name.into(), real));
}
}
all.dedup_by_key(|(repo, pkg)| (repo.to_string(), pkg.pkg_manifest.pkg_ident()));
all
all.into_iter()
.unique_by(|(repo, pkg)| (repo.to_string(), pkg.pkg_manifest.pkg_ident()))
.collect()
}
}