chore: auto-commit (97 archivos)
- .claude/CLAUDE.md - .claude/agents/fn-recopilador/SKILL.md - .claude/rules/INDEX.md - .claude/rules/cpp_apps.md - bash/functions/infra/build_cpp_windows.sh - cpp/CMakeLists.txt - cpp/PATTERNS.md - cpp/framework/app_base.cpp - cpp/framework/app_base.h - dev/issues/README.md - ... Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,34 +1,51 @@
|
||||
#!/usr/bin/env bash
|
||||
# build_cpp_windows — Cross-compila apps C++ del registry para Windows con
|
||||
# mingw-w64. Configura el build dir cpp/build/windows/ con la toolchain la
|
||||
# primera vez y construye el target indicado (o todos).
|
||||
#
|
||||
# Uso (funcion via source):
|
||||
# source bash/functions/infra/build_cpp_windows.sh
|
||||
# build_cpp_windows my_app # construye target especifico
|
||||
# build_cpp_windows # construye todos
|
||||
#
|
||||
# Uso (script directo):
|
||||
# bash bash/functions/infra/build_cpp_windows.sh my_app
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
REGISTRY_ROOT="${FN_REGISTRY_ROOT:-$(cd "$(dirname "$0")/../../.." && pwd)}"
|
||||
CPP_ROOT="$REGISTRY_ROOT/cpp"
|
||||
BUILD_DIR="$CPP_ROOT/build/windows"
|
||||
TOOLCHAIN="$CPP_ROOT/toolchains/mingw-w64.cmake"
|
||||
TARGET="${1:-}"
|
||||
build_cpp_windows() {
|
||||
local target="${1:-}"
|
||||
local registry_root="${FN_REGISTRY_ROOT:-$(cd "$(dirname "${BASH_SOURCE[0]}")/../../.." && pwd)}"
|
||||
local cpp_root="$registry_root/cpp"
|
||||
local build_dir="${BUILD_WIN:-$cpp_root/build/windows}"
|
||||
local toolchain="$cpp_root/toolchains/mingw-w64.cmake"
|
||||
|
||||
# Check mingw is available
|
||||
if ! command -v x86_64-w64-mingw32-g++ &>/dev/null; then
|
||||
echo "[build_cpp_windows] Error: mingw-w64 not found. Install with: sudo apt install mingw-w64"
|
||||
exit 1
|
||||
fi
|
||||
if ! command -v x86_64-w64-mingw32-g++ &>/dev/null; then
|
||||
echo "[build_cpp_windows] Error: mingw-w64 not found. Install with: sudo apt install mingw-w64" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Configure if needed
|
||||
if [ ! -f "$BUILD_DIR/CMakeCache.txt" ]; then
|
||||
echo "[build_cpp_windows] Configuring cmake with mingw-w64 toolchain..."
|
||||
cmake -B "$BUILD_DIR" -S "$CPP_ROOT" -DCMAKE_TOOLCHAIN_FILE="$TOOLCHAIN"
|
||||
fi
|
||||
if [ ! -f "$build_dir/CMakeCache.txt" ]; then
|
||||
echo "[build_cpp_windows] Configuring cmake with mingw-w64 toolchain..." >&2
|
||||
cmake -B "$build_dir" -S "$cpp_root" -DCMAKE_TOOLCHAIN_FILE="$toolchain"
|
||||
else
|
||||
# Re-run cmake to pick up new add_subdirectory entries cuando se anade
|
||||
# una app nueva al CMakeLists.txt (no rompe builds incrementales).
|
||||
cmake "$build_dir" >/dev/null
|
||||
fi
|
||||
|
||||
# Build
|
||||
if [ -n "$TARGET" ]; then
|
||||
echo "[build_cpp_windows] Cross-compiling target: $TARGET"
|
||||
cmake --build "$BUILD_DIR" --target "$TARGET" -- -j"$(nproc)"
|
||||
else
|
||||
echo "[build_cpp_windows] Cross-compiling all targets..."
|
||||
cmake --build "$BUILD_DIR" -- -j"$(nproc)"
|
||||
fi
|
||||
if [ -n "$target" ]; then
|
||||
echo "[build_cpp_windows] Cross-compiling target: $target" >&2
|
||||
cmake --build "$build_dir" --target "$target" -- -j"$(nproc)"
|
||||
else
|
||||
echo "[build_cpp_windows] Cross-compiling all targets..." >&2
|
||||
cmake --build "$build_dir" -- -j"$(nproc)"
|
||||
fi
|
||||
|
||||
echo "[build_cpp_windows] Done. Windows binaries in $BUILD_DIR"
|
||||
if [ -n "$TARGET" ]; then
|
||||
file "$BUILD_DIR"/**/"$TARGET".exe 2>/dev/null || file "$BUILD_DIR/$TARGET".exe 2>/dev/null || true
|
||||
echo "[build_cpp_windows] Done. Windows binaries in $build_dir" >&2
|
||||
}
|
||||
|
||||
# Invocacion directa como script (compatibilidad).
|
||||
if [ "${BASH_SOURCE[0]}" = "${0}" ]; then
|
||||
build_cpp_windows "$@"
|
||||
fi
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
---
|
||||
name: e2e_run_cpp_windows
|
||||
lang: bash
|
||||
domain: infra
|
||||
description: "Cross-compila una app C++ del registry para Windows con mingw-w64, deploy al Desktop\\apps de Windows (matando instancia previa con taskkill.exe), lanza el .exe nativamente desde WSL y devuelve stdout + exit code. Pensado para tests headless tipo altsnap_jitter_test."
|
||||
tags: [windows, e2e, cross-compile, test, mingw]
|
||||
purity: impure
|
||||
kind: function
|
||||
signature: "e2e_run_cpp_windows(target string, --no-build, --no-deploy) int"
|
||||
params:
|
||||
- name: target
|
||||
desc: "Nombre del target CMake del registry (ej. altsnap_jitter_test)"
|
||||
- name: --no-build
|
||||
desc: "Saltar cross-compile (usa el .exe ya construido en cpp/build/windows/)"
|
||||
- name: --no-deploy
|
||||
desc: "Saltar copia a Desktop\\apps (asume que ya esta deployed)"
|
||||
output: "Exit code del .exe (0 = pass, no-cero = fail). stdout/stderr del .exe se imprimen tal cual."
|
||||
uses_functions:
|
||||
- build_cpp_windows_bash_infra
|
||||
uses_types: []
|
||||
returns: ""
|
||||
returns_optional: false
|
||||
error_type: "exit_code_bash_core"
|
||||
imports: []
|
||||
example: |
|
||||
source bash/functions/infra/e2e_run_cpp_windows.sh
|
||||
e2e_run_cpp_windows altsnap_jitter_test
|
||||
# cross-compila, taskkill previo, copia a /mnt/c/Users/lucas/Desktop/apps/altsnap_jitter_test/
|
||||
# ejecuta y devuelve exit code
|
||||
tested: false
|
||||
file_path: "bash/functions/infra/e2e_run_cpp_windows.sh"
|
||||
---
|
||||
|
||||
Lanzador para tests e2e de apps C++ en Windows desde WSL. Workflow:
|
||||
|
||||
1. **Cross-compile** via `build_cpp_windows_bash_infra` (skipable con `--no-build`).
|
||||
2. **Localiza** `${target}.exe` bajo `cpp/build/windows/apps/<target>/` o el arbol completo de build.
|
||||
3. **Mata instancia previa** con `taskkill.exe /IM <target>.exe /F` (evita `Permission denied` al copiar el exe).
|
||||
4. **Deploy** a `/mnt/c/Users/lucas/Desktop/apps/<target>/` con sidecars (`assets/`, `runtime/`, `enrichers/`, `*.dll`).
|
||||
5. **Run** nativamente desde WSL (`./target.exe` con cwd en deploy_dir, asi `local_files/` se crea ahi).
|
||||
6. **Exit code** del .exe propaga al return de la funcion.
|
||||
|
||||
Variables de entorno:
|
||||
- `FN_REGISTRY_ROOT` — raiz del registry (auto-detectado).
|
||||
- `BUILD_WIN` — directorio de build cross (default `cpp/build/windows`).
|
||||
- `WIN_DESKTOP_APPS` — root de deploy en Windows (default `/mnt/c/Users/lucas/Desktop/apps`).
|
||||
|
||||
Requiere WSL2 con interop a Windows (cmd.exe, taskkill.exe en PATH) y mingw-w64.
|
||||
Executable
+127
@@ -0,0 +1,127 @@
|
||||
#!/usr/bin/env bash
|
||||
# e2e_run_cpp_windows — Cross-compile a C++ app del registry para Windows
|
||||
# con mingw-w64, deploy al Desktop de Windows (matando una posible instancia
|
||||
# previa con taskkill.exe), lanzar el .exe nativamente desde WSL y devolver
|
||||
# stdout + exit code. Pensado para apps tipo headless smoke / regression
|
||||
# test (ej. altsnap_jitter_test) que arrancan, ejecutan un guion y salen.
|
||||
#
|
||||
# Uso (funcion via source):
|
||||
# source bash/functions/infra/e2e_run_cpp_windows.sh
|
||||
# e2e_run_cpp_windows altsnap_jitter_test # build + deploy + run
|
||||
# e2e_run_cpp_windows altsnap_jitter_test --no-build # solo deploy + run
|
||||
# e2e_run_cpp_windows altsnap_jitter_test --no-deploy # solo run (asume ya esta en Desktop)
|
||||
#
|
||||
# Requisitos:
|
||||
# - WSL2 con interop a Windows habilitado (cmd.exe / taskkill.exe en PATH).
|
||||
# - mingw-w64 instalado: sudo apt install mingw-w64
|
||||
# - cpp/build/windows/ configurable via build_cpp_windows.sh.
|
||||
# - C:\Users\lucas\Desktop accesible bajo /mnt/c/Users/lucas/Desktop.
|
||||
#
|
||||
# Salida:
|
||||
# - stdout/stderr del .exe se imprimen tal cual.
|
||||
# - Exit code de la funcion = exit code del .exe (0 = pass).
|
||||
|
||||
e2e_run_cpp_windows() {
|
||||
set -euo pipefail
|
||||
local target="${1:-}"
|
||||
if [ -z "$target" ]; then
|
||||
echo "[e2e_run_cpp_windows] Uso: e2e_run_cpp_windows <app_name> [--no-build] [--no-deploy]" >&2
|
||||
return 2
|
||||
fi
|
||||
shift
|
||||
local do_build=1
|
||||
local do_deploy=1
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
--no-build) do_build=0 ;;
|
||||
--no-deploy) do_deploy=0 ;;
|
||||
*) echo "[e2e_run_cpp_windows] Flag desconocida: $1" >&2; return 2 ;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
local registry_root="${FN_REGISTRY_ROOT:-}"
|
||||
if [ -z "$registry_root" ]; then
|
||||
# Walk up from cwd looking for the registry.db sentinel.
|
||||
local d="$PWD"
|
||||
while [ "$d" != "/" ]; do
|
||||
if [ -f "$d/registry.db" ] && [ -d "$d/cpp" ]; then
|
||||
registry_root="$d"; break
|
||||
fi
|
||||
d="$(dirname "$d")"
|
||||
done
|
||||
fi
|
||||
if [ -z "$registry_root" ]; then
|
||||
echo "[e2e_run_cpp_windows] No se localiza la raiz del registry. Exporta FN_REGISTRY_ROOT." >&2
|
||||
return 2
|
||||
fi
|
||||
local cpp_root="$registry_root/cpp"
|
||||
local build_dir="${BUILD_WIN:-$cpp_root/build/windows}"
|
||||
local desktop_root="${WIN_DESKTOP_APPS:-/mnt/c/Users/lucas/Desktop/apps}"
|
||||
local deploy_dir="$desktop_root/$target"
|
||||
|
||||
# 1. Cross-compile.
|
||||
if [ "$do_build" -eq 1 ]; then
|
||||
echo "[e2e_run_cpp_windows] cross-compile target=$target" >&2
|
||||
# Propagate registry_root so build_cpp_windows doesn't trip over its
|
||||
# own BASH_SOURCE-based detection.
|
||||
export FN_REGISTRY_ROOT="$registry_root"
|
||||
# shellcheck source=./build_cpp_windows.sh
|
||||
source "$registry_root/bash/functions/infra/build_cpp_windows.sh"
|
||||
build_cpp_windows "$target"
|
||||
fi
|
||||
|
||||
# 2. Locate built .exe.
|
||||
local exe_src
|
||||
exe_src="$(find "$build_dir/apps/$target" -maxdepth 2 -name "${target}.exe" -type f 2>/dev/null | head -1 || true)"
|
||||
if [ -z "$exe_src" ]; then
|
||||
# Fallback: search the whole build tree (some targets land elsewhere).
|
||||
exe_src="$(find "$build_dir" -name "${target}.exe" -type f 2>/dev/null | head -1 || true)"
|
||||
fi
|
||||
if [ -z "$exe_src" ]; then
|
||||
echo "[e2e_run_cpp_windows] No se encontro ${target}.exe en $build_dir" >&2
|
||||
return 1
|
||||
fi
|
||||
echo "[e2e_run_cpp_windows] exe: $exe_src" >&2
|
||||
|
||||
# 3. Deploy a Desktop\apps\<target>.
|
||||
if [ "$do_deploy" -eq 1 ]; then
|
||||
# Mata instancia previa si esta corriendo (evita "Permission denied" al cp).
|
||||
if command -v taskkill.exe &>/dev/null; then
|
||||
taskkill.exe /IM "${target}.exe" /F >/dev/null 2>&1 || true
|
||||
fi
|
||||
mkdir -p "$deploy_dir"
|
||||
cp -f "$exe_src" "$deploy_dir/"
|
||||
# Copia assets si existen junto al exe (TTFs, runtime, ...).
|
||||
local exe_dir
|
||||
exe_dir="$(dirname "$exe_src")"
|
||||
for sidecar in assets runtime enrichers; do
|
||||
if [ -d "$exe_dir/$sidecar" ]; then
|
||||
cp -rf "$exe_dir/$sidecar" "$deploy_dir/"
|
||||
fi
|
||||
done
|
||||
# DLLs sueltos (mingw runtime, sqlite, etc.) si los hubiera.
|
||||
find "$exe_dir" -maxdepth 1 -name "*.dll" -exec cp -f {} "$deploy_dir/" \; 2>/dev/null || true
|
||||
echo "[e2e_run_cpp_windows] deployed -> $deploy_dir" >&2
|
||||
fi
|
||||
|
||||
# 4. Run desde WSL. cd al deploy_dir para que exe_dir() apunte al sitio
|
||||
# correcto (local_files/imgui.ini se crea ahi).
|
||||
if [ ! -x "$deploy_dir/${target}.exe" ]; then
|
||||
echo "[e2e_run_cpp_windows] No hay ${target}.exe en $deploy_dir" >&2
|
||||
return 1
|
||||
fi
|
||||
echo "[e2e_run_cpp_windows] launch $deploy_dir/${target}.exe" >&2
|
||||
(
|
||||
cd "$deploy_dir"
|
||||
./"${target}.exe"
|
||||
)
|
||||
local rc=$?
|
||||
echo "[e2e_run_cpp_windows] exit=$rc" >&2
|
||||
return "$rc"
|
||||
}
|
||||
|
||||
# Invocacion directa como script.
|
||||
if [ "${BASH_SOURCE[0]:-}" = "${0:-}" ] && [ -n "${BASH_SOURCE[0]:-}" ]; then
|
||||
e2e_run_cpp_windows "$@"
|
||||
fi
|
||||
@@ -0,0 +1,36 @@
|
||||
---
|
||||
name: keepass_delete
|
||||
kind: function
|
||||
lang: bash
|
||||
domain: infra
|
||||
version: "1.0.0"
|
||||
purity: impure
|
||||
signature: "keepass_delete(entry: string)"
|
||||
description: "Elimina una entry del KeePassXC database via keepassxc-cli rm. La entry pasa a la papelera dentro del .kdbx (no se borra fisicamente)."
|
||||
tags: [keepass, keepassxc, secret, credential, delete]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: "error_go_core"
|
||||
imports: []
|
||||
params:
|
||||
- name: entry
|
||||
desc: "path de la entry a eliminar"
|
||||
output: "ninguno (exit 0 si OK)"
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "bash/functions/infra/keepass_delete.sh"
|
||||
---
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```bash
|
||||
source keepass_delete.sh
|
||||
keepass_delete "Servers/old-server"
|
||||
```
|
||||
|
||||
## Notas
|
||||
|
||||
- KeePassXC mueve a Recycle Bin interno por defecto. Vaciar manualmente desde GUI si quieres borrado fisico.
|
||||
@@ -0,0 +1,44 @@
|
||||
# keepass_delete
|
||||
# --------------
|
||||
# Elimina una entry del KeePassXC database.
|
||||
#
|
||||
# REQUIERE:
|
||||
# - keepassxc-cli instalado
|
||||
# - KEEPASS_DB (env): ruta absoluta al .kdbx
|
||||
# - master password en pass o env KEEPASS_PASSWORD
|
||||
#
|
||||
# USO (sourced):
|
||||
# source keepass_delete.sh
|
||||
# keepass_delete "Servers/old-server"
|
||||
|
||||
keepass_delete() {
|
||||
local entry="$1"
|
||||
|
||||
if [ -z "$entry" ]; then
|
||||
echo "keepass_delete: se requiere entry" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
local db="${KEEPASS_DB:-}"
|
||||
if [ -z "$db" ] || [ ! -f "$db" ]; then
|
||||
echo "keepass_delete: KEEPASS_DB no valida: $db" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
local master
|
||||
if [ -n "${KEEPASS_PASSWORD:-}" ]; then
|
||||
master="$KEEPASS_PASSWORD"
|
||||
else
|
||||
master=$(pass show "${KEEPASS_MASTER_ENTRY:-meta/keepassxc-master}" 2>/dev/null | head -n1)
|
||||
if [ -z "$master" ]; then
|
||||
echo "keepass_delete: no master pass" >&2
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
printf '%s\n' "$master" | keepassxc-cli rm -q "$db" "$entry" >/dev/null 2>&1
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "keepass_delete: fallo al borrar '$entry'" >&2
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
---
|
||||
name: keepass_dump
|
||||
kind: function
|
||||
lang: bash
|
||||
domain: infra
|
||||
version: "1.0.0"
|
||||
purity: impure
|
||||
signature: "keepass_dump() -> json"
|
||||
description: "Exporta toda la BD KeePassXC como array JSON. Una sola apertura del .kdbx via keepassxc-cli export -f xml + python3 etree para parsear. Cada elemento: {path,title,username,password,url,notes}."
|
||||
tags: [keepass, keepassxc, dump, export, batch]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: "error_go_core"
|
||||
imports: []
|
||||
params: []
|
||||
output: "array JSON de objetos {path,title,username,password,url,notes}"
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "bash/functions/infra/keepass_dump.sh"
|
||||
---
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```bash
|
||||
source keepass_dump.sh
|
||||
data=$(keepass_dump)
|
||||
|
||||
# Filtrar por grupo
|
||||
echo "$data" | jq '.[] | select(.path | startswith("Servers/"))'
|
||||
|
||||
# Solo passwords no vacios
|
||||
echo "$data" | jq '.[] | select(.password != "")'
|
||||
|
||||
# Contar
|
||||
echo "$data" | jq 'length'
|
||||
```
|
||||
|
||||
## Notas
|
||||
|
||||
- KeePassXC 2.6.x export solo soporta `xml` y `csv` (no JSON nativo). Por eso pasamos por python3.
|
||||
- 2.7.0+ tiene `-f json` directo; este wrapper sigue funcionando.
|
||||
- Output ya descifrado (master password aplicada en export). El atributo `Protected="True"` del XML solo es marker.
|
||||
- El leading "Root" del KDBX se omite en `path`.
|
||||
@@ -0,0 +1,91 @@
|
||||
# keepass_dump
|
||||
# ------------
|
||||
# Exporta toda la BD KeePassXC como array JSON. Una sola apertura del .kdbx.
|
||||
# Cada elemento: {path, title, username, password, url, notes}.
|
||||
#
|
||||
# REQUIERE:
|
||||
# - keepassxc-cli instalado
|
||||
# - python3 (stdlib xml.etree)
|
||||
# - KEEPASS_DB (env): ruta absoluta al .kdbx
|
||||
# - master password en pass o env KEEPASS_PASSWORD
|
||||
#
|
||||
# USO (sourced):
|
||||
# source keepass_dump.sh
|
||||
# data=$(keepass_dump)
|
||||
# echo "$data" | jq '.[] | select(.path | startswith("Servers/"))'
|
||||
|
||||
keepass_dump() {
|
||||
local db="${KEEPASS_DB:-}"
|
||||
if [ -z "$db" ] || [ ! -f "$db" ]; then
|
||||
echo "keepass_dump: KEEPASS_DB no valida: $db" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
local master
|
||||
if [ -n "${KEEPASS_PASSWORD:-}" ]; then
|
||||
master="$KEEPASS_PASSWORD"
|
||||
else
|
||||
master=$(pass show "${KEEPASS_MASTER_ENTRY:-meta/keepassxc-master}" 2>/dev/null | head -n1)
|
||||
if [ -z "$master" ]; then
|
||||
echo "keepass_dump: no master pass" >&2
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
local xml
|
||||
xml=$(printf '%s\n' "$master" | keepassxc-cli export -q -f xml "$db" 2>/dev/null)
|
||||
if [ $? -ne 0 ] || [ -z "$xml" ]; then
|
||||
echo "keepass_dump: export xml fallo (master incorrecta?)" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
printf '%s' "$xml" | python3 -c '
|
||||
import sys, json, re
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
root = ET.fromstring(sys.stdin.read())
|
||||
out = []
|
||||
|
||||
def clean(s):
|
||||
if not s:
|
||||
return ""
|
||||
s = s.strip().rstrip("/")
|
||||
s = s.replace("/", "_")
|
||||
s = re.sub(r"\s+", "_", s)
|
||||
s = re.sub(r"_+", "_", s)
|
||||
s = s.strip("_")
|
||||
return s
|
||||
|
||||
def walk(group, path):
|
||||
name_el = group.find("Name")
|
||||
raw_name = name_el.text if name_el is not None and name_el.text else ""
|
||||
name = clean(raw_name)
|
||||
new_path = path + [name] if name and name != "Root" else path
|
||||
for entry in group.findall("Entry"):
|
||||
rec = {}
|
||||
for s in entry.findall("String"):
|
||||
k_el = s.find("Key")
|
||||
v_el = s.find("Value")
|
||||
if k_el is None or k_el.text is None:
|
||||
continue
|
||||
rec[k_el.text] = (v_el.text if v_el is not None and v_el.text else "")
|
||||
title = clean(rec.get("Title", ""))
|
||||
full = "/".join(new_path + [title]) if title else "/".join(new_path)
|
||||
out.append({
|
||||
"path": full,
|
||||
"title": title,
|
||||
"username": rec.get("UserName", ""),
|
||||
"password": rec.get("Password", ""),
|
||||
"url": rec.get("URL", ""),
|
||||
"notes": rec.get("Notes", ""),
|
||||
})
|
||||
for sub in group.findall("Group"):
|
||||
walk(sub, new_path)
|
||||
|
||||
root_grp = root.find("Root/Group")
|
||||
if root_grp is not None:
|
||||
walk(root_grp, [])
|
||||
|
||||
print(json.dumps(out, ensure_ascii=False))
|
||||
'
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
---
|
||||
name: keepass_generate
|
||||
kind: function
|
||||
lang: bash
|
||||
domain: infra
|
||||
version: "1.0.0"
|
||||
purity: impure
|
||||
signature: "keepass_generate(entry: string, length?: int, username?: string, url?: string) -> string"
|
||||
description: "Genera un password aleatorio (lower+upper+digits+special), lo almacena en una entry nueva y lo imprime a stdout. Length default 24."
|
||||
tags: [keepass, keepassxc, secret, credential, generate, random]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: "error_go_core"
|
||||
imports: []
|
||||
params:
|
||||
- name: entry
|
||||
desc: "path de la entry a crear"
|
||||
- name: length
|
||||
desc: "longitud del password (default 24)"
|
||||
- name: username
|
||||
desc: "username opcional"
|
||||
- name: url
|
||||
desc: "url opcional"
|
||||
output: "password generado en texto plano"
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "bash/functions/infra/keepass_generate.sh"
|
||||
---
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```bash
|
||||
source keepass_generate.sh
|
||||
pwd=$(keepass_generate "Servers/new-vps" 32 "deploy" "https://vps.example.com")
|
||||
echo "Generated: $pwd"
|
||||
```
|
||||
|
||||
## Notas
|
||||
|
||||
- Genera con `keepassxc-cli generate -l -U -n -s` (lower, upper, numbers, special).
|
||||
- Inserta con `keepassxc-cli add -p` reusando la misma sesion.
|
||||
- El grupo padre debe existir.
|
||||
@@ -0,0 +1,63 @@
|
||||
# keepass_generate
|
||||
# ----------------
|
||||
# Genera un password aleatorio, lo almacena en una entry nueva del KeePassXC database
|
||||
# y lo imprime a stdout.
|
||||
#
|
||||
# REQUIERE:
|
||||
# - keepassxc-cli instalado
|
||||
# - KEEPASS_DB (env): ruta absoluta al .kdbx
|
||||
# - master password en pass o env KEEPASS_PASSWORD
|
||||
#
|
||||
# USO (sourced):
|
||||
# source keepass_generate.sh
|
||||
# pwd=$(keepass_generate "Servers/new-server")
|
||||
# pwd=$(keepass_generate "Servers/new-server" 32)
|
||||
# pwd=$(keepass_generate "Servers/new-server" 32 "admin" "https://new.example.com")
|
||||
|
||||
keepass_generate() {
|
||||
local entry="$1"
|
||||
local length="${2:-24}"
|
||||
local username="${3:-}"
|
||||
local url="${4:-}"
|
||||
|
||||
if [ -z "$entry" ]; then
|
||||
echo "keepass_generate: se requiere entry" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
local db="${KEEPASS_DB:-}"
|
||||
if [ -z "$db" ] || [ ! -f "$db" ]; then
|
||||
echo "keepass_generate: KEEPASS_DB no valida: $db" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
local master
|
||||
if [ -n "${KEEPASS_PASSWORD:-}" ]; then
|
||||
master="$KEEPASS_PASSWORD"
|
||||
else
|
||||
master=$(pass show "${KEEPASS_MASTER_ENTRY:-meta/keepassxc-master}" 2>/dev/null | head -n1)
|
||||
if [ -z "$master" ]; then
|
||||
echo "keepass_generate: no master pass" >&2
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
local pwd
|
||||
pwd=$(keepassxc-cli generate -L "$length" -l -U -n -s 2>/dev/null)
|
||||
if [ -z "$pwd" ]; then
|
||||
echo "keepass_generate: fallo al generar password" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
local args=(-q -p)
|
||||
[ -n "$username" ] && args+=(-u "$username")
|
||||
[ -n "$url" ] && args+=(--url "$url")
|
||||
|
||||
printf '%s\n%s\n' "$master" "$pwd" | keepassxc-cli add "${args[@]}" "$db" "$entry" >/dev/null 2>&1
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "keepass_generate: fallo al insertar '$entry'" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
printf '%s' "$pwd"
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
---
|
||||
name: keepass_get
|
||||
kind: function
|
||||
lang: bash
|
||||
domain: infra
|
||||
version: "1.0.0"
|
||||
purity: impure
|
||||
signature: "keepass_get(entry: string, attr?: string) -> string"
|
||||
description: "Lee un atributo (Password por defecto) de una entry del KeePassXC database via keepassxc-cli. Resuelve master password desde pass (meta/keepassxc-master) o env KEEPASS_PASSWORD."
|
||||
tags: [keepass, keepassxc, secret, credential, get]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: "error_go_core"
|
||||
imports: []
|
||||
params:
|
||||
- name: entry
|
||||
desc: "path de la entry dentro del .kdbx (ej. 'Servers/prod-mysql')"
|
||||
- name: attr
|
||||
desc: "atributo a leer (Password, UserName, URL, Notes, Title); default Password"
|
||||
output: "valor del atributo en texto plano (sin newline final)"
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "bash/functions/infra/keepass_get.sh"
|
||||
---
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```bash
|
||||
export KEEPASS_DB="/mnt/d/Tr4Shhh_FOLDER/Sync/PssDtbs/PassDataBase.kdbx"
|
||||
source keepass_get.sh
|
||||
|
||||
pwd=$(keepass_get "Servers/prod-mysql")
|
||||
user=$(keepass_get "Servers/prod-mysql" UserName)
|
||||
```
|
||||
|
||||
## Setup
|
||||
|
||||
Master password se guarda una vez en pass:
|
||||
|
||||
```bash
|
||||
pass insert meta/keepassxc-master
|
||||
```
|
||||
|
||||
## Notas
|
||||
|
||||
- Wrappea `keepassxc-cli show -s -a <attr>`.
|
||||
- Cada call reabre la BD (CLI stateless). Para batch, usa `keepass_dump`.
|
||||
- `KEEPASS_PASSWORD` env tiene prioridad sobre `pass`.
|
||||
@@ -0,0 +1,57 @@
|
||||
# keepass_get
|
||||
# -----------
|
||||
# Lee un atributo de una entry del KeePassXC database.
|
||||
# Atributo por defecto: Password. Tambien admite UserName, URL, Notes, Title, etc.
|
||||
#
|
||||
# REQUIERE:
|
||||
# - keepassxc-cli instalado
|
||||
# - KEEPASS_DB (env): ruta absoluta al .kdbx
|
||||
# - master password en pass: `pass insert meta/keepassxc-master`
|
||||
# o env KEEPASS_PASSWORD
|
||||
# - override pass entry con KEEPASS_MASTER_ENTRY
|
||||
#
|
||||
# USO (sourced):
|
||||
# source keepass_get.sh
|
||||
# pwd=$(keepass_get "Servers/prod-mysql")
|
||||
# user=$(keepass_get "Servers/prod-mysql" UserName)
|
||||
# url=$(keepass_get "Servers/prod-mysql" URL)
|
||||
|
||||
keepass_get() {
|
||||
local entry="$1"
|
||||
local attr="${2:-Password}"
|
||||
|
||||
if [ -z "$entry" ]; then
|
||||
echo "keepass_get: se requiere path de entry" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
local db="${KEEPASS_DB:-}"
|
||||
if [ -z "$db" ]; then
|
||||
echo "keepass_get: KEEPASS_DB no definida" >&2
|
||||
return 1
|
||||
fi
|
||||
if [ ! -f "$db" ]; then
|
||||
echo "keepass_get: db no existe: $db" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
local master
|
||||
if [ -n "${KEEPASS_PASSWORD:-}" ]; then
|
||||
master="$KEEPASS_PASSWORD"
|
||||
else
|
||||
master=$(pass show "${KEEPASS_MASTER_ENTRY:-meta/keepassxc-master}" 2>/dev/null | head -n1)
|
||||
if [ -z "$master" ]; then
|
||||
echo "keepass_get: no master pass (set KEEPASS_PASSWORD o pass insert ${KEEPASS_MASTER_ENTRY:-meta/keepassxc-master})" >&2
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
local value
|
||||
value=$(printf '%s\n' "$master" | keepassxc-cli show -q -s -a "$attr" "$db" "$entry" 2>/dev/null)
|
||||
if [ $? -ne 0 ] || [ -z "$value" ]; then
|
||||
echo "keepass_get: no se pudo leer '$entry' attr '$attr'" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
printf '%s' "$value"
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
---
|
||||
name: keepass_list
|
||||
kind: function
|
||||
lang: bash
|
||||
domain: infra
|
||||
version: "1.0.0"
|
||||
purity: impure
|
||||
signature: "keepass_list(prefix?: string) -> json"
|
||||
description: "Lista paths de entries del KeePassXC database como array JSON. Filtra opcionalmente por prefijo de grupo. Internamente usa keepass_dump y proyecta solo los paths."
|
||||
tags: [keepass, keepassxc, list]
|
||||
uses_functions:
|
||||
- keepass_dump_bash_infra
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: "error_go_core"
|
||||
imports: []
|
||||
params:
|
||||
- name: prefix
|
||||
desc: "prefijo de path para filtrar (ej. 'Servers/'); vacio = todas"
|
||||
output: "array JSON de strings con paths"
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "bash/functions/infra/keepass_list.sh"
|
||||
---
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```bash
|
||||
source keepass_dump.sh
|
||||
source keepass_list.sh
|
||||
|
||||
all=$(keepass_list)
|
||||
servers=$(keepass_list "Servers/")
|
||||
echo "$servers" | jq -r '.[]'
|
||||
```
|
||||
|
||||
## Notas
|
||||
|
||||
- Auto-sourcea `keepass_dump.sh` desde el mismo directorio si no esta cargado.
|
||||
- Para acceder a campos completos (password, username, url) usa `keepass_dump` directo.
|
||||
@@ -0,0 +1,39 @@
|
||||
# keepass_list
|
||||
# ------------
|
||||
# Lista paths de entries del KeePassXC database como array JSON.
|
||||
# Filtra opcionalmente por prefijo de grupo.
|
||||
#
|
||||
# REQUIERE:
|
||||
# - keepass_dump (sourced o en PATH del registry)
|
||||
# - jq instalado
|
||||
#
|
||||
# USO (sourced):
|
||||
# source keepass_dump.sh
|
||||
# source keepass_list.sh
|
||||
# all=$(keepass_list)
|
||||
# servers=$(keepass_list "Servers/")
|
||||
|
||||
keepass_list() {
|
||||
local prefix="$1"
|
||||
|
||||
if ! declare -F keepass_dump >/dev/null 2>&1; then
|
||||
local here
|
||||
here=$(dirname "${BASH_SOURCE[0]}")
|
||||
if [ -f "$here/keepass_dump.sh" ]; then
|
||||
# shellcheck source=keepass_dump.sh
|
||||
source "$here/keepass_dump.sh"
|
||||
else
|
||||
echo "keepass_list: keepass_dump no disponible" >&2
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
local dump
|
||||
dump=$(keepass_dump) || return 1
|
||||
|
||||
if [ -n "$prefix" ]; then
|
||||
printf '%s' "$dump" | jq --arg p "$prefix" '[.[] | .path | select(startswith($p))]'
|
||||
else
|
||||
printf '%s' "$dump" | jq '[.[] | .path]'
|
||||
fi
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
---
|
||||
name: keepass_search
|
||||
kind: function
|
||||
lang: bash
|
||||
domain: infra
|
||||
version: "1.0.0"
|
||||
purity: impure
|
||||
signature: "keepass_search(term: string) -> json"
|
||||
description: "Busca entries en el KeePassXC database por substring. Devuelve array JSON de paths que matchean (title/username/url/notes)."
|
||||
tags: [keepass, keepassxc, search, query]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: "error_go_core"
|
||||
imports: []
|
||||
params:
|
||||
- name: term
|
||||
desc: "substring a buscar (case-insensitive)"
|
||||
output: "array JSON de strings con paths matched, ej: [\"Servers/prod\", \"Web/github\"]"
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "bash/functions/infra/keepass_search.sh"
|
||||
---
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```bash
|
||||
source keepass_search.sh
|
||||
matches=$(keepass_search "github")
|
||||
# [
|
||||
# "Web/github-personal",
|
||||
# "Web/github-work"
|
||||
# ]
|
||||
```
|
||||
|
||||
## Notas
|
||||
|
||||
- Wrappea `keepassxc-cli search`.
|
||||
- El leading `/` del CLI se quita antes de devolver.
|
||||
@@ -0,0 +1,50 @@
|
||||
# keepass_search
|
||||
# --------------
|
||||
# Busca entries en el KeePassXC database por substring (en title, username, url, notes).
|
||||
# Devuelve un array JSON de paths que matchean.
|
||||
#
|
||||
# REQUIERE:
|
||||
# - keepassxc-cli instalado
|
||||
# - jq instalado
|
||||
# - KEEPASS_DB (env): ruta absoluta al .kdbx
|
||||
# - master password en pass o env KEEPASS_PASSWORD
|
||||
#
|
||||
# USO (sourced):
|
||||
# source keepass_search.sh
|
||||
# matches=$(keepass_search "github")
|
||||
# echo "$matches" | jq .
|
||||
|
||||
keepass_search() {
|
||||
local term="$1"
|
||||
|
||||
if [ -z "$term" ]; then
|
||||
echo "keepass_search: se requiere termino de busqueda" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
local db="${KEEPASS_DB:-}"
|
||||
if [ -z "$db" ] || [ ! -f "$db" ]; then
|
||||
echo "keepass_search: KEEPASS_DB no valida: $db" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
local master
|
||||
if [ -n "${KEEPASS_PASSWORD:-}" ]; then
|
||||
master="$KEEPASS_PASSWORD"
|
||||
else
|
||||
master=$(pass show "${KEEPASS_MASTER_ENTRY:-meta/keepassxc-master}" 2>/dev/null | head -n1)
|
||||
if [ -z "$master" ]; then
|
||||
echo "keepass_search: no master pass" >&2
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
local out
|
||||
out=$(printf '%s\n' "$master" | keepassxc-cli locate -q "$db" "$term" 2>/dev/null)
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "keepass_search: keepassxc-cli locate fallo" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
printf '%s\n' "$out" | grep -v '^$' | sed 's|^/||' | jq -R . | jq -s .
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
---
|
||||
name: keepass_set
|
||||
kind: function
|
||||
lang: bash
|
||||
domain: infra
|
||||
version: "1.0.0"
|
||||
purity: impure
|
||||
signature: "keepass_set(entry: string, password: string, username?: string, url?: string)"
|
||||
description: "Crea o sobreescribe una entry en el KeePassXC database. Auto-detecta si existe (edit) o no (add). Soporta username y url opcionales."
|
||||
tags: [keepass, keepassxc, secret, credential, set, write]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: "error_go_core"
|
||||
imports: []
|
||||
params:
|
||||
- name: entry
|
||||
desc: "path de la entry (ej. 'Servers/prod-mysql'); si el grupo no existe falla"
|
||||
- name: password
|
||||
desc: "password en texto plano a almacenar"
|
||||
- name: username
|
||||
desc: "username opcional"
|
||||
- name: url
|
||||
desc: "url opcional"
|
||||
output: "ninguno (exit 0 si OK)"
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "bash/functions/infra/keepass_set.sh"
|
||||
---
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```bash
|
||||
source keepass_set.sh
|
||||
keepass_set "Servers/prod-mysql" "secret123" "admin" "https://prod.example.com"
|
||||
```
|
||||
|
||||
## Notas
|
||||
|
||||
- Add: `keepassxc-cli add -p`. Edit: `keepassxc-cli edit -p`.
|
||||
- Existencia detectada via `show -q` (exit code).
|
||||
- El grupo (parte antes del ultimo `/`) debe existir; KeePassXC no auto-crea jerarquia.
|
||||
@@ -0,0 +1,58 @@
|
||||
# keepass_set
|
||||
# -----------
|
||||
# Crea o sobreescribe una entry en el KeePassXC database.
|
||||
# Auto-detecta si existe (edit) o no (add).
|
||||
#
|
||||
# REQUIERE:
|
||||
# - keepassxc-cli instalado
|
||||
# - KEEPASS_DB (env): ruta absoluta al .kdbx
|
||||
# - master password en pass o env KEEPASS_PASSWORD
|
||||
#
|
||||
# USO (sourced):
|
||||
# source keepass_set.sh
|
||||
# keepass_set "Servers/prod-mysql" "secret123"
|
||||
# keepass_set "Servers/prod-mysql" "secret123" "admin" "https://prod.example.com"
|
||||
|
||||
keepass_set() {
|
||||
local entry="$1"
|
||||
local password="$2"
|
||||
local username="${3:-}"
|
||||
local url="${4:-}"
|
||||
|
||||
if [ -z "$entry" ] || [ -z "$password" ]; then
|
||||
echo "keepass_set: se requieren entry y password" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
local db="${KEEPASS_DB:-}"
|
||||
if [ -z "$db" ] || [ ! -f "$db" ]; then
|
||||
echo "keepass_set: KEEPASS_DB no valida: $db" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
local master
|
||||
if [ -n "${KEEPASS_PASSWORD:-}" ]; then
|
||||
master="$KEEPASS_PASSWORD"
|
||||
else
|
||||
master=$(pass show "${KEEPASS_MASTER_ENTRY:-meta/keepassxc-master}" 2>/dev/null | head -n1)
|
||||
if [ -z "$master" ]; then
|
||||
echo "keepass_set: no master pass" >&2
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
local cmd="add"
|
||||
if printf '%s\n' "$master" | keepassxc-cli show -q "$db" "$entry" >/dev/null 2>&1; then
|
||||
cmd="edit"
|
||||
fi
|
||||
|
||||
local args=(-q -p)
|
||||
[ -n "$username" ] && args+=(-u "$username")
|
||||
[ -n "$url" ] && args+=(--url "$url")
|
||||
|
||||
printf '%s\n%s\n' "$master" "$password" | keepassxc-cli "$cmd" "${args[@]}" "$db" "$entry" >/dev/null 2>&1
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "keepass_set: fallo al $cmd '$entry'" >&2
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
Reference in New Issue
Block a user