feat(release): introduce interactive mode
This commit is contained in:
+143
-37
@@ -1,46 +1,143 @@
|
||||
#!/usr/bin/env bash
|
||||
# Escritor único de versión + tag de un paquete del toolbox.
|
||||
# Uso: scripts/release <paquete> <patch|minor|major>
|
||||
# Unico escritor de version + tag + push de un paquete del toolbox.
|
||||
# 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
|
||||
|
||||
if [ "$#" -lt 2 ]; then
|
||||
echo "Uso: scripts/release <paquete> <patch|minor|major>" >&2
|
||||
exit 1
|
||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
CONFIG_PHP="$ROOT/packages/commands/src/Toolbox/Config.php"
|
||||
|
||||
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
|
||||
|
||||
PACKAGE="$1"
|
||||
TYPE="$2"
|
||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
MANIFEST="$ROOT/packages/$PACKAGE/duckbrain.json"
|
||||
MANIFEST="$(manifest "$PACKAGE")"
|
||||
[ -f "$MANIFEST" ] || die "No existe el paquete '$PACKAGE' ($MANIFEST)"
|
||||
|
||||
if [ ! -f "$MANIFEST" ]; then
|
||||
echo "No existe el paquete '$PACKAGE' ($MANIFEST)" >&2
|
||||
exit 1
|
||||
# --- eleccion de incremento ---------------------------------------------------
|
||||
if [ "${#POSITIONAL[@]}" -ge 2 ]; then
|
||||
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
|
||||
|
||||
case "$TYPE" in
|
||||
patch|minor|major) ;;
|
||||
*) echo "Incremento inválido: '$TYPE' (usa patch|minor|major)" >&2; exit 1 ;;
|
||||
*) die "Incremento invalido: '$TYPE' (usa patch|minor|major)" ;;
|
||||
esac
|
||||
|
||||
VERSION="$(php -r '$d=json_decode(file_get_contents($argv[1]),true); echo $d["version"] ?? "";' "$MANIFEST")"
|
||||
if [ -z "$VERSION" ]; then
|
||||
echo "El manifiesto no declara 'version': $MANIFEST" >&2
|
||||
exit 1
|
||||
VERSION="$(version_of "$PACKAGE")"
|
||||
[ -n "$VERSION" ] || die "El manifiesto no declara 'version': $MANIFEST"
|
||||
NEW_VERSION="$(bump "$VERSION" "$TYPE")"
|
||||
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
|
||||
|
||||
NEW_VERSION="$(php -r '
|
||||
$v = ltrim($argv[1], "vV");
|
||||
$type = $argv[2];
|
||||
$p = explode(".", preg_replace("/[-+].*/", "", $v));
|
||||
$p = array_pad($p, 3, 0);
|
||||
$p = array_map("intval", $p);
|
||||
if ($type === "major") { $p[0]++; $p[1] = 0; $p[2] = 0; }
|
||||
elseif ($type === "minor") { $p[1]++; $p[2] = 0; }
|
||||
else { $p[2]++; }
|
||||
echo implode(".", $p);
|
||||
' "$VERSION" "$TYPE")"
|
||||
# --- resumen y confirmacion ---------------------------------------------------
|
||||
echo "Release: $PACKAGE v$VERSION -> v$NEW_VERSION (tag $TAG)"
|
||||
if [ "$PACKAGE" = "commands" ] && [ "$CURRENT_CONFIG" != "$NEW_VERSION" ]; then
|
||||
echo " Config::VERSION: v$CURRENT_CONFIG -> v$NEW_VERSION (se sincroniza)"
|
||||
fi
|
||||
if [ -z "$CHANGES" ]; then
|
||||
echo "Cambios desde el ultimo release: ninguno"
|
||||
else
|
||||
echo "Cambios desde el ultimo release:"
|
||||
echo "$CHANGES" | sed 's/^/ /'
|
||||
fi
|
||||
|
||||
confirm "Confirmar release?" || die "Cancelado; sin cambios."
|
||||
|
||||
# --- aplicar -------------------------------------------------------------------
|
||||
php -r '
|
||||
$file = $argv[1];
|
||||
$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");
|
||||
' "$MANIFEST" "$NEW_VERSION"
|
||||
|
||||
TAG="$PACKAGE-v$NEW_VERSION"
|
||||
|
||||
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
|
||||
if [ "$PACKAGE" = "commands" ] && [ "$CURRENT_CONFIG" != "$NEW_VERSION" ]; then
|
||||
sed -i "s/const VERSION = '[^']*'/const VERSION = '$NEW_VERSION'/" "$CONFIG_PHP"
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user