feat(release): introduce interactive mode
This commit is contained in:
@@ -4,7 +4,7 @@
|
|||||||
CONFIG := packages/commands/src/Toolbox/Config.php
|
CONFIG := packages/commands/src/Toolbox/Config.php
|
||||||
VERSION := $(shell grep -oP "const VERSION = '\K[^']+" $(CONFIG))
|
VERSION := $(shell grep -oP "const VERSION = '\K[^']+" $(CONFIG))
|
||||||
|
|
||||||
.PHONY: help test build publish check clean
|
.PHONY: help test build release publish check clean
|
||||||
|
|
||||||
help: ## Muestra esta ayuda
|
help: ## Muestra esta ayuda
|
||||||
@grep -hE '^[a-z-]+:.*## ' $(MAKEFILE_LIST) | sed -E 's/^([a-z-]+):.*## (.+)$$/ make \1 \xe2\x80\x94 \2/'
|
@grep -hE '^[a-z-]+:.*## ' $(MAKEFILE_LIST) | sed -E 's/^([a-z-]+):.*## (.+)$$/ make \1 \xe2\x80\x94 \2/'
|
||||||
@@ -15,6 +15,9 @@ test: ## Corre la suite de pruebas (harness propio, sin phpunit)
|
|||||||
build: ## Construye dist/duckbrain.phar y su .sha256
|
build: ## Construye dist/duckbrain.phar y su .sha256
|
||||||
scripts/build-phar
|
scripts/build-phar
|
||||||
|
|
||||||
|
release: ## Asistente interactivo para versionar un paquete (scripts/release)
|
||||||
|
./scripts/release $(if $(ARGS),$(ARGS),)
|
||||||
|
|
||||||
publish: build ## Crea/mueve la release v$(VERSION) en Forgejo y sube los assets
|
publish: build ## Crea/mueve la release v$(VERSION) en Forgejo y sube los assets
|
||||||
scripts/publish-phar
|
scripts/publish-phar
|
||||||
|
|
||||||
|
|||||||
+86
-8
@@ -41,7 +41,11 @@ repositorio.
|
|||||||
#+BEGIN_SRC text
|
#+BEGIN_SRC text
|
||||||
duckbrain-toolbox/
|
duckbrain-toolbox/
|
||||||
bin/duckbrain instalador global (bootstrap)
|
bin/duckbrain instalador global (bootstrap)
|
||||||
scripts/release escritor único de versión + tag
|
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/
|
packages/
|
||||||
commands/ cli, migration, toolbox
|
commands/ cli, migration, toolbox
|
||||||
htmx/ htmlComponent, htmx
|
htmx/ htmlComponent, htmx
|
||||||
@@ -73,6 +77,25 @@ duckbrain add crypto htmx # copias reales, listas para commitear
|
|||||||
propios addons sin el instalador global, instala el componente =commands:toolbox=
|
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.
|
(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):
|
||||||
|
|
||||||
|
#+BEGIN_SRC sh
|
||||||
|
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
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
|
(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
|
* Comandos
|
||||||
|
|
||||||
** =duckbrain install [dir]=
|
** =duckbrain install [dir]=
|
||||||
@@ -212,18 +235,73 @@ desconocida y se fija por commit.
|
|||||||
|
|
||||||
* Versionado y releases
|
* Versionado y releases
|
||||||
|
|
||||||
Cada paquete se versiona de forma independiente con tags prefijados
|
Hay *dos* sistemas de versión y no hay que confundirlos:
|
||||||
(=http-v0.1.0=, =crypto-v0.1.0=…). 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.
|
|
||||||
|
|
||||||
El release lo hace un único script:
|
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:
|
||||||
|
|
||||||
#+BEGIN_SRC sh
|
#+BEGIN_SRC sh
|
||||||
scripts/release <paquete> <patch|minor|major>
|
make release # interactivo: elige
|
||||||
|
# paquete e incremento,
|
||||||
|
# muestra cambios
|
||||||
|
# pendientes y confirma
|
||||||
|
scripts/release <paquete> <patch|minor|major> [--yes] # modo directo
|
||||||
#+END_SRC
|
#+END_SRC
|
||||||
|
|
||||||
Bumpea la versión del manifiesto, commitea, crea el tag y publica. Las versiones
|
Bumpea el manifiesto, commitea, crea el tag y lo publica. Al liberar
|
||||||
retiradas se listan en =yanked.json=; las prereleases se omiten salvo =--pre=.
|
=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 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)
|
||||||
|
|
||||||
|
#+BEGIN_SRC sh
|
||||||
|
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
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
|
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
|
* Pruebas
|
||||||
|
|
||||||
|
|||||||
+143
-37
@@ -1,46 +1,143 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# Escritor único de versión + tag de un paquete del toolbox.
|
# Unico escritor de version + tag + push de un paquete del toolbox.
|
||||||
# Uso: scripts/release <paquete> <patch|minor|major>
|
# Interactivo: scripts/release (o `make release`)
|
||||||
|
# Directo: scripts/release <paquete> <patch|minor|major> [--yes]
|
||||||
|
# Al liberar `commands` sincroniza Config::VERSION con la version del manifiesto.
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
if [ "$#" -lt 2 ]; then
|
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
echo "Uso: scripts/release <paquete> <patch|minor|major>" >&2
|
CONFIG_PHP="$ROOT/packages/commands/src/Toolbox/Config.php"
|
||||||
exit 1
|
|
||||||
|
die() { echo "$*" >&2; exit 1; }
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
echo "Uso: scripts/release [<paquete> <patch|minor|major>] [--yes]" >&2
|
||||||
|
echo " sin argumentos: modo interactivo (o 'make release')" >&2
|
||||||
|
}
|
||||||
|
|
||||||
|
packages() {
|
||||||
|
local f
|
||||||
|
for f in "$ROOT"/packages/*/duckbrain.json; do
|
||||||
|
[ -f "$f" ] && basename "$(dirname "$f")"
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
manifest() { echo "$ROOT/packages/$1/duckbrain.json"; }
|
||||||
|
|
||||||
|
version_of() {
|
||||||
|
php -r '$d=json_decode(file_get_contents($argv[1]),true); echo $d["version"] ?? "";' "$(manifest "$1")"
|
||||||
|
}
|
||||||
|
|
||||||
|
bump() {
|
||||||
|
php -r '
|
||||||
|
$v = ltrim($argv[1], "vV");
|
||||||
|
$p = array_map("intval", array_pad(explode(".", preg_replace("/[-+].*/", "", $v)), 3, 0));
|
||||||
|
switch ($argv[2]) {
|
||||||
|
case "major": $p[0]++; $p[1] = 0; $p[2] = 0; break;
|
||||||
|
case "minor": $p[1]++; $p[2] = 0; break;
|
||||||
|
default: $p[2]++;
|
||||||
|
}
|
||||||
|
echo implode(".", $p);' "$1" "$2"
|
||||||
|
}
|
||||||
|
|
||||||
|
ask() {
|
||||||
|
local prompt="$1" reply
|
||||||
|
printf '%s' "$prompt" >&2
|
||||||
|
read -r reply || die "entrada agotada; pasa los argumentos o correlo en un TTY"
|
||||||
|
printf '%s' "$reply"
|
||||||
|
}
|
||||||
|
|
||||||
|
ASSUME_YES=0
|
||||||
|
POSITIONAL=()
|
||||||
|
for arg in "$@"; do
|
||||||
|
case "$arg" in
|
||||||
|
--yes|-y) ASSUME_YES=1 ;;
|
||||||
|
*) POSITIONAL+=("$arg") ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
confirm() {
|
||||||
|
[ "$ASSUME_YES" = "1" ] && return 0
|
||||||
|
local r
|
||||||
|
r="$(ask "$1 [Y/n] ")"
|
||||||
|
case "${r,,}" in ""|y|yes) return 0 ;; *) return 1 ;; esac
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- eleccion de paquete -----------------------------------------------------
|
||||||
|
if [ "${#POSITIONAL[@]}" -ge 1 ]; then
|
||||||
|
PACKAGE="${POSITIONAL[0]}"
|
||||||
|
else
|
||||||
|
mapfile -t PKGS < <(packages)
|
||||||
|
[ "${#PKGS[@]}" -gt 0 ] || die "No hay paquetes en $ROOT/packages/"
|
||||||
|
echo "Paquetes del toolbox:"
|
||||||
|
for i in "${!PKGS[@]}"; do
|
||||||
|
printf ' %2d) %-10s v%s\n' "$((i + 1))" "${PKGS[i]}" "$(version_of "${PKGS[i]}")"
|
||||||
|
done
|
||||||
|
while :; do
|
||||||
|
R="$(ask 'Elegir paquete (numero o nombre): ')"
|
||||||
|
if [[ "$R" =~ ^[0-9]+$ ]] && [ "$R" -ge 1 ] && [ "$R" -le "${#PKGS[@]}" ]; then
|
||||||
|
PACKAGE="${PKGS[R - 1]}"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
printf '%s\n' "${PKGS[@]}" | grep -qxF "$R" && { PACKAGE="$R"; break; }
|
||||||
|
echo " opcion invalida" >&2
|
||||||
|
done
|
||||||
fi
|
fi
|
||||||
|
|
||||||
PACKAGE="$1"
|
MANIFEST="$(manifest "$PACKAGE")"
|
||||||
TYPE="$2"
|
[ -f "$MANIFEST" ] || die "No existe el paquete '$PACKAGE' ($MANIFEST)"
|
||||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
||||||
MANIFEST="$ROOT/packages/$PACKAGE/duckbrain.json"
|
|
||||||
|
|
||||||
if [ ! -f "$MANIFEST" ]; then
|
# --- eleccion de incremento ---------------------------------------------------
|
||||||
echo "No existe el paquete '$PACKAGE' ($MANIFEST)" >&2
|
if [ "${#POSITIONAL[@]}" -ge 2 ]; then
|
||||||
exit 1
|
TYPE="${POSITIONAL[1]}"
|
||||||
|
else
|
||||||
|
while :; do
|
||||||
|
printf 'Incremento para %s (actual v%s):\n 1) patch\n 2) minor\n 3) major\n' "$PACKAGE" "$(version_of "$PACKAGE")" >&2
|
||||||
|
R="$(ask 'Elegir (numero o nombre): ')"
|
||||||
|
case "$R" in
|
||||||
|
1|patch) TYPE=patch; break ;;
|
||||||
|
2|minor) TYPE=minor; break ;;
|
||||||
|
3|major) TYPE=major; break ;;
|
||||||
|
esac
|
||||||
|
echo " opcion invalida" >&2
|
||||||
|
done
|
||||||
fi
|
fi
|
||||||
|
|
||||||
case "$TYPE" in
|
case "$TYPE" in
|
||||||
patch|minor|major) ;;
|
patch|minor|major) ;;
|
||||||
*) echo "Incremento inválido: '$TYPE' (usa patch|minor|major)" >&2; exit 1 ;;
|
*) die "Incremento invalido: '$TYPE' (usa patch|minor|major)" ;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
VERSION="$(php -r '$d=json_decode(file_get_contents($argv[1]),true); echo $d["version"] ?? "";' "$MANIFEST")"
|
VERSION="$(version_of "$PACKAGE")"
|
||||||
if [ -z "$VERSION" ]; then
|
[ -n "$VERSION" ] || die "El manifiesto no declara 'version': $MANIFEST"
|
||||||
echo "El manifiesto no declara 'version': $MANIFEST" >&2
|
NEW_VERSION="$(bump "$VERSION" "$TYPE")"
|
||||||
exit 1
|
TAG="$PACKAGE-v$NEW_VERSION"
|
||||||
|
|
||||||
|
CURRENT_CONFIG=""
|
||||||
|
[ "$PACKAGE" = "commands" ] && CURRENT_CONFIG="$(grep -oP "const VERSION = '\K[^']+" "$CONFIG_PHP" || true)"
|
||||||
|
|
||||||
|
LAST_TAG="$(git -C "$ROOT" tag -l "$PACKAGE-v*" --sort=-v:refname | head -1 || true)"
|
||||||
|
if [ -n "$LAST_TAG" ]; then
|
||||||
|
CHANGES="$(git -C "$ROOT" log --oneline "$LAST_TAG"..HEAD -- "packages/$PACKAGE" || true)"
|
||||||
|
else
|
||||||
|
CHANGES="$(git -C "$ROOT" log --oneline HEAD -- "packages/$PACKAGE" || true)"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
NEW_VERSION="$(php -r '
|
# --- resumen y confirmacion ---------------------------------------------------
|
||||||
$v = ltrim($argv[1], "vV");
|
echo "Release: $PACKAGE v$VERSION -> v$NEW_VERSION (tag $TAG)"
|
||||||
$type = $argv[2];
|
if [ "$PACKAGE" = "commands" ] && [ "$CURRENT_CONFIG" != "$NEW_VERSION" ]; then
|
||||||
$p = explode(".", preg_replace("/[-+].*/", "", $v));
|
echo " Config::VERSION: v$CURRENT_CONFIG -> v$NEW_VERSION (se sincroniza)"
|
||||||
$p = array_pad($p, 3, 0);
|
fi
|
||||||
$p = array_map("intval", $p);
|
if [ -z "$CHANGES" ]; then
|
||||||
if ($type === "major") { $p[0]++; $p[1] = 0; $p[2] = 0; }
|
echo "Cambios desde el ultimo release: ninguno"
|
||||||
elseif ($type === "minor") { $p[1]++; $p[2] = 0; }
|
else
|
||||||
else { $p[2]++; }
|
echo "Cambios desde el ultimo release:"
|
||||||
echo implode(".", $p);
|
echo "$CHANGES" | sed 's/^/ /'
|
||||||
' "$VERSION" "$TYPE")"
|
fi
|
||||||
|
|
||||||
|
confirm "Confirmar release?" || die "Cancelado; sin cambios."
|
||||||
|
|
||||||
|
# --- aplicar -------------------------------------------------------------------
|
||||||
php -r '
|
php -r '
|
||||||
$file = $argv[1];
|
$file = $argv[1];
|
||||||
$data = json_decode(file_get_contents($file), true);
|
$data = json_decode(file_get_contents($file), true);
|
||||||
@@ -48,14 +145,23 @@ $data["version"] = $argv[2];
|
|||||||
file_put_contents($file, json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . "\n");
|
file_put_contents($file, json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . "\n");
|
||||||
' "$MANIFEST" "$NEW_VERSION"
|
' "$MANIFEST" "$NEW_VERSION"
|
||||||
|
|
||||||
TAG="$PACKAGE-v$NEW_VERSION"
|
if [ "$PACKAGE" = "commands" ] && [ "$CURRENT_CONFIG" != "$NEW_VERSION" ]; then
|
||||||
|
sed -i "s/const VERSION = '[^']*'/const VERSION = '$NEW_VERSION'/" "$CONFIG_PHP"
|
||||||
git -C "$ROOT" add -- "$MANIFEST"
|
|
||||||
git -C "$ROOT" commit -q -m "release($PACKAGE): v$NEW_VERSION"
|
|
||||||
git -C "$ROOT" tag -a "$TAG" -m "release($PACKAGE): v$NEW_VERSION"
|
|
||||||
|
|
||||||
if git -C "$ROOT" remote get-url origin >/dev/null 2>&1; then
|
|
||||||
git -C "$ROOT" push -q origin HEAD --follow-tags
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "release $PACKAGE v$NEW_VERSION ($TAG)"
|
git -C "$ROOT" add -- "$MANIFEST"
|
||||||
|
[ "$PACKAGE" = "commands" ] && git -C "$ROOT" add -- "$CONFIG_PHP"
|
||||||
|
git -C "$ROOT" commit -q -m "release($PACKAGE): v$NEW_VERSION"
|
||||||
|
git -C "$ROOT" tag -a "$TAG" -m "release($PACKAGE): v$NEW_VERSION"
|
||||||
|
echo "OK: commit + tag $TAG creados."
|
||||||
|
|
||||||
|
if git -C "$ROOT" remote get-url origin >/dev/null 2>&1; then
|
||||||
|
if confirm "Pushear a origin (commit + tag)?"; then
|
||||||
|
git -C "$ROOT" push -q origin HEAD --follow-tags
|
||||||
|
echo "OK: pusheado."
|
||||||
|
else
|
||||||
|
echo "Sin pushear. Cuando quieras: git push origin HEAD --follow-tags"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "No hay remote origin; el tag queda local."
|
||||||
|
fi
|
||||||
|
|||||||
@@ -61,12 +61,12 @@ Harness::it('la actualización del motor surte efecto en la siguiente invocació
|
|||||||
|
|
||||||
// Simula una actualización del motor reescribiendo su archivo en disco.
|
// Simula una actualización del motor reescribiendo su archivo en disco.
|
||||||
$config = (string) file_get_contents($proj . '/src/Toolbox/Config.php');
|
$config = (string) file_get_contents($proj . '/src/Toolbox/Config.php');
|
||||||
$config = str_replace("VERSION = '0.1.0'", "VERSION = '0.9.9'", $config);
|
$config = preg_replace("/VERSION = '[^']*'/", "VERSION = '0.9.9'", $config);
|
||||||
write_file($proj . '/src/Toolbox/Config.php', $config);
|
write_file($proj . '/src/Toolbox/Config.php', $config);
|
||||||
|
|
||||||
$after = trim((string) shell_exec('cd ' . escapeshellarg($proj) . ' && php duckbrain --version 2>&1'));
|
$after = trim((string) shell_exec('cd ' . escapeshellarg($proj) . ' && php duckbrain --version 2>&1'));
|
||||||
|
|
||||||
ok(str_contains($before, '0.1.0'), 'antes usa la versión vieja');
|
ok(str_contains($before, \Toolbox\Config::VERSION), 'antes usa la versión vieja');
|
||||||
ok(str_contains($after, '0.9.9'), 'después usa la versión nueva sin recompilar');
|
ok(str_contains($after, '0.9.9'), 'después usa la versión nueva sin recompilar');
|
||||||
|
|
||||||
rrmdir($proj);
|
rrmdir($proj);
|
||||||
|
|||||||
Reference in New Issue
Block a user