Merge pull request #17 from crystal-linux/development

v3.5.0 Development -> Main
i18n
Michal 2 years ago committed by GitHub
commit 683fc6bf3b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

49
Cargo.lock generated

@ -4,7 +4,7 @@ version = 3
[[package]]
name = "Amethyst"
version = "3.4.0"
version = "3.5.0"
dependencies = [
"clap",
"colored",
@ -14,6 +14,7 @@ dependencies = [
"regex",
"rm_rf",
"serde",
"spinoff",
"ureq",
]
@ -252,6 +253,12 @@ dependencies = [
"cfg-if",
]
[[package]]
name = "maplit"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d"
[[package]]
name = "matches"
version = "0.1.9"
@ -447,6 +454,12 @@ dependencies = [
"stacker",
]
[[package]]
name = "rustversion"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97477e48b4cf8603ad5f7aaf897467cf42ab4218a38ef76fb14c2d6773a6d6a8"
[[package]]
name = "ryu"
version = "1.0.11"
@ -517,6 +530,18 @@ dependencies = [
"serde",
]
[[package]]
name = "spinoff"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c139aa6a2b4ed01ef761dfd593eb5b02218dbf35a3a0f10940b72f5bfe70426"
dependencies = [
"colored",
"maplit",
"once_cell",
"strum",
]
[[package]]
name = "stacker"
version = "0.1.15"
@ -536,6 +561,28 @@ version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]]
name = "strum"
version = "0.24.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f"
dependencies = [
"strum_macros",
]
[[package]]
name = "strum_macros"
version = "0.24.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59"
dependencies = [
"heck",
"proc-macro2",
"quote",
"rustversion",
"syn",
]
[[package]]
name = "syn"
version = "1.0.99"

@ -1,6 +1,6 @@
[package]
name = "Amethyst"
version = "3.4.0"
version = "3.5.0"
authors = ["michal <michal@tar.black>", "axtlos <axtlos@tar.black>"]
edition = "2021"
description = "A fast and efficient AUR helper"
@ -26,4 +26,5 @@ ureq = { version = "2.4.0", default-features = false, features = [ "native-tls",
serde = { version = "1.0.138", default-features = false, features = [ "derive", "serde_derive" ] }
native-tls = { version = "0.2.10", default-features = false }
libc = { version = "0.2.126", default-features = false }
rm_rf = { version = "0.6.2", default-features = false }
rm_rf = { version = "0.6.2", default-features = false }
spinoff = { version = "0.5.3", default-features = false }

@ -3,7 +3,7 @@
# Developer: Michal S <michal[at]tar[dot]black>
pkgname=amethyst
pkgver=3.4.0
pkgver=3.5.0
pkgrel=1
pkgdesc="A fast and efficient AUR helper"
arch=('x86_64')

@ -66,21 +66,8 @@ Tested on latest Cargo (1.60.0-nightly)
### TODO:
#### v3.3.0
- ~~Add clean function~~
- ~~Make flags global~~
- ~~Add pacdiff function~~
- ~~Squash any remaining bugs~~
#### v3.4.0
- ~~New AUR upgrade checker~~
- ~~Bugfixes, bugfixes, bugfixes!~~~
#### v3.5.0
- Implement some sort of spinner for longer operations
#### v3.6.0
- Allow editing of PKGBUILDs before install
- Allow to choose provider of package
- Highlight optdepends at the end of an install operation
<!--

@ -15,7 +15,7 @@ pub struct Args {
pub no_confirm: bool,
/// Loops sudo in the background to ensure it doesn't time out during long builds
#[clap(long = "sudoloop")]
#[clap(long = "sudoloop", global = true)]
pub sudoloop: bool,
}
@ -37,18 +37,26 @@ pub enum Operation {
#[clap(name = "query", aliases = & ["q", "qu", "l", "ls", "-Q"])]
Query(QueryArgs),
/// Gets info about a package
#[clap(name = "info", aliases = & ["inf", "in", "i", "-Qi"])]
Info(InfoArgs),
/// Upgrades locally installed packages to their latest versions
#[clap(name = "upgrade", aliases = & ["upg", "up", "u", "-Syu"])]
Upgrade,
Upgrade(UpgradeArgs),
/// Removes all orphaned packages
#[clap(name = "clean", aliases = & ["cln", "cl", "-Sc"])]
Clean,
/// Runs pacdiff
#[clap(name = "diff", aliases = & ["dif", "di", "-d"])]
Diff,
}
impl Default for Operation {
fn default() -> Self {
Self::Upgrade
Self::Upgrade(UpgradeArgs::default())
}
}
@ -59,9 +67,11 @@ pub struct InstallArgs {
pub packages: Vec<String>,
/// Installs only from the AUR
#[clap(long, short)]
pub aur: bool,
/// Install the packages from the pacman-defined repositories
#[clap(long, short)]
pub repo: bool,
}
@ -97,3 +107,21 @@ pub struct QueryArgs {
#[clap(long, short, from_global)]
pub repo: bool,
}
#[derive(Default, Debug, Clone, Parser)]
pub struct InfoArgs {
/// The name of the package(s) to get info on
#[clap(required = true)]
pub package: String,
}
#[derive(Default, Debug, Clone, Parser)]
pub struct UpgradeArgs {
/// Upgrades only repo/native packages
#[clap(long, short)]
pub repo: bool,
/// Upgrades only from the AUR
#[clap(long, short)]
pub aur: bool,
}

@ -1,20 +1,23 @@
use std::env;
use crate::internal::commands::ShellCommand;
use crate::internal::error::SilentUnwrap;
use crate::internal::exit_code::AppExitCode;
use crate::{info, prompt, warn};
use crate::{prompt, spinner, warn};
pub fn detect() {
info!("Scanning for pacnew files");
// Start spinner
let sp = spinner!("Scanning for pacnew files");
let mut pacnew = vec![];
// Run `find` to find pacnew files and split by lines into a vec
let find = std::process::Command::new("sudo")
.arg("pacdiff")
.arg("-f")
.output()
.unwrap();
let find_lines = std::str::from_utf8(&find.stdout).unwrap().split('\n');
let find = ShellCommand::pacdiff()
.args(&["-o", "-f"])
.elevated()
.wait_with_output()
.silent_unwrap(AppExitCode::PacmanError);
let find_lines = find.stdout.split('\n');
for line in find_lines {
if !line.is_empty() {
pacnew.push(line.to_string());
@ -23,16 +26,28 @@ pub fn detect() {
// If pacnew files are found, warn the user and prompt to pacdiff
if !pacnew.is_empty() {
let choice = prompt!(default false, "It appears that at least one program you have installed / upgraded has installed a .pacnew/.pacsave config file. Would you like to run pacdiff to deal with this? You can always deal with this later by running `sudo pacdiff`");
sp.stop_bold("It appears that at least one program you have installed / upgraded has installed a .pacnew/.pacsave config file. These are created when you have modified a program's configuration, and a package upgrade could not automatically merge the new file.");
let choice = prompt!(default false, "Would you like to run pacdiff to deal with this? You can always deal with this later by running `sudo pacdiff`");
if choice {
warn!("Unless you've set an alternative using the DIFFPROG environment variable, pacdiff uses `vimdiff` by default to edit files for merging. Make sure you know how to exit vim before proceeding");
let cont = prompt!(default false, "Continue?");
if cont {
if env::var("PACDIFF_WARNING").unwrap_or_else(|_| "1".to_string()) != "0" {
warn!("Pacdiff uses vimdiff by default to edit files for merging. You can focus panes by mousing over them and pressing left click, and scroll up and down using your mouse's scroll wheel (or the arrow keys). To exit vimdiff, press the following key combination: ESC, :qa!, ENTER");
warn!("You can surpress this warning in the future by setting the `PACDIFF_WARNING` environment variable to `0`");
let cont = prompt!(default false, "Continue?");
if cont {
ShellCommand::pacdiff()
.elevated()
.wait()
.silent_unwrap(AppExitCode::PacmanError);
}
} else {
ShellCommand::pacdiff()
.elevated()
.wait()
.silent_unwrap(AppExitCode::PacmanError);
}
}
} else {
sp.stop_bold("No pacnew files found");
}
}

@ -8,20 +8,6 @@ pub fn init(options: Options) {
let verbosity = options.verbosity;
let homedir = env::var("HOME").unwrap();
// Initialise stateful directory
if !Path::new(&format!("{}/.local/share/ame", homedir)).exists() {
if verbosity >= 1 {
log!("Initialising stateful directory");
}
std::fs::create_dir_all(format!("{}/.local/share/ame", homedir)).unwrap_or_else(|e| {
crash!(
AppExitCode::FailedCreatingPaths,
"Failed to create stateful directory: {}",
e
);
});
}
// If cache path doesn't exist, create it, if it does, delete it and recreate it
if !Path::new(&format!("{}/.cache/ame/", homedir)).exists() {
if verbosity >= 1 {

@ -14,6 +14,9 @@ pub struct Package {
#[serde(rename = "MakeDepends")]
#[serde(default)]
pub make_depends: Vec<String>,
#[serde(rename = "OptDepends")]
#[serde(default)]
pub opt_depends: Vec<String>,
}
#[derive(serde::Deserialize)]

@ -50,6 +50,13 @@ macro_rules! prompt {
}
}
#[macro_export]
macro_rules! spinner {
($($arg:tt)+) => {
$crate::internal::utils::spinner_fn(format!($($arg)+))
}
}
pub fn log_info<S: ToString>(msg: S) {
let msg = msg.to_string();
let msg = if internal::uwu_enabled() {
@ -137,3 +144,40 @@ pub fn prompt_yn<S: ToString>(question: S, default_true: bool) -> bool {
default_true
}
}
pub struct Spinner {
spinner: spinoff::Spinner,
}
impl Spinner {
pub fn stop_bold(self, text: &str) {
let text = if internal::uwu_enabled() {
uwu!(text)
} else {
text.to_string()
};
let symbol = Box::new(format!("{}", OK_SYMBOL.purple()));
let text = Box::new(format!("{}", text.bold()));
let symbol: &'static str = Box::leak(symbol);
let text: &'static str = Box::leak(text);
self.spinner.stop_and_persist(symbol, text);
}
}
pub fn spinner_fn(text: String) -> Spinner {
let text = if internal::uwu_enabled() {
uwu!(&text)
} else {
text
};
Spinner {
spinner: spinoff::Spinner::new(
spinoff::Spinners::Line,
format!("{}", text.bold()),
spinoff::Color::Magenta,
),
}
}

@ -6,9 +6,11 @@ use clap::Parser;
use internal::commands::ShellCommand;
use internal::error::SilentUnwrap;
use crate::args::{InstallArgs, Operation, QueryArgs, RemoveArgs, SearchArgs};
use crate::args::{
InfoArgs, InstallArgs, Operation, QueryArgs, RemoveArgs, SearchArgs, UpgradeArgs,
};
use crate::internal::exit_code::AppExitCode;
use crate::internal::{init, sort, start_sudoloop, structs::Options};
use crate::internal::{detect, init, sort, start_sudoloop, structs::Options};
#[global_allocator]
static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc;
@ -51,14 +53,16 @@ fn main() {
Operation::Remove(remove_args) => cmd_remove(remove_args, options),
Operation::Search(search_args) => cmd_search(search_args, options),
Operation::Query(query_args) => cmd_query(query_args),
Operation::Upgrade => {
info!("Performing system upgrade");
operations::upgrade(options);
}
Operation::Info(info_args) => cmd_info(info_args),
Operation::Upgrade(upgrade_args) => cmd_upgrade(upgrade_args, options),
Operation::Clean => {
info!("Removing orphaned packages");
operations::clean(options);
}
Operation::Diff => {
info!("Running pacdiff");
detect();
}
}
}
@ -80,11 +84,45 @@ fn cmd_install(args: InstallArgs, options: Options) {
if !sorted.repo.is_empty() {
// If repo packages found, install them
operations::install(sorted.repo, options);
operations::install(sorted.repo.clone(), options);
}
if !sorted.aur.is_empty() {
// If AUR packages found, install them
operations::aur_install(sorted.aur, options);
operations::aur_install(sorted.aur.clone(), options);
}
// Show optional dependencies for installed packages
info!("Showing optional dependencies for installed packages");
for r in sorted.repo {
info!("{}:", r);
std::process::Command::new("expac")
.args(&["-S", "-l", "\n ", " %O", &r])
.spawn()
.unwrap()
.wait()
.unwrap();
}
for a in sorted.aur {
info!("{}:", a);
let dir_bytes = std::process::Command::new("mktemp")
.arg("-d")
.output()
.unwrap()
.stdout;
let dir = String::from_utf8(dir_bytes).unwrap();
std::process::Command::new("bash")
.arg("-c")
.arg(format!("\
cd {}
curl -L https://aur.archlinux.org/cgit/aur.git/plain/PKGBUILD?h={} -o PKGBUILD -s
source PKGBUILD
printf ' %s\\n' \"${{optdepends[@]}}\"
", dir, a))
.spawn()
.unwrap()
.wait()
.unwrap();
std::fs::remove_dir_all(&std::path::Path::new(&dir.trim())).unwrap();
}
}
@ -150,3 +188,16 @@ fn cmd_query(args: QueryArgs) {
.silent_unwrap(AppExitCode::PacmanError);
}
}
fn cmd_info(args: InfoArgs) {
ShellCommand::pacman()
.arg("-Qi")
.arg(args.package)
.wait()
.silent_unwrap(AppExitCode::PacmanError);
}
fn cmd_upgrade(args: UpgradeArgs, options: Options) {
info!("Performing system upgrade");
operations::upgrade(options, args);
}

@ -8,7 +8,7 @@ use crate::internal::commands::ShellCommand;
use crate::internal::error::SilentUnwrap;
use crate::internal::exit_code::AppExitCode;
use crate::internal::rpc::rpcinfo;
use crate::{crash, info, log, prompt, Options};
use crate::{crash, info, log, prompt, warn, Options};
pub fn aur_install(a: Vec<String>, options: Options) {
// Initialise variables
@ -23,6 +23,8 @@ pub fn aur_install(a: Vec<String>, options: Options) {
info!("Installing packages {} from the AUR", a.join(", "));
let mut failed = vec![];
for package in a {
// Query AUR for package info
let rpcres = rpcinfo(package);
@ -67,10 +69,11 @@ pub fn aur_install(a: Vec<String>, options: Options) {
}
// Sort dependencies and makedepends
log!("Sorting dependencies");
let sorted = crate::internal::sort(&rpcres.package.as_ref().unwrap().depends, options);
log!("Sorting make dependencies");
let md_sorted =
if verbosity >= 1 {
log!("Sorting dependencies and makedepends");
}
let mut sorted = crate::internal::sort(&rpcres.package.as_ref().unwrap().depends, options);
let mut md_sorted =
crate::internal::sort(&rpcres.package.as_ref().unwrap().make_depends, options);
if verbosity >= 1 {
@ -85,6 +88,29 @@ pub fn aur_install(a: Vec<String>, options: Options) {
asdeps: true,
};
// Get a list of installed packages
let installed = ShellCommand::pacman()
.elevated()
.args(&["-Qq"])
.wait_with_output()
.silent_unwrap(AppExitCode::PacmanError)
.stdout
.split_whitespace()
.collect::<Vec<&str>>()
.iter()
.map(|s| s.to_string())
.collect::<Vec<String>>();
// Remove installed packages from sorted dependencies and makedepends
if verbosity >= 1 {
log!("Removing installed packages from sorted dependencies and makedepends");
}
sorted.aur.retain(|x| !installed.contains(x));
sorted.repo.retain(|x| !installed.contains(x));
md_sorted.aur.retain(|x| !installed.contains(x));
md_sorted.repo.retain(|x| !installed.contains(x));
// If dependencies are not found in AUR or repos, crash
if !sorted.nf.is_empty() || !md_sorted.nf.is_empty() {
crash!(
@ -143,15 +169,19 @@ pub fn aur_install(a: Vec<String>, options: Options) {
// Install dependencies and makedepends
if !sorted.repo.is_empty() {
crate::operations::install(sorted.repo, newopts);
crate::operations::install(md_sorted.repo, newopts);
}
if !sorted.aur.is_empty() {
crate::operations::aur_install(sorted.aur, newopts);
}
if !md_sorted.repo.is_empty() {
crate::operations::install(md_sorted.repo, newopts);
}
if !md_sorted.aur.is_empty() {
crate::operations::aur_install(md_sorted.aur, newopts);
}
// Build makepkg args
let mut makepkg_args = vec!["-rsci", "--skippgp"];
let mut makepkg_args = vec!["-rsci", "--skippgp", "--needed"];
if options.asdeps {
makepkg_args.push("--asdeps")
}
@ -169,13 +199,10 @@ pub fn aur_install(a: Vec<String>, options: Options) {
.silent_unwrap(AppExitCode::MakePkgError);
if !status.success() {
// If build failed, crash
// If build failed, push to failed vec
fs::remove_dir_all(format!("{}/{}", cachedir, pkg)).unwrap();
crash!(
AppExitCode::PacmanError,
"Error encountered while installing {}, aborting",
pkg,
);
failed.push(pkg.clone());
return;
}
// Return to cachedir
@ -184,4 +211,9 @@ pub fn aur_install(a: Vec<String>, options: Options) {
// Remove package from cache
remove_dir_all(format!("{}/{}", cachedir, &pkg)).unwrap();
}
// If any packages failed to build, warn user with failed packages
if !failed.is_empty() {
warn!("Failed to build packages {}", failed.join(", "));
}
}

@ -1,10 +1,11 @@
use crate::args::UpgradeArgs;
use crate::internal::commands::ShellCommand;
use crate::internal::detect;
use crate::internal::error::SilentUnwrap;
use crate::internal::exit_code::AppExitCode;
use crate::internal::rpc::rpcinfo;
use crate::operations::aur_install::aur_install;
use crate::{info, log, prompt, Options};
use crate::{info, log, prompt, spinner, warn, Options};
#[derive(Debug)]
struct QueriedPackage {
@ -12,36 +13,49 @@ struct QueriedPackage {
pub version: String,
}
pub fn upgrade(options: Options) {
pub fn upgrade(options: Options, args: UpgradeArgs) {
// Initialise variables
let verbosity = options.verbosity;
let noconfirm = options.noconfirm;
// Build pacman args
let mut pacman_args = vec!["-Syu"];
if noconfirm {
pacman_args.push("--noconfirm");
}
let args = if !args.aur && !args.repo {
UpgradeArgs {
aur: true,
repo: true,
}
} else {
args
};
if args.repo {
// Build pacman args
let mut pacman_args = vec!["-Syu"];
if noconfirm {
pacman_args.push("--noconfirm");
}
if verbosity >= 1 {
log!("Upgrading repo packages");
}
if verbosity >= 1 {
log!("Upgrading repo packages");
}
// Upgrade repo packages
let pacman_result = ShellCommand::pacman()
.elevated()
.args(pacman_args)
.wait()
.silent_unwrap(AppExitCode::PacmanError);
// Upgrade repo packages
let pacman_result = ShellCommand::pacman()
.elevated()
.args(pacman_args)
.wait()
.silent_unwrap(AppExitCode::PacmanError);
if pacman_result.success() {
// If pacman was successful, notify user
info!("Successfully upgraded repo packages");
} else {
// Otherwise warn user
warn!("Failed to upgrade repo packages.",);
}
}
if pacman_result.success() {
// If pacman was successful, notify user
info!("Successfully upgraded repo packages");
} else {
// Otherwise, prompt user whether to continue
let cont = prompt!(default false,
"Failed to upgrade repo packages, continue to upgrading AUR packages?",
);
if args.repo && args.aur {
let cont = prompt!(default false, "Continue to upgrade AUR packages?");
if !cont {
// If user doesn't want to continue, break
info!("Exiting");
@ -49,69 +63,76 @@ pub fn upgrade(options: Options) {
}
}
if verbosity >= 1 {
log!("Checking AUR upgrades...");
}
// List non-native packages using `pacman -Qm` and collect to a Vec<String>
let non_native = ShellCommand::pacman()
.arg("-Qm")
.args(&["--color", "never"])
.wait_with_output()
.silent_unwrap(AppExitCode::PacmanError);
// Collect by lines to a Vec<String>
let mut non_native = non_native.stdout.split('\n').collect::<Vec<&str>>();
// Remove last element, which is an empty line
non_native.pop();
// Parse non-native packages into a Vec<QueriedPackage>
let mut parsed_non_native: Vec<QueriedPackage> = vec![];
for pkg in non_native {
// Split by space
let split = pkg.split(' ').collect::<Vec<&str>>();
if args.aur {
if verbosity >= 1 {
log!("{:?}", split);
log!("Checking AUR upgrades...");
}
// Create QueriedPackage and push it to parsed_non_native
let name = split[0].to_string();
let version = split[1].to_string();
parsed_non_native.push(QueriedPackage { name, version });
}
if verbosity >= 1 {
log!("{:?}", &parsed_non_native);
}
// Check if AUR package versions are the same as installed
let mut aur_upgrades = vec![];
for pkg in parsed_non_native {
// Query AUR
let rpc_result = rpcinfo((&*pkg.name).to_string());
// Start spinner
let sp = spinner!("Checking AUR upgrades...");
// List non-native packages using `pacman -Qm` and collect to a Vec<String>
let non_native = ShellCommand::pacman()
.arg("-Qm")
.args(&["--color", "never"])
.wait_with_output()
.silent_unwrap(AppExitCode::PacmanError);
// Collect by lines to a Vec<String>
let mut non_native = non_native.stdout.split('\n').collect::<Vec<&str>>();
// Remove last element, which is an empty line
non_native.pop();
// Parse non-native packages into a Vec<QueriedPackage>
let mut parsed_non_native: Vec<QueriedPackage> = vec![];
for pkg in non_native {
// Split by space
let split = pkg.split(' ').collect::<Vec<&str>>();
if verbosity >= 1 {
log!("{:?}", split);
}
// Create QueriedPackage and push it to parsed_non_native
let name = split[0].to_string();
let version = split[1].to_string();
parsed_non_native.push(QueriedPackage { name, version });
}
if !rpc_result.found {
// If package not found, skip
continue;
if verbosity >= 1 {
log!("{:?}", &parsed_non_native);
}
// If versions differ, push to a vector
if rpc_result.package.unwrap().version != pkg.version {
aur_upgrades.push(pkg.name);
// Check if AUR package versions are the same as installed
let mut aur_upgrades = vec![];
for pkg in parsed_non_native {
// Query AUR
let rpc_result = rpcinfo((&*pkg.name).to_string());
if !rpc_result.found {
// If package not found, skip
continue;
}
// If versions differ, push to a vector
if rpc_result.package.unwrap().version != pkg.version {
aur_upgrades.push(pkg.name);
}
}
}
// If vector isn't empty, prompt to install AUR packages from vector, effectively upgrading
if !aur_upgrades.is_empty() {
let cont = prompt!(default false,
"Found AUR packages {} have new versions available, upgrade?",
aur_upgrades.join(", "),
);
if cont {
aur_install(aur_upgrades, options);
};
} else {
info!("No upgrades available for installed AUR packages");
sp.stop_bold("Finished!");
// If vector isn't empty, prompt to install AUR packages from vector, effectively upgrading
if !aur_upgrades.is_empty() {
let cont = prompt!(default true,
"Found AUR packages {} have new versions available, upgrade?",
aur_upgrades.join(", "),
);
if cont {
aur_install(aur_upgrades, options);
};
} else {
info!("No upgrades available for installed AUR packages");
}
}
// Check for .pacnew files

Loading…
Cancel
Save