Add Comments to Installed Pacman Packages

Following this topic, here’s a technique that allows you to add free text to the database of your installed packages.

Since Pacman 6, there are free fields available in our packages. Here, we will make a slight detour of this functionality.

The idea is to create a simple text file with comments to insert into our packages. Example of pkg-note.text:

# remove comment
wxwidgets-common =
# add or update comments
python-ujson = not useful ?
python-rich = i love it !
python-selenium = AUR package :( == bad

These comments are injected into our local database using a simple Pacman hook. With each installation/update, the hook will read our pkg-note.txt file and update our local database.

To read these comments, currently, only pacman -Qii allows it.

pacman -Qii python-selenium
...
Extended Data   : pkgtype=pkg
                  note=AUR package :( == bad

Installation:

  • Place the 3 files in the same directory.
  • Create symbolic links for the hook and its script. (sometime create /hooks.bin/ directory)
  • Update your text file (before update/install packages).
 chmod +x pkg-note.py
 sudo ln -s $PWD/pkg-note.hook /etc/pacman.d/hooks/pkg-note.hook -f
 sudo ln -s $PWD/pkg-note.py /etc/pacman.d/hooks.bin/pkg-note.py -f

Save comments without pacman action?

echo -e "wxwidgets-common \n python-rich" | sudo ./pkg-note.py

hook file

#/etc/pacman.d/hooks/pkg-note.hook
[Trigger]
Operation = Upgrade
Operation = Install
Type = Package
Target = *

[Action]
Description = Manage comments
When = PostTransaction
Exec = /etc/pacman.d/hooks.bin/pkg-note.py
NeedsTargets

script file

#!/usr/bin/env python
from pathlib import Path
import subprocess
import sys

KEY_ALPM = "%XDATA%"
KEY = "note"

path = Path(__file__).resolve(True).parent
DATABASE = path / "pkg-note.txt"

def where_is_desc(pkg):
    """/var/lib/pacman/local/pacman-6.1.0-7/desc"""
    proc = subprocess.run(f"/usr/bin/pacman -Q {pkg}", shell=True, capture_output=True, text=True)
    if proc.stderr:
        return
    version = proc.stdout.split()[1]
    try:
        return next(Path("/var/lib/pacman/local/").glob(f"{pkg}-{version}*/desc"))
    except StopIteration:
        pass


def change_content(data:str, new_value, key=KEY) -> str:
    """ add, change or delete key """
    if KEY_ALPM not in data and new_value:
        data = f"{data}\n{KEY_ALPM}\n{key} = {new_value}\n"
    else:
        lines = data.splitlines()
        if KEY_ALPM in lines:
            i = lines.index(KEY_ALPM)
            content = [l for l in lines[i:] if l]
            if [l for l in content if l.startswith(f"{key}=")]:
                l = next(l for l in content if l.startswith(f"{key}="))
                if new_value:
                    data = data.replace(l, f"{key}={new_value}")
                else:
                    data = data.replace(l, "")
            elif new_value:
                content = "\n".join(content)
                data = data.replace(content, f"{content}\n{key}={new_value}")
    return data


if __name__ == "__main__":
   
    notes = {}
    with open(DATABASE, 'r') as file_:
        for line in file_:
            try:
                if line and not line.startswith("#") :
                    key, value = line.split("=", maxsplit=1)
                    notes[key.strip()] = value.strip().replace('=','=')
            except ValueError as err:
                print("BAD", DATABASE, "file")
                print("line:", line)
                print(err)
    if not notes:
        exit(0)

    for line in sys.stdin:
        line = line.strip()
        if line in notes:
            if desc := where_is_desc(line):
                if data := change_content(desc.read_text(), notes[line], key=KEY):
                    desc.write_text(data)
                    print(f"  # {desc} modified")

Useful?

I don’t know. But it’s a good proof of concept and it might give some ideas to developers.

4 Likes