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
+82
View File
@@ -0,0 +1,82 @@
#!/usr/bin/env bash
set -euo pipefail
source "$stpRoot/lib/log.sh"
source "$stpRoot/lib/utils.sh"
latestGithubReleaseTag() {
local repository="$1"
curl -fsSL "https://api.github.com/repos/${repository}/releases/latest" \
| grep '"tag_name"' \
| cut -d'"' -f4
}
ensureBasePackagesAreInstalled() {
local requiredPackages=(curl wget git nano gpg ca-certificates)
local missingPackages=()
for packageName in "${requiredPackages[@]}"; do
util::isAptInstalled "$packageName" || missingPackages+=("$packageName")
done
if [[ ${#missingPackages[@]} -gt 0 ]]; then
util::aptUpdateOnce
sudo apt-get install -y "${missingPackages[@]}"
fi
}
installYqFromGithub() {
local version
version="$(latestGithubReleaseTag "mikefarah/yq")"
sudo wget -qO /usr/local/bin/yq \
"https://github.com/mikefarah/yq/releases/download/${version}/yq_linux_amd64"
sudo chmod +x /usr/local/bin/yq
log::ok "yq ${version} instalado"
}
ensureYqIsInstalled() {
if util::cmdExists yq; then
log::info "yq ya disponible: $(yq --version 2>&1 | head -1)"
return
fi
log::info "Instalando yq..."
installYqFromGithub
}
ageIsAvailableInApt() {
apt-cache show age &>/dev/null 2>&1
}
installAgeFromApt() {
sudo apt-get install -y age
}
installAgeFromGithub() {
local version
version="$(latestGithubReleaseTag "FiloSottile/age")"
local temporaryDirectory
temporaryDirectory="$(mktemp -d)"
trap 'rm -rf "$temporaryDirectory"' RETURN
curl -fsSL \
"https://github.com/FiloSottile/age/releases/download/${version}/age-${version}-linux-amd64.tar.gz" \
| tar -xz -C "$temporaryDirectory"
sudo mv "$temporaryDirectory/age/age" "$temporaryDirectory/age/age-keygen" /usr/local/bin/
}
ensureAgeIsInstalled() {
if util::cmdExists age; then
log::info "age ya disponible: $(age --version 2>&1)"
return
fi
log::info "Instalando age..."
if ageIsAvailableInApt; then
installAgeFromApt
else
installAgeFromGithub
fi
log::ok "age instalado"
}
log::info "Verificando dependencias base"
ensureBasePackagesAreInstalled
ensureYqIsInstalled
ensureAgeIsInstalled
+37
View File
@@ -0,0 +1,37 @@
#!/usr/bin/env bash
set -euo pipefail
source "$stpRoot/lib/log.sh"
source "$stpRoot/lib/utils.sh"
readonly claudeCodeKeyring="/etc/apt/keyrings/claude-code.asc"
readonly claudeCodeSources="/etc/apt/sources.list.d/claude-code.list"
readonly claudeCodeSourcesEntry="deb [signed-by=${claudeCodeKeyring}] https://downloads.claude.ai/claude-code/apt/stable stable main"
repoIsConfigured() {
[[ -f "$claudeCodeSources" ]]
}
addSigningKey() {
sudo install -m 644 "$stpRoot/config/keys/claude-code.asc" "$claudeCodeKeyring"
log::ok "Clave GPG instalada: $claudeCodeKeyring"
}
addRepository() {
echo "$claudeCodeSourcesEntry" | sudo tee "$claudeCodeSources" > /dev/null
sudo apt-get update -qq
log::ok "Repositorio configurado: $claudeCodeSources"
}
if repoIsConfigured; then
log::info "Repositorio claude-code ya configurado"
else
addSigningKey
addRepository
fi
if util::isAptInstalled "claude-code"; then
log::info "Ya instalado (apt): claude-code"
else
sudo apt-get install -y claude-code
log::ok "Instalado: claude-code"
fi
+55
View File
@@ -0,0 +1,55 @@
#!/usr/bin/env bash
set -euo pipefail
source "$stpRoot/lib/log.sh"
source "$stpRoot/lib/utils.sh"
readonly cupsArchive="$stpRoot/secrets/cupsConfig.zip"
cupsIsInstalled() {
util::cmdExists lpstat
}
printersAreConfigured() {
sudo test -s /etc/cups/printers.conf
}
restoreConfig() {
log::info "Restaurando configuración de CUPS desde secrets/cupsConfig.zip..."
unzip -q "$cupsArchive" -d "$cupsTemp"
sudo cp "$cupsTemp/printers.conf" /etc/cups/printers.conf
sudo chown root:lp /etc/cups/printers.conf
sudo chmod 600 /etc/cups/printers.conf
if [[ -d "$cupsTemp/ppd" ]]; then
sudo mkdir -p /etc/cups/ppd
sudo cp -r "$cupsTemp/ppd/." /etc/cups/ppd/
sudo chown -R root:lp /etc/cups/ppd/
sudo chmod 644 /etc/cups/ppd/*.ppd 2>/dev/null || true
fi
sudo systemctl restart cups
log::ok "Configuración de CUPS restaurada"
}
if ! cupsIsInstalled; then
log::info "CUPS no instalado, salteando"
exit 0
fi
if [[ ! -f "$cupsArchive" ]]; then
log::info "Sin respaldo de CUPS (secrets/cupsConfig.zip), salteando"
exit 0
fi
util::requireSudo
if printersAreConfigured; then
log::info "CUPS ya tiene impresoras configuradas, salteando restauración"
exit 0
fi
cupsTemp="$(mktemp -d)"
trap 'rm -rf "$cupsTemp"' EXIT
restoreConfig
+41
View File
@@ -0,0 +1,41 @@
#!/usr/bin/env bash
set -euo pipefail
source "$stpRoot/lib/log.sh"
source "$stpRoot/lib/utils.sh"
readonly easyEffectsAppId="com.github.wwmm.easyeffects"
readonly easyEffectsApplicationDirectory="$HOME/.var/app/com.github.wwmm.easyeffects"
readonly easyEffectsConfig="$easyEffectsApplicationDirectory/config/easyeffects/db/easyeffectsrc"
readonly easyEffectsArchive="$stpRoot/secrets/easyEffectsConfig.zip"
easyEffectsIsInstalled() {
util::isFlatpakInstalled "$easyEffectsAppId"
}
easyEffectsConfigExists() {
[[ -f "$easyEffectsConfig" ]]
}
restoreConfig() {
log::info "Restaurando configuración desde secrets/easyEffectsConfig.zip..."
mkdir -p "$easyEffectsApplicationDirectory"
unzip -qo "$easyEffectsArchive" -d "$easyEffectsApplicationDirectory"
log::ok "Configuración restaurada en: $easyEffectsApplicationDirectory"
}
if ! easyEffectsIsInstalled; then
log::info "EasyEffects no instalado aún, salteando restauración de config"
exit 0
fi
if [[ ! -f "$easyEffectsArchive" ]]; then
log::info "Sin respaldo de configuración (secrets/easyEffectsConfig.zip), salteando"
exit 0
fi
if easyEffectsConfigExists; then
log::info "Configuración de EasyEffects ya existe, salteando restauración"
exit 0
fi
restoreConfig
+414
View File
@@ -0,0 +1,414 @@
#!/usr/bin/env bash
set -euo pipefail
source "$stpRoot/lib/log.sh"
source "$stpRoot/lib/utils.sh"
source "$stpRoot/lib/yaml.sh"
readonly registryConfig="$stpRoot/config/registry.yaml"
fieldOf() {
local entryId="$1" field="$2" default="${3:-}"
local value
value="$(yq ".registry[] | select(.id == \"$entryId\") | .${field}" "$registryConfig" 2>/dev/null)"
if util::isYamlNull "$value"; then
echo "$default"
else
echo "$value"
fi
}
arrayFieldOf() {
local entryId="$1" field="$2"
yq -r ".registry[] | select(.id == \"$entryId\") | .${field}[]" "$registryConfig" 2>/dev/null || true
}
# ---- PPA ----
ppaOwnerFrom() {
local ppaAddress="$1"
local owner="${ppaAddress#ppa:}"
echo "${owner%%/*}"
}
ppaIsAlreadyAdded() {
local ppaAddress="$1"
find /etc/apt/sources.list.d/ -name "*$(ppaOwnerFrom "$ppaAddress")*" 2>/dev/null | grep -q .
}
applyPpa() {
local entryId="$1"
local ppaAddress
ppaAddress="$(fieldOf "$entryId" "address")"
if ppaIsAlreadyAdded "$ppaAddress"; then
log::info "PPA ya existe: $ppaAddress"
return
fi
util::cmdExists add-apt-repository \
|| sudo apt-get install -y software-properties-common
sudo add-apt-repository -y "$ppaAddress"
sudo apt-get update -qq
log::ok "PPA agregado: $ppaAddress"
}
# ---- APT ----
installedVersionOf() {
local packageName="$1"
dpkg-query -W -f='${Version}' "$packageName" 2>/dev/null
}
aptVersionIsInRange() {
local installedVersion="$1" minVersion="$2" maxVersion="$3"
if [[ -n "$minVersion" ]] && dpkg --compare-versions "$installedVersion" lt "$minVersion"; then
return 1
fi
if [[ -n "$maxVersion" ]] && dpkg --compare-versions "$installedVersion" gt "$maxVersion"; then
return 1
fi
return 0
}
applyAptPackage() {
local entryId="$1"
local packageName minVersion maxVersion
packageName="$(fieldOf "$entryId" "package" "$entryId")"
minVersion="$(fieldOf "$entryId" "minVersion")"
maxVersion="$(fieldOf "$entryId" "maxVersion")"
if util::isAptInstalled "$packageName"; then
local installedVersion
installedVersion="$(installedVersionOf "$packageName")"
if aptVersionIsInRange "$installedVersion" "$minVersion" "$maxVersion"; then
log::info "Ya instalado (apt): $packageName ($installedVersion)"
return
fi
log::warn "$packageName: versión $installedVersion instalada, fuera del rango requerido"
fi
util::aptUpdateOnce
if [[ -n "$maxVersion" ]]; then
sudo apt-get install -y --allow-downgrades "${packageName}=${maxVersion}"
log::ok "Instalado (apt): ${packageName}=${maxVersion}"
else
sudo apt-get install -y "$packageName"
log::ok "Instalado (apt): $packageName"
fi
}
# ---- SNAP ----
applySnapPackage() {
local entryId="$1"
local packageName classicFlag installFlags
packageName="$(fieldOf "$entryId" "package" "$entryId")"
classicFlag="$(fieldOf "$entryId" "classic" "false")"
installFlags=()
[[ "$classicFlag" == "true" ]] && installFlags+=(--classic)
if util::isSnapInstalled "$packageName"; then
log::info "Ya instalado (snap): $packageName"
return
fi
if ! util::cmdExists snap; then
log::warn "snap no disponible, salteando: $entryId"
return
fi
sudo snap install "$packageName" "${installFlags[@]}"
log::ok "Instalado (snap): $packageName"
}
# ---- FLATPAK ----
applyFlatpakApp() {
local entryId="$1"
local appId remote
appId="$(fieldOf "$entryId" "appId" "$entryId")"
remote="$(fieldOf "$entryId" "remote" "flathub")"
if util::isFlatpakInstalled "$appId"; then
log::info "Ya instalado (flatpak): $appId"
return
fi
if ! util::cmdExists flatpak; then
log::warn "flatpak no disponible, salteando: $entryId"
return
fi
flatpak install -y "$remote" "$appId"
log::ok "Instalado (flatpak): $appId"
}
# ---- DOTFILE ----
dotfileSymlinkAlreadyExists() {
local destination="$1"
[[ -L "$destination" ]]
}
backupExistingFile() {
local destination="$1"
[[ -e "$destination" ]] || return 0
mv "$destination" "${destination}.stpbackup"
log::info "Copia de seguridad: ~/${destination#${HOME}/}"
}
deployFileFromElement() {
local sourceFile="$1" elementSourceDirectory="$2"
local relativePath destination
relativePath="${sourceFile#${elementSourceDirectory}/}"
destination="$HOME/$relativePath"
if dotfileSymlinkAlreadyExists "$destination"; then
log::info "Enlace ya existe: ~/$relativePath"
return
fi
backupExistingFile "$destination"
mkdir -p "$(dirname "$destination")"
ln -sfn "$sourceFile" "$destination"
log::ok "Enlazado: ~/$relativePath"
}
applyDotfile() {
local entryId="$1"
local elementSourceDirectory="$stpRoot/dotfiles/$entryId"
if [[ ! -d "$elementSourceDirectory" ]]; then
log::warn "Carpeta no encontrada: dotfiles/$entryId/"
return
fi
while IFS= read -r sourceFile; do
deployFileFromElement "$sourceFile" "$elementSourceDirectory"
done < <(find "$elementSourceDirectory" -not -type d | sort)
}
# ---- SERVICE ----
applyService() {
local entryId="$1"
local serviceName scope state
serviceName="$(fieldOf "$entryId" "name" "$entryId")"
scope="$(fieldOf "$entryId" "scope" "system")"
state="$(fieldOf "$entryId" "state" "enable")"
local scopeFlags=()
[[ "$scope" == "user" ]] && scopeFlags+=(--user)
case "$state" in
enable)
systemctl "${scopeFlags[@]}" enable --now "$serviceName" 2>/dev/null \
|| log::warn "No se pudo habilitar: $serviceName"
;;
disable)
systemctl "${scopeFlags[@]}" disable --now "$serviceName" 2>/dev/null || true
;;
mask)
systemctl "${scopeFlags[@]}" mask "$serviceName" 2>/dev/null || true
;;
esac
log::ok "Servicio [$scope] $state: $serviceName"
}
# ---- PIPEWIRE ----
allPipewirePackagesAreInstalled() {
local entryId="$1"
while IFS= read -r packageName; do
util::isYamlNull "$packageName" && continue
util::isAptInstalled "$packageName" || return 1
done < <(arrayFieldOf "$entryId" "packages")
}
installMissingPipewirePackages() {
local entryId="$1"
local missingPackages=()
while IFS= read -r packageName; do
util::isYamlNull "$packageName" && continue
util::isAptInstalled "$packageName" || missingPackages+=("$packageName")
done < <(arrayFieldOf "$entryId" "packages")
if [[ ${#missingPackages[@]} -gt 0 ]]; then
util::aptUpdateOnce
sudo apt-get install -y "${missingPackages[@]}"
fi
}
disablePulseaudio() {
systemctl --user --now disable pulseaudio.service pulseaudio.socket 2>/dev/null || true
systemctl --user mask pulseaudio 2>/dev/null || true
}
enablePipewireUserServices() {
local entryId="$1"
while IFS= read -r serviceName; do
util::isYamlNull "$serviceName" && continue
systemctl --user --now enable "$serviceName" 2>/dev/null \
|| log::warn "No se pudo habilitar servicio de usuario: $serviceName"
done < <(arrayFieldOf "$entryId" "userServices")
}
applyPipewire() {
local entryId="$1"
allPipewirePackagesAreInstalled "$entryId" \
|| installMissingPipewirePackages "$entryId"
local shouldReplace
shouldReplace="$(fieldOf "$entryId" "replacePulseaudio" "false")"
[[ "$shouldReplace" == "true" ]] && disablePulseaudio
enablePipewireUserServices "$entryId"
log::ok "PipeWire configurado"
}
# ---- VIDEO ----
detectGpuVendor() {
if lspci 2>/dev/null | grep -qi nvidia; then echo "nvidia"
elif lspci 2>/dev/null | grep -qi "amd\|radeon"; then echo "amd"
elif lspci 2>/dev/null | grep -qi intel; then echo "intel"
else echo "unknown"
fi
}
applyVideoDrivers() {
local entryId="$1"
local gpuVendor
gpuVendor="$(detectGpuVendor)"
log::info "GPU detectada: $gpuVendor"
if [[ "$gpuVendor" == "unknown" ]]; then
log::warn "No se pudo identificar la GPU, salteando drivers"
return
fi
local missingPackages=()
while IFS= read -r packageName; do
util::isYamlNull "$packageName" && continue
util::isAptInstalled "$packageName" || missingPackages+=("$packageName")
done < <(arrayFieldOf "$entryId" "drivers.${gpuVendor}.packages")
if [[ ${#missingPackages[@]} -eq 0 ]]; then
log::info "Drivers $gpuVendor ya instalados"
return
fi
util::aptUpdateOnce
sudo apt-get install -y "${missingPackages[@]}"
log::ok "Drivers $gpuVendor instalados"
}
# ---- DOCKER ----
dockerIsInstalled() {
util::cmdExists docker
}
findDockerComposeFile() {
local destinationDirectory="$1"
local composeFile
for composeFile in "compose.yaml" "compose.yml" "docker-compose.yaml" "docker-compose.yml"; do
[[ -f "$destinationDirectory/$composeFile" ]] && echo "$destinationDirectory/$composeFile" && return 0
done
return 1
}
deployDockerConfigFile() {
local sourceFile="$1" sourceDirectory="$2" destinationDirectory="$3"
local relativePath destination
relativePath="${sourceFile#${sourceDirectory}/}"
destination="$destinationDirectory/$relativePath"
if [[ -L "$destination" ]]; then
log::info "Enlace ya existe: $relativePath"
return
fi
[[ -e "$destination" ]] && mv "$destination" "${destination}.stpbackup"
mkdir -p "$(dirname "$destination")"
ln -sfn "$sourceFile" "$destination"
log::ok "Enlazado: $relativePath"
}
applyDocker() {
local entryId="$1"
local rawDestination destination autostart
rawDestination="$(fieldOf "$entryId" "destination" "~/docker/$entryId")"
destination="${rawDestination/#\~/$HOME}"
autostart="$(fieldOf "$entryId" "autostart" "false")"
local sourceDirectory="$stpRoot/docker/$entryId"
if [[ ! -d "$sourceDirectory" ]]; then
log::warn "Carpeta no encontrada: docker/$entryId/"
return
fi
if ! dockerIsInstalled; then
log::warn "Docker no instalado, salteando: $entryId"
return
fi
mkdir -p "$destination"
while IFS= read -r sourceFile; do
deployDockerConfigFile "$sourceFile" "$sourceDirectory" "$destination"
done < <(find "$sourceDirectory" -not -type d | sort)
if [[ "$autostart" == "true" ]]; then
local composeFile
if composeFile="$(findDockerComposeFile "$destination")"; then
docker compose -f "$composeFile" up -d \
|| log::warn "No se pudo iniciar el servicio: $entryId"
log::ok "Servicio Docker iniciado: $entryId"
else
log::warn "No se encontró archivo compose en: $destination"
fi
fi
log::ok "Docker configurado: $entryId"
}
# ---- Dispatcher ----
dispatchByType() {
local entryId="$1" entryType="$2"
case "$entryType" in
ppa) applyPpa "$entryId" ;;
apt) applyAptPackage "$entryId" ;;
snap) applySnapPackage "$entryId" ;;
flatpak) applyFlatpakApp "$entryId" ;;
dotfile) applyDotfile "$entryId" ;;
service) applyService "$entryId" ;;
pipewire) applyPipewire "$entryId" ;;
video) applyVideoDrivers "$entryId" ;;
docker) applyDocker "$entryId" ;;
*) log::warn "Tipo no reconocido '$entryType' en entrada: $entryId" ;;
esac
}
processRegistry() {
local totalProcessed=0
while IFS=$'\t' read -r entryId entryType; do
util::isYamlNull "$entryId" && continue
util::isYamlNull "$entryType" && continue
log::info "[$entryType] $entryId"
dispatchByType "$entryId" "$entryType"
((++totalProcessed))
done < <(yq -r '.registry[] | [.id, .type] | @tsv' "$registryConfig")
log::ok "$totalProcessed entrada(s) del registro procesadas"
}
if ! yaml::has "$registryConfig" ".registry[0]"; then
log::info "Registro vacío, salteando"
exit 0
fi
processRegistry
Executable
+84
View File
@@ -0,0 +1,84 @@
#!/usr/bin/env bash
set -euo pipefail
source "$stpRoot/lib/log.sh"
source "$stpRoot/lib/utils.sh"
readonly sshDestination="$HOME/.ssh"
readonly encryptedArchive="$stpRoot/secrets/sshKeys.tar.gz.age"
sshPermissionsForFile() {
local keyFilename="$1"
case "$keyFilename" in
*.pub|known_hosts|config) echo 644 ;;
*) echo 600 ;;
esac
}
ensureSshDirectoryExists() {
if [[ ! -d "$sshDestination" ]]; then
mkdir -p "$sshDestination"
chmod 700 "$sshDestination"
fi
}
decryptArchiveInto() {
local workingDirectory="$1"
log::info "Ingresá la passphrase para descifrar las claves SSH:"
if ! age -d -o "$workingDirectory/sshKeys.tar.gz" "$encryptedArchive"; then
log::error "Error al descifrar. Verificá la passphrase."
return 1
fi
tar -xzf "$workingDirectory/sshKeys.tar.gz" -C "$workingDirectory"
}
installSshKey() {
local sourceFile="$1"
local keyFilename
keyFilename="$(basename "$sourceFile")"
local destination="$sshDestination/$keyFilename"
if [[ -f "$destination" ]]; then
log::warn "Ya existe (salteando): $keyFilename"
return 1
fi
cp "$sourceFile" "$destination"
chmod "$(sshPermissionsForFile "$keyFilename")" "$destination"
log::ok "Instalada: $keyFilename"
}
installAllKeysFrom() {
local sourceDirectory="$1"
local installedCount=0 skippedCount=0
for sourceFile in "$sourceDirectory/.ssh/"*; do
[[ -f "$sourceFile" ]] || continue
if installSshKey "$sourceFile"; then
((++installedCount))
else
((++skippedCount))
fi
done
log::ok "$installedCount clave(s) instaladas, $skippedCount salteada(s)"
}
if [[ ! -f "$encryptedArchive" ]]; then
log::warn "Archivo de claves no encontrado: secrets/sshKeys.tar.gz.age"
log::warn "Para cifrar tus claves actuales: bash scripts/encryptSsh.sh"
exit 0
fi
if ! util::cmdExists age; then
log::error "age no está instalado. Ejecutá primero el módulo bootstrap"
exit 1
fi
log::info "Restaurando claves SSH..."
workingDirectory="$(mktemp -d)"
trap 'rm -rf "$workingDirectory"' EXIT
decryptArchiveInto "$workingDirectory"
ensureSshDirectoryExists
installAllKeysFrom "$workingDirectory"
+46
View File
@@ -0,0 +1,46 @@
#!/usr/bin/env bash
set -euo pipefail
source "$stpRoot/lib/log.sh"
source "$stpRoot/lib/utils.sh"
readonly thunderbirdApplicationDirectory="$HOME/snap/thunderbird/common"
readonly thunderbirdProfileDirectory="$thunderbirdApplicationDirectory/.thunderbird"
readonly thunderbirdArchive="$stpRoot/secrets/thunderbirdProfile.zip"
thunderbirdIsInstalled() {
util::isSnapInstalled "thunderbird"
}
thunderbirdProfileExists() {
[[ -f "$thunderbirdProfileDirectory/profiles.ini" ]]
}
installThunderbird() {
sudo snap install thunderbird
log::ok "Thunderbird instalado"
}
restoreProfile() {
log::info "Restaurando perfil desde secrets/thunderbirdProfile.zip..."
mkdir -p "$thunderbirdApplicationDirectory"
unzip -qo "$thunderbirdArchive" -d "$thunderbirdApplicationDirectory"
log::ok "Perfil restaurado en: $thunderbirdProfileDirectory"
}
if thunderbirdIsInstalled; then
log::info "Thunderbird ya instalado"
else
installThunderbird
fi
if [[ ! -f "$thunderbirdArchive" ]]; then
log::info "Sin respaldo de perfil (secrets/thunderbirdProfile.zip), salteando restauración"
exit 0
fi
if thunderbirdProfileExists; then
log::info "Perfil de Thunderbird ya existe, salteando restauración"
exit 0
fi
restoreProfile
+40
View File
@@ -0,0 +1,40 @@
#!/usr/bin/env bash
set -euo pipefail
source "$stpRoot/lib/log.sh"
source "$stpRoot/lib/utils.sh"
readonly wireplumberStateDirectory="$HOME/.local/state/wireplumber"
readonly wireplumberArchive="$stpRoot/secrets/wireplumberState.zip"
wireplumberIsInstalled() {
util::cmdExists wireplumber
}
wireplumberStateExists() {
[[ -f "$wireplumberStateDirectory/default-nodes" ]]
}
restoreState() {
log::info "Restaurando estado desde secrets/wireplumberState.zip..."
mkdir -p "$HOME/.local/state"
unzip -qo "$wireplumberArchive" -d "$HOME/.local/state"
log::ok "Estado restaurado en: $wireplumberStateDirectory"
log::info "Reiniciá WirePlumber para aplicar los cambios: systemctl --user restart wireplumber"
}
if ! wireplumberIsInstalled; then
log::info "WirePlumber no instalado, salteando"
exit 0
fi
if [[ ! -f "$wireplumberArchive" ]]; then
log::info "Sin respaldo de estado (secrets/wireplumberState.zip), salteando"
exit 0
fi
if wireplumberStateExists; then
log::info "Estado de WirePlumber ya existe, salteando restauración"
exit 0
fi
restoreState