feat: replace jpassmenu with a python version
There is no reason for it to be a Rust program
This commit is contained in:
parent
f082fe7a61
commit
0e170dd365
12 changed files with 157 additions and 926 deletions
105
scripts/jpassmenu/jpassmenu.py
Normal file
105
scripts/jpassmenu/jpassmenu.py
Normal file
|
@ -0,0 +1,105 @@
|
|||
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()
|
Loading…
Add table
Add a link
Reference in a new issue