Skip to content

env-backup

Back up .env files found via Everything CLI (es.exe) into env-files/.

Metadata

  • Author: ropean, Claude Sonnet (Anthropic)
  • Version: 1.0.0
  • Dependencies: Everything (es.exe)

Code

python
#!/usr/bin/env python3
"""
@title env-backup
@description Back up .env files found via Everything CLI (es.exe) into env-files/.
@author ropean, Claude Sonnet (Anthropic)
@version 1.0.0

Runs:  es.exe wfn:.env !path:win-dotfiles
and copies results into env-files/, organised by project name.

@example
    python scripts/env-backup.py [--es-path PATH] [--query QUERY] [-o DIR] [--dry-run]

Options:
    --es-path PATH   Path to es.exe (auto-detected if omitted)
    --query QUERY    Everything search query
    -o / --output    Target directory (default: env-files/ in repo root)
    --dry-run        Show what would be copied without copying

@requires Everything (es.exe)
"""

import argparse
import shlex
import shutil
import subprocess
import sys
from pathlib import Path

SCRIPTS_DIR = Path(__file__).resolve().parent
REPO_ROOT    = SCRIPTS_DIR.parent
DEFAULT_OUT  = REPO_ROOT / "env-files"
DEFAULT_QUERY = "wfn:.env !path:win-dotfiles"

ES_CANDIDATE_PATHS = [
    REPO_ROOT / "bin" / "es.exe",
    Path("C:/Program Files/Everything/es.exe"),
    Path("C:/Program Files (x86)/Everything/es.exe"),
]

EXCLUDE_PATTERNS  = ["/scoop/", "/.vscode/", "/.cursor/", "appdata/local", "$recycle.bin"]
COMMON_CONFIG_DIRS = {"env", "config", "configs", ".config", "environments", "settings"}


# ── Everything ────────────────────────────────────────────────────────────────

def find_es() -> Path | None:
    """Locate es.exe: PATH first, then common install locations."""
    in_path = shutil.which("es") or shutil.which("es.exe")
    if in_path:
        return Path(in_path)
    for p in ES_CANDIDATE_PATHS:
        if p.exists():
            return p
    return None


def query_everything(es: Path, query: str) -> list[str]:
    cmd = [str(es)] + shlex.split(query)
    result = subprocess.run(cmd, capture_output=True, text=True,
                            encoding="utf-8", errors="replace")
    if result.returncode != 0:
        stderr = result.stderr.strip()
        if not stderr or "IPC" in stderr or "Everything" in stderr:
            print("[error] Cannot connect to Everything — is it running?", file=sys.stderr)
        else:
            print(f"[error] es.exe: {stderr}", file=sys.stderr)
        sys.exit(1)
    return [l.strip() for l in result.stdout.splitlines() if l.strip()]


# ── File helpers ──────────────────────────────────────────────────────────────

def should_exclude(path: str) -> bool:
    normalized = path.replace("\\", "/").lower()
    return any(pat in normalized for pat in EXCLUDE_PATTERNS)


def project_name(src: Path) -> str:
    parent = src.parent
    if parent.name.lower() in COMMON_CONFIG_DIRS:
        return parent.parent.name
    return parent.name


# ── Main ──────────────────────────────────────────────────────────────────────

def parse_args() -> argparse.Namespace:
    p = argparse.ArgumentParser(
        description="Back up .env files via Everything CLI (es.exe)."
    )
    p.add_argument("--es-path", metavar="PATH",
                   help="Path to es.exe (auto-detected if omitted)")
    p.add_argument("--query", default=DEFAULT_QUERY,
                   help=f"Everything search query (default: {DEFAULT_QUERY!r})")
    p.add_argument("-o", "--output", default=str(DEFAULT_OUT), metavar="DIR",
                   help=f"Target directory (default: {DEFAULT_OUT.name}/)")
    p.add_argument("--dry-run", action="store_true",
                   help="Print actions without copying")
    return p.parse_args()


def main() -> None:
    args = parse_args()

    # ── Resolve es.exe ────────────────────────────────────────────────────────
    if args.es_path:
        es = Path(args.es_path)
        if not es.exists():
            print(f"[error] es.exe not found at: {es}", file=sys.stderr)
            sys.exit(1)
    else:
        es = find_es()
        if es is None:
            print(
                "[error] es.exe not found.\n"
                "  • Ensure Everything is installed and es.exe is in PATH, or\n"
                "  • specify its location:  --es-path \"C:\\...\\es.exe\"",
                file=sys.stderr,
            )
            sys.exit(1)

    print(f"[es]    {es}",       file=sys.stderr)
    print(f"[query] {args.query}", file=sys.stderr)

    # ── Query ─────────────────────────────────────────────────────────────────
    paths = query_everything(es, args.query)
    print(f"[found] {len(paths)} file(s)\n", file=sys.stderr)

    out_dir = Path(args.output)
    ok = skip = fail = 0

    for raw in paths:
        src = Path(raw)

        if should_exclude(raw):
            print(f"  skip  {raw}", file=sys.stderr)
            skip += 1
            continue

        name = project_name(src)
        dst  = out_dir / name / src.name

        if args.dry_run:
            print(f"  dry   {src}{dst}")
            ok += 1
            continue

        try:
            dst.parent.mkdir(parents=True, exist_ok=True)
            shutil.copy2(src, dst)
            print(f"  ✓  {name}/{src.name}")
            ok += 1
        except OSError as e:
            print(f"  ✗  {raw}: {e}", file=sys.stderr)
            fail += 1

    print(f"\n[done] {ok} copied, {skip} skipped, {fail} failed", file=sys.stderr)


if __name__ == "__main__":
    main()

File Information

  • Filename: env-backup.py
  • Category: python
  • Language: PYTHON

View on GitHub