Files
duckbrain-toolbox/README.org
T

14 KiB
Raw Blame History

Duckbrain toolbox

Instalador y catálogo de paquetes de Duckbrain. Trae el núcleo a un proyecto y gestiona sus addons con un solo comando, manteniendo la filosofía del núcleo: sin Composer obligatorio, código legible y copias reales desplegables (hosting compartido, FTP, etc.).

Qué resuelve

El núcleo Duckbrain mantiene su código simple y sin dependencias, y todo lo accesorio vive en addons. Antes, instalar un addon era copiar archivos a mano dentro de src/: cada proyecto quedaba con una versión distinta, las correcciones no se propagaban y se acumulaba código muerto.

El toolbox convierte eso en:

duckbrain install mi-proyecto
duckbrain add crypto
duckbrain update

Conceptos

Concepto Qué es
Repositorio Dónde vive el código (canal de distribución)
Paquete Una familia de componentes (http, crypto, =text=…)
Componente La unidad instalable (una clase o una familia)
Manifiesto duckbrain.json: qué archivos aporta cada componente
Catálogo Los paquetes disponibles en el toolbox
Lock .duckbrain/lock.json: qué quedó instalado y con qué hash

La unidad que se pide con duckbrain add es el componente, no el paquete ni el repositorio.

Estructura del repositorio

duckbrain-toolbox/
  bin/duckbrain                 instalador global (bootstrap)
  src/autoload.php              mapea Toolbox\ -> packages/commands/src/Toolbox/
  scripts/release               version + tag + push (interactivo o directo)
  scripts/build-phar            construye dist/duckbrain.phar autocontenido
  scripts/publish-phar          publica el phar como release en Forgejo
  Makefile                      test / build / release / publish / check / clean
  packages/
    commands/  cli, migration, toolbox
    htmx/      htmlComponent, htmx
    http/      curlRequest, aria2, proxy, compreFace
    crypto/    crypto, nonce, twoFactor
    text/      filter, replacement
    cache/     apcache
    id/        ulid
    test/      harness

Cada paquete tiene su duckbrain.json. Sólo se copian los archivos declarados por el componente que pides; los metadatos del paquete no se instalan.

Instalación

El instalador global arranca un proyecto desde cero y no necesita nada previo:

git clone git@git.kj2.me:kj/duckbrain-toolbox.git
ln -s "$PWD/duckbrain-toolbox/bin/duckbrain" ~/.local/bin/duckbrain

duckbrain install mi-proyecto     # descarga el núcleo Duckbrain
cd mi-proyecto
duckbrain add crypto htmx         # copias reales, listas para commitear

Uso opcional dentro del proyecto: si quieres que el proyecto gestione sus propios addons sin el instalador global, instala el componente commands:toolbox (es un add más, no un requisito). A partir de ahí ./duckbrain funciona solo.

Alternativa sin clone del repo: descargar el phar autocontenido publicado como release y ejecutarlo directamente (los enlaces simbólicos no valen: self-update reemplaza el binario con rename() sobre su propia ruta):

VERSION=$(curl -s https://git.kj2.me/api/v1/repos/kj/duckbrain-toolbox/releases \
  | jq -r '.[].tag_name' | grep -E '^v[0-9]' | sort -V | tail -1)
curl -fsSL "https://git.kj2.me/kj/duckbrain-toolbox/releases/download/$VERSION/duckbrain.phar" \
  -o ~/.local/bin/duckbrain
chmod +x ~/.local/bin/duckbrain

duckbrain --version      # ya es el instalador global; se actualiza solo:
duckbrain self-update

(Para probar el tuyo local sin release, usa cp dist/duckbrain.phar ~/.local/bin/duckbrain en lugar del curl; make build + ln -s solo sirve mientras no llames a self-update.)

Comandos

duckbrain install [dir]

Descarga el núcleo Duckbrain en dir. Es sólo del instalador global.

duckbrain add <componente|paquete>

Instala componentes, resolviendo dependencias y copiando archivos.

duckbrain add crypto                 # la familia completa
duckbrain add http:aria2             # un solo componente
duckbrain add http@^0.1              # fijando restricción de versión
duckbrain add htmx --link            # enlaza en lugar de copiar (desarrollo)

duckbrain remove <componente>

Borra sólo los archivos de ese componente; si alguno fue editado localmente, se bloquea y pide --force, --backup o --keep-local.

duckbrain update [core|<componente>] [--dry-run]

duckbrain update core        # sólo el núcleo
duckbrain update http        # un paquete
duckbrain update             # todo: catálogo -> core -> componentes
duckbrain update --dry-run   # muestra el plan sin escribir

duckbrain list / duckbrain doctor / duckbrain catalog / duckbrain adopt

  • list: core y componentes instalados con versión y modo.
  • doctor: matriz de compatibilidad, sin efectos secundarios.
  • catalog: paquetes y componentes disponibles.
  • adopt: registra copias hechas a mano en proyectos antiguos.

Manifiesto duckbrain.json

{
  "name": "http",
  "version": "0.1.0",
  "description": "Utilidades HTTP",
  "core": { "requires": ">=0.1", "tested": "0.1" },
  "php": ">=8.1",
  "components": {
    "curlRequest": { "files": ["src/Libs/CurlRequest.php"] },
    "aria2":       { "files": ["src/Libs/Aria2.php"] },
    "compreFace": {
      "files": ["src/Libs/CompreFace.php"],
      "require": { "http:curlRequest": ">=0.1" }
    }
  },
  "default": ["curlRequest", "aria2", "compreFace"]
}
  • files: archivos (o directorios) que aporta el componente.
  • require: dependencias entre componentes, con rangos semver.
  • core.requires / core.tested: compatibilidad con el núcleo.
  • post-install: comandos de configuración (se muestran y confirman).

Destino de los archivos

  • un archivo bajo src/ → ROOT_CORE/ (quitando el prefijo src/);
  • cualquier otro archivo → ROOT_DIR/.

Así crypto/src/Libs/Crypto.php → src/Libs/Crypto.php y commands/duckbrain → duckbrain.

Estado del proyecto

  • .duckbrain/manifest.json: lo que quieres (restricciones de versión).
  • .duckbrain/lock.json: lo que tienes (core y componentes, con versión, commit del toolbox y hash por archivo).

Los hashes permiten detectar ediciones locales. La actualización respeta lo que editaste y te pregunta antes de pisarlo.

Migrar un proyecto existente

Un proyecto que ya tenía Duckbrain y addons copiados a mano entra al toolbox sin perder nada. Clave: adopt no modifica archivos; sólo registra en .duckbrain/lock.json lo que encuentra. Los cambios de contenido los hace update, y sólo cuando tú lo pides.

# 0. Red de seguridad
git add -A && git commit -m "antes de migrar a duckbrain-toolbox"

# 1. Registrar el core y las copias existentes (NO toca archivos)
duckbrain adopt

# 2. Traer lo canónico (y respaldar antes de pisar)
duckbrain update core --backup
duckbrain add http crypto text --backup

# 3. Estado y compatibilidad
duckbrain list
duckbrain doctor

# 4. Commitear la migración
git add -A && git commit -m "migrado a duckbrain-toolbox"

Cómo trata adopt cada archivo:

Caso del archivo Qué hace adopt ¿Actualiza?
Coincide con el catálogo Lo registra como adopted No
Existe pero difiere Lo registra con origin: local No
Del core (existe en el core) Registra el hash actual en lock.core No
Ajeno al core/addons No lo toca No

Las copias divergentes quedan marcadas como edición local: un update posterior te avisa y no las pisa salvo --force o --backup. Así puedes revisar el git diff con calma (por ejemplo, el arreglo de Replacement en multipaste-vip) y decidir cuándo reconciliar. Como un proyecto heredado no tiene hashes previos, --backup respalda también la primera escritura.

Compatibilidad

Cada componente declara hasta qué versión del núcleo fue probado:

  core < requires        avisa "requiere core >=X"
  requires ≤ core ≤ tested   compatible
  core > tested          avisa "no probado"
  sin datos              avisa "sin datos"

Nunca bloquea: se confirma con --yes. Al core se le sigue la pista por sus tags semver (vX.Y.Z, empezando por v0.1.0); si no hay tags, la versión es desconocida y se fija por commit.

Los valores tested de los manifiestos no son decorativos: los escribe sólo quien corrió el barrido de compatibilidad (make compat), que monta un proyecto temporal por paquete contra el core real publicado y lo ejercita por HTTP (migraciones sobre sqlite, runner phpunit real; los clientes con servicio externo — aria2, proxy, compreFace — sólo a nivel de carga/instantiación, consignado así en tests/compat/report.json). Cualquier Deprecated=/=Warning=/=Notice durante el barrido cuenta como fallo. Un test de la suite (group11) exige que cada tested declarado tenga su ok en ese reporte y que no se estampen componentes deliberadamente sin datos (commands:cli=/=commands:toolbox, que son el instalador y no consumen el core). Al aparecer un tag nuevo del core, el ritual es: make compat → arreglar lo que falle → estampar → make release.

Versionado y releases

Hay dos sistemas de versión y no hay que confundirlos:

  1. Paquetes/componentes — la versión vive en packages/<paquete>/duckbrain.json y se versiona de forma independiente con tags prefijados (http-v0.1.0, =crypto-v0.1.0=…). La única forma de cambiarla es el asistente:

    make release                                          # interactivo: elige
                                                          # paquete e incremento,
                                                          # muestra cambios
                                                          # pendientes y confirma
    scripts/release <paquete> <patch|minor|major> [--yes]  # modo directo

    Bumpea el manifiesto, commitea, crea el tag y lo publica. Al liberar commands sincroniza también Config::VERSION en el mismo commit. El tag es el descubrimiento; el commit es la identidad, así que el lock pinea commits y no se rompe si un tag se mueve. Las versiones retiradas se listan en yanked.json; las prereleases se omiten salvo --pre. Para detectar bumps olvidados: git log <paquete>-v<última>..HEAD -- packages/<paquete> (vacío = al día).

  2. Instalador/phar — Config::VERSION (packages/commands/src/Toolbox/Config.php): es lo que imprime la CLI y la referencia con la que self-update compara las releases del servidor. No se edita a mano; la mantiene el release de commands. Se publica con make publish (ver Makefile).

Makefile

make help lista los objetivos:

Objetivo Qué hace
make test Corre la suite (php tests/run.php). En verde antes de commitear.
make build Genera dist/duckbrain.phar + dist/duckbrain.phar.sha256.
make compat Barrido de compatibilidad contra el core real (scripts/compat-sweep); requiere red. ARGS"–only pkg"= para un paquete.
make release Asistente interactivo de releases de paquetes (ARGS"pkg minor –yes"= para modo directo).
make publish build + publica la release v$(Config::VERSION) con los assets. Requiere FORGEJO_TOKEN.
make check self-update --check contra el servidor: actual/disponible, exit 1 si hay nuevo.
make clean Borra dist/.

El token para publicar es un token personal de la API de Forgejo (Ajustes → Aplicaciones, scope write:repository, con caducidad y revocable). Los PAT de Forgejo no pueden limitarse a un solo repositorio: para aislar el riesgo, crea un usuario bot con acceso Write solo en duckbrain-toolbox y usa su token. dist/ está en gitignore: nunca se commitea.

self-update (el phar)

duckbrain self-update             # se auto-reemplaza con la última release
duckbrain self-update --check     # actual vs disponible (exit 1 si hay nueva)
duckbrain self-update --force     # reemplaza aunque esté al día

Lee la lista de releases (https://git.kj2.me/api/v1/repos/kj/duckbrain-toolbox/releases, sobrescribible con DUCKBRAIN_UPDATE_URL) y elige el mayor tag semver con la misma clase que el core usa con sus tags; tags que no son semver y drafts se ignoran. No existe la ruta /releases/latest/download/ en Forgejo, así que no se depende de ella: basta con subir make publish la versión real vX.Y.Z. La descarga se verifica contra el .sha256 publicado y el reemplazo es atómico (temp + rename) sobre Phar::running(false). Si Config::VERSION > remota= no se descarga nada.

Para tocar el instalador con el phar en mente: build-phar ya ejecuta con php -d phar.readonly=0 (crear phars está bloqueado por defecto), el código que corre dentro del phar no debe usar glob() (no soporta el stream phar://; usa DirectoryIterator / file_get_contents()), y la raíz del toolbox se resuelve con Config::toolboxDir(), que ya antepone phar:// cuando toca.

Pruebas

php tests/run.php

Contacto

Puedes encontrarme en Telegram como @keyjay o por correo: webmaster@outcontrol.net