Repository: comtrya/comtrya (language: rust)

Issue:
yay/pacman provider - should be able to filter packages with `query`
Hi, 

I recently moved my dotfiles to comtrya and I'm using the yay/pacman provider to provision packages.

Important bit of information is I don't just use the setup to bootstrap on a new machine, but I use it to change things too. So for example if I find a new useful tool (like `s-tui`) I will not manually install the package but instead put it in my manifest and run comtrya. This way, my manifest is always up-to-date with what I want.

I noticed that the yay cmdline does not set the `--needed` flag which results in very long manifest execution because it reinstalls all pacman packages. Would it be ok to add the flag or is there a reason it's not included?

Note: this flag does not work for AUR packages, unfortunately: https://github.com/Jguer/yay/issues/885
But it does work for regular packages. Here's an example (first without --neeed and then with):

```bash
» yay -S --noconfirm --nocleanmenu --nodiffmenu acpi
warning: acpi-1.7-3 is up to date -- reinstalling
resolving dependencies...
looking for conflicting packages...

Packages (1) acpi-1.7-3

Total Installed Size:  0.03 MiB
Net Upgrade Size:      0.00 MiB

:: Proceed with installation? [Y/n]
(1/1) checking keys in keyring                                                                                      [#####################################################################] 100%
(1/1) checking package integrity                                                                                    [#####################################################################] 100%
(1/1) loading package files                                                                                         [#####################################################################] 100%
(1/1) checking for file conflicts                                                                                   [#####################################################################] 100%
(1/1) checking available disk space                                                                                 [#####################################################################] 100%
:: Processing package changes...
(1/1) reinstalling acpi                                                                                             [#####################################################################] 100%
:: Running post-transaction hooks...
(1/1) Arming ConditionNeedsUpdate...

» yay -S --noconfirm --nocleanmenu --nodiffmenu acpi --needed
warning: acpi-1.7-3 is up to date -- skipping
 there is nothing to do
```


For AUR packages I have a hacky solution with koto where I write to a file so the installation is only performed the first time.


Discussion / hints:
Hey @mriehl,

Thanks for the detailed issue. I also use Comtrya in this way 😃

There's a few things to this:

First, as a quick workaround you can add `extra_args: --needed` to your `package.install` actions, but this will require you to ensure you keep your Arch and AUR dependencies in separate actions.

Second, I had this problem with the `brew` provider too! It was __really__ slow. Each `PackageProvider` has a `query` function which can reduce over the package list to find uninstalled packages - this hasn't been implemented yet for Pacman/yay.

It's been a while since I've ran Arch on my machine, but I seem to remember `pacman -Q` would allow me to query the index of installed packages. Is there a command to explicitly check if a package is already installed?

Lastly, thanks for using Comtrya and for opening an issue. There's been a few breaking changes lately - so I hope this didn't disrupt your workflow.

- #134 
- #122 
Thanks for the fantastic reply!
Looks like I missed a lot from checking the code and reading the docs, I'll use this new knowledge asap :+1: 
I might also send some PRs for the gitbook docs.

For checking installed packages yes `pacman -Q` will exit with 0 if the package is installed and with nonzero if the package is not installed.
I think `--needed` will work as-is because `yay` is alreay clever enough to use the flag where it makes sense and just ignores it for AUR packages (but since there is an issue they might implement this for AUR too at some point)
Can confirm using

```yaml
  extra_args:
  - --needed
```

just works. It's super fast now with `--needed` and the koto marker file workaround.
But you're right, the `query` function would be the super optimal solution because it just works(tm) even with AUR packages.
I'll take a shot at implementing the `query` filter for the `yay` provider if it's cool with you since I do have arch on my machine to test :smiley: 
> Looks like I missed a lot from checking the code and reading the docs, I'll use this new knowledge asap 👍
> I might also send some PRs for the gitbook docs.

The docs are super lacking and I would really appreciate any and all help I can get 😃
> I'll take a shot at implementing the `query` filter for the `yay` provider if it's cool with you since I do have arch on my machine to test 😃

Go for it! If you need any guidance, feel free to jump to the Discord at https://rawkode.chat - there's a #comtrya channel

Fix patch:
diff --git a/src/actions/package/providers/yay.rs b/src/actions/package/providers/yay.rs
index 73db9504..92e1f736 100644
--- a/src/actions/package/providers/yay.rs
+++ b/src/actions/package/providers/yay.rs
@@ -3,7 +3,10 @@ use crate::actions::package::PackageVariant;
 use crate::atoms::command::Exec;
 use crate::steps::Step;
 use serde::{Deserialize, Serialize};
+use std::collections::HashSet;
+use std::process::Command;
 use tracing::warn;
+use tracing::{debug, trace};
 use which::which;
 
 #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
@@ -76,10 +79,46 @@ impl PackageProvider for Yay {
     }
 
     fn query(&self, package: &PackageVariant) -> Vec<String> {
-        package.packages()
+        let requested_already_installed: HashSet<String> = String::from_utf8(
+            Command::new("yay")
+                .args(
+                    vec![String::from("-Q"), String::from("-q")]
+                        .into_iter()
+                        .chain(package.packages().into_iter()),
+                )
+                .output()
+                .unwrap()
+                .stdout,
+        )
+        .unwrap()
+        .split('\n')
+        .map(String::from)
+        .collect();
+
+        debug!(
+            "all requested installed packages: {:?}",
+            requested_already_installed
+        );
+        package
+            .packages()
+            .into_iter()
+            .filter(|p| {
+                if requested_already_installed.contains(p) {
+                    trace!("{}: already installed", p);
+                    false
+                } else {
+                    debug!("{}: doesn't appear to be installed", p);
+                    true
+                }
+            })
+            .collect()
     }
 
     fn install(&self, package: &PackageVariant) -> Vec<Step> {
+        let need_installed = self.query(package);
+        if need_installed.is_empty() {
+            return vec![];
+        }
         vec![Step {
             atom: Box::new(Exec {
                 command: String::from("yay"),
@@ -91,7 +130,7 @@ impl PackageProvider for Yay {
                         String::from("--nodiffmenu"),
                     ],
                     package.extra_args.clone(),
-                    package.packages(),
+                    need_installed,
                 ]
                 .concat(),
                 ..Default::default()


Test patch: