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:
@@ -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`
|
||||
@@ -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
|
||||
```
|
||||
@@ -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`
|
||||
@@ -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:*)"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
```
|
||||
Reference in New Issue
Block a user