feat: add bash infra functions — Gitea, Android SDK, Mantine, Capacitor
Nuevas funciones bash: gestión Gitea (create_repo, list_repos, add_collaborator, push_directory), install_android_sdk, install_mantine, frontend_doctor. Pipelines: capacitor_build_apk y gitea_init_app. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,76 @@
|
||||
---
|
||||
name: capacitor_build_apk
|
||||
kind: pipeline
|
||||
lang: bash
|
||||
domain: pipelines
|
||||
version: "1.0.0"
|
||||
purity: impure
|
||||
signature: "capacitor_build_apk(web_app_dir: string, [app_id: string], [app_name: string]) -> void"
|
||||
description: "Pipeline que convierte una web app en un APK de Android usando Capacitor. Valida el entorno (ANDROID_HOME, Java 17+), construye el bundle web si no existe dist/, inicializa Capacitor si no está configurado, añade la plataforma Android, sincroniza y compila el APK con Gradle. El APK final queda en el directorio raíz de la web app."
|
||||
tags: [android, apk, capacitor, mobile, build, pipeline, bash]
|
||||
uses_functions:
|
||||
- install_android_sdk_bash_infra
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: "error_go_core"
|
||||
imports: []
|
||||
params:
|
||||
- name: web_app_dir
|
||||
desc: "directorio raíz de la web app; debe contener package.json; si no existe dist/ se ejecuta pnpm build automáticamente"
|
||||
- name: app_id
|
||||
desc: "identificador de la app Android en formato reverse-DNS (default: com.fnregistry.app)"
|
||||
- name: app_name
|
||||
desc: "nombre visible de la app Android; si se omite, se lee del campo name de package.json"
|
||||
output: "APK de debug en <web_app_dir>/<app_name>.apk; imprime ruta y tamaño en MB al finalizar"
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "bash/functions/pipelines/capacitor_build_apk.sh"
|
||||
---
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```bash
|
||||
# Build con defaults (app-id y app-name desde package.json)
|
||||
./bash/functions/pipelines/capacitor_build_apk.sh ~/projects/my-web-app
|
||||
|
||||
# Build especificando app-id y app-name
|
||||
./bash/functions/pipelines/capacitor_build_apk.sh ~/projects/my-web-app \
|
||||
--app-id com.miempresa.miapp \
|
||||
--app-name "Mi Aplicación"
|
||||
```
|
||||
|
||||
## Flujo
|
||||
|
||||
1. **Validación** — verifica que `web_app_dir` existe, tiene `package.json`, que `ANDROID_HOME` está seteado (o sourcea `$HOME/android-sdk/env.sh`) y que Java 17+ está disponible.
|
||||
2. **Build web** — si no existe `dist/`, ejecuta `pnpm build` en el directorio de la app.
|
||||
3. **Init Capacitor** — si no existe `capacitor.config.ts`, instala `@capacitor/core`, `@capacitor/cli` y `@capacitor/android` via npm y genera el archivo de configuración con el `appId`, `appName` y `webDir: dist`.
|
||||
4. **Add Android** — si no existe el directorio `android/`, ejecuta `npx cap add android`.
|
||||
5. **Sync** — ejecuta `npx cap sync android` para copiar los assets web al proyecto Android.
|
||||
6. **Build APK** — ejecuta `./gradlew assembleDebug` desde `android/`; si falla sale con exit 1.
|
||||
7. **Copia APK** — copia `android/app/build/outputs/apk/debug/app-debug.apk` a `<web_app_dir>/<app_name>.apk`.
|
||||
8. **Resultado** — imprime la ruta del APK y su tamaño en MB.
|
||||
|
||||
## Requisitos
|
||||
|
||||
- **Node.js** y **pnpm** disponibles en PATH
|
||||
- **Java 17+** disponible en PATH
|
||||
- **Android SDK** instalado: `ANDROID_HOME` seteado, o bien `$HOME/android-sdk/env.sh` existente (generado por `install_android_sdk`)
|
||||
- **Gradle wrapper** presente en el directorio `android/` (generado por `cap add android`)
|
||||
|
||||
## Notas
|
||||
|
||||
El pipeline usa `set -euo pipefail` — cualquier fallo detiene la ejecución inmediatamente.
|
||||
|
||||
El APK generado es un **debug build**, apto para desarrollo y pruebas. Para publicar en Play Store se necesita un release build firmado (`assembleRelease` con un keystore).
|
||||
|
||||
`install_android_sdk_bash_infra` se referencia como dependencia previa: el usuario debe haberlo ejecutado (o haber instalado el SDK manualmente) antes de invocar este pipeline.
|
||||
|
||||
La detección del `app_name` desde `package.json` usa `node -e` inline, lo que requiere que Node.js esté disponible. Si el campo `name` no existe en el JSON, se usa el valor por defecto `app`.
|
||||
|
||||
Para instalar el APK en un dispositivo Android conectado por USB (con depuración USB activada):
|
||||
|
||||
```bash
|
||||
adb install <web_app_dir>/<app_name>.apk
|
||||
```
|
||||
@@ -0,0 +1,208 @@
|
||||
#!/usr/bin/env bash
|
||||
# capacitor_build_apk
|
||||
# -------------------
|
||||
# Pipeline que convierte una web app buildeada en un APK de Android usando Capacitor.
|
||||
# Asume que el Android SDK está instalado (via install_android_sdk o manualmente).
|
||||
#
|
||||
# USO:
|
||||
# ./capacitor_build_apk.sh <web_app_dir> [--app-id com.example.app] [--app-name "My App"]
|
||||
#
|
||||
# ARGUMENTOS:
|
||||
# web_app_dir Directorio de la web app (debe contener package.json)
|
||||
# --app-id ID de la app Android (default: com.fnregistry.app)
|
||||
# --app-name Nombre visible de la app (default: name de package.json)
|
||||
#
|
||||
# REQUISITOS:
|
||||
# - Node.js + pnpm instalados en PATH
|
||||
# - Java 17+ instalado en PATH
|
||||
# - Android SDK: ANDROID_HOME seteado o $HOME/android-sdk/env.sh disponible
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Parseo de argumentos
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
WEB_APP_DIR=""
|
||||
APP_ID="com.fnregistry.app"
|
||||
APP_NAME=""
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--app-id)
|
||||
APP_ID="$2"
|
||||
shift 2
|
||||
;;
|
||||
--app-name)
|
||||
APP_NAME="$2"
|
||||
shift 2
|
||||
;;
|
||||
-*)
|
||||
echo "[capacitor_build_apk] ERROR: argumento desconocido: $1" >&2
|
||||
echo "USO: $0 <web_app_dir> [--app-id com.example.app] [--app-name \"My App\"]" >&2
|
||||
exit 1
|
||||
;;
|
||||
*)
|
||||
WEB_APP_DIR="$1"
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [[ -z "$WEB_APP_DIR" ]]; then
|
||||
echo "[capacitor_build_apk] ERROR: web_app_dir es obligatorio." >&2
|
||||
echo "USO: $0 <web_app_dir> [--app-id com.example.app] [--app-name \"My App\"]" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 1. Validación
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
echo "[capacitor_build_apk] Validando entorno..."
|
||||
|
||||
# Verificar que web_app_dir existe y tiene package.json
|
||||
if [[ ! -d "$WEB_APP_DIR" ]]; then
|
||||
echo "[capacitor_build_apk] ERROR: directorio no existe: $WEB_APP_DIR" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ ! -f "$WEB_APP_DIR/package.json" ]]; then
|
||||
echo "[capacitor_build_apk] ERROR: no se encontró package.json en $WEB_APP_DIR" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Resolver app name desde package.json si no se pasó
|
||||
if [[ -z "$APP_NAME" ]]; then
|
||||
APP_NAME=$(node -e "const p = require('$WEB_APP_DIR/package.json'); process.stdout.write(p.name || 'app');" 2>/dev/null || echo "app")
|
||||
echo "[capacitor_build_apk] App name detectado desde package.json: $APP_NAME"
|
||||
fi
|
||||
|
||||
# Verificar ANDROID_HOME o sourcea env.sh
|
||||
if [[ -z "${ANDROID_HOME:-}" ]]; then
|
||||
ANDROID_ENV="$HOME/android-sdk/env.sh"
|
||||
if [[ -f "$ANDROID_ENV" ]]; then
|
||||
echo "[capacitor_build_apk] ANDROID_HOME no seteado, sourceando $ANDROID_ENV ..."
|
||||
# shellcheck source=/dev/null
|
||||
source "$ANDROID_ENV"
|
||||
else
|
||||
echo "[capacitor_build_apk] ERROR: ANDROID_HOME no está seteado y no se encontró $ANDROID_ENV" >&2
|
||||
echo " Instala el SDK con install_android_sdk o setea ANDROID_HOME manualmente." >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "[capacitor_build_apk] ANDROID_HOME: $ANDROID_HOME"
|
||||
|
||||
# Verificar Java 17+
|
||||
if ! command -v java &>/dev/null; then
|
||||
echo "[capacitor_build_apk] ERROR: java no está en PATH." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
JAVA_VERSION=$(java -version 2>&1 | head -1 | grep -oP '(?<=version ")([0-9]+)' | head -1 || echo "0")
|
||||
if [[ "$JAVA_VERSION" -lt 17 ]]; then
|
||||
echo "[capacitor_build_apk] ERROR: se requiere Java 17+. Versión detectada: $JAVA_VERSION" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "[capacitor_build_apk] Java $JAVA_VERSION detectado."
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 2. Build web (si no existe dist/)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
if [[ ! -d "$WEB_APP_DIR/dist" ]]; then
|
||||
echo "[capacitor_build_apk] No se encontró dist/, ejecutando pnpm build..."
|
||||
(cd "$WEB_APP_DIR" && pnpm build)
|
||||
echo "[capacitor_build_apk] Build web completado."
|
||||
else
|
||||
echo "[capacitor_build_apk] dist/ ya existe, omitiendo build web."
|
||||
fi
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 3. Init Capacitor (si no existe capacitor.config.ts)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
if [[ ! -f "$WEB_APP_DIR/capacitor.config.ts" ]]; then
|
||||
echo "[capacitor_build_apk] Instalando dependencias de Capacitor..."
|
||||
(cd "$WEB_APP_DIR" && npm install @capacitor/core @capacitor/cli @capacitor/android)
|
||||
|
||||
echo "[capacitor_build_apk] Generando capacitor.config.ts..."
|
||||
cat > "$WEB_APP_DIR/capacitor.config.ts" <<CAPCONFIG
|
||||
import type { CapacitorConfig } from '@capacitor/cli';
|
||||
|
||||
const config: CapacitorConfig = {
|
||||
appId: '${APP_ID}',
|
||||
appName: '${APP_NAME}',
|
||||
webDir: 'dist',
|
||||
server: { androidScheme: 'https' }
|
||||
};
|
||||
|
||||
export default config;
|
||||
CAPCONFIG
|
||||
|
||||
echo "[capacitor_build_apk] capacitor.config.ts generado."
|
||||
else
|
||||
echo "[capacitor_build_apk] capacitor.config.ts ya existe, omitiendo init."
|
||||
fi
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 4. Add Android (si no existe el directorio android/)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
if [[ ! -d "$WEB_APP_DIR/android" ]]; then
|
||||
echo "[capacitor_build_apk] Añadiendo plataforma Android..."
|
||||
(cd "$WEB_APP_DIR" && npx cap add android)
|
||||
echo "[capacitor_build_apk] Plataforma Android añadida."
|
||||
else
|
||||
echo "[capacitor_build_apk] Directorio android/ ya existe, omitiendo cap add."
|
||||
fi
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 5. Sync
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
echo "[capacitor_build_apk] Sincronizando assets web con Android..."
|
||||
(cd "$WEB_APP_DIR" && npx cap sync android)
|
||||
echo "[capacitor_build_apk] Sync completado."
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 6. Build APK
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
echo "[capacitor_build_apk] Compilando APK con Gradle..."
|
||||
if ! (cd "$WEB_APP_DIR/android" && ./gradlew assembleDebug); then
|
||||
echo "[capacitor_build_apk] ERROR: Gradle falló. Revisa los logs anteriores." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
APK_SOURCE="$WEB_APP_DIR/android/app/build/outputs/apk/debug/app-debug.apk"
|
||||
|
||||
if [[ ! -f "$APK_SOURCE" ]]; then
|
||||
echo "[capacitor_build_apk] ERROR: Gradle terminó sin error pero no se encontró el APK en $APK_SOURCE" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 7. Copia APK al directorio raíz
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
APK_DEST="$WEB_APP_DIR/${APP_NAME}.apk"
|
||||
cp "$APK_SOURCE" "$APK_DEST"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 8. Resultado
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
APK_SIZE_BYTES=$(stat -c%s "$APK_DEST" 2>/dev/null || stat -f%z "$APK_DEST" 2>/dev/null || echo "0")
|
||||
APK_SIZE_MB=$(awk "BEGIN {printf \"%.1f\", $APK_SIZE_BYTES/1048576}")
|
||||
|
||||
echo ""
|
||||
echo "---------------------------------------------------------------------"
|
||||
echo "APK generado: $APK_DEST"
|
||||
echo "Tamaño: ${APK_SIZE_MB} MB"
|
||||
echo ""
|
||||
echo "Para instalar en un dispositivo conectado por USB:"
|
||||
echo " adb install '$APK_DEST'"
|
||||
echo "---------------------------------------------------------------------"
|
||||
@@ -0,0 +1,67 @@
|
||||
---
|
||||
name: gitea_init_app
|
||||
kind: pipeline
|
||||
lang: bash
|
||||
domain: pipelines
|
||||
version: "1.0.0"
|
||||
purity: impure
|
||||
signature: "gitea_init_app(directory: string, owner: string, name: string, private: string) -> string"
|
||||
description: "Pipeline que crea un repositorio en Gitea, sube el directorio local y añade a egutierrez como colaborador admin. Compone gitea_create_repo → gitea_push_directory → gitea_add_collaborator."
|
||||
tags: [gitea, git, pipeline, repo, create, push, launcher, infra]
|
||||
uses_functions:
|
||||
- gitea_create_repo_bash_infra
|
||||
- gitea_push_directory_bash_infra
|
||||
- gitea_add_collaborator_bash_infra
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: "error_go_core"
|
||||
imports: []
|
||||
params:
|
||||
- name: directory
|
||||
desc: "ruta al directorio local a subir como repositorio"
|
||||
- name: owner
|
||||
desc: "usuario u organización en Gitea que será propietaria del repo"
|
||||
- name: name
|
||||
desc: "nombre del repositorio (opcional: se infiere del basename del directorio)"
|
||||
- name: private
|
||||
desc: "si el repo debe ser privado, 'true' o 'false' (default: false)"
|
||||
output: "URL del repositorio creado en Gitea"
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "bash/functions/pipelines/gitea_init_app.sh"
|
||||
---
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```bash
|
||||
export GITEA_URL="$(pass agentes/gitea-url)"
|
||||
export GITEA_TOKEN="$(pass agentes/dataforge-token)"
|
||||
|
||||
# Crear repo con nombre inferido del directorio
|
||||
bash bash/functions/pipelines/gitea_init_app.sh /home/lucas/myapp myorg
|
||||
|
||||
# Nombre explícito y repo privado
|
||||
bash bash/functions/pipelines/gitea_init_app.sh /home/lucas/myapp myorg my-custom-name true
|
||||
|
||||
# Con flags
|
||||
bash bash/functions/pipelines/gitea_init_app.sh \
|
||||
--directory /home/lucas/myapp \
|
||||
--owner myorg \
|
||||
--name my-app \
|
||||
--private true
|
||||
```
|
||||
|
||||
## Pasos del pipeline
|
||||
|
||||
1. `gitea_create_repo owner name private` — crea el repo (idempotente si ya existe)
|
||||
2. `gitea_push_directory directory owner repo` — inicializa git y hace push del directorio
|
||||
3. `gitea_add_collaborator owner repo egutierrez admin` — añade colaborador con permisos admin
|
||||
|
||||
## Notas
|
||||
|
||||
- Requiere `GITEA_URL` y `GITEA_TOKEN` seteadas.
|
||||
- Si el repo ya existe (409), el pipeline continúa con el push y añade el colaborador.
|
||||
- El colaborador `egutierrez` es fijo en el pipeline — para variarlo usar las funciones individuales.
|
||||
- La URL del repo se imprime a stdout al finalizar.
|
||||
@@ -0,0 +1,79 @@
|
||||
#!/usr/bin/env bash
|
||||
# Pipeline: gitea_init_app — Crea repo en Gitea, sube directorio y añade colaborador egutierrez
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
source "$SCRIPT_DIR/../infra/gitea_create_repo.sh"
|
||||
source "$SCRIPT_DIR/../infra/gitea_push_directory.sh"
|
||||
source "$SCRIPT_DIR/../infra/gitea_add_collaborator.sh"
|
||||
|
||||
main() {
|
||||
local directory=""
|
||||
local owner=""
|
||||
local name=""
|
||||
local private="false"
|
||||
|
||||
# Parsear argumentos
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--directory) directory="$2"; shift 2 ;;
|
||||
--owner) owner="$2"; shift 2 ;;
|
||||
--name) name="$2"; shift 2 ;;
|
||||
--private) private="$2"; shift 2 ;;
|
||||
*)
|
||||
# Argumentos posicionales: directory owner [name] [private]
|
||||
if [[ -z "$directory" ]]; then
|
||||
directory="$1"
|
||||
elif [[ -z "$owner" ]]; then
|
||||
owner="$1"
|
||||
elif [[ -z "$name" ]]; then
|
||||
name="$1"
|
||||
else
|
||||
private="$1"
|
||||
fi
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [[ -z "$directory" || -z "$owner" ]]; then
|
||||
echo "gitea_init_app: uso: gitea_init_app <directory> <owner> [name] [private]" >&2
|
||||
echo "gitea_init_app: o con flags: --directory <dir> --owner <owner> [--name <name>] [--private true]" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Inferir nombre del repo desde basename del directorio si no se especificó
|
||||
if [[ -z "$name" ]]; then
|
||||
name=$(basename "$directory")
|
||||
echo "gitea_init_app: nombre inferido del directorio: '$name'" >&2
|
||||
fi
|
||||
|
||||
if [[ -z "${GITEA_URL:-}" ]]; then
|
||||
echo "gitea_init_app: GITEA_URL no está seteada" >&2
|
||||
return 1
|
||||
fi
|
||||
if [[ -z "${GITEA_TOKEN:-}" ]]; then
|
||||
echo "gitea_init_app: GITEA_TOKEN no está seteado" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
echo "gitea_init_app: iniciando pipeline para '$owner/$name'..." >&2
|
||||
echo "gitea_init_app: directorio fuente: '$directory'" >&2
|
||||
|
||||
# Paso 1: Crear repo
|
||||
echo "gitea_init_app: [1/3] creando repositorio..." >&2
|
||||
gitea_create_repo "$owner" "$name" "$private" "" > /dev/null
|
||||
|
||||
# Paso 2: Subir directorio
|
||||
echo "gitea_init_app: [2/3] subiendo directorio al repositorio..." >&2
|
||||
gitea_push_directory "$directory" "$owner" "$name"
|
||||
|
||||
# Paso 3: Añadir colaborador egutierrez con permisos admin
|
||||
echo "gitea_init_app: [3/3] añadiendo colaborador egutierrez..." >&2
|
||||
gitea_add_collaborator "$owner" "$name" "egutierrez" "admin"
|
||||
|
||||
echo "gitea_init_app: pipeline completado — ${GITEA_URL}/${owner}/${name}" >&2
|
||||
echo "${GITEA_URL}/${owner}/${name}"
|
||||
}
|
||||
|
||||
main "$@"
|
||||
Reference in New Issue
Block a user