Commit a1842e7f6d71
Changed files (2)
pkgs
my
scripts
pkgs/my/scripts/bin/lazyworktree-raffi
@@ -1,51 +1,94 @@
-#!/usr/bin/env bash
-# List git repositories under ~/src/ for raffi script filter.
-# Outputs Alfred Script Filter JSON format.
-# Results are cached for 2 hours.
-# Usage: lazyworktree-raffi [query]
-set -euo pipefail
+#!/usr/bin/env python3
+"""List git repositories under ~/src/ for raffi script filter.
-CACHE_DIR="${XDG_CACHE_HOME:-$HOME/.cache}/lazyworktree-raffi"
-CACHE_FILE="$CACHE_DIR/repos.json"
-CACHE_MAX_AGE=7200 # 2 hours in seconds
-SRC_DIR="$HOME/src"
+Outputs Alfred Script Filter JSON format.
+Results are cached for 2 hours.
+Usage: lazyworktree-raffi [query]
+"""
-mkdir -p "$CACHE_DIR"
+import json
+import os
+import sys
+import tempfile
+import time
-# Rebuild cache if missing or older than CACHE_MAX_AGE
-rebuild=0
-if [[ ! -f "$CACHE_FILE" ]]; then
- rebuild=1
-elif [[ "$(uname)" == "Darwin" ]]; then
- file_age=$(( $(date +%s) - $(stat -f %m "$CACHE_FILE") ))
- [[ "$file_age" -gt "$CACHE_MAX_AGE" ]] && rebuild=1
-else
- file_age=$(( $(date +%s) - $(stat -c %Y "$CACHE_FILE") ))
- [[ "$file_age" -gt "$CACHE_MAX_AGE" ]] && rebuild=1
-fi
+CACHE_MAX_AGE = 7200 # 2 hours
+SRC_DIR = os.path.expanduser("~/src")
-if [[ "$rebuild" -eq 1 ]]; then
- find "$SRC_DIR" -name .git -type d -not -path '*/worktrees/*' 2>/dev/null \
- | sed 's|/\.git$||' \
- | sort \
- | jq -R --arg src "$SRC_DIR" '{
- title: (. | ltrimstr($src + "/") ),
- subtitle: .,
- arg: .
- }' \
- | jq -s '.' \
- > "$CACHE_FILE"
-fi
-query="${*:-}"
+def cache_paths():
+ cache_home = os.environ.get("XDG_CACHE_HOME", os.path.expanduser("~/.cache"))
+ cache_dir = os.path.join(cache_home, "lazyworktree-raffi")
+ os.makedirs(cache_dir, exist_ok=True)
+ return cache_dir, os.path.join(cache_dir, "repos.json")
-if [[ -z "$query" ]]; then
- jq '{ items: . }' "$CACHE_FILE"
-else
- jq --arg q "$query" '
- [ .[] | select(
- .title | ascii_downcase | contains($q | ascii_downcase)
- )]
- | { items: . }
- ' "$CACHE_FILE"
-fi
+
+def needs_rebuild(cache_file):
+ try:
+ age = time.time() - os.path.getmtime(cache_file)
+ return age > CACHE_MAX_AGE
+ except FileNotFoundError:
+ return True
+
+
+def find_repos():
+ repos = []
+ for root, dirs, _files in os.walk(SRC_DIR):
+ # Skip worktree internal directories
+ if "worktrees" in root.split(os.sep):
+ dirs.clear()
+ continue
+ if ".git" in dirs or ".git" in _files:
+ repos.append(root)
+ dirs.clear() # Don't descend into nested repos
+ repos.sort()
+ return [
+ {
+ "title": os.path.relpath(r, SRC_DIR),
+ "subtitle": r,
+ "arg": r,
+ }
+ for r in repos
+ ]
+
+
+def rebuild_cache(cache_dir, cache_file):
+ repos = find_repos()
+ fd, tmpfile = tempfile.mkstemp(suffix=".json", dir=cache_dir)
+ try:
+ with os.fdopen(fd, "w") as f:
+ json.dump(repos, f)
+ os.replace(tmpfile, cache_file)
+ except BaseException:
+ os.unlink(tmpfile)
+ raise
+
+
+def load_cache(cache_file):
+ try:
+ with open(cache_file) as f:
+ data = f.read()
+ if not data:
+ return []
+ return json.loads(data)
+ except (FileNotFoundError, json.JSONDecodeError):
+ return []
+
+
+def main():
+ cache_dir, cache_file = cache_paths()
+
+ if needs_rebuild(cache_file):
+ rebuild_cache(cache_dir, cache_file)
+
+ items = load_cache(cache_file)
+
+ query = " ".join(sys.argv[1:]).strip().lower()
+ if query:
+ items = [i for i in items if query in i["title"].lower()]
+
+ json.dump({"items": items}, sys.stdout)
+
+
+if __name__ == "__main__":
+ main()
pkgs/my/scripts/default.nix
@@ -5,7 +5,7 @@
stdenv.mkDerivation {
pname = "vde-scripts";
- version = "0.8";
+ version = "0.9";
src = ./.;