Configuracion inicial completa del entorno personal

Modulos de restauracion:
- bootstrap: instala yq, age y dependencias base (curl, wget, git, nano, gpg)
- ssh: descifra e instala claves SSH desde secrets/sshKeys.tar.gz.age
- registry: aplica paquetes apt/snap/flatpak, dotfiles, servicios y configs Docker
- thunderbird: instala Thunderbird snap y restaura perfil desde ZIP
- claudeCode: configura repositorio apt de Anthropic e instala claude-code
- easyEffects: restaura configuracion y presets desde ZIP
- wireplumber: restaura dispositivo Bluetooth por defecto y perfiles de audio
- cups: restaura impresoras y drivers PPD desde ZIP

Scripts de captura (correr antes de push):
- scripts/encryptSsh.sh: cifra ~/.ssh con age
- scripts/thunderbird/capture.sh: captura perfil de Thunderbird snap
- scripts/easyEffects/capture.sh: captura config de EasyEffects flatpak
- scripts/wireplumber/capture.sh: captura estado de WirePlumber
- scripts/cups/capture.sh: captura impresoras CUPS y PPDs (requiere sudo)

Registro de aplicaciones (config/registry.yaml):
- 9 paquetes apt, 1 snap (dbeaver-ce), 22 flatpaks incluyendo VSCodium,
  Bitwarden, Inkscape, LibreOffice, OBS Studio, Nextcloud Desktop, entre otros

Secretos incluidos:
- secrets/sshKeys.tar.gz.age: claves SSH cifradas con age
- secrets/thunderbirdProfile.zip: perfil de Thunderbird sin emails ni cache
- secrets/easyEffectsConfig.zip: ajustes y presets de salida de audio
- secrets/wireplumberState.zip: estado de audio incluyendo auriculares Bluetooth
- secrets/cupsConfig.zip: 5 impresoras configuradas con sus drivers

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-20 18:16:40 -03:00
parent ff401597be
commit bd78fd9fbe
34 changed files with 2348 additions and 2 deletions
+205
View File
@@ -0,0 +1,205 @@
# Agregar entrada al registro STP
Agregá una nueva entrada al registro STP para: $ARGUMENTS
---
## Procedimiento
1. Leé `config/registry.yaml` para verificar que no exista ya una entrada con el mismo `id`.
2. Identificá el tipo correcto según la descripción (ver sección **Tipos** más abajo).
3. Insertá la nueva entrada en `config/registry.yaml`, dentro de la lista `registry:`, respetando el orden:
- Los `ppa` deben aparecer **antes** de cualquier `apt` que los requiera.
- Dentro de cada grupo, el orden es cosmético; seguí el orden existente.
4. Confirmá el resultado mostrando la entrada agregada.
**Archivo a editar:** `config/registry.yaml`
---
## Tipos
### `apt` — paquete del gestor de paquetes
```yaml
- id: nombrePaquete
type: apt
# package: nombre-real # solo si el nombre real difiere del id
# minVersion: "1.2.3" # versión mínima aceptable (opcional)
# maxVersion: "1.2.3" # versión máxima aceptable (opcional)
```
Campos:
- `id` — requerido, camelCase
- `package` — opcional; omitir si el id coincide con el nombre real del paquete apt
- `minVersion` — opcional; versión mínima aceptable (cadena de versión Debian, ej. `"2.38.1"`)
- `maxVersion` — opcional; versión máxima a instalar
**Comportamiento de instalación según los campos presentes:**
| `minVersion` | `maxVersion` | Qué instala |
|---|---|---|
| — | — | última versión disponible |
| `1.0` | — | última disponible (best-effort ≥ 1.0) |
| — | `2.0` | `package=2.0` |
| `1.0` | `2.0` | `package=2.0` (techo del rango aceptable) |
| `1.5` | `1.5` | `package=1.5` (versión exacta) |
Cuando `maxVersion` está presente, el módulo siempre instala esa versión exacta con `--allow-downgrades`, lo que garantiza que la máquina no quede con una versión superior al límite aunque ya tuviera el paquete instalado. Si la versión instalada ya satisface el rango `[minVersion, maxVersion]`, el paso se omite sin reinstalar.
### `ppa` — repositorio PPA de Ubuntu
```yaml
- id: nombrePpa
type: ppa
address: ppa:usuario/repositorio
```
Campos:
- `id` — requerido, camelCase
- `address` — requerido, formato `ppa:usuario/repositorio`
> Debe aparecer en el archivo **antes** del `apt` que instala paquetes de ese PPA.
### `snap` — aplicación snap
```yaml
- id: nombreApp
type: snap
# package: nombre-snap # solo si el nombre real difiere del id
# classic: true # solo si el snap requiere confinamiento clásico
```
Campos:
- `id` — requerido, camelCase
- `package` — opcional
- `classic` — opcional; omitir si es `false`
### `flatpak` — aplicación Flatpak
```yaml
- id: nombreApp
type: flatpak
appId: com.ejemplo.App
# remote: flathub # opcional; flathub por defecto
```
Campos:
- `id` — requerido, camelCase
- `appId` — requerido, ID completo de la aplicación (ej. `org.gimp.GIMP`)
- `remote` — opcional; omitir si es flathub
### `dotfile` — configuración enlazada a `$HOME`
```yaml
- id: nombreApp
type: dotfile
```
Campos:
- `id` — requerido, camelCase; debe coincidir con una carpeta dentro de `dotfiles/`
Los archivos en `dotfiles/nombreApp/` se enlazan simbólicamente a su ruta equivalente en `$HOME`.
Ejemplo: `dotfiles/bash/.bashrc``~/.bashrc`
Si la carpeta `dotfiles/nombreApp/` no existe, crearla y colocar los archivos adentro antes de agregar la entrada.
### `service` — servicio systemd
```yaml
- id: nombreServicio
type: service
# name: nombre-servicio # solo si el nombre real difiere del id
scope: system # system | user
state: enable # enable | disable | mask
```
Campos:
- `id` — requerido, camelCase
- `name` — opcional
- `scope` — requerido: `system` o `user`
- `state` — requerido: `enable`, `disable` o `mask`
### `pipewire` — configuración completa de PipeWire
```yaml
- id: pipewire
type: pipewire
replacePulseaudio: true
packages:
- pipewire
- pipewire-audio
- pipewire-pulse
- wireplumber
- libspa-0.2-bluetooth
userServices:
- pipewire
- pipewire-pulse
- wireplumber
```
Campos:
- `replacePulseaudio``true` para deshabilitar y enmascarar PulseAudio
- `packages` — lista de paquetes apt a instalar
- `userServices` — lista de servicios systemd de usuario a habilitar
### `video` — drivers de GPU (detección automática con `lspci`)
```yaml
- id: gpuDrivers
type: video
drivers:
nvidia:
packages:
- nvidia-driver-535
- nvidia-settings
amd:
packages:
- mesa-vulkan-drivers
- radeontop
- vainfo
intel:
packages:
- intel-media-va-driver
- vainfo
```
Solo se instalan los paquetes del vendor detectado. Podés incluir uno, dos o los tres vendors.
---
### `docker` — configuración Docker (Compose o Dockerfile)
```yaml
- id: nombreServicio
type: docker
# destination: ~/docker/nombreServicio # destino en el sistema (opcional, ese es el valor por defecto)
# autostart: true # ejecutar docker compose up -d tras el deploy (opcional, false por defecto)
```
Campos:
- `id` — requerido, camelCase
- `destination` — opcional; directorio de destino donde se enlazan los archivos. Soporta `~`. Por defecto: `~/docker/<id>`
- `autostart` — opcional; si `true`, ejecuta `docker compose up -d` después de enlazar los archivos. Requiere un archivo compose en el destino
Los archivos del servicio deben estar en `docker/<id>/` dentro del repo. Se enlazan simbólicamente a `destination/`, respetando la estructura de subdirectorios.
Ejemplo de estructura:
```
docker/
miServicio/
compose.yaml
.env.example
config/
nginx.conf
```
Si Docker no está instalado en la máquina, el paso se saltea con un aviso sin interrumpir el resto del STP.
---
## Convenciones
- `id` siempre en **camelCase**, sin guiones ni guiones bajos
- Omitir campos opcionales cuando no aplican — no usar `null` ni strings vacíos
- El campo `name`, `package` o `appId` solo es necesario cuando difiere del `id`
+20
View File
@@ -0,0 +1,20 @@
Cifrá las claves SSH para almacenarlas en el repo.
Ejecutá el siguiente comando y seguí las instrucciones en pantalla:
```bash
bash scripts/encryptSsh.sh
```
Si el archivo `secrets/sshKeys.tar.gz.age` se genera correctamente, hacé commit:
```bash
git add secrets/sshKeys.tar.gz.age
git commit -m "update encrypted ssh keys"
```
Para verificar que el archivo se puede descifrar antes de hacer commit:
```bash
age --decrypt secrets/sshKeys.tar.gz.age | tar -tzv
```
+47
View File
@@ -0,0 +1,47 @@
Creá un nuevo módulo STP llamado `$ARGUMENTS`.
Seguí estos pasos:
1. Creá el archivo `modules/$ARGUMENTS.sh` respetando la guía de estilo de `.claude/style.md`:
```bash
#!/usr/bin/env bash
set -euo pipefail
source "$stpRoot/lib/log.sh"
source "$stpRoot/lib/utils.sh"
source "$stpRoot/lib/yaml.sh"
readonly nombreConfig="$stpRoot/config/$ARGUMENTS.yaml"
# Funciones auxiliares (camelCase, una responsabilidad cada una)
helperEspecifico() { ... }
# Función principal
configurar$ARGUMENTS() {
log::info "Configurando $ARGUMENTS..."
helperEspecifico
log::ok "$ARGUMENTS configurado"
}
# Verificación inicial: salir si no hay configuración
if [[ ! -f "$nombreConfig" ]]; then
log::info "Sin configuración para $ARGUMENTS, salteando"
exit 0
fi
configurar$ARGUMENTS
```
2. Si el módulo necesita configuración propia, creá `config/$ARGUMENTS.yaml`.
3. Agregá `$ARGUMENTS` en `config/modules` en la posición lógica dentro del flujo:
`bootstrap → ssh → registry → <nombre>`
4. Dale permisos de ejecución: `chmod +x modules/$ARGUMENTS.sh`
Convenciones obligatorias (ver `.claude/style.md`):
- camelCase para todas las variables y funciones, sin guión bajo
- Nombres completos, sin abreviaciones (`packageName` no `pkg`)
- Funciones booleanas como preguntas (`configuraciónEsVálida()`)
- Verificar estado antes de actuar (idempotencia)
- Comandos directos — no usar `util::run`
+18
View File
@@ -0,0 +1,18 @@
{
"permissions": {
"allow": [
"Bash(bash:*)",
"Bash(chmod:*)",
"Bash(find:*)",
"Bash(grep:*)",
"Bash(ls:*)",
"Bash(mv:*)",
"Bash(cp:*)",
"Bash(mkdir:*)",
"Bash(yq:*)",
"Bash(age:*)",
"Bash(tar:*)",
"Bash(git:*)"
]
}
}
+146
View File
@@ -0,0 +1,146 @@
# Guía de estilo — STP
## Nomenclatura: camelCase sin excepciones
Todo identificador del proyecto usa camelCase. No se usa guión bajo (`_`) ni guión (`-`) en nombres de variables, funciones o archivos Bash.
| Tipo | Correcto | Incorrecto |
|------|----------|------------|
| Variable local | `packageName` | `package_name`, `pkg` |
| Variable exportada | `stpRoot`, `verbose` | `STP_ROOT`, `VERBOSE` |
| Constante (`readonly`) | `readonly packagesConfig` | `readonly PACKAGES_CONFIG` |
| Función | `installAptPackages()` | `install_apt_packages()` |
| Función privada | `log::emit` | `log::_emit`, `_emit` |
| Archivo Bash | `encryptSsh.sh` | `encrypt-ssh.sh`, `encrypt_ssh.sh` |
Los nombres de archivos YAML (`packages.yaml`, `audio.yaml`) son palabras simples y no requieren separador.
---
## Clean Code — Robert C. Martin
### Una función, una responsabilidad
Cada función hace exactamente una cosa. Un bloque de código que necesita un comentario para explicarse debe convertirse en una función con un nombre descriptivo.
```bash
# MAL — lógica mezclada con comentario explicativo
if apt-cache show age &>/dev/null 2>&1; then
sudo apt-get install -y age # instala desde apt si está disponible
else
# descarga binario de GitHub...
fi
# BIEN — la intención es legible sin comentarios
if ageIsAvailableInApt; then
installAgeFromApt
else
installAgeFromGithub
fi
```
### Nombres completos, sin abreviaciones
```bash
# MAL
for pkg in "${packages[@]}"; do ...
for svc in "${services[@]}"; do ...
# BIEN
for packageName in "${packages[@]}"; do ...
for serviceName in "${services[@]}"; do ...
```
Palabras prohibidas como nombre de variable: `pkg`, `svc`, `src`, `dest`, `dir`, `tmp`, `cfg`, `val`, `ret`, `f`, `i` (salvo índices numéricos en bucles simples).
### Predicados como preguntas
Las funciones booleanas se nombran como preguntas que responden verdadero o falso:
```bash
pipewireIsEnabled() # ¿PipeWire está habilitado en la config?
isPpaAlreadyAdded() # ¿el PPA ya fue registrado en sources.list?
dotfilesDirectoryIsEmpty() # ¿no hay dotfiles para desplegar?
```
### Comentarios solo para el PORQUÉ
El código expresa el QUÉ. Solo se agrega un comentario cuando el PORQUÉ no es evidente: una restricción externa, una solución provisional, un comportamiento sorpresivo.
```bash
# MAL — el comentario dice lo mismo que el código
# Verifica si el PPA ya está en sources.list.d
isPpaAlreadyAdded "$ppaAddress" && continue
# BIEN — el código habla por sí mismo; el comentario solo si hay un "porqué" no obvio
```
---
## Código funcional sin modo simulación
No existe `--dry-run`. El sistema es **idempotente**: verifica el estado actual antes de actuar y omite lo que ya está configurado.
```bash
# Antes de instalar un paquete
util::isAptInstalled "$packageName" && continue
# Antes de agregar un PPA
isPpaAlreadyAdded "$ppaAddress" && continue
# Antes de instalar una clave SSH
[[ -f "$destination" ]] && { log::warn "Ya existe: $keyFilename"; return 1; }
```
Ejecutar el STP dos veces produce exactamente el mismo resultado que ejecutarlo una vez.
---
## Agregar una nueva configuración al sistema
La forma principal de agregar algo al STP es agregar una entrada a `config/registry.yaml`. No se necesita crear un módulo nuevo para la mayoría de los casos.
```yaml
# Basta con agregar una línea a la lista:
- id: neovim
type: apt
# La próxima ejecución de stp.sh la detecta y aplica
```
Tipos disponibles en el registro: `ppa`, `apt`, `snap`, `flatpak`, `dotfile`, `service`, `pipewire`, `video`.
Solo creá un módulo nuevo cuando la configuración requiera pasos que ningún tipo del registro puede manejar (compilación desde fuente, instaladores externos, configuración interactiva, etc.).
---
## Estructura de un módulo
```bash
#!/usr/bin/env bash
set -euo pipefail
source "$stpRoot/lib/log.sh"
source "$stpRoot/lib/utils.sh"
source "$stpRoot/lib/yaml.sh" # solo si el módulo lee YAML
readonly nombreConfig="$stpRoot/config/nombre.yaml"
# Funciones auxiliares específicas (las más pequeñas y concretas)
helperEspecifico() { ... }
# Funciones principales que usan las auxiliares
hacerAlgo() {
log::info "Descripción breve..."
helperEspecifico
log::ok "Completado"
}
# Verificación inicial: salir si no hay configuración
if [[ ! -f "$nombreConfig" ]]; then
log::info "Sin configuración para este módulo, salteando"
exit 0
fi
# Punto de entrada al final del archivo
hacerAlgo
```