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,54 @@
|
||||
---
|
||||
name: frontend_doctor
|
||||
kind: function
|
||||
lang: bash
|
||||
domain: infra
|
||||
version: "1.0.0"
|
||||
purity: impure
|
||||
signature: "frontend_doctor(project_dir: string) -> diagnostics_stdout"
|
||||
description: "Diagnostica la salud de un proyecto frontend Mantine. Verifica Node, React, Mantine, PostCSS, TypeScript, vite.config y detecta residuos de shadcn/@base-ui. Imprime tabla de checks con exit code 0/1."
|
||||
tags: [frontend, mantine, doctor, diagnostics, health, validation]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: "error_go_core"
|
||||
imports: []
|
||||
params:
|
||||
- name: project_dir
|
||||
desc: "directorio del proyecto frontend con package.json"
|
||||
output: "tabla de checks con ✓/✗ por cada validación y resumen final"
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "bash/functions/infra/frontend_doctor.sh"
|
||||
---
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```bash
|
||||
# Diagnosticar un proyecto
|
||||
bash frontend_doctor.sh ./apps/rapid_dashboards/frontend
|
||||
|
||||
# Output:
|
||||
# === Frontend Doctor: ./apps/rapid_dashboards/frontend ===
|
||||
#
|
||||
# ✓ Node >= 18 22.12.0
|
||||
# ✓ Package manager detected pnpm
|
||||
# ✓ node_modules present
|
||||
# ✓ @mantine/core 7.17.0
|
||||
# ✓ @mantine/hooks
|
||||
# ✓ @mantine/charts
|
||||
# ✓ React >= 18 19.2.4
|
||||
# ✓ postcss.config present
|
||||
# ✓ TypeScript >= 5 6.0.2
|
||||
# ✓ vite.config present
|
||||
# ✓ No shadcn residual
|
||||
# ✓ No @base-ui residual
|
||||
#
|
||||
# Resultado: todo OK
|
||||
```
|
||||
|
||||
## Notas
|
||||
|
||||
Checks informativos, no modifica nada. Util para validar que un proyecto esta correctamente configurado despues de instalar Mantine o migrar desde shadcn. Exit code 0 si todo OK, 1 si hay problemas.
|
||||
@@ -0,0 +1,150 @@
|
||||
# frontend_doctor
|
||||
# ----------------
|
||||
# Diagnostica la salud de un proyecto frontend Mantine.
|
||||
# Verifica dependencias, configuracion y versiones.
|
||||
# Imprime tabla de checks y retorna exit code 0 (ok) o 1 (fallos).
|
||||
#
|
||||
# USO (sourced):
|
||||
# source frontend_doctor.sh
|
||||
# frontend_doctor /path/to/frontend
|
||||
#
|
||||
# USO (directo):
|
||||
# bash frontend_doctor.sh /path/to/frontend
|
||||
|
||||
frontend_doctor() {
|
||||
local project_dir="$1"
|
||||
local failures=0
|
||||
|
||||
if [ -z "$project_dir" ]; then
|
||||
echo "frontend_doctor: se requiere project_dir" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [ ! -f "$project_dir/package.json" ]; then
|
||||
echo "frontend_doctor: no existe package.json en $project_dir" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
echo "=== Frontend Doctor: $project_dir ==="
|
||||
echo ""
|
||||
|
||||
# Helper: check y reportar
|
||||
_check() {
|
||||
local label="$1"
|
||||
local ok="$2"
|
||||
local detail="$3"
|
||||
if [ "$ok" = "1" ]; then
|
||||
printf " ✓ %-35s %s\n" "$label" "$detail"
|
||||
else
|
||||
printf " ✗ %-35s %s\n" "$label" "$detail"
|
||||
((failures++))
|
||||
fi
|
||||
}
|
||||
|
||||
# 1. Node >= 18
|
||||
local node_ver=""
|
||||
local node_ok=0
|
||||
if command -v node &>/dev/null; then
|
||||
node_ver=$(node -v 2>/dev/null | sed 's/v//')
|
||||
local node_major=$(echo "$node_ver" | cut -d. -f1)
|
||||
[ "$node_major" -ge 18 ] 2>/dev/null && node_ok=1
|
||||
fi
|
||||
_check "Node >= 18" "$node_ok" "${node_ver:-not found}"
|
||||
|
||||
# 2. Package manager
|
||||
local pm_ok=0
|
||||
local pm_name="none"
|
||||
if [ -f "$project_dir/pnpm-lock.yaml" ]; then
|
||||
pm_name="pnpm"; pm_ok=1
|
||||
elif [ -f "$project_dir/yarn.lock" ]; then
|
||||
pm_name="yarn"; pm_ok=1
|
||||
elif [ -f "$project_dir/package-lock.json" ]; then
|
||||
pm_name="npm"; pm_ok=1
|
||||
fi
|
||||
_check "Package manager detected" "$pm_ok" "$pm_name"
|
||||
|
||||
# 3. node_modules existe
|
||||
local nm_ok=0
|
||||
[ -d "$project_dir/node_modules" ] && nm_ok=1
|
||||
_check "node_modules present" "$nm_ok" ""
|
||||
|
||||
# 4. @mantine/core instalado
|
||||
local mantine_ok=0
|
||||
local mantine_ver=""
|
||||
if [ -f "$project_dir/node_modules/@mantine/core/package.json" ]; then
|
||||
mantine_ver=$(node -e "console.log(require('$project_dir/node_modules/@mantine/core/package.json').version)" 2>/dev/null)
|
||||
mantine_ok=1
|
||||
fi
|
||||
_check "@mantine/core" "$mantine_ok" "${mantine_ver:-not installed}"
|
||||
|
||||
# 5. @mantine/hooks
|
||||
local hooks_ok=0
|
||||
[ -d "$project_dir/node_modules/@mantine/hooks" ] && hooks_ok=1
|
||||
_check "@mantine/hooks" "$hooks_ok" ""
|
||||
|
||||
# 6. @mantine/charts
|
||||
local charts_ok=0
|
||||
[ -d "$project_dir/node_modules/@mantine/charts" ] && charts_ok=1
|
||||
_check "@mantine/charts" "$charts_ok" ""
|
||||
|
||||
# 7. React >= 18
|
||||
local react_ok=0
|
||||
local react_ver=""
|
||||
if [ -f "$project_dir/node_modules/react/package.json" ]; then
|
||||
react_ver=$(node -e "console.log(require('$project_dir/node_modules/react/package.json').version)" 2>/dev/null)
|
||||
local react_major=$(echo "$react_ver" | cut -d. -f1)
|
||||
[ "$react_major" -ge 18 ] 2>/dev/null && react_ok=1
|
||||
fi
|
||||
_check "React >= 18" "$react_ok" "${react_ver:-not found}"
|
||||
|
||||
# 8. postcss.config presente
|
||||
local postcss_ok=0
|
||||
if [ -f "$project_dir/postcss.config.cjs" ] || [ -f "$project_dir/postcss.config.js" ] || [ -f "$project_dir/postcss.config.mjs" ]; then
|
||||
postcss_ok=1
|
||||
fi
|
||||
_check "postcss.config present" "$postcss_ok" ""
|
||||
|
||||
# 9. TypeScript >= 5
|
||||
local ts_ok=0
|
||||
local ts_ver=""
|
||||
if [ -f "$project_dir/node_modules/typescript/package.json" ]; then
|
||||
ts_ver=$(node -e "console.log(require('$project_dir/node_modules/typescript/package.json').version)" 2>/dev/null)
|
||||
local ts_major=$(echo "$ts_ver" | cut -d. -f1)
|
||||
[ "$ts_major" -ge 5 ] 2>/dev/null && ts_ok=1
|
||||
fi
|
||||
_check "TypeScript >= 5" "$ts_ok" "${ts_ver:-not found}"
|
||||
|
||||
# 10. vite.config presente
|
||||
local vite_ok=0
|
||||
if [ -f "$project_dir/vite.config.ts" ] || [ -f "$project_dir/vite.config.js" ]; then
|
||||
vite_ok=1
|
||||
fi
|
||||
_check "vite.config present" "$vite_ok" ""
|
||||
|
||||
# 11. Shadcn residual (warning)
|
||||
local shadcn_clean=1
|
||||
if [ -f "$project_dir/components.json" ]; then
|
||||
shadcn_clean=0
|
||||
fi
|
||||
_check "No shadcn residual" "$shadcn_clean" "$([ "$shadcn_clean" = "0" ] && echo 'components.json found')"
|
||||
|
||||
# 12. @base-ui residual (warning)
|
||||
local baseui_clean=1
|
||||
if [ -d "$project_dir/node_modules/@base-ui" ]; then
|
||||
baseui_clean=0
|
||||
fi
|
||||
_check "No @base-ui residual" "$baseui_clean" "$([ "$baseui_clean" = "0" ] && echo '@base-ui still installed')"
|
||||
|
||||
echo ""
|
||||
if [ "$failures" -eq 0 ]; then
|
||||
echo " Resultado: todo OK"
|
||||
return 0
|
||||
else
|
||||
echo " Resultado: $failures problema(s) encontrado(s)"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
||||
frontend_doctor "$@"
|
||||
fi
|
||||
@@ -0,0 +1,53 @@
|
||||
---
|
||||
name: gitea_add_collaborator
|
||||
kind: function
|
||||
lang: bash
|
||||
domain: infra
|
||||
version: "1.0.0"
|
||||
purity: impure
|
||||
signature: "gitea_add_collaborator(owner: string, repo: string, username: string, permission: string) -> void"
|
||||
description: "Añade un colaborador a un repositorio Gitea con el nivel de permisos indicado. Silencioso si el colaborador ya existe (422)."
|
||||
tags: [gitea, git, collaborator, permission, repo, api, infra]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: "error_go_core"
|
||||
imports: []
|
||||
params:
|
||||
- name: owner
|
||||
desc: "usuario u organización propietaria del repositorio"
|
||||
- name: repo
|
||||
desc: "nombre del repositorio"
|
||||
- name: username
|
||||
desc: "nombre de usuario del colaborador a añadir"
|
||||
- name: permission
|
||||
desc: "nivel de permisos: 'read', 'write' o 'admin' (default: admin)"
|
||||
output: "vacío — efectos observables a través de la API de Gitea"
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "bash/functions/infra/gitea_add_collaborator.sh"
|
||||
---
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```bash
|
||||
source bash/functions/infra/gitea_add_collaborator.sh
|
||||
|
||||
export GITEA_URL="https://git.example.com"
|
||||
export GITEA_TOKEN="$(pass agentes/dataforge-token)"
|
||||
|
||||
# Añadir colaborador con permiso admin (default)
|
||||
gitea_add_collaborator "myorg" "my-app" "egutierrez"
|
||||
|
||||
# Añadir colaborador con permiso de solo lectura
|
||||
gitea_add_collaborator "myorg" "my-app" "reviewer" "read"
|
||||
```
|
||||
|
||||
## Notas
|
||||
|
||||
- Requiere `GITEA_URL` y `GITEA_TOKEN` seteadas.
|
||||
- Un 422 de la API indica que el usuario ya es colaborador — se trata como éxito silencioso.
|
||||
- La función no produce salida a stdout; los mensajes informativos van a stderr.
|
||||
- Nivel `admin` da acceso completo al repo incluyendo settings.
|
||||
@@ -0,0 +1,52 @@
|
||||
#!/usr/bin/env bash
|
||||
# gitea_add_collaborator — Añade un colaborador a un repositorio Gitea
|
||||
|
||||
gitea_add_collaborator() {
|
||||
local owner="$1"
|
||||
local repo="$2"
|
||||
local username="$3"
|
||||
local permission="${4:-admin}"
|
||||
|
||||
if [[ -z "${GITEA_URL:-}" ]]; then
|
||||
echo "gitea_add_collaborator: GITEA_URL no está seteada" >&2
|
||||
return 1
|
||||
fi
|
||||
if [[ -z "${GITEA_TOKEN:-}" ]]; then
|
||||
echo "gitea_add_collaborator: GITEA_TOKEN no está seteado" >&2
|
||||
return 1
|
||||
fi
|
||||
if [[ -z "$owner" || -z "$repo" || -z "$username" ]]; then
|
||||
echo "gitea_add_collaborator: se requieren owner, repo y username" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
local payload
|
||||
payload=$(printf '{"permission":"%s"}' "$permission")
|
||||
|
||||
echo "gitea_add_collaborator: añadiendo '$username' a '$owner/$repo' con permiso '$permission'..." >&2
|
||||
|
||||
local response http_code
|
||||
response=$(curl -s -w "\n%{http_code}" \
|
||||
-X PUT \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: token ${GITEA_TOKEN}" \
|
||||
-d "$payload" \
|
||||
"${GITEA_URL}/api/v1/repos/${owner}/${repo}/collaborators/${username}")
|
||||
|
||||
http_code=$(echo "$response" | tail -n1)
|
||||
local body
|
||||
body=$(echo "$response" | head -n -1)
|
||||
|
||||
if [[ "$http_code" == "204" || "$http_code" == "200" ]]; then
|
||||
echo "gitea_add_collaborator: '$username' añadido a '$owner/$repo'" >&2
|
||||
return 0
|
||||
fi
|
||||
|
||||
if [[ "$http_code" == "422" ]]; then
|
||||
echo "gitea_add_collaborator: '$username' ya es colaborador de '$owner/$repo' (silencioso)" >&2
|
||||
return 0
|
||||
fi
|
||||
|
||||
echo "gitea_add_collaborator: error (HTTP ${http_code}): ${body}" >&2
|
||||
return 1
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
---
|
||||
name: gitea_create_repo
|
||||
kind: function
|
||||
lang: bash
|
||||
domain: infra
|
||||
version: "1.0.0"
|
||||
purity: impure
|
||||
signature: "gitea_create_repo(owner: string, name: string, private: string, description: string) -> string"
|
||||
description: "Crea un repositorio en Gitea para un owner. Intenta crearlo en org primero; si el owner no es una org (404/422), lo crea en el usuario autenticado. No falla fatalmente si el repo ya existe (409)."
|
||||
tags: [gitea, git, repo, create, infra, api]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: "error_go_core"
|
||||
imports: []
|
||||
params:
|
||||
- name: owner
|
||||
desc: "usuario u organización propietaria del repo"
|
||||
- name: name
|
||||
desc: "nombre del repositorio a crear"
|
||||
- name: private
|
||||
desc: "si el repo es privado, 'true' o 'false' (default: false)"
|
||||
- name: description
|
||||
desc: "descripción del repositorio (opcional)"
|
||||
output: "JSON del repositorio creado según la API de Gitea"
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "bash/functions/infra/gitea_create_repo.sh"
|
||||
---
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```bash
|
||||
source bash/functions/infra/gitea_create_repo.sh
|
||||
|
||||
export GITEA_URL="https://git.example.com"
|
||||
export GITEA_TOKEN="$(pass agentes/dataforge-token)"
|
||||
|
||||
# Crear repo público en org o usuario
|
||||
repo_json=$(gitea_create_repo "myorg" "my-app")
|
||||
|
||||
# Crear repo privado con descripción
|
||||
repo_json=$(gitea_create_repo "myorg" "my-app" "true" "Mi aplicación principal")
|
||||
|
||||
# Extraer la URL del clon
|
||||
clone_url=$(echo "$repo_json" | jq -r '.clone_url')
|
||||
```
|
||||
|
||||
## Notas
|
||||
|
||||
- Requiere variables de entorno `GITEA_URL` y `GITEA_TOKEN` seteadas antes de invocar.
|
||||
- El fallback org → usuario ocurre con HTTP 404 o 422 en el endpoint de orgs.
|
||||
- Un 409 se reporta a stderr pero la función retorna 0 — el repo ya existe es una condición aceptable para idempotencia.
|
||||
- Los mensajes informativos van a stderr; el JSON de respuesta va a stdout.
|
||||
@@ -0,0 +1,83 @@
|
||||
#!/usr/bin/env bash
|
||||
# gitea_create_repo — Crea un repositorio en Gitea para un owner (org o usuario)
|
||||
|
||||
gitea_create_repo() {
|
||||
local owner="$1"
|
||||
local name="$2"
|
||||
local private="${3:-false}"
|
||||
local description="${4:-}"
|
||||
|
||||
if [[ -z "${GITEA_URL:-}" ]]; then
|
||||
echo "gitea_create_repo: GITEA_URL no está seteada" >&2
|
||||
return 1
|
||||
fi
|
||||
if [[ -z "${GITEA_TOKEN:-}" ]]; then
|
||||
echo "gitea_create_repo: GITEA_TOKEN no está seteado" >&2
|
||||
return 1
|
||||
fi
|
||||
if [[ -z "$owner" || -z "$name" ]]; then
|
||||
echo "gitea_create_repo: se requieren owner y name" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
local payload
|
||||
payload=$(printf '{"name":"%s","private":%s,"description":"%s","auto_init":false}' \
|
||||
"$name" "$private" "$description")
|
||||
|
||||
echo "gitea_create_repo: intentando crear '$owner/$name' en org..." >&2
|
||||
|
||||
local response http_code
|
||||
response=$(curl -s -w "\n%{http_code}" \
|
||||
-X POST \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: token ${GITEA_TOKEN}" \
|
||||
-d "$payload" \
|
||||
"${GITEA_URL}/api/v1/orgs/${owner}/repos")
|
||||
|
||||
http_code=$(echo "$response" | tail -n1)
|
||||
local body
|
||||
body=$(echo "$response" | head -n -1)
|
||||
|
||||
if [[ "$http_code" == "201" ]]; then
|
||||
echo "gitea_create_repo: repo '$owner/$name' creado en org" >&2
|
||||
echo "$body"
|
||||
return 0
|
||||
fi
|
||||
|
||||
if [[ "$http_code" == "409" ]]; then
|
||||
echo "gitea_create_repo: repo '$owner/$name' ya existe (409)" >&2
|
||||
echo "$body"
|
||||
return 0
|
||||
fi
|
||||
|
||||
if [[ "$http_code" == "404" || "$http_code" == "422" ]]; then
|
||||
echo "gitea_create_repo: org no encontrada (${http_code}), intentando en usuario..." >&2
|
||||
response=$(curl -s -w "\n%{http_code}" \
|
||||
-X POST \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: token ${GITEA_TOKEN}" \
|
||||
-d "$payload" \
|
||||
"${GITEA_URL}/api/v1/user/repos")
|
||||
|
||||
http_code=$(echo "$response" | tail -n1)
|
||||
body=$(echo "$response" | head -n -1)
|
||||
|
||||
if [[ "$http_code" == "201" ]]; then
|
||||
echo "gitea_create_repo: repo '$owner/$name' creado en usuario" >&2
|
||||
echo "$body"
|
||||
return 0
|
||||
fi
|
||||
|
||||
if [[ "$http_code" == "409" ]]; then
|
||||
echo "gitea_create_repo: repo '$owner/$name' ya existe (409)" >&2
|
||||
echo "$body"
|
||||
return 0
|
||||
fi
|
||||
|
||||
echo "gitea_create_repo: error al crear en usuario (HTTP ${http_code}): ${body}" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
echo "gitea_create_repo: error inesperado (HTTP ${http_code}): ${body}" >&2
|
||||
return 1
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
---
|
||||
name: gitea_list_repos
|
||||
kind: function
|
||||
lang: bash
|
||||
domain: infra
|
||||
version: "1.0.0"
|
||||
purity: impure
|
||||
signature: "gitea_list_repos(owner: string) -> string"
|
||||
description: "Lista repositorios de un owner en Gitea. Intenta listar como org primero; si falla, lista como usuario. Imprime una línea por repo en formato name<TAB>html_url<TAB>description."
|
||||
tags: [gitea, git, repo, list, org, user, api, infra]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: "error_go_core"
|
||||
imports: []
|
||||
params:
|
||||
- name: owner
|
||||
desc: "nombre del usuario u organización cuyos repos se listan"
|
||||
output: "una línea por repositorio con columnas separadas por tabulador: name, html_url, description"
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "bash/functions/infra/gitea_list_repos.sh"
|
||||
---
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```bash
|
||||
source bash/functions/infra/gitea_list_repos.sh
|
||||
|
||||
export GITEA_URL="https://git.example.com"
|
||||
export GITEA_TOKEN="$(pass agentes/dataforge-token)"
|
||||
|
||||
# Listar todos los repos de una org
|
||||
gitea_list_repos "myorg"
|
||||
# my-app https://git.example.com/myorg/my-app Mi aplicación principal
|
||||
# infra https://git.example.com/myorg/infra
|
||||
|
||||
# Iterar sobre los repos
|
||||
while IFS=$'\t' read -r name url desc; do
|
||||
echo "Repo: $name — $url"
|
||||
done < <(gitea_list_repos "myorg")
|
||||
```
|
||||
|
||||
## Notas
|
||||
|
||||
- Requiere `GITEA_URL` y `GITEA_TOKEN` seteadas.
|
||||
- Usa `jq` si está disponible; fallback con grep/sed en caso contrario.
|
||||
- El límite es 50 repos por página. Para owners con más de 50 repos habría que implementar paginación.
|
||||
- Los mensajes informativos van a stderr; los datos tabulados van a stdout.
|
||||
@@ -0,0 +1,53 @@
|
||||
#!/usr/bin/env bash
|
||||
# gitea_list_repos — Lista repositorios de un owner (org o usuario) en Gitea
|
||||
|
||||
gitea_list_repos() {
|
||||
local owner="$1"
|
||||
|
||||
if [[ -z "${GITEA_URL:-}" ]]; then
|
||||
echo "gitea_list_repos: GITEA_URL no está seteada" >&2
|
||||
return 1
|
||||
fi
|
||||
if [[ -z "${GITEA_TOKEN:-}" ]]; then
|
||||
echo "gitea_list_repos: GITEA_TOKEN no está seteado" >&2
|
||||
return 1
|
||||
fi
|
||||
if [[ -z "$owner" ]]; then
|
||||
echo "gitea_list_repos: se requiere owner" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
echo "gitea_list_repos: listando repos de '$owner' (intentando org)..." >&2
|
||||
|
||||
local response http_code body
|
||||
response=$(curl -s -w "\n%{http_code}" \
|
||||
-H "Authorization: token ${GITEA_TOKEN}" \
|
||||
"${GITEA_URL}/api/v1/orgs/${owner}/repos?limit=50")
|
||||
|
||||
http_code=$(echo "$response" | tail -n1)
|
||||
body=$(echo "$response" | head -n -1)
|
||||
|
||||
if [[ "$http_code" != "200" ]]; then
|
||||
echo "gitea_list_repos: org no encontrada (${http_code}), intentando usuario..." >&2
|
||||
response=$(curl -s -w "\n%{http_code}" \
|
||||
-H "Authorization: token ${GITEA_TOKEN}" \
|
||||
"${GITEA_URL}/api/v1/users/${owner}/repos?limit=50")
|
||||
|
||||
http_code=$(echo "$response" | tail -n1)
|
||||
body=$(echo "$response" | head -n -1)
|
||||
|
||||
if [[ "$http_code" != "200" ]]; then
|
||||
echo "gitea_list_repos: error listando repos de usuario (HTTP ${http_code}): ${body}" >&2
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# Formatear salida como name\thtml_url\tdescription
|
||||
if command -v jq &>/dev/null; then
|
||||
echo "$body" | jq -r '.[] | [.name, .html_url, (.description // "")] | @tsv'
|
||||
else
|
||||
# Fallback sin jq: extraer campos básicos con grep/sed
|
||||
echo "$body" | grep -o '"name":"[^"]*"\|"html_url":"[^"]*"\|"description":"[^"]*"' \
|
||||
| paste - - - | sed 's/"name":"//;s/"html_url":"//;s/"description":"//;s/"//g'
|
||||
fi
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
---
|
||||
name: gitea_push_directory
|
||||
kind: function
|
||||
lang: bash
|
||||
domain: infra
|
||||
version: "1.0.0"
|
||||
purity: impure
|
||||
signature: "gitea_push_directory(directory: string, owner: string, repo: string, branch: string) -> void"
|
||||
description: "Inicializa git en un directorio local y lo sube a un repositorio Gitea existente. Si el directorio ya tiene .git, actualiza el remote y pushea cambios pendientes. Protege registry.db añadiéndolo al .gitignore antes del commit."
|
||||
tags: [gitea, git, push, directory, sync, infra, repo]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: "error_go_core"
|
||||
imports: []
|
||||
params:
|
||||
- name: directory
|
||||
desc: "ruta absoluta o relativa al directorio local a subir"
|
||||
- name: owner
|
||||
desc: "usuario u organización propietaria del repositorio Gitea destino"
|
||||
- name: repo
|
||||
desc: "nombre del repositorio Gitea destino (debe existir previamente)"
|
||||
- name: branch
|
||||
desc: "rama en la que hacer push (default: main)"
|
||||
output: "vacío — efectos observables en el repositorio Gitea destino"
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "bash/functions/infra/gitea_push_directory.sh"
|
||||
---
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```bash
|
||||
source bash/functions/infra/gitea_push_directory.sh
|
||||
|
||||
export GITEA_URL="https://git.example.com"
|
||||
export GITEA_TOKEN="$(pass agentes/dataforge-token)"
|
||||
|
||||
# Subir directorio a repo existente
|
||||
gitea_push_directory "/home/lucas/myproject" "myorg" "my-app"
|
||||
|
||||
# Subir a rama específica
|
||||
gitea_push_directory "/home/lucas/myproject" "myorg" "my-app" "develop"
|
||||
```
|
||||
|
||||
## Notas
|
||||
|
||||
- Requiere `GITEA_URL` y `GITEA_TOKEN` seteadas.
|
||||
- El token se embebe en la URL del remote para autenticación (nunca se imprime a stdout/stderr, se enmascara con ***).
|
||||
- Si `registry.db` existe en el directorio, se añade automáticamente al `.gitignore` local.
|
||||
- Si el `.git` ya existe con un remote diferente, se redirige al repo indicado sin perder el historial local.
|
||||
- Usa `--force-with-lease` para el primer push y fallback a push normal (para repos vacíos recién creados).
|
||||
- El commit se firma con `agent@fn-registry` si no hay configuración git en el entorno.
|
||||
@@ -0,0 +1,95 @@
|
||||
#!/usr/bin/env bash
|
||||
# gitea_push_directory — Inicializa git en un directorio y lo sube a un repo Gitea existente
|
||||
|
||||
gitea_push_directory() {
|
||||
local directory="$1"
|
||||
local owner="$2"
|
||||
local repo="$3"
|
||||
local branch="${4:-main}"
|
||||
|
||||
if [[ -z "${GITEA_URL:-}" ]]; then
|
||||
echo "gitea_push_directory: GITEA_URL no está seteada" >&2
|
||||
return 1
|
||||
fi
|
||||
if [[ -z "${GITEA_TOKEN:-}" ]]; then
|
||||
echo "gitea_push_directory: GITEA_TOKEN no está seteado" >&2
|
||||
return 1
|
||||
fi
|
||||
if [[ -z "$directory" || -z "$owner" || -z "$repo" ]]; then
|
||||
echo "gitea_push_directory: se requieren directory, owner y repo" >&2
|
||||
return 1
|
||||
fi
|
||||
if [[ ! -d "$directory" ]]; then
|
||||
echo "gitea_push_directory: directorio '$directory' no existe" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Construir URL con credenciales embebidas para autenticación
|
||||
local gitea_host
|
||||
gitea_host=$(echo "$GITEA_URL" | sed 's|https\?://||')
|
||||
local remote_url="https://${GITEA_TOKEN}@${gitea_host}/${owner}/${repo}.git"
|
||||
local display_url="https://***@${gitea_host}/${owner}/${repo}.git"
|
||||
|
||||
echo "gitea_push_directory: procesando '$directory' → '$owner/$repo' (rama: $branch)..." >&2
|
||||
|
||||
# Añadir registry.db al .gitignore local si existe en el directorio
|
||||
if [[ -f "$directory/registry.db" ]]; then
|
||||
echo "gitea_push_directory: añadiendo registry.db al .gitignore..." >&2
|
||||
if [[ ! -f "$directory/.gitignore" ]] || ! grep -qxF "registry.db" "$directory/.gitignore"; then
|
||||
echo "registry.db" >> "$directory/.gitignore"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Gestionar estado del repositorio git
|
||||
if [[ -d "$directory/.git" ]]; then
|
||||
local existing_remote
|
||||
existing_remote=$(git -C "$directory" remote get-url origin 2>/dev/null || echo "")
|
||||
|
||||
if [[ -z "$existing_remote" ]]; then
|
||||
echo "gitea_push_directory: añadiendo remote origin..." >&2
|
||||
git -C "$directory" remote add origin "$remote_url"
|
||||
else
|
||||
# Comparar remote sin token para detectar si apunta al mismo repo
|
||||
local clean_existing
|
||||
clean_existing=$(echo "$existing_remote" | sed 's|https://[^@]*@||;s|https://||')
|
||||
local clean_target="${gitea_host}/${owner}/${repo}.git"
|
||||
|
||||
if [[ "$clean_existing" != "$clean_target" ]]; then
|
||||
echo "gitea_push_directory: remote apunta a otro destino ('$clean_existing'), actualizando..." >&2
|
||||
git -C "$directory" remote set-url origin "$remote_url"
|
||||
else
|
||||
echo "gitea_push_directory: remote ya apunta al destino correcto, actualizando token..." >&2
|
||||
git -C "$directory" remote set-url origin "$remote_url"
|
||||
fi
|
||||
fi
|
||||
else
|
||||
echo "gitea_push_directory: inicializando nuevo repositorio git..." >&2
|
||||
git -C "$directory" init
|
||||
git -C "$directory" remote add origin "$remote_url"
|
||||
fi
|
||||
|
||||
# Configurar rama por defecto
|
||||
git -C "$directory" checkout -B "$branch" 2>/dev/null || true
|
||||
|
||||
# Añadir y commitear cambios si los hay
|
||||
git -C "$directory" add -A
|
||||
|
||||
local status
|
||||
status=$(git -C "$directory" status --porcelain)
|
||||
|
||||
if [[ -n "$status" ]]; then
|
||||
echo "gitea_push_directory: commiteando cambios..." >&2
|
||||
git -C "$directory" -c user.email="agent@fn-registry" -c user.name="fn-registry agent" \
|
||||
commit -m "chore: sync from fn-registry agent"
|
||||
else
|
||||
echo "gitea_push_directory: sin cambios pendientes, solo haciendo push..." >&2
|
||||
fi
|
||||
|
||||
echo "gitea_push_directory: haciendo push a $display_url..." >&2
|
||||
git -C "$directory" push --set-upstream origin "$branch" --force-with-lease 2>&1 \
|
||||
| sed "s|${GITEA_TOKEN}|***|g" >&2 \
|
||||
|| git -C "$directory" push --set-upstream origin "$branch" 2>&1 \
|
||||
| sed "s|${GITEA_TOKEN}|***|g" >&2
|
||||
|
||||
echo "gitea_push_directory: push completado a '$owner/$repo' rama '$branch'" >&2
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
---
|
||||
name: install_android_sdk
|
||||
kind: function
|
||||
lang: bash
|
||||
domain: infra
|
||||
version: "1.0.0"
|
||||
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."
|
||||
tags: [android, sdk, jdk, java, install, infra, mobile]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: "error_go_core"
|
||||
imports: []
|
||||
params: []
|
||||
output: "sin salida estructurada; imprime progreso y resumen final con rutas de instalacion"
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "bash/functions/infra/install_android_sdk.sh"
|
||||
---
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```bash
|
||||
# Instalacion en directorio por defecto ($HOME/android-sdk)
|
||||
source install_android_sdk.sh
|
||||
|
||||
# Instalacion en directorio personalizado
|
||||
ANDROID_SDK_DIR=/opt/android source install_android_sdk.sh
|
||||
|
||||
# Si ya esta instalado:
|
||||
# Android SDK ya instalado en: /home/user/android-sdk
|
||||
|
||||
# Instalacion completa imprime:
|
||||
# Descargando JDK 17...
|
||||
# JDK 17 instalado: /home/user/android-sdk/jdk-17/jdk-17.0.x+y
|
||||
# Descargando Android cmdline-tools...
|
||||
# cmdline-tools instalados
|
||||
# Aceptando licencias de Android SDK...
|
||||
# Instalando platform-tools, platforms;android-34, build-tools;34.0.0...
|
||||
#
|
||||
# Android SDK instalado en: /home/user/android-sdk
|
||||
# JDK 17: /home/user/android-sdk/jdk-17/jdk-17.0.x+y
|
||||
# Para activar: source /home/user/android-sdk/env.sh
|
||||
|
||||
# Activar entorno en sesion actual
|
||||
source ~/android-sdk/env.sh
|
||||
```
|
||||
|
||||
## Notas
|
||||
|
||||
Requiere `curl` y `unzip` (disponibles en la mayoria de distros Linux). No requiere root ni sudo.
|
||||
|
||||
El JDK se descarga desde Adoptium (Eclipse Temurin) via su API oficial. La URL de cmdline-tools apunta a la version 11076708 (2024). Si Google actualiza la version, cambiar la URL con el nuevo numero de build.
|
||||
|
||||
La reorganizacion del zip es necesaria porque Google distribuye cmdline-tools con estructura `cmdline-tools/bin/...` pero sdkmanager espera estar en `cmdline-tools/latest/bin/sdkmanager` para que Android Studio y otras herramientas lo detecten correctamente.
|
||||
|
||||
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`.
|
||||
@@ -0,0 +1,134 @@
|
||||
#!/usr/bin/env bash
|
||||
# install_android_sdk — Descarga e instala Android SDK command-line tools y JDK 17
|
||||
# localmente (sin root/sudo) en $ANDROID_SDK_DIR (default: $HOME/android-sdk).
|
||||
set -euo pipefail
|
||||
|
||||
install_android_sdk() {
|
||||
local sdk_dir="${ANDROID_SDK_DIR:-$HOME/android-sdk}"
|
||||
local tmp_dir
|
||||
tmp_dir="$(mktemp -d)"
|
||||
|
||||
# Limpia temporales al salir
|
||||
trap 'rm -rf "$tmp_dir"' EXIT
|
||||
|
||||
# 1. Verifica si ya está instalado
|
||||
if [[ -f "$sdk_dir/cmdline-tools/latest/bin/sdkmanager" ]]; then
|
||||
if JAVA_HOME="$(ls -d "$sdk_dir"/jdk-17/jdk-17* 2>/dev/null | head -1)" \
|
||||
"$sdk_dir/cmdline-tools/latest/bin/sdkmanager" --version &>/dev/null; then
|
||||
echo "Android SDK ya instalado en: $sdk_dir"
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
|
||||
mkdir -p "$sdk_dir"
|
||||
|
||||
# 2. Descarga JDK 17 si no existe
|
||||
local jdk_dir
|
||||
jdk_dir="$(ls -d "$sdk_dir"/jdk-17/jdk-17* 2>/dev/null | head -1 || true)"
|
||||
|
||||
if [[ -z "$jdk_dir" ]]; then
|
||||
echo "Descargando JDK 17..."
|
||||
local jdk_tar="$tmp_dir/jdk17.tar.gz"
|
||||
local jdk_url="https://api.adoptium.net/v3/binary/latest/17/ga/linux/x64/jdk/hotspot/normal/eclipse"
|
||||
|
||||
if ! curl -fL --progress-bar -o "$jdk_tar" "$jdk_url"; then
|
||||
echo "ERROR: fallo al descargar JDK 17 desde $jdk_url" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
mkdir -p "$sdk_dir/jdk-17"
|
||||
echo "Extrayendo JDK 17..."
|
||||
if ! tar -xzf "$jdk_tar" -C "$sdk_dir/jdk-17"; then
|
||||
echo "ERROR: fallo al extraer JDK 17" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
jdk_dir="$(ls -d "$sdk_dir"/jdk-17/jdk-17* 2>/dev/null | head -1 || true)"
|
||||
if [[ -z "$jdk_dir" ]]; then
|
||||
echo "ERROR: no se encontro directorio jdk-17* tras la extraccion" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
if ! JAVA_HOME="$jdk_dir" "$jdk_dir/bin/java" -version &>/dev/null; then
|
||||
echo "ERROR: java -version fallo tras instalar JDK" >&2
|
||||
return 1
|
||||
fi
|
||||
echo "JDK 17 instalado: $jdk_dir"
|
||||
else
|
||||
echo "JDK 17 ya presente: $jdk_dir"
|
||||
fi
|
||||
|
||||
export JAVA_HOME="$jdk_dir"
|
||||
|
||||
# 3. Descarga Android cmdline-tools si no existen
|
||||
if [[ ! -f "$sdk_dir/cmdline-tools/latest/bin/sdkmanager" ]]; then
|
||||
echo "Descargando Android cmdline-tools..."
|
||||
local tools_zip="$tmp_dir/cmdline-tools.zip"
|
||||
local tools_url="https://dl.google.com/android/repository/commandlinetools-linux-11076708_latest.zip"
|
||||
|
||||
if ! curl -fL --progress-bar -o "$tools_zip" "$tools_url"; then
|
||||
echo "ERROR: fallo al descargar Android cmdline-tools desde $tools_url" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
local tools_tmp="$tmp_dir/cmdline-tools-extracted"
|
||||
mkdir -p "$tools_tmp"
|
||||
echo "Extrayendo cmdline-tools..."
|
||||
if ! unzip -q "$tools_zip" -d "$tools_tmp"; then
|
||||
echo "ERROR: fallo al extraer cmdline-tools" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
# La estructura del zip es cmdline-tools/bin/..., reorganizar a cmdline-tools/latest/
|
||||
mkdir -p "$sdk_dir/cmdline-tools"
|
||||
if [[ -d "$tools_tmp/cmdline-tools" ]]; then
|
||||
mv "$tools_tmp/cmdline-tools" "$sdk_dir/cmdline-tools/latest"
|
||||
else
|
||||
echo "ERROR: estructura inesperada en el zip de cmdline-tools" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [[ ! -f "$sdk_dir/cmdline-tools/latest/bin/sdkmanager" ]]; then
|
||||
echo "ERROR: sdkmanager no encontrado tras extraer cmdline-tools" >&2
|
||||
return 1
|
||||
fi
|
||||
echo "cmdline-tools instalados"
|
||||
else
|
||||
echo "cmdline-tools ya presentes"
|
||||
fi
|
||||
|
||||
local sdkmanager="$sdk_dir/cmdline-tools/latest/bin/sdkmanager"
|
||||
export ANDROID_HOME="$sdk_dir"
|
||||
export ANDROID_SDK_ROOT="$sdk_dir"
|
||||
export PATH="$JAVA_HOME/bin:$sdk_dir/cmdline-tools/latest/bin:$sdk_dir/platform-tools:$PATH"
|
||||
|
||||
# 4. Acepta licencias e instala paquetes necesarios
|
||||
echo "Aceptando licencias de Android SDK..."
|
||||
if ! yes | "$sdkmanager" --licenses; then
|
||||
echo "ERROR: fallo al aceptar licencias de Android SDK" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
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
|
||||
echo "ERROR: fallo al instalar paquetes de Android SDK" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 5. Genera archivo de entorno
|
||||
local env_file="$sdk_dir/env.sh"
|
||||
cat > "$env_file" <<EOF
|
||||
export JAVA_HOME="$JAVA_HOME"
|
||||
export ANDROID_HOME="$sdk_dir"
|
||||
export ANDROID_SDK_ROOT="$sdk_dir"
|
||||
export PATH="\$JAVA_HOME/bin:$sdk_dir/cmdline-tools/latest/bin:$sdk_dir/platform-tools:\$PATH"
|
||||
EOF
|
||||
|
||||
# 6. Resumen final
|
||||
echo ""
|
||||
echo "Android SDK instalado en: $sdk_dir"
|
||||
echo "JDK 17: $JAVA_HOME"
|
||||
echo "Para activar: source $sdk_dir/env.sh"
|
||||
}
|
||||
|
||||
install_android_sdk
|
||||
@@ -0,0 +1,40 @@
|
||||
---
|
||||
name: install_mantine
|
||||
kind: function
|
||||
lang: bash
|
||||
domain: infra
|
||||
version: "1.0.0"
|
||||
purity: impure
|
||||
signature: "install_mantine(project_dir: string) -> void"
|
||||
description: "Instala Mantine UI con todas sus dependencias (@mantine/core, hooks, charts, notifications, form) y PostCSS en un proyecto frontend. Detecta package manager por lockfile. Genera postcss.config.cjs si no existe. Idempotente."
|
||||
tags: [mantine, frontend, install, react, ui, postcss]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: "error_go_core"
|
||||
imports: []
|
||||
params:
|
||||
- name: project_dir
|
||||
desc: "directorio del proyecto frontend con package.json"
|
||||
output: "sin salida; muestra progreso de instalación"
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "bash/functions/infra/install_mantine.sh"
|
||||
---
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```bash
|
||||
# Instalar Mantine en un proyecto con pnpm
|
||||
source install_mantine.sh
|
||||
install_mantine ./apps/rapid_dashboards/frontend
|
||||
|
||||
# Uso directo
|
||||
bash install_mantine.sh ./frontend
|
||||
```
|
||||
|
||||
## Notas
|
||||
|
||||
Detecta el package manager por lockfile: pnpm-lock.yaml → pnpm, yarn.lock → yarn, package-lock.json → npm. Instala las dependencias core de Mantine v7+ y el stack PostCSS necesario. Si postcss.config.cjs ya existe no lo sobreescribe.
|
||||
@@ -0,0 +1,97 @@
|
||||
# install_mantine
|
||||
# ---------------
|
||||
# Instala dependencias de Mantine UI en un proyecto frontend.
|
||||
# Detecta package manager por lockfile (pnpm > yarn > npm).
|
||||
# Genera postcss.config.cjs si no existe.
|
||||
# Idempotente: no reinstala si ya estan presentes.
|
||||
#
|
||||
# USO (sourced):
|
||||
# source install_mantine.sh
|
||||
# install_mantine /path/to/frontend
|
||||
#
|
||||
# USO (directo):
|
||||
# bash install_mantine.sh /path/to/frontend
|
||||
|
||||
install_mantine() {
|
||||
local project_dir="$1"
|
||||
|
||||
if [ -z "$project_dir" ]; then
|
||||
echo "install_mantine: se requiere project_dir" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [ ! -f "$project_dir/package.json" ]; then
|
||||
echo "install_mantine: no existe package.json en $project_dir" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Detectar package manager
|
||||
local pm="npm"
|
||||
local add_cmd="install"
|
||||
local add_dev_flag="--save-dev"
|
||||
if [ -f "$project_dir/pnpm-lock.yaml" ] || [ -f "$project_dir/pnpm-workspace.yaml" ]; then
|
||||
pm="pnpm"
|
||||
add_cmd="add"
|
||||
add_dev_flag="-D"
|
||||
elif [ -f "$project_dir/yarn.lock" ]; then
|
||||
pm="yarn"
|
||||
add_cmd="add"
|
||||
add_dev_flag="--dev"
|
||||
elif [ -f "$project_dir/package-lock.json" ]; then
|
||||
pm="npm"
|
||||
add_cmd="install"
|
||||
add_dev_flag="--save-dev"
|
||||
fi
|
||||
|
||||
echo "Detectado package manager: $pm"
|
||||
|
||||
# Dependencias runtime
|
||||
local runtime_deps="@mantine/core @mantine/hooks @mantine/charts @mantine/notifications @mantine/form"
|
||||
echo "Instalando dependencias Mantine..."
|
||||
(cd "$project_dir" && $pm $add_cmd $runtime_deps 2>&1)
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "install_mantine: fallo instalando dependencias runtime" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Dependencias PostCSS (dev)
|
||||
local dev_deps="postcss postcss-preset-mantine postcss-simple-vars"
|
||||
echo "Instalando dependencias PostCSS..."
|
||||
(cd "$project_dir" && $pm $add_cmd $add_dev_flag $dev_deps 2>&1)
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "install_mantine: fallo instalando dependencias PostCSS" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Generar postcss.config.cjs si no existe
|
||||
if [ ! -f "$project_dir/postcss.config.cjs" ]; then
|
||||
echo "Generando postcss.config.cjs..."
|
||||
cat > "$project_dir/postcss.config.cjs" << 'POSTCSS'
|
||||
module.exports = {
|
||||
plugins: {
|
||||
'postcss-preset-mantine': {},
|
||||
'postcss-simple-vars': {
|
||||
variables: {
|
||||
'mantine-breakpoint-xs': '36em',
|
||||
'mantine-breakpoint-sm': '48em',
|
||||
'mantine-breakpoint-md': '62em',
|
||||
'mantine-breakpoint-lg': '75em',
|
||||
'mantine-breakpoint-xl': '88em',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
POSTCSS
|
||||
echo "postcss.config.cjs creado"
|
||||
else
|
||||
echo "postcss.config.cjs ya existe, no se sobreescribe"
|
||||
fi
|
||||
|
||||
echo "Mantine instalado correctamente en $project_dir"
|
||||
}
|
||||
|
||||
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
||||
install_mantine "$@"
|
||||
fi
|
||||
@@ -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