feat(kotlin-compose): design system fn.compose:ui + toolbelt android Linux-first

Design system Compose (kotlin/functions/ui, modulo Gradle `fn.compose:ui`):
- FnTokens + FnTheme con la paleta heredada al hex de cpp/DESIGN_SYSTEM.md
  (Mantine v9 dark + indigo), identica a la web @fn_library y a las apps C++.
- 26 componentes Compose (Layout/Display/Inputs/Feedback/Data/Charts) +
  FnTheme + FnTokens registrados en el registry (28 entradas kind=component
  lang=kt domain=ui), descubribles via fn_search. Habilitan init_kotlin_app.

Recuperacion: el commit cb6d9e6 habia anadido `kotlin/functions/ui/` al
.gitignore, por eso el design system nunca se versiono y se perdio del working
tree. Des-ignorado; el .gitignore interno del modulo ya excluye
build/.gradle/local.properties. La gallery (apps/gallery_kt) se recupero del
sub-repo Gitea y sus 27 componentes se reconstruyeron con su MainActivity como
contrato exacto.

Toolbelt Android Linux-first (antes asumia WSL2 + Windows):
- adb_wsl 1.1.0, android_emulator_start 1.1.0, android_emulator_list 1.1.0:
  resuelven adb/emulator nativos del SDK ($ANDROID_HOME), .exe solo fallback WSL2.
- android_emulator_start: fix `timeout adb_run wait-for-device` (timeout no puede
  ejecutar una funcion del shell; ahora invoca el binario $ADB directamente).
- install_android_sdk 1.0.1: fix licencias bajo pipefail (SIGPIPE de `yes`) +
  trap EXIT con variable unbound.
- docs/capabilities/android.md regenerado Linux-first + seccion design system.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-04 23:43:59 +02:00
parent c65f1698ae
commit efc9911925
55 changed files with 3170 additions and 109 deletions
+42 -44
View File
@@ -3,17 +3,17 @@ name: adb_wsl
kind: function
lang: bash
domain: infra
version: "1.0.0"
version: "1.1.0"
purity: impure
signature: "source adb_wsl.sh [ADB=<path>] [ANDROID_SDK_WIN=<sdk_root>]"
description: "Wrapper sourceable para usar adb.exe Windows desde WSL2. Resuelve binario, convierte paths, espera boot del emulador."
tags: ["android", "adb", "wsl", "windows"]
signature: "source adb_wsl.sh [ADB=<path>] [ANDROID_HOME=<sdk_root>]"
description: "Wrapper sourceable para resolver e invocar adb. Linux-first: usa el adb nativo del Android SDK ($ANDROID_HOME) o del PATH; fallback a adb.exe solo si detecta WSL2. Expone adb_run, adb_devices, adb_pick_serial, adb_s, adb_wait_boot."
tags: ["android", "adb", "linux", "emulator", "wsl"]
params:
- name: ADB
desc: "Env var opcional. Path absoluto a adb.exe. Si no se fija, se construye desde ANDROID_SDK_WIN o el default /mnt/c/Users/lucas/AppData/Local/Android/Sdk."
- name: ANDROID_SDK_WIN
desc: "Env var opcional. Raiz del Android SDK montado en WSL. Default: /mnt/c/Users/lucas/AppData/Local/Android/Sdk."
output: "Source-able shell helpers: adb_run, adb_devices, adb_wsl_to_win, adb_wait_boot. Define ADB env var apuntando a Windows adb.exe via ANDROID_SDK_WIN."
desc: "Env var opcional. Path absoluto al binario adb (override explicito). Si no se fija, se resuelve Linux-first: $ANDROID_HOME/platform-tools/adb, luego adb del PATH, luego adb.exe si WSL2."
- name: ANDROID_HOME
desc: "Env var opcional. Raiz del Android SDK nativo. Si esta presente, se usa $ANDROID_HOME/platform-tools/adb. Tambien se acepta ANDROID_SDK_ROOT."
output: "Source-able shell helpers: adb_run, adb_devices, adb_pick_serial, adb_s, adb_wait_boot, adb_wsl_to_win. Resuelve y fija la env var ADB al binario adb disponible."
uses_functions: []
uses_types: []
returns: []
@@ -26,24 +26,33 @@ test_file_path: ""
file_path: "bash/functions/infra/adb_wsl.sh"
---
## Uso
## Cuando usarla
Sourcéala como capa base de cualquier script que hable con un device o emulador Android via adb. Es la dependencia comun de todo el toolbelt android del registry (`android_screenshot`, `android_input_*`, `android_logcat`, `android_app_*`, `android_push/pull`). En Linux nativo resuelve el adb del SDK automaticamente; no hace falta configurar nada si `ANDROID_HOME` esta exportado (o `adb` esta en el PATH).
## Ejemplo
```bash
# Sourcear (usa SDK default)
# Linux nativo: con el SDK instalado y ANDROID_HOME exportado, resuelve solo.
source ~/android-sdk/env.sh
source bash/functions/infra/adb_wsl.sh
adb_devices
# List of devices attached
# emulator-5554 device
# Sourcear con SDK custom
ANDROID_SDK_WIN=/mnt/d/Android/Sdk source bash/functions/infra/adb_wsl.sh
# Fijar binario adb explicito (override)
ADB=/opt/android/platform-tools/adb source bash/functions/infra/adb_wsl.sh
# Sourcear con binario fijo
ADB=/mnt/c/my/tools/adb.exe source bash/functions/infra/adb_wsl.sh
# Smoke test
bash bash/functions/infra/adb_wsl.sh --self-test
# Android Debug Bridge version 1.0.41
```
## Funciones expuestas
### `adb_run "<args...>"`
Ejecuta `$ADB` con los argumentos dados. Retorna el exit code de `adb.exe`.
Ejecuta `$ADB` con los argumentos dados. Retorna el exit code de adb.
```bash
adb_run shell ls /sdcard/
@@ -54,45 +63,34 @@ adb_run install app.apk
Alias de `adb_run devices`. Lista dispositivos/emuladores conectados.
```bash
adb_devices
# List of devices attached
# emulator-5554 device
```
### `adb_pick_serial [--serial <S>] [...]`
### `adb_wsl_to_win <path_wsl>`
Convierte un path WSL a formato Windows con `wslpath -w`. Si `wslpath` no está disponible retorna el path sin convertir.
Resuelve el serial a usar (multi-device). Lee `--serial X` de los args y setea los globals `ADB_PICK_SERIAL` y `ADB_PICK_REST`. Si no se pasa, autoselecciona el primer device/emulador conectado.
```bash
win_path=$(adb_wsl_to_win /home/lucas/proyecto/app.apk)
# C:\Users\lucas\AppData\Local\... (o la ruta Windows equivalente)
adb_run install "$win_path"
adb_pick_serial "$@" || { echo "no device" >&2; exit 3; }
serial="$ADB_PICK_SERIAL"; set -- "${ADB_PICK_REST[@]}"
```
### `adb_s <serial> <args...>`
Atajo de `adb_run -s <serial> <args...>` para multi-device.
### `adb_wait_boot [timeout_s]`
Espera a que el emulador/dispositivo complete el boot (`sys.boot_completed = 1`). Útil tras lanzar un AVD en CI.
Espera a que el emulador/dispositivo complete el boot (`sys.boot_completed = 1`). Polling cada 3s. Retorna `0` si bootó, `1` si timeout (default 120s).
```bash
adb_wait_boot # timeout 120s
adb_wait_boot 60 # timeout 60s
```
### `adb_wsl_to_win <path_wsl>`
Retorna `0` si el boot se completó, `1` si expiró el timeout.
Legacy WSL: convierte path WSL→Windows con `wslpath -w`. En Linux nativo (sin `wslpath`) devuelve el path tal cual.
## Smoke test
## Gotchas
```bash
bash bash/functions/infra/adb_wsl.sh --self-test
# OK
```
- **Linux-first.** El default ya NO es Windows. Resolucion: `$ADB``$ANDROID_HOME/platform-tools/adb``adb` del PATH → (solo si `/proc/version` indica WSL2) `adb.exe`. En un PC Linux con el SDK instalado funciona sin configurar nada.
- **Necesita el SDK o adb en PATH.** Si no encuentra adb aborta con mensaje a stderr. Instala con `fn run install_android_sdk_bash_infra` y exporta `ANDROID_HOME` (o `source ~/android-sdk/env.sh`).
- **`ADB` se resuelve una sola vez al sourcing.** Cambiar el SDK despues requiere re-sourcear.
- **Sourcéala con bash, no zsh.** Los consumidores usan `${BASH_SOURCE[0]}` para localizar este archivo; ejecutarlos con `bash <file>` (no `zsh`/`source` desde zsh) resuelve el path correctamente.
## Notas
## Capability growth log
- El script es **source-able**: define funciones en el shell actual, no crea subshell.
- `ADB` se resuelve una sola vez al sourcing. Si el binario no existe en disco, la carga falla con mensaje en stderr y `return 1` / `exit 1`.
- `adb_wait_boot` hace polling cada 3 segundos. Ajustar `interval` si el emulador es especialmente lento.
- En WSL2 `wslpath` siempre está disponible; el fallback existe para entornos Linux puros que accidentalmente sourceen el archivo.
- Si el emulador requiere `-s <serial>`, pasar el flag directamente a `adb_run`: `adb_run -s emulator-5554 shell ...`.
---
- v1.1.0 (2026-06-03) — Linux-first: la resolucion de adb ahora prioriza el adb nativo del SDK (`$ANDROID_HOME/platform-tools/adb`) y del PATH; el adb.exe de Windows queda como fallback legacy solo bajo WSL2. Se elimina el default hardcodeado `/mnt/c/Users/lucas/...`. Todo el toolbelt android (~20 funciones) pasa a funcionar en Linux nativo sin preexportar `ADB`.
+25 -10
View File
@@ -1,20 +1,35 @@
#!/usr/bin/env bash
# adb_wsl — Wrapper sourceable para usar adb.exe Windows desde WSL2.
# adb_wsl — Wrapper sourceable para resolver e invocar adb.
# Linux-first: usa el adb nativo del Android SDK o del PATH. Conserva un
# fallback a adb.exe SOLO cuando se detecta WSL2 (legacy). El nombre del
# archivo se mantiene por compatibilidad con sus consumidores del registry.
# Uso: source bash/functions/infra/adb_wsl.sh
# Smoke test: bash bash/functions/infra/adb_wsl.sh --self-test
# ---------------------------------------------------------------------------
# Resolver ADB
# Resolver ADB (Linux-first; fallback WSL legacy)
# ---------------------------------------------------------------------------
# El caller puede fijar ADB antes de sourcing para apuntar a otro binario.
# Prioridad de resolucion:
# 1. $ADB preexportada por el caller (override explicito).
# 2. adb nativo del Android SDK ($ANDROID_HOME / $ANDROID_SDK_ROOT).
# 3. adb del PATH.
# 4. (legacy) adb.exe de Windows, solo si corremos dentro de WSL2.
if [[ -z "${ADB:-}" ]]; then
_sdk_root="${ANDROID_SDK_WIN:-/mnt/c/Users/lucas/AppData/Local/Android/Sdk}"
ADB="${_sdk_root}/platform-tools/adb.exe"
unset _sdk_root
_sdk="${ANDROID_HOME:-${ANDROID_SDK_ROOT:-}}"
if [[ -n "$_sdk" && -x "$_sdk/platform-tools/adb" ]]; then
ADB="$_sdk/platform-tools/adb"
elif command -v adb &>/dev/null; then
ADB="$(command -v adb)"
elif grep -qiE "(microsoft|wsl)" /proc/version 2>/dev/null; then
_sdk_win="${ANDROID_SDK_WIN:-/mnt/c/Users/$USER/AppData/Local/Android/Sdk}"
ADB="${_sdk_win}/platform-tools/adb.exe"
unset _sdk_win
fi
unset _sdk
fi
if [[ ! -f "$ADB" ]]; then
echo "adb_wsl: ADB no encontrado en '$ADB'. Fija ADB= o ANDROID_SDK_WIN= antes de sourcear." >&2
if [[ -z "${ADB:-}" ]] || ! command -v "$ADB" &>/dev/null; then
echo "adb_wsl: adb no encontrado. Instala el SDK (fn run install_android_sdk_bash_infra), exporta ANDROID_HOME, o fija ADB= antes de sourcear." >&2
# Solo abortamos si el script se ejecuta directamente; si se sourcea,
# permitimos continuar para que el caller maneje el error.
return 1 2>/dev/null || exit 1
@@ -22,8 +37,8 @@ fi
# ---------------------------------------------------------------------------
# adb_run "<args...>"
# Ejecuta el ADB Windows con los argumentos dados.
# Retorna el exit code de adb.exe.
# Ejecuta adb (el binario resuelto en $ADB) con los argumentos dados.
# Retorna el exit code de adb.
# ---------------------------------------------------------------------------
adb_run() {
"$ADB" "$@"
+20 -14
View File
@@ -3,11 +3,11 @@ name: android_emulator_list
kind: function
lang: bash
domain: infra
version: "1.0.0"
version: "1.1.0"
purity: impure
signature: "android_emulator_list([--json])"
description: "Lista los AVDs disponibles invocando emulator.exe Windows desde WSL2."
tags: [android, emulator, wsl]
description: "Lista los AVDs disponibles. Linux-first: usa el emulator nativo del Android SDK ($ANDROID_HOME); fallback a emulator.exe solo bajo WSL2."
tags: [android, emulator, linux, avd, wsl]
uses_functions: []
uses_types: []
returns: []
@@ -17,35 +17,41 @@ imports: []
params:
- name: "--json"
desc: "Optional flag, outputs JSON array instead of newline-separated names"
output: "Lista de AVDs disponibles en el SDK Windows. Una por linea, o JSON array con --json."
output: "Lista de AVDs disponibles en el SDK. Una por linea, o JSON array con --json."
tested: false
tests: []
test_file_path: ""
file_path: "bash/functions/infra/android_emulator_list.sh"
notes: "Lee env var EMULATOR o ANDROID_SDK_WIN. Default Windows path: /mnt/c/Users/lucas/AppData/Local/Android/Sdk/emulator/emulator.exe. Exit 0 si lista (incluso vacia). Exit 1 solo si el binario no existe o no es ejecutable."
notes: "Resuelve el binario emulator Linux-first ($ANDROID_HOME/emulator/emulator -> emulator del PATH -> emulator.exe si WSL2). Override con EMULATOR=. Exit 0 si lista (incluso vacia). Exit 1 solo si el binario no existe."
---
## Ejemplo
```bash
source ~/android-sdk/env.sh # exporta ANDROID_HOME
# Listar AVDs (una por linea)
android_emulator_list
# Pixel_API34
# Listar AVDs en formato JSON
android_emulator_list --json
# ["Pixel_7_API_34","Pixel_4_API_30"]
# ["Pixel_API34"]
# Sobreescribir ruta del emulador
EMULATOR="/custom/path/emulator.exe" android_emulator_list
# Sobreescribir SDK base
ANDROID_SDK_WIN="/mnt/d/Android/Sdk" android_emulator_list
EMULATOR="/opt/android/emulator/emulator" android_emulator_list
```
## Notas
## Cuando usarla
El script es ejecutable directamente (`chmod +x`) o invocable con `bash android_emulator_list.sh`.
Antes de arrancar un emulador, para validar que el AVD existe (lo hace `deploy_capacitor_to_emulator` y `run_kotlin_app_tests` internamente). Útil también para listar qué AVDs hay creados en la máquina.
`emulator.exe -list-avds` imprime warnings a stderr que se descartan con `2>/dev/null`. La captura con `mapfile` filtra ademas lineas vacias para producir una lista limpia.
## Gotchas
La variable `EMULATOR` tiene prioridad sobre `ANDROID_SDK_WIN`. Si ninguna esta definida se usa el path Windows por defecto de Lucas.
- **Linux-first.** El default ya no es Windows. Resuelve `$ANDROID_HOME/emulator/emulator`, luego `emulator` del PATH, y solo bajo WSL2 cae a `emulator.exe`.
- `emulator -list-avds` imprime warnings a stderr que se descartan con `2>/dev/null`. La captura con `mapfile` filtra líneas vacías.
- Override del binario con `EMULATOR=`; override del SDK con `ANDROID_HOME=`.
## Capability growth log
- v1.1.0 (2026-06-03) — Linux-first: resuelve el emulator nativo del SDK (`$ANDROID_HOME`) y del PATH antes que `emulator.exe`; se elimina el default hardcodeado `/mnt/c/Users/lucas/...`.
+16 -5
View File
@@ -1,12 +1,23 @@
#!/usr/bin/env bash
# android_emulator_list — Lista los AVDs disponibles invocando emulator.exe Windows desde WSL2.
# android_emulator_list — Lista los AVDs disponibles. Linux-first: usa el
# emulator nativo del Android SDK; fallback a emulator.exe solo bajo WSL2.
set -euo pipefail
# Resolve emulator binary
EMULATOR="${EMULATOR:-${ANDROID_SDK_WIN:-/mnt/c/Users/lucas/AppData/Local/Android/Sdk}/emulator/emulator.exe}"
# Resolve emulator binary (Linux-first; WSL fallback)
if [[ -z "${EMULATOR:-}" ]]; then
_sdk="${ANDROID_HOME:-${ANDROID_SDK_ROOT:-}}"
if [[ -n "$_sdk" && -x "$_sdk/emulator/emulator" ]]; then
EMULATOR="$_sdk/emulator/emulator"
elif command -v emulator &>/dev/null; then
EMULATOR="$(command -v emulator)"
elif grep -qiE "(microsoft|wsl)" /proc/version 2>/dev/null; then
EMULATOR="${ANDROID_SDK_WIN:-/mnt/c/Users/$USER/AppData/Local/Android/Sdk}/emulator/emulator.exe"
fi
unset _sdk
fi
if [[ ! -x "$EMULATOR" ]]; then
echo "error: emulator binary not found or not executable: $EMULATOR" >&2
if [[ -z "${EMULATOR:-}" ]] || ! command -v "$EMULATOR" &>/dev/null; then
echo "error: emulator no encontrado. Instala el SDK (fn run install_android_sdk_bash_infra) + el paquete 'emulator', exporta ANDROID_HOME, o fija EMULATOR=." >&2
exit 1
fi
+23 -13
View File
@@ -3,14 +3,14 @@ name: android_emulator_start
kind: function
lang: bash
domain: infra
version: "1.0.0"
version: "1.1.0"
purity: impure
signature: "android_emulator_start(avd_name: string, timeout_s: int) -> string"
description: "Arranca un AVD en background y espera a que termine de bootear. Idempotente: si ya hay emulador corriendo no lanza otro."
tags: [android, emulator, wsl]
description: "Arranca un AVD Android en background y espera a que termine de bootear. Linux-first: resuelve el emulator/adb nativos del SDK; fallback a binarios .exe solo bajo WSL2. Idempotente: si ya hay un emulador corriendo, imprime 'already running' y su serial sin lanzar otro."
tags: [android, emulator, linux, avd, wsl]
params:
- name: avd_name
desc: "Nombre del AVD a arrancar (visible con android_emulator_list o `emulator.exe -list-avds`)"
desc: "Nombre del AVD a arrancar (visible con android_emulator_list o `emulator -list-avds`)"
- name: timeout_s
desc: "Timeout total en segundos para esperar el boot completo. Opcional, default 180"
output: "Serial del device emulado (ej. emulator-5554) en stdout. Exit 0 = boot completo, exit 1 = timeout o emulador murio."
@@ -29,21 +29,31 @@ file_path: "bash/functions/infra/android_emulator_start.sh"
## Ejemplo
```bash
source ~/android-sdk/env.sh # exporta ANDROID_HOME -> resuelve emulator/adb nativos
source bash/functions/infra/android_emulator_start.sh
# Arrancar AVD con timeout por defecto (180s)
serial=$(android_emulator_start "Pixel_6_API_34")
serial=$(android_emulator_start "Pixel_API34")
echo "Emulador listo: $serial" # emulator-5554
# Con timeout personalizado
serial=$(android_emulator_start "Pixel_6_API_34" 300)
serial=$(android_emulator_start "Pixel_API34" 300)
```
## Notas
Para ver la ventana del emulador en un escritorio Linux, exporta `DISPLAY` (y `XAUTHORITY`) antes de invocar.
- Sourcea `adb_wsl.sh` del mismo directorio si existe (provee `ADB`, `adb_run`, `adb_wait_boot`). Si no, usa implementacion inline.
- Resuelve `EMULATOR` y `ADB` desde `ANDROID_SDK_WIN` (default `/mnt/c/Users/lucas/AppData/Local/Android/Sdk`) o desde las variables de entorno `EMULATOR=` / `ADB=` si ya están fijadas.
- Idempotente: si `adb devices` ya muestra un `emulator-*`, imprime "already running" + el serial y sale con exit 0 sin lanzar un segundo proceso.
- Log del emulador en `/tmp/emulator_<avd>.log`. PID en `/tmp/emulator_<avd>.pid`.
- El timeout total se reparte: primera mitad para `adb wait-for-device`, segunda mitad para esperar `sys.boot_completed=1`.
- Diseñado para WSL2 con Android SDK instalado en Windows. En Linux nativo basta cambiar las rutas de los binarios via `EMULATOR=` y `ADB=`.
## Cuando usarla
Cuando un script necesita un emulador booteado antes de instalar un APK o correr tests instrumentados (`gradle_instrumented_test`, `run_kotlin_app_tests`). Es idempotente, así que se puede llamar al principio de cualquier pipeline sin comprobar antes si ya hay uno arriba.
## Gotchas
- **Linux-first.** Resuelve `EMULATOR`/`ADB` desde `$ANDROID_HOME/{emulator/emulator, platform-tools/adb}` o del PATH; `emulator.exe`/`adb.exe` solo como fallback bajo WSL2. Override manual con `EMULATOR=`/`ADB=`.
- **Necesita `DISPLAY` para ventana.** Sin un servidor X accesible el emulador puede fallar al abrir ventana. Para headless/CI añade `-no-window` (editar la función o lanzar el emulador aparte).
- **Aceleración KVM.** Requiere acceso a `/dev/kvm` (grupo `kvm` o ACL). Sin ella el boot es lentísimo o falla.
- Log del emulador en `/tmp/emulator_<avd>.log`, PID en `/tmp/emulator_<avd>.pid`.
- El timeout total se reparte: primera mitad para `adb wait-for-device`, segunda para `sys.boot_completed=1`.
## Capability growth log
- v1.1.0 (2026-06-03) — Linux-first: resuelve emulator/adb nativos del SDK (`$ANDROID_HOME`) antes que los `.exe` de Windows (ahora solo fallback WSL2); se elimina el default hardcodeado `/mnt/c/Users/lucas/...`. fix: `timeout <n> adb_run wait-for-device` fallaba siempre porque `timeout` no puede ejecutar la función shell `adb_run`; ahora invoca el binario `"$ADB"` directamente.
+29 -14
View File
@@ -11,11 +11,17 @@ if [[ -f "$_ADB_WSL_SH" ]]; then
# shellcheck source=adb_wsl.sh
source "$_ADB_WSL_SH"
else
# Fallback inline: resolver ADB
# Fallback inline: resolver ADB (Linux-first; WSL fallback)
if [[ -z "${ADB:-}" ]]; then
_sdk_root="${ANDROID_SDK_WIN:-/mnt/c/Users/lucas/AppData/Local/Android/Sdk}"
ADB="${_sdk_root}/platform-tools/adb.exe"
unset _sdk_root
_sdk="${ANDROID_HOME:-${ANDROID_SDK_ROOT:-}}"
if [[ -n "$_sdk" && -x "$_sdk/platform-tools/adb" ]]; then
ADB="$_sdk/platform-tools/adb"
elif command -v adb &>/dev/null; then
ADB="$(command -v adb)"
else
ADB="${ANDROID_SDK_WIN:-/mnt/c/Users/$USER/AppData/Local/Android/Sdk}/platform-tools/adb.exe"
fi
unset _sdk
fi
adb_run() { "$ADB" "$@"; }
adb_wait_boot() {
@@ -33,12 +39,18 @@ else
fi
# ---------------------------------------------------------------------------
# Resolver EMULATOR
# Resolver EMULATOR (Linux-first; WSL fallback)
# ---------------------------------------------------------------------------
if [[ -z "${EMULATOR:-}" ]]; then
_sdk_root="${ANDROID_SDK_WIN:-/mnt/c/Users/lucas/AppData/Local/Android/Sdk}"
EMULATOR="${_sdk_root}/emulator/emulator.exe"
unset _sdk_root
_sdk="${ANDROID_HOME:-${ANDROID_SDK_ROOT:-}}"
if [[ -n "$_sdk" && -x "$_sdk/emulator/emulator" ]]; then
EMULATOR="$_sdk/emulator/emulator"
elif command -v emulator &>/dev/null; then
EMULATOR="$(command -v emulator)"
elif grep -qiE "(microsoft|wsl)" /proc/version 2>/dev/null; then
EMULATOR="${ANDROID_SDK_WIN:-/mnt/c/Users/$USER/AppData/Local/Android/Sdk}/emulator/emulator.exe"
fi
unset _sdk
fi
# ---------------------------------------------------------------------------
@@ -49,12 +61,12 @@ android_emulator_start() {
local timeout_s="${2:-180}"
# Validaciones de entorno
if [[ ! -f "$EMULATOR" ]]; then
echo "android_emulator_start: emulator.exe no encontrado en '$EMULATOR'. Fija EMULATOR= o ANDROID_SDK_WIN=." >&2
if [[ -z "${EMULATOR:-}" ]] || ! command -v "$EMULATOR" &>/dev/null; then
echo "android_emulator_start: emulator no encontrado. Instala el SDK + paquete 'emulator', exporta ANDROID_HOME, o fija EMULATOR=." >&2
return 1
fi
if [[ ! -f "$ADB" ]]; then
echo "android_emulator_start: adb.exe no encontrado en '$ADB'. Fija ADB= o ANDROID_SDK_WIN=." >&2
if [[ -z "${ADB:-}" ]] || ! command -v "$ADB" &>/dev/null; then
echo "android_emulator_start: adb no encontrado. Instala platform-tools, exporta ANDROID_HOME, o fija ADB=." >&2
return 1
fi
@@ -74,9 +86,12 @@ android_emulator_start() {
local emu_pid=$!
echo "$emu_pid" > "$pid_file"
# Esperar a que el dispositivo aparezca en adb
# Esperar a que el dispositivo aparezca en adb.
# Usamos el binario "$ADB" directamente (no la funcion adb_run): `timeout`
# ejecuta un comando externo y no puede ver funciones del shell, asi que
# `timeout ... adb_run` fallaba siempre con "command not found".
local wait_timeout=$(( timeout_s / 2 ))
if ! timeout "$wait_timeout" adb_run wait-for-device 2>/dev/null; then
if ! timeout "$wait_timeout" "$ADB" wait-for-device 2>/dev/null; then
echo "android_emulator_start: timeout esperando que el dispositivo aparezca en adb (${wait_timeout}s)." >&2
return 1
fi
+16 -1
View File
@@ -3,7 +3,7 @@ name: install_android_sdk
kind: function
lang: bash
domain: infra
version: "1.0.0"
version: "1.0.1"
purity: impure
signature: "install_android_sdk() -> void"
description: "Descarga e instala Android SDK command-line tools y JDK 17 localmente (sin root/sudo) en $ANDROID_SDK_DIR (default: $HOME/android-sdk). Idempotente: detecta instalacion existente y sale sin hacer nada. Genera env.sh con JAVA_HOME, ANDROID_HOME y PATH listos para hacer source."
@@ -50,6 +50,17 @@ ANDROID_SDK_DIR=/opt/android source install_android_sdk.sh
source ~/android-sdk/env.sh
```
## Cuando usarla
Cuando necesites un Android SDK funcional en una maquina Linux sin permisos de root: CI, contenedores, o un PC de desarrollo donde quieras un SDK aislado en `$HOME`. Instala la base minima para compilar (cmdline-tools + JDK 17 + platform-tools + API 34 + build-tools). Hazle `source` para tener `sdkmanager`/`avdmanager`/`adb` en el PATH antes de invocar `gradle_run`, `gradle_assemble_debug` o `capacitor_build_apk`.
## Gotchas
- **No instala `emulator` ni system images.** Solo la base de compilacion. Para correr un AVD: tras hacer `source env.sh`, instala `emulator` y una imagen (`sdkmanager "emulator" "system-images;android-34;google_apis;x86_64"`) y crea el AVD con `avdmanager create avd`.
- **Aceleracion KVM:** el emulador necesita acceso a `/dev/kvm`. Verifica con `[ -w /dev/kvm ]`; si no, anade tu usuario al grupo `kvm` (`sudo usermod -aG kvm $USER` + re-login) o concede ACL.
- **URL de cmdline-tools clavada** a la build 11076708 (2024). Si Google la retira, actualizar `tools_url` en el `.sh`.
- **Idempotente:** re-ejecutar no reinstala; detecta `sdkmanager` existente y sale en 0.
## Notas
Requiere `curl` y `unzip` (disponibles en la mayoria de distros Linux). No requiere root ni sudo.
@@ -61,3 +72,7 @@ La reorganizacion del zip es necesaria porque Google distribuye cmdline-tools co
El archivo `env.sh` generado en `$ANDROID_SDK_DIR/env.sh` contiene las variables de entorno necesarias (`JAVA_HOME`, `ANDROID_HOME`, `ANDROID_SDK_ROOT`, `PATH`) y puede hacerse source desde `.bashrc`, `.zshrc` o desde scripts de CI.
Paquetes instalados: `platform-tools` (adb, fastboot), `platforms;android-34` (API 34), `build-tools;34.0.0`.
## Capability growth log
- v1.0.1 (2026-06-03) — fix: `yes | sdkmanager --licenses` daba falso negativo bajo `pipefail` (SIGPIPE de `yes`, exit 141) abortando una instalacion exitosa; ahora se desactiva `pipefail` solo en ese pipe. fix: el trap `EXIT` referenciaba `$tmp_dir` (variable `local`) fuera del scope de la funcion → "unbound variable" con `set -u`; ahora es global con expansion defensiva.
+13 -3
View File
@@ -5,11 +5,14 @@ set -euo pipefail
install_android_sdk() {
local sdk_dir="${ANDROID_SDK_DIR:-$HOME/android-sdk}"
local tmp_dir
# tmp_dir es global a proposito: el trap EXIT se dispara al terminar el
# script (fuera del scope de la funcion), donde una variable `local` ya no
# existiria y `set -u` la marcaria como unbound. La expansion defensiva
# ${tmp_dir:-} evita el fallo aunque el trap corra antes de la asignacion.
tmp_dir="$(mktemp -d)"
# Limpia temporales al salir
trap 'rm -rf "$tmp_dir"' EXIT
trap 'rm -rf "${tmp_dir:-}"' EXIT
# 1. Verifica si ya está instalado
if [[ -f "$sdk_dir/cmdline-tools/latest/bin/sdkmanager" ]]; then
@@ -103,11 +106,18 @@ install_android_sdk() {
export PATH="$JAVA_HOME/bin:$sdk_dir/cmdline-tools/latest/bin:$sdk_dir/platform-tools:$PATH"
# 4. Acepta licencias e instala paquetes necesarios
# `yes` recibe SIGPIPE (exit 141) cuando sdkmanager termina de leer y cierra
# el pipe; bajo `set -o pipefail` eso convierte un exito real en falso
# negativo. Desactivamos pipefail solo aqui para que el exit del pipeline
# refleje el de sdkmanager (ultimo comando), no el SIGPIPE de `yes`.
echo "Aceptando licencias de Android SDK..."
if ! yes | "$sdkmanager" --licenses; then
set +o pipefail
if ! yes | "$sdkmanager" --licenses >/dev/null 2>&1; then
set -o pipefail
echo "ERROR: fallo al aceptar licencias de Android SDK" >&2
return 1
fi
set -o pipefail
echo "Instalando platform-tools, platforms;android-34, build-tools;34.0.0..."
if ! "$sdkmanager" "platform-tools" "platforms;android-34" "build-tools;34.0.0"; then