Skip to content

ander0code/pgflow

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

14 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

🐘 pgflow

Visual TUI for PostgreSQL backup & restore. Dump a production database over an SSH tunnel and restore it into your local Postgres β€” all from a clean terminal UI, without memorizing pg_dump / pg_restore flags. Inspired by lazygit and built as a sibling to lazyports.

 🐘 pgflow          local βœ“ Β· prod βœ“ Β· tΓΊnel abierto        25 backup(s) Β· 16:45:07
 β”Œβ”€ Backups (25) β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”β”Œβ”€ Detalle ──────────────────┐
 β”‚ β–Ύ tienda-web  (21)                          β”‚β”‚ shop_20260610_1645.dump     β”‚
 β”‚    βœ“  shop_20260610_1645.dump   1.7MB       β”‚β”‚                             β”‚
 β”‚    βœ“  shop_20260610_1135.dump   1.7MB       β”‚β”‚ carpeta  tienda-web         β”‚
 β”‚    βœ—  shop_20260610_0801.dump      0B       β”‚β”‚ tamaΓ±o   1.7 MB             β”‚
 β”‚ β–Έ blog  (3)                                 β”‚β”‚ objetos  1004               β”‚
 β”‚ β–Ύ api   (1)                                 β”‚β”‚ creado   2026-06-10 16:46   β”‚
 β”‚    βœ“  api_20260601_0930.dump   4.2MB        β”‚β”‚ estado   βœ“ vΓ‘lido           β”‚
 β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
  b backup  r restore  v verify  d delete  t tΓΊnel  c config  ? ayuda  q quit

Plataformas soportadas: macOS Β· Linux Β· Windows 10 1809+ β€” un solo binario, mismo cΓ³digo fuente.

Por quΓ© pgflow

  • Un solo binario, cero runtime β€” Go puro con Bubble Tea. No hay que instalar Python ni Node.
  • Flujos guiados β€” backup (3 pasos) y restore (4 pasos) como asistentes con barra de progreso, resumen y confirmaciΓ³n. Puedes retroceder con ←.
  • TΓΊnel SSH automΓ‘tico β€” abre el tΓΊnel a producciΓ³n solo cuando hace falta (vΓ­a un alias de ~/.ssh/config con LocalForward).
  • Integridad a la vista β€” cada dump se verifica con pg_restore --list; los corruptos salen marcados con βœ—.
  • Restore seguro β€” --single-transaction (todo o nada). El modo reemplazar (DROP + CREATE) avisa en rojo porque es irreversible.
  • Errores en cristiano β€” tΓΊnel caΓ­do, credenciales, disco lleno, encoding incompatible, objetos duplicados… traducidos a mensajes accionables.
  • Modo CLI β€” pgflow --list [--json] para scripting.

Plataformas y compatibilidad

pgflow se compila desde el mismo cΓ³digo fuente para los tres sistemas operativos. La matriz de soporte:

SO VersiΓ³n mΓ­nima ssh psql / pg_dump / pg_restore Instalador Arquitecturas
macOS 10.15 (Catalina) brew install openssh (o built-in) brew install postgresql bash install.sh amd64 Β· arm64 (Apple Silicon)
Linux glibc 2.31+ (Ubuntu 20.04 / Debian 11 / RHEL 8.4+) openssh-client postgresql-client bash install.sh amd64 Β· arm64
Windows 10 build 1809+ Β· 11 Β· Server 2019+ built-in (OpenSSH client) postgresql.org/download/windows .\install.ps1 amd64 Β· arm64

CΓ³mo se logra la portabilidad

  • TΓΊnel SSH cross-platform. internal/tunnel corre ssh -N <alias> como subprocess de Go (sin ssh -f ni pkill). Cierra por PID via Process.Kill(). Funciona idΓ©ntico en macOS, Linux y Windows β€” el ssh.exe que viene con Windows 10 1809+ respeta los mismos flags.
  • Build tags OS-especΓ­ficos, sin condicionales dispersos. Solo hay un archivo con cΓ³digo distinto por OS: tunnel_windows.go (8 lΓ­neas). El resto del cΓ³digo es portable sin condiciΓ³n.
    • tunnel_unix.go β†’ //go:build !windows β†’ compila en darwin + linux
    • tunnel_windows.go β†’ //go:build windows β†’ compila solo en windows
  • Rutas. Todo via filepath.Join + os.UserHomeDir() β€” Go resuelve %USERPROFILE% en Windows, $HOME en Unix, asΓ­ que ~/.pgflow.conf y ~/.ssh/config "simplemente funcionan".
  • TUI. Bubble Tea v1.3+ trae soporte Windows built-in (tty_windows.go en la lib, abre CONIN$ y activa VT input/output).
  • No hay flash de consola en Windows. Al spawnear ssh.exe como hijo, le aplicamos CREATE_NO_WINDOW (0x08000000) para que no aparezca una ventana parpadeante.
  • Cero dependencias de shell. No usamos pkill, bash, ni nada que no exista igual en los 3 OSes. Todo va por os/exec.

VerificaciΓ³n continua (CI)

.github/workflows/ci.yml corre en cada push/PR:

  • Job test: matrix ubuntu-latest Γ— macos-latest Γ— windows-latest β†’ go build + go vet + gofmt -l + go test -race.
  • Job cross: desde ubuntu-latest, cross-compila los 6 targets (darwin/linux/windows Γ— amd64/arm64) con CGO_ENABLED=0.

Si quieres probarlo en tu mΓ‘quina antes de instalar:

git clone https://github.com/ander0code/pgflow.git ~/pgflow
cd ~/pgflow
go run .                  # necesita un TTY real (no pipelines)

Cross-compile

make build-all            # β†’ dist/pgflow-{darwin,linux,windows}-{amd64,arm64}[.exe] + checksums.txt

Los binarios Windows llevan sufijo .exe; los Unix no.

VerificaciΓ³n SHA256 (recomendada)

Cada release publica dist/checksums.txt con el SHA256 de cada binario. Los instaladores (install.sh / install.ps1) lo descargan y verifican automΓ‘ticamente; si la firma no coincide, no instalan. Para verificar manualmente tras descargar:

sha256sum -c dist/checksums.txt   # macOS / Linux
Get-FileHash dist\pgflow-windows-amd64.exe -Algorithm SHA256   # Windows

Requisitos

  • Go 1.25+ (solo para compilar).
  • En el PATH:
    • psql Β· pg_dump Β· pg_restore (cliente PostgreSQL).
    • ssh (cliente OpenSSH).
SO Comando
macOS brew install go postgresql
Debian / Ubuntu sudo apt install golang postgresql-client openssh-client
Fedora / RHEL sudo dnf install golang postgresql openssh-clients
Arch / Manjaro sudo pacman -S go postgresql-clients openssh
Windows 10/11 Instalar PostgreSQL. OpenSSH ya viene integrado (si no: Add-WindowsCapability -Online -Name OpenSSH.Client~~~~0.0.1.0).

InstalaciΓ³n

RΓ‘pida β€” binario prebuilt (no necesitas Go) ⚑

macOS / Linux:

curl -fsSL https://raw.githubusercontent.com/ander0code/pgflow/main/install.sh | bash

Windows (PowerShell):

irm https://raw.githubusercontent.com/ander0code/pgflow/main/install.ps1 | iex

Descarga el binario de la ΓΊltima release a ~/.local/bin/pgflow (Windows: %LOCALAPPDATA%\pgflow\pgflow.exe). Si ese directorio no estΓ‘ en tu PATH, aΓ±Γ‘delo (ver abajo) y luego ejecuta pgflow.

# macOS / Linux β€” aΓ±ade a ~/.zshrc o ~/.bashrc:
export PATH="$HOME/.local/bin:$PATH"
# Windows β€” PATH de usuario:
[Environment]::SetEnvironmentVariable('Path',"$env:LOCALAPPDATA\pgflow;" + [Environment]::GetEnvironmentVariable('Path','User'),'User')

Desde el cΓ³digo (necesita Go 1.25+)

git clone https://github.com/ander0code/pgflow.git ~/pgflow
cd ~/pgflow
make install            # build + copia a ~/.local/bin   (Windows: .\install.ps1)

Los instaladores install.sh / install.ps1 tambiΓ©n funcionan desde el clon: intentan descargar el release y, si falla, compilan del cΓ³digo (requiere Go).

Sin instalar: go run . Β· desinstalar: bash uninstall.sh (Windows: .\uninstall.ps1).

ConfiguraciΓ³n

La configuraciΓ³n vive en ~/.pgflow.conf (mismo formato que la versiΓ³n anterior, asΓ­ que tu config existente sigue funcionando). EdΓ­tala a mano β€” copia pgflow.conf.example β€” o desde la app en Configurar conexiones (c).

El alias de producciΓ³n debe traer su propio LocalForward en ~/.ssh/config:

Host mi-servidor-db
    HostName 1.2.3.4
    User deploy
    LocalForward 5433 localhost:5432

Las contraseΓ±as pueden ir en ~/.pgflow.conf (con chmod 600 en Unix) o, mejor, en ~/.pgpass. ~/.pgflow.conf estΓ‘ en .gitignore; nunca se sube al repo.

Permisos del rol de producciΓ³n

pg_dump se ejecuta con el rol que configures y necesita poder leer todas las tablas de la base (hace LOCK TABLE … IN ACCESS SHARE MODE sobre todas a la vez). Un rol de aplicaciΓ³n normalmente solo accede a parte de la base, asΓ­ que el backup falla con permission denied for table ….

Dale lectura completa al rol, en el servidor y como admin:

-- PostgreSQL 14+
GRANT pg_read_all_data TO tu_rol;

En versiones anteriores: GRANT SELECT ON ALL TABLES IN SCHEMA public TO tu_rol; (mΓ‘s ALTER DEFAULT PRIVILEGES … GRANT SELECT … para tablas futuras), o usa un rol superusuario para el backup.

Uso

pgflow                 # abre la TUI
pgflow --list          # lista los backups (texto)
pgflow --list --json   # lista los backups (JSON, para jq/pipe)
pgflow --version
pgflow --help

Atajos dentro de la TUI

Tecla AcciΓ³n
↑ ↓ / j k navegar
g / G primero / ΓΊltimo
enter / space colapsar carpeta Β· restaurar el dump seleccionado
b backup de producciΓ³n (asistente)
r restaurar a local (asistente)
v verificar la integridad del dump
d borrar un dump (con confirmaciΓ³n)
t abrir / cerrar el tΓΊnel SSH
c configurar / probar conexiones
^r refrescar
← / h (en los asistentes) volver un paso
esc cancelar el asistente / cerrar modal
q / ctrl+c salir

CΓ³mo estΓ‘ construido

Diagrama de capas

                        β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                        β”‚            main.go              β”‚
                        β”‚  flags CLI + arranque Tea.New   β”‚
                        β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                         β”‚
                        β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                        β”‚           internal/tui          β”‚
                        β”‚  Model Β· Update Β· View          β”‚
                        β”‚  pantallas Β· modales Β· commands β”‚
                        β””β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”˜
                          β”‚          β”‚          β”‚        β”‚
                  β”Œβ”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β” β”Œβ”€β”€β”€β”€β–Όβ”€β”€β”€β”€β” β”Œβ”€β”€β”€β–Όβ”€β”€β”€β”€β” β”Œβ–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                  β”‚  config   β”‚ β”‚   pg    β”‚ β”‚ tunnel β”‚ β”‚  backups     β”‚
                  β”‚ ~/.pgflow β”‚ β”‚ psql    β”‚ β”‚  SSH   β”‚ β”‚  escaneo     β”‚
                  β”‚  .conf    β”‚ β”‚ pg_dump β”‚ β”‚Local-  β”‚ β”‚  del dir     β”‚
                  β”‚           β”‚ β”‚pg_restoreβ”‚ β”‚Forward β”‚ β”‚  .dump       β”‚
                  β””β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”¬β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                        β”‚            β”‚          β”‚
                        β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                              config (credenciales)

Reglas de dependencia (sin ciclos):

  • tui β†’ {config, pg, tunnel, backups}
  • backups β†’ pg
  • pg β†’ config
  • tunnel β†’ config

Archivos y responsabilidad

Archivo OS Responsabilidad
main.go todos Flags CLI (--list / --json / --version / --help) + tea.NewProgram(..., WithAltScreen())
internal/config/config.go todos Lee / escribe ~/.pgflow.conf (formato shell). Tipos Config y Conn. Load() / Save(). expandHome() resuelve $HOME, ${HOME}, ~.
internal/pg/pg.go todos Wrappers de psql / pg_dump / pg_restore. Dump/restore en streaming con --verbose lΓ­nea por lΓ­nea. Verify con pg_restore --list. CountTables. Clasificadores de error en espaΓ±ol (classifyConnError, classifyDumpError, classifyRestoreError, deniedObject) β€” el caso de permisos parsea el objeto denegado y sugiere GRANT pg_read_all_data.
internal/tunnel/tunnel.go todos API pΓΊblica: IsUp, Ensure, Close, IsOpen. Maneja el ssh subprocess con sync.Mutex. Reap automΓ‘tico en caso de fallo.
internal/tunnel/tunnel_unix.go !windows //go:build !windows β€” hideWindow es no-op en Unix.
internal/tunnel/tunnel_windows.go windows //go:build windows β€” hideWindow aplica CREATE_NO_WINDOW para que ssh.exe no haga flash de consola.
internal/tunnel/tunnel_test.go todos Lifecycle tests con el patrΓ³n TestHelperProcess (re-ejecuta el binario como ssh fake). Cubre: puerto cerrado, ya arriba, ciclo completo ensure/close, reap en fallo.
internal/backups/backups.go todos Escanea BackupDir β†’ []Folder con []Dump (size, mtime, validez). Scan, Total. Verifica cada dump con pg.Verify.
internal/tui/model.go todos Model, Init, Update, View. Key handling. Wizards: backup 3 pasos, restore 4 pasos (con sub-paso 31). Estado de salud (local/tΓΊnel/prod). Splash, modales, status bar.
internal/tui/render.go todos Toda la UI: dashboard, detail panel, wizards, modales, splash, run-screen con logs en vivo. Helpers: humanSize, truncate, truncateLeft, padR, padLine.
internal/tui/styles.go todos Paleta Catppuccin Mocha + morado Charm #7D56F4. Estilos lipgloss (modales sin fondo, paneles con borde, fila seleccionada morada).
internal/tui/commands.go todos Funciones que devuelven tea.Cmd (trabajo async). PatrΓ³n chan logEvent para dump/restore en vivo con waitForLog. Timers (tickCmd, spinnerTickCmd, splashTickCmd, statusClearCmd).
internal/tui/messages.go todos Tipos tea.Msg: scanDoneMsg, statusDoneMsg, prodDBsMsg, localDBsMsg, streamStartedMsg, logEventMsg, verifyDoneMsg, deleteDoneMsg, tunnelDoneMsg, connTestMsg.
internal/tui/render_smoke_test.go todos TestViewAcrossStates (recorre todas las pantallas y verifica que View() no entra en pΓ‘nico) + TestSnapshot (imprime el render con ANSI quitado, ΓΊtil con -v).
Makefile n/a Targets: build, run, test, vet, tidy, install, uninstall, clean, build-all, release. PLATFORMS = los 6 targets.
install.sh / uninstall.sh unix Instalador bash para macOS / Linux (Homebrew, apt).
install.ps1 / uninstall.ps1 windows Instalador PowerShell para Windows. Verifica Go y herramientas runtime, build, copia a %LOCALAPPDATA%\pgflow, hint para aΓ±adir al PATH.
.github/workflows/ci.yml n/a CI matrix ubuntu Γ— macos Γ— windows + cross-build de los 6 targets.
pgflow.sh n/a VersiΓ³n original en Bash (legacy, referencia).
pgflow.conf.example n/a Plantilla de config (se commitea; sin credenciales).
AGENTS.md n/a GuΓ­a para que un agente (humano o IA) entienda y modifique el repo rΓ‘pido.

PatrΓ³n destacado: streaming de logs en vivo

streamDumpCmd (en internal/tui/commands.go) crea un chan logEvent con buffer, lanza una goroutine que corre pg.DumpStream(..., onLine) β€” cada lΓ­nea de stderr de pg_dump --verbose se manda al canal β€” y termina con logEvent{done:true, ...}. Devuelve streamStartedMsg{ch}. El modelo guarda el canal y emite waitForLog(ch), que bloquea en <-ch y devuelve logEventMsg. En cada evento no-final el modelo vuelve a emitir waitForLog; en el final corta. La pantalla de ejecuciΓ³n (renderRunScreen) muestra la cola de lΓ­neas + spinner + tiempo.

PatrΓ³n destacado: errores humanos

internal/pg/pg.go traduce stderr de psql/pg_dump/pg_restore a mensajes accionables en espaΓ±ol. Ejemplos:

Stderr crudo Mensaje pgflow
connection refused conexiΓ³n rechazada β€” ΒΏel tΓΊnel estΓ‘ caΓ­do? (pulsa t)
password authentication failed contraseΓ±a incorrecta
permission denied for table auth_group el rol Β«produserΒ» no puede leer la tabla Β«auth_groupΒ». GRANT pg_read_all_data TO produser; β€” PostgreSQL 14+
no space left on device sin espacio en disco
database already exists (en restore) objetos duplicados β€” usa 'crear base nueva'

El caso de permisos parsea el tipo de objeto (table, schema, sequence, view) y el nombre, y construye una sugerencia de GRANT.

CΓ³mo extender (recetas)

  • Nuevo campo de config: agrΓ©galo a Config/Save/Load (config.go) y a buildCfgFields (model.go, con su section).
  • Nueva acciΓ³n de dashboard: aΓ±ade un case en handleDashboardKey (model.go) y, si es async, un comando en commands.go + un mensaje en messages.go + su case en Update.
  • Nuevo paso de asistente: ajusta la mΓ‘quina m.step en *Select/*Back y crumb().
  • Nuevo modal: aΓ±ade la constante al enum modal, manΓ©jalo en handleModalKey y renderModal.

Tras cualquier cambio de UI, corre el snapshot test para verlo:

go test ./internal/tui/ -run TestSnapshot -v

Nombre de los dumps

El nombre del archivo .dump se arma con una plantilla por base de datos (tokenizable), un prefijo por carpeta (opcional), y un contador autoincremental (opcional, si la plantilla lo usa). Todo vive en ~/.pgflow.json.

Defaults:

  • Con prefijo de carpeta: {prefix}-{db}-{datetime} β†’ TIENDA-WEB-shopdb-20260621_183045.dump
  • Sin prefijo: {db}_{datetime} β†’ shopdb_20260621_183045.dump

Tokens disponibles en la plantilla:

Token Valor
{db} nombre de la base
{date} fecha en formato YYYYMMDD
{time} hora en formato HHMMSS
{datetime} fecha + hora, YYYYMMDD_HHMMSS
{seq} contador autoincremental de 3 dΓ­gitos (001, 002, ...)
{prefix} el prefijo configurado en la carpeta destino

CΓ³mo editarlo: en el paso Confirmar del backup:

  • e β€” editar el nombre del archivo concreto (no persistente)
  • t β€” editar la plantilla por db (se guarda en ~/.pgflow.json)
  • p β€” editar el prefijo de la carpeta destino (se guarda)

Si la plantilla usa {seq}, el contador se autoincrementa automΓ‘ticamente cada vez que un backup termina OK.

Limitaciones conocidas

  • Scroll parcial. El dashboard y los pickers tienen scroll implementado (windowBounds + scrollHint) β€” el cursor se mantiene visible y aparece un indicador pos/total ↑↓. La pantalla de log en vivo durante dump/restore (renderRunScreen) muestra los ΓΊltimos N lΓ­neas sin scroll interactivo (suficiente para esa vista).
  • No se adoptan tΓΊneles externos. Si abres el tΓΊnel por tu cuenta (sin que pgflow lo gestione), pgflow lo detecta con IsUp pero al pulsar c para cerrarlo reporta "puerto abierto por otro proceso β€” pgflow no lo controla" en vez de intentar matarlo.
  • Windows < 10 1809 no soportado porque no trae ssh.exe built-in. Funciona si instalas OpenSSH manualmente, pero no es ruta soportada oficialmente.

Licencia

MIT.

La versiΓ³n original en Bash sigue disponible en pgflow.sh.

About

🐘 Visual TUI for PostgreSQL backup & restore β€” dump a production DB over an SSH tunnel and restore it into a fresh local database. One Go binary, Bubble Tea UI.

Topics

Resources

License

Stars

0 stars

Watchers

0 watching

Forks

Packages

 
 
 

Contributors