synxs-ar/laravel-worktrees

Persistent, isolated Laravel dev environments ("desks") backed by git worktrees — dynamic free-port resolution for PHP + Vite, per-desk SQLite, unique app keys and storage links.

Maintainers

Package info

github.com/synxs-ar/laravel-worktrees

pkg:composer/synxs-ar/laravel-worktrees

Transparency log

Statistics

Installs: 8

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v1.2.0 2026-07-23 13:40 UTC

This package is auto-updated.

Last update: 2026-07-23 13:40:27 UTC


README

Corré varias copias de tu app Laravel al mismo tiempo, cada una aislada, sin que se pisen.

Cada copia se llama desk (wt-desk-01, wt-desk-02, …). Pensalo como un escritorio de trabajo: tiene su propio puerto, base de datos, storage, vendor/, node_modules/ y APP_KEY. Trabajás en una rama en un desk, en otra rama en otro desk, y ninguna toca a la otra.

Ideal para trabajar en varias features a la vez — o para que varios agentes de IA programen en paralelo sin chocarse.

En 30 segundos

php artisan wt:new     # crea y deja TODO listo (te hace unas preguntas)
php artisan wt:up 1    # lo prende (PHP + Vite). No pregunta nada.

Eso es todo. wt:new arma el desk completo. wt:up solo lo enciende.

Instalar

composer require --dev synxs-ar/laravel-worktrees

(Opcional) publicar la config si querés tocar valores por defecto:

php artisan vendor:publish --tag=worktrees-config

No hace falta tocar el .gitignore: el primer wt:new agrega /worktrees y /.wt-desks solo.

Necesitás

  • PHP ^8.1
  • Laravel 10, 11, 12 o 13
  • git instalado
  • Para una base aislada: un servidor PostgreSQL (por defecto) o MySQL andando

Los comandos

Comando Qué hace
wt:new [label] Crea el desk y lo configura entero: worktree, dependencias, .env, base de datos, storage y puerto. Queda listo.
wt:up {desk} Solo lo prende (PHP + Vite). No pregunta nada.
wt:label {desk} [label] Ponerle, ver o sacarle un nombre al desk.
wt:list Lista todos los desks y sus puertos.
wt:rm {desk} Borra un desk y libera su número.

{desk} acepta el nombre completo o solo el número: wt:up wt-desk-01 y wt:up 1 son lo mismo.

wt:new — crear y configurar

Primero arma el desk:

✓ Ajustando .gitignore
✓ Creando git worktree
✓ Instalando dependencias (composer)
✓ Instalando node modules (npm)
✓ Preparando .env
✓ Copiando config local (vite, …)
✓ Generando APP_KEY   (único por desk — sesiones y encriptación aisladas)

Y ahí mismo te pregunta 3 cosas y las recuerda:

1. ¿Base de datos aislada o compartida?

  • Aislada → te pide host / puerto / usuario / contraseña / nombre. Si la base no existe, elegís:
    • crearla vacía (le corre tus migraciones), o
    • copiar una existente (te muestra las que hay y clona esquema + datos).
  • Compartida → usa la base de tu proyecto. Nunca le corre migraciones.

2. ¿Storage aislado o compartido?

  • Aislado → el desk tiene su propio storage/.
  • Compartido → comparte los archivos subidos (storage/app/public) con tu proyecto principal. Logs, cache y sesiones siguen separados.

3. ¿Qué puerto para PHP?

  • Vacío (auto) → se busca un puerto libre solo, cada vez que prendés.
  • Un número fijo (ej: 8080) → siempre usa ese. Si está ocupado, no arranca y te avisa (elegiste ese a propósito, no te lo cambio a escondidas).

Cuando termina, el desk está listo. No queda nada para decidir después.

El worktree queda detached (sin rama fija). Adentro hacés checkout de la rama que quieras — la identidad del desk (puerto, base, storage) no se mueve.

wt:up — prender

No pregunta nada. Lee lo que guardó wt:new y levanta el server:

  PHP  http://127.0.0.1:18001
  Vite http://127.0.0.1:27501

  wt-desk-01 is up. Ctrl+C to stop.

Ctrl+C corta todo limpio (PHP y Vite), sin dejar procesos colgados.

  • --no-vite → prende solo PHP, sin Vite.

Ponerle un nombre (label)

Por defecto el desk se muestra como MiApp [wt-desk-01] en el navegador, logs y mails. Ponele un nombre para reconocerlo de un vistazo:

php artisan wt:new checkout-redesign   # nace como "MiApp [checkout-redesign]"
php artisan wt:label 1 billing-fix     # renombrar cuando quieras
php artisan wt:label 1                 # ver el nombre actual
php artisan wt:label 1 --clear         # volver a "MiApp [wt-desk-01]"

Cosas para saber

Copiar una base

  • Postgres: la base que copiás no puede tener conexiones abiertas (cerrá lo que la esté usando y reintentá).
  • MySQL: copia tablas y datos, pero no copia vistas, triggers ni stored procedures.

Puertos

  • Los puertos automáticos siempre quedan arriba de 10001. En Windows con Hyper-V / WSL2 / Docker los rangos por debajo de 10000 están reservados y tiran error 10013.
  • El puerto de Vite siempre es automático.

Si algo falla en wt:new

Si cancelás o hay un error (server caído, credenciales mal), wt:new deshace todo y libera el número. No te deja desks a medio hacer. Arreglás el problema y volvés a correr wt:new.

Config (config/worktrees.php)

Clave Env Default Para qué
host WORKTREES_HOST 127.0.0.1 Dónde se sirve.
port_floor WORKTREES_PORT_FLOOR 10001 Piso mínimo de puertos automáticos.
php_base_port WORKTREES_PHP_BASE_PORT 18000 Puerto PHP preferido = base + número.
vite_base_port WORKTREES_VITE_BASE_PORT 27500 Puerto Vite preferido = base + número.
database.engine WORKTREES_DB_ENGINE pgsql Motor para base aislada (pgsql / mysql).
database.name WORKTREES_DB_NAME {base}_{slug} Plantilla del nombre de base aislada.
registry_path .wt-desks/registry.json Dónde recuerda los desks (fuera de git).
worktrees_path worktrees Dónde se crean los desks.
php / composer / npm WORKTREES_PHP / …COMPOSER / …NPM php / composer / npm Binarios que se usan.

¿Por qué Postgres por defecto? Tiene soporte completo de ALTER TABLE, así que tus migraciones corren sin cambios. SQLite tiene ALTER TABLE limitado y rompe muchas suites de migraciones reales.

Notas de Windows

Anda de una en Windows:

  • storage:link usa una junction (mklink /J) si el symlink necesita permisos que no tiene.
  • wt:rm borra las junctions como links (sin seguirlas hacia el destino) y maneja rutas de node_modules que pasan el MAX_PATH.
  • Ctrl+C corta todo el árbol de procesos (taskkill /T).

Licencia

MIT © Synxs