refactor: nixify / clean remaining dots scripts

This commit is contained in:
hektor committed 2026-11-06 21:00:20 +01:00
1 parent 9030f33842
commit 9110789527
53 files changed
+265 -655

No files matched your search

+87
View File
@@ -0,0 +1,87 @@
{
config,
lib,
pkgs,
...
}:
let
script =
name: args:
pkgs.writeShellApplication (
{
inherit name;
text = builtins.readFile ./scripts/${name};
}
// args
);
pythonScript =
name: runtimeInputs:
pkgs.writeShellApplication {
inherit name runtimeInputs;
text = ''exec ${lib.getExe pkgs.python3} ${pkgs.writeTextDir name (builtins.readFile ./scripts/${name})}/${name} "$@"'';
};
ssh-hosts = script "ssh-hosts" {
runtimeInputs = with pkgs; [
gnugrep
gawk
];
};
scripts = with pkgs; {
bak.runtimeInputs = [ coreutils ];
notify.runtimeInputs = [ libnotify ];
pdftitle.runtimeInputs = [
poppler-utils
gawk
coreutils
];
fzf-book.runtimeInputs = [
findutils
fzf
coreutils
];
fzf-ssh.runtimeInputs = [
ssh-hosts
fzf
];
save-ssh-host = {
runtimeInputs = [
ssh-hosts
fzf
];
excludeShellChecks = [ "SC2029" ];
};
fzf-fontnames.runtimeInputs = [
fontconfig
fzf
gawk
coreutils
];
get-anki-decks.runtimeInputs = [
curl
jq
];
get-anki-reviews-status = {
runtimeInputs = [ coreutils ];
runtimeEnv.ANKI_REVIEWS = config.anki.reviewsCache;
};
};
pythonScripts = with pkgs; {
pomo = [ libnotify ];
taskdeps = [ ];
};
in
{
options.bin.enable = lib.mkOption {
default = false;
type = lib.types.bool;
};
config = lib.mkIf config.bin.enable {
home.packages = [
ssh-hosts
]
++ lib.mapAttrsToList script scripts
++ lib.mapAttrsToList pythonScript pythonScripts;
};
}
+30
View File
@@ -0,0 +1,30 @@
# back up a file by copying it to a new file with a .bak extension
# reference: https://askubuntu.com/questions/962489/is-there-any-way-to-create-backup-copy-of-a-file-without-type-its-name-twice
usage() {
echo "Usage: bak [-t|--timestamp] <file to back up>"
exit 1
}
timestamp=false
while [[ $# -gt 0 ]]; do
case $1 in
-t|--timestamp)
timestamp=true
shift
;;
*)
break
;;
esac
done
[ $# -eq 1 ] || usage
if $timestamp; then
date="$(date +%Y-%m-%d_%H-%M-%S)"
cp -vpn "$1"{,."$date".bak}
else
cp -vpn "$1"{,.bak}
fi
+26
View File
@@ -0,0 +1,26 @@
# Generate menu of book filenames and save paths
# Preview window contains metadata
function get_book_paths {
find "$HOME/doc/books/" -regex '.*\.\(pdf\|epub\|djvu\)' -type f | sort
}
function select_file {
get_book_paths | fzf --delimiter=/ --with-nth=-1
}
function open {
if [ -n "$1" ]; then
echo "Opening \"$1\""
zathura "$1" --fork
else
echo "No file selected"
exit 1
fi
}
case "${1:-}" in
--open) open "$(select_file)" ;;
--help) printf "open \n" >&2 ;;
*) open "$(select_file)" ;;
esac
+28
View File
@@ -0,0 +1,28 @@
fn="/tmp/fontnames.txt"
contains_dash() {
[[ "$1" =~ - ]]
}
update() {
: > "$fn"
font_list=$(fc-list -f "%{fullname}\n")
echo "$font_list" | while read -r line ; do
first="$(echo "$line" | cut -d',' -f1)"
last="$(echo "$line" | cut -d',' -f2)"
if contains_dash "$first"; then
echo "$last" >> "$fn"
else
echo "$first" >> "$fn"
fi
done
}
case "${1:-}" in
--update) update;;
*) sort -u "$fn" | awk 'NF' | fzf;;
esac
+1
View File
@@ -0,0 +1 @@
ssh "$(ssh-hosts | fzf)"
+1
View File
@@ -0,0 +1 @@
curl 127.0.0.1:8765 -X POST -d '{"action": "deckNames", "version": 6}' | jq '.result[]' -r
+10
View File
@@ -0,0 +1,10 @@
# Get Anki review status from exit code
num_reviews="$(cat "$ANKI_REVIEWS")"
if [ "$num_reviews" -ge "400" ]
then
exit 0
else
exit 1
fi
+6
View File
@@ -0,0 +1,6 @@
# Pipe into notify-send
#
# e.g. `echo "Hello world" | notify`
read -r msg || true
notify-send "$msg" "$@"
+1
View File
@@ -0,0 +1 @@
pdfinfo "$1" | head -n 1 | awk '{for (i=2; i<NF; i++) printf $i " "; print $NF}'
+87
View File
@@ -0,0 +1,87 @@
#!/usr/bin/env python3
# vim: set filetype=python:
"""
Pomodoro timer
- Writes pomodoro timer to temporary file so statusbar can read it
- Notification on session finish
- Notification on break finish
"""
import atexit
import os
import signal
import subprocess
from argparse import ArgumentParser
from time import sleep
POMO_PATH = os.path.join(
os.getenv("XDG_DATA_HOME", os.path.expanduser("~/.local/share")), "pomo"
)
@atexit.register
def clear():
if os.path.exists(POMO_PATH):
os.remove(POMO_PATH)
def format_mins_secs(mins, secs):
return f"{mins:02d}:{secs:02d}"
def countdown(duration):
while duration != 0:
mins = duration // 60
secs = duration % 60
with open(POMO_PATH, "w") as f:
f.write(format_mins_secs(mins, secs))
sleep(1)
duration -= 1
def main(args):
work_duration = args.work_duration * 60
break_duration = args.break_duration * 60
repeats = args.repeats
while repeats != 0:
subprocess.run(["notify-send", "Get started"])
countdown(work_duration)
if break_duration != 0:
subprocess.run(["notify-send", "Time for a break"])
countdown(break_duration)
subprocess.run(["notify-send", "Break is over, back to work"])
repeats -= 1
def handle_signal(signal, frame):
# Wait for clear to finish
clear()
print("Exiting")
exit(0)
if __name__ == "__main__":
parser = ArgumentParser()
parser.add_argument(
"-w", "--work-duration", type=int, help="Session duration", default=25
)
parser.add_argument(
"-b", "--break-duration", type=int, help="Break duration", default=5
)
parser.add_argument(
"-r", "--repeats", type=int, help="Numer of sessions", default=1
)
parser.add_argument("-c", "--clear", action="store_true", help="Clear timer")
args = parser.parse_args()
signal.signal(signal.SIGINT, handle_signal)
signal.signal(signal.SIGTERM, handle_signal)
if args.clear:
clear()
else:
main(args)
+10
View File
@@ -0,0 +1,10 @@
selected_hosts="$(ssh-hosts | fzf -m)"
for host in $selected_hosts; do
echo "Saving $host"
directories="$(ssh "$host" ls | fzf -m)"
for directory in $directories; do
echo "Saving $host:$directory"
ssh "$host" "(tar cvzf - ~/$directory)" > "${host}_${directory}.tar.gz"
done
done
+1
View File
@@ -0,0 +1 @@
grep -E 'Host [a-z0-9\-]*$' ~/.ssh/config | awk '{print $2}'
+93
View File
@@ -0,0 +1,93 @@
#!/usr/bin/env python3
import argparse
import json
import subprocess
from collections import defaultdict
def get_task_data():
command = ["task", "+PENDING", "or", "+WAITING", "-COMPLETED", "-DELETED", "export"]
return json.loads(subprocess.check_output(command))
def parse_task_data(data):
dependency_graph = defaultdict(list)
task_details = {}
dependent_tasks = set()
for task in data:
task_id = task["uuid"]
task_details[task_id] = {
"id": task.get("id", "?"),
"description": task.get("description", "No description"),
"status": task.get("status", "Unknown status"),
}
for dependency in task.get("depends", []):
dependency_graph[dependency].append(task_id)
dependent_tasks.add(task_id)
root_tasks = set(task_details.keys()) - dependent_tasks
return task_details, dependency_graph, root_tasks
def render_dependency_dag(task_details, dependency_graph, root_tasks, show_id):
dag_lines = []
global_visited = set()
def dfs(task_id, prefix="", is_last=True, visited=None):
if visited is None:
visited = set()
if task_id in visited:
return
visited.add(task_id)
global_visited.add(task_id)
task_info = task_details[task_id]
task_line = f"{prefix}{'└── ' if is_last else '├── '}{str(task_info['id']) + ': ' if show_id else ''}{task_info['description']} ({task_info['status']})"
dag_lines.append(task_line)
children = dependency_graph.get(task_id, [])
for idx, child in enumerate(children):
child_is_last = idx == len(children) - 1
child_prefix = prefix + (" " if is_last else "│ ")
dfs(child, child_prefix, child_is_last, visited.copy())
root_tasks_with_children = [
root for root in root_tasks if dependency_graph.get(root, [])
]
for root in sorted(
root_tasks_with_children,
key=lambda x: len(dependency_graph.get(x, [])),
reverse=True,
):
if root not in global_visited:
dfs(root)
dag_lines.append("")
return "\n".join(dag_lines).rstrip()
def main(args):
data = get_task_data()
task_details, dependency_graph, root_tasks = parse_task_data(data)
ascii_dag = render_dependency_dag(
task_details, dependency_graph, root_tasks, show_id=args.show_id
)
print(ascii_dag)
if __name__ == "__main__":
parser = argparse.ArgumentParser(
description="Generates a task dependency DAG for Taskwarrior tasks."
)
parser.add_argument(
"--show-id",
action="store_true",
default=False,
help="Include task IDs in the output.",
)
args = parser.parse_args()
main(args)