Últimamente estoy trabajando en algunos proyectos en paralelo, son pequeñas aplicaciones web, relativamente sencillas y pensadas para estar disponibles online.
Por comodidad intento que tengan todas la estructura más similar posible, que usen todas las mismas tecnologías, que estén alojadas en el mismo sitio y que tengan un flujo de desarrollo lo más similar posible.
Hacer esto te evita un montón de problemas, y agiliza tu desarrollo bastante.
Todos estos proyectos están corriendo en instancias de Google Cloud Platform.
Son SPAs en Vue, proyectos Nuxt o custom JS.
Todos tienen un nginx delante para gestionar las URLS, un certbot para el SSL. (así por seguridad se pueden visitar las webs bajo https), algunas tienen sistema de login privado o tracking de visitas... Nada exótico.
La mayoría incluso usa el mismo framework que he montado para construir la UI CSS Bedrock\ o bloques más elaborados construidos a partir de Bedrock y que alojo en el proyecto que llamo Plantillas
Te animo a que le eches un vistazo a Bedrock y abraces las convenciones de tener una sola manera de hacer las cosas.
Tener este flujo de desarrollo tan similar tiene innumerables ventajas, cierto que es que acotas tu margen de posibilidades al reducir tu stack de tecnología pero ganas muchísimo al mantener solo un tipo de código y un flujo de trabajo, depuras los errores más facilmente, sabes siempre qué esta pasando, puedes saltar de un proyecto a otro sin cambiar de mapa mental y sobre todo puedes automatizar algunas partes de la gestión del proyecto con un poco de cuidado.
La dolorosa vuelta a Windows.
Hace un año que volví a Windows por temas de presupuesto para mi trabajo del día a día, ahora que no trabajo profesionalmente en estos proyectos, la inversión en máquinas mac se me hacía un poco cuesta arriba y por cuestiones de ahorro y versatilidad me pasé a windows de nuevo.
He desarrollado siempre mi trabajo en mac y linux y no voy a negar que la vuelta al entorno Windows ha sido un poco difícil, para mí acostumbrado a la maravilla de un terminal que funcionaba de verdad, volver a windows ha sido un camino lleno de altibajos.
El problema por ejemplo de desplegar cualquier cosa desde Windows, aprender todos los comandos y la nueva sintaxis ha sido duro (no me escondo...)
Incluso los proyectos pequeños necesitan de un montón de pasos: construir el proyecto, copiar los ficheros al servidor, recargar nginx, gestionar los certificados... en un primer momento todo de manera manual, luego usando scripts para agilizar que vas guardando entre proyectos y tienes que perder un rato editando cada vez que cambias de proyecto y al final acumulas un montón de scripts que no sabes luego ni donde los guardaste.
El camino de los golpes.
La primera vez que montas un proyecto con subida al servidor, tardas una tarde.
La segunda vez un poquito menos pero no recuerdas bien el orden de montaje y tropiezas, buscas y copias código del otro proyecto, falla y te desesperas.
La tercera vez te escribes un script para evitar tropiezos y automatizas alguna parte del flujo del proyecto, sabes que esos scripts se van a perder en alguna carpeta.
Here we go, again.
Hasta que empiezas a entender que lo que necesitas es una herramienta que te ayude a gestionar todo esto.
Si las cosas están en un solo lugar, solo se pueden romper una vez.
Sueñas con que tenga un botón bien gordo que accione toda la magia y lo haga todo de una vez. Y te emocionas al pensar que vas a hacer con todo ese tiempo que has ganado... (spoiler: hacer más herramientas y más proyectos).
Y así es más o menos como acabas montando algo como Lanzadera: un módulo PowerShell que convierte todos tus problemas a la hora de montar y desplegar un proyecto en dos o tres comandos reproducibles.
Aviso para caminantes.
En primer lugar tengo que advertir que es una herramienta hecha como un traje a medida para mi flujo de trabajo, he decidido ir con un stack de desarrollo, tener unas convenciones, usar un sistema operativo concreto y alojar los proyectos de manera similar en el mismo lugar.
Esto hace que la solución esté muy pensada en lo que YO necesito así que probablemente no puedas usar Lanzadera para tu propio entorno pero sí espero que este proyecto te aporte ideas sobre como poner tus proyectos sobre raíles y hacer más sencillo todos esos flujos y tareas que hacen posible que un proyecto se pueda publicar y actualizar correctamente.
¿Qué es Lanzadera?.
Lanzadera es una herramienta de deploy para proyectos web en GCP. Gestiona la configuración de nginx, los certificados SSL con certbot, la autenticación PHP, un contador de visitas, la subida de archivos por SSH/SCP... todo desde Windows con PowerShell 5.1.
No es un sistema de CI/CD. No tiene servidor, no tiene interfaz web, no requiere una cuenta en ningún servicio externo.
Es un módulo PowerShell que se instala una vez en la máquina y luego está disponible en cualquier proyecto.
El flujo completo para poner en marcha un proyecto nuevo es:
install.ps1 → configura el proyecto (una vez)
deploy.ps1 setup → prepara el servidor (una vez por dominio)
deploy.ps1 web → sube el código (cada vez que quieras deployar)
Es decir: Inicializa un proyecto en local y configura todo, se conecta con el servidor y organiza todo en el servidor, luego te provee de comandos para que puedas ir haciendo releases, subiendo datos de la web o actualizaciones de código.
¿Cómo funciona?.
La config de máquina.
Lanzadera separa la configuración en dos niveles. Por seguridad (y para que no puedas subir datos comprometidos por error a un repo o en el despliegue) La config de máquina vive fuera de todos los repositorios, en ~\.lanzadera\config.json, y contiene las credenciales comunes a todos los proyectos: el usuario SSH, la ruta a la clave privada, el email para certbot, y la lista de instancias GCP a las que tienes acceso.
Ejemplo del config
{
"user": "oscar",
"key": "C:/Users/oscar/.ssh/google_compute_engine",
"email": "tu@email.com",
"targets": {
"principal": {
"instance": "nombre-instancia-gcp",
"zone": "us-central1-a",
"project": "id-proyecto-gcp",
"path": "/var/www"
}
}
}
Esto se configura una sola vez. A partir de ahí, cualquier proyecto que use ese target hereda automáticamente las credenciales.
La config del proyecto.
La config del proyecto vive en deploy.config.json en la raíz de cada repo (en .gitignore, para que las rutas locales no se compartan). Define el dominio, la carpeta de build, si tiene auth, si tiene tracker de visitas...
{
"target": "principal",
"domain": "miproyecto.omata.es",
"buildCommand": "npm run build",
"distPath": "dist",
"auth": { "enabled": false },
"tracker": { "enabled": false }
}
Generarla a mano es innecesario porque
install.ps1hace todo esto con un asistente interactivo.
En el momento de la instalación inicial a través de un asistente por linea de comandos se te preguntarán las cosas más habituales para configurar correctamente el proyecto. Posteriormente siempre puedes editar este archivo si las necesidades de tu proyecto cambian.
El setup del servidor.
El primer deploy de un dominio nuevo requiere configurar el servidor desde cero. deploy.ps1 setup hace eso en orden:
- Crea el webroot en el servidor
- Sube una config nginx HTTP-only y recarga nginx
- Ejecuta certbot para obtener el certificado SSL
- Sube la config nginx completa con HTTPS y recarga nginx
- Si el proyecto tiene auth, instala
php_mini_auth - Si el proyecto tiene tracker instala
migas
El orden importa: certbot necesita que nginx esté corriendo en HTTP para completar el challenge, así que no puedes saltarte los pasos intermedios. Lanzadera los gestiona en el orden correcto y no continúa si algo falla.
Tras el setup, para deployar solo necesitas:
.\scripts\deploy.ps1 web
Esto subirá todo tu proyecto al servidor recién configurado.
La separación código / datos.
En varios de mis proyectos hay una carpeta data/ dentro de dist/ con cientos de archivos: JSON de contenido, imágenes, audio.
Esos archivos no cambian con cada deploy de código y subirlos enteros cada vez es improductivo, lento y no tiene mucho sentido, así que para evitar eso:
Lanzadera tiene dos modos de subida:
deploy.ps1 web— sube tododist/excepto la carpeta de datosdeploy.ps1 data— sincroniza solo los archivos de datos que no existen en remoto
La sincronización de datos compara los nombres de fichero antes de subir. Si un archivo ya está en el servidor, no se toca. Si es nuevo, se sube.
Eso hace que añadir contenido a un proyecto sea rápido aunque el volumen acumulado sea grande.
Para los casos en los que sí quieres forzar la subida completa:
.\scripts\deploy.ps1 data -Force
Imagina que necesitas asegurarte de que reseteaste bien todo el contenido de datos.
Hooks.
Algunos proyectos necesitan lógica personalizada al final de una fase. Para eso he creado los hooks: scripts PowerShell del proyecto que Lanzadera ejecuta cuando completa setup o data.
Están inspirados en los hooks de un montón de tecnologías como por ejemplo git.
La idea detrás de esto que puedas tener cierto control de personalización entre las fases de ejecución de Lanzadera, imagina que necesitas instalar algo solo para ese proyecto al final de la fase de setup.
Entiende esto último como una personalización de ese proyecto y no algo que debería ir por sistema en todas las configs de un proyecto.
Se declaran en deploy.config.json:
{
"postSetup": "scripts/post-setup.ps1",
"postData": "scripts/sync-db.ps1"
}
El script recibe un objeto $ctx con la IP del servidor, el usuario SSH, la clave y el webroot, y las funciones de Lanzadera (Invoke-SSH, Invoke-SCP...) están disponibles directamente. Esto hace que un hook sea solo la lógica específica del proyecto, sin tener que reimplementar la conexión SSH.
Un ejemplo real: instalar PocketBase tras el setup.
# scripts/post-setup.ps1
param($ctx, $projectConfig)
Invoke-SSH $ctx "mkdir -p /opt/pocketbase && wget -q -O /tmp/pb.zip https://..."
Invoke-SSH $ctx "unzip -o /tmp/pb.zip -d /opt/pocketbase && chmod +x /opt/pocketbase/pocketbase"
Invoke-SCPFile $ctx ".\config\pocketbase.service" "/tmp/pocketbase.service"
Invoke-SSH $ctx "sudo mv /tmp/pocketbase.service /etc/systemd/system/ && sudo systemctl enable --now pocketbase"
Lo bueno de los hooks es que la lógica específica de cada proyecto queda documentada en el propio repo, versionada con git, y se ejecuta automáticamente sin que tengas que recordarla.
Releases semánticas.
Para proyectos que usan versionado semántico, he creado el comando release hace todo el flujo de una vez:
Ejemplo
.\scripts\deploy.ps1 release # patch: 1.0.0 → 1.0.1
.\scripts\deploy.ps1 release minor # minor: 1.0.1 → 1.1.0
.\scripts\deploy.ps1 release major # major: 1.1.0 → 2.0.0
Verifica que el working tree esté limpio, hace el build, deploya, crea un tag anotado y hace push a GitHub con los tags. Si el working tree tiene cambios sin commitear, se detiene antes de hacer nada (No queremos subir nada que no esté en la rama buena)
Equivalentes npm.
Si el proyecto tiene package.json, install.ps1 añade los scripts de deploy automáticamente:
{
"scripts": {
"deploy": "powershell -File scripts/deploy.ps1 web",
"deploy:data": "powershell -File scripts/deploy.ps1 data",
"deploy:release": "powershell -File scripts/deploy.ps1 release"
}
}
Esto último es por comodidad, cuanto menos tengas que pensar mejor, así puedes hacer npm run deploy sin recordar la sintaxis de PowerShell, a mí me resulta muy útil cuando cambio entre proyectos con frecuencia.
Por qué no GitHub Actions / otro CI.
La respuesta corta es que no necesito un sistema tan complejo para proyectos personales.
GitHub Actions requiere configurar secretos en el repositorio (la clave SSH, el proyecto GCP...), mantener un fichero .yml de workflow, y esperar a que el runner arranque cada vez que pusheas. Para un proyecto personal donde deploya una sola persona, desde una sola máquina, eso es más fricción que ayuda.
Lanzadera corre en la máquina local, con las credenciales ya configuradas, y la retroalimentación es inmediata: ves el output en el terminal y sabes exactamente qué pasó. Si algo falla, puedes reintentarlo al momento.
Conclusiones.
He creado una herramienta muy personalizada para poder montar, desplegar y actualizar proyectos sin fricción.
Estoy metido en varios proyectos a la vez y hago subidas y actualizaciones frecuentes de los mismos, quito y pongo login, subo nuevos datos o releases.
Unificar todos estos pasos en una herramienta ha supuesto una mejora enorme en mis tiempos de entrega, puedo estar saltando de un proyecto a otro haciendo cambios en proyectos que dependen unos de otros sin perder el tiempo.
Es cierto que estoy muy atado ahora a un tipo de flujo pero al haber hecho esto modular siento que podría reescribir parte de la lógica modular del proyecto y adaptarme a hacer por ejemplo despliegues en Amazon o en otro hosting propio, volver a mac solo implicaría reescribir los scripts de deploy...
En definitiva, estoy muy contento con el resultado.
Roadmap.
Qué hacer a partir de ahora, todavía me queda trabajo por pulir, es posible que surjan nuevos addons como el que ya tiene de login o el de tracking de visitas, así que me gustaría trabajar en el sistema de plugins para que fuera un poco más modular y robusto.
Por lo demás espero hacer pocas adaptaciones y centrarme en usarlo para sacarle partido con el resto del proyectos.
En definitiva las mejores herramientas son aquellas que no necesitan mantenimiento y son casi invisibles. Funcionan y te olvidas de que existen.
¿Dónde encuentro Lanzadera?
El código está en github.com/Katamo/lanzadera. Los requisitos son Windows con PowerShell 5.1, gcloud CLI configurado, OpenSSH instalado y una clave SSH con acceso a las instancias GCP.
Si te animas a usarla o te sirve de inspiración para hacer la tuya propia, me haría un poquito feliz saberlo.