configuration.nix/scripts/jpassmenu/jpassmenu.py
Jalil David Salamé Messina 1fdbf8af97
All checks were successful
/ check (push) Successful in 9s
/ build-packages (push) Successful in 15s
/ build-vm (push) Successful in 29s
/ report-size (push) Successful in 1m16s
feat: replace jpassmenu with a python version
There is no reason for it to be a Rust program
2025-05-17 13:52:31 +02:00

105 lines
2.8 KiB
Python

from os import environ
from pathlib import Path
import subprocess
import click
@click.command(
"jpassmenu", context_settings={"show_default": True, "max_content_width": 120}
)
@click.option(
"--type",
"typeit",
help="Type the password using ydotool instead of copying it to the clipboard",
)
@click.option(
"--store-dir",
type=click.Path(exists=True, file_okay=False, path_type=Path),
envvar="PASSWORD_STORE_DIR",
default=Path("~/.password-store"),
)
@click.option(
"--pass-bin",
default="pass",
help="Path to the pass binary\n\nNeeds to support `pass show` and `pass show --clip`",
)
@click.option(
"--menu-bin", default="fuzzel", help="Path to the dmenu compatible menu binary"
)
@click.argument("menu_args", nargs=-1)
def main(
typeit: bool, store_dir: Path, pass_bin: str, menu_bin: str, menu_args: list[str]
) -> None:
menu_args = (
["--dmenu"] if not menu_args and menu_bin.endswith("fuzzel") else menu_args
)
store_dir = store_dir.expanduser().absolute()
# Get all files in store_dir
secrets = (
dirpath / fname
for dirpath, _dirnames, filenames in store_dir.walk()
for fname in filenames
)
# Filter for files ending in .gpg and strip the extension
secrets = (
secret.with_suffix("")
for secret in secrets
if secret.is_file() and secret.suffix == ".gpg"
)
# Make the paths relative to store_dir and turn to strings
secrets = sorted(str(secret.relative_to(store_dir)) for secret in secrets)
if not secrets:
click.secho(f"No valid entries found in {store_dir}", err=True, fg="red")
paths = "\n".join(secrets)
menu_output = subprocess.run(
[menu_bin, *menu_args],
input=paths,
encoding="UTF-8",
check=True,
capture_output=True,
)
selected = menu_output.stdout
if not selected:
click.echo("No secret selected")
return
# If PASSWORD_STORE_DIR and --store-dir disagree, set PASSWORD_STORE_DIR to --store-dir
env_store = (
Path(environ.get("PASSWORD_STORE_DIR", default="~/.password-store"))
.expanduser()
.absolute()
)
if store_dir != env_store:
environ["PASSWORD_STORE_DIR"] = str(store_dir)
pass_cmd = (
[pass_bin, "show", selected]
if typeit
else [pass_bin, "show", "--clip", selected]
)
pass_output = subprocess.run(
pass_cmd,
encoding="UTF-8",
check=True,
capture_output=typeit,
)
if not typeit:
return
pass_entry = pass_output.stdout
secret = pass_entry.splitlines()[0].strip()
_ = subprocess.run(
["ydotool", "type", "--file", "-"],
input=secret,
encoding="UTF-8",
check=True,
)
if __name__ == "__main__":
main()