feat: funciones bash de deploy — rsync_deploy y gitea_create_webhook
rsync_deploy sincroniza directorio local a remoto via SSH con exclusiones estándar (.git, node_modules, *.db, etc.). gitea_create_webhook crea webhook de push en un repo Gitea para auto-deploy en cada commit.
This commit is contained in:
@@ -0,0 +1,50 @@
|
||||
---
|
||||
name: gitea_create_webhook
|
||||
kind: function
|
||||
lang: bash
|
||||
domain: infra
|
||||
version: "1.0.0"
|
||||
purity: impure
|
||||
signature: "gitea_create_webhook(owner: string, repo: string, target_url: string, secret?: string) -> json"
|
||||
description: "Crea un webhook de push en un repositorio Gitea. El webhook notifica a target_url en cada push."
|
||||
tags: [gitea, webhook, push, deploy, ci, 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: target_url
|
||||
desc: "URL que recibirá el POST del webhook en cada push"
|
||||
- name: secret
|
||||
desc: "secreto compartido para firmar el payload (opcional)"
|
||||
output: "JSON con webhook_id, owner, repo, target_url"
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "bash/functions/infra/gitea_create_webhook.sh"
|
||||
---
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```bash
|
||||
source bash/functions/infra/gitea_create_webhook.sh
|
||||
|
||||
export GITEA_URL="https://git.example.com"
|
||||
export GITEA_TOKEN="$(pass agentes/dataforge-token)"
|
||||
|
||||
# Crear webhook para auto-deploy
|
||||
gitea_create_webhook "myorg" "dag_engine" "http://vps:9090/webhook/push" "mi_secreto"
|
||||
# {"webhook_id":42,"owner":"myorg","repo":"dag_engine","target_url":"http://vps:9090/webhook/push"}
|
||||
```
|
||||
|
||||
## Notas
|
||||
|
||||
- Requiere `GITEA_URL` y `GITEA_TOKEN` como variables de entorno.
|
||||
- Solo escucha eventos `push`. Para otros eventos, modificar el array `events` en el payload.
|
||||
- Si el webhook ya existe para la misma URL, Gitea crea uno duplicado (no es idempotente).
|
||||
@@ -0,0 +1,60 @@
|
||||
#!/usr/bin/env bash
|
||||
# gitea_create_webhook — Crea un webhook de push en un repositorio Gitea
|
||||
set -euo pipefail
|
||||
|
||||
gitea_create_webhook() {
|
||||
local owner="$1"
|
||||
local repo="$2"
|
||||
local target_url="$3"
|
||||
local secret="${4:-}"
|
||||
|
||||
if [[ -z "$owner" || -z "$repo" || -z "$target_url" ]]; then
|
||||
echo "usage: gitea_create_webhook <owner> <repo> <target_url> [secret]" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
local gitea_url="${GITEA_URL:?GITEA_URL no seteada}"
|
||||
local gitea_token="${GITEA_TOKEN:?GITEA_TOKEN no seteada}"
|
||||
|
||||
# Payload JSON para el webhook
|
||||
local payload
|
||||
payload=$(cat <<EOF
|
||||
{
|
||||
"type": "gitea",
|
||||
"active": true,
|
||||
"events": ["push"],
|
||||
"config": {
|
||||
"url": "$target_url",
|
||||
"content_type": "json",
|
||||
"secret": "$secret"
|
||||
}
|
||||
}
|
||||
EOF
|
||||
)
|
||||
|
||||
local response http_code body
|
||||
response=$(curl -s -w "\n%{http_code}" \
|
||||
-X POST \
|
||||
-H "Authorization: token $gitea_token" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "$payload" \
|
||||
"${gitea_url}/api/v1/repos/${owner}/${repo}/hooks")
|
||||
|
||||
http_code=$(echo "$response" | tail -1)
|
||||
body=$(echo "$response" | sed '$d')
|
||||
|
||||
if [[ "$http_code" -ge 200 && "$http_code" -lt 300 ]]; then
|
||||
# Extraer webhook ID del response
|
||||
local webhook_id
|
||||
webhook_id=$(echo "$body" | grep -oP '"id":\s*\K[0-9]+' | head -1)
|
||||
printf '{"webhook_id":%s,"owner":"%s","repo":"%s","target_url":"%s"}\n' \
|
||||
"$webhook_id" "$owner" "$repo" "$target_url"
|
||||
else
|
||||
echo "gitea_create_webhook: HTTP $http_code — $body" >&2
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then
|
||||
gitea_create_webhook "$@"
|
||||
fi
|
||||
@@ -0,0 +1,52 @@
|
||||
---
|
||||
name: rsync_deploy
|
||||
kind: function
|
||||
lang: bash
|
||||
domain: infra
|
||||
version: "1.0.0"
|
||||
purity: impure
|
||||
signature: "rsync_deploy(local_dir: string, ssh_alias: string, remote_dir: string) -> json"
|
||||
description: "Sincroniza un directorio local a un host remoto via rsync+SSH. Excluye archivos de desarrollo y bases de datos locales. Crea el directorio remoto si no existe."
|
||||
tags: [rsync, deploy, sync, ssh, remote, infra]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: "error_go_core"
|
||||
imports: []
|
||||
params:
|
||||
- name: local_dir
|
||||
desc: "ruta al directorio local a sincronizar (ej: apps/dag_engine/)"
|
||||
- name: ssh_alias
|
||||
desc: "alias SSH del host destino definido en ~/.ssh/config (ej: myserver)"
|
||||
- name: remote_dir
|
||||
desc: "ruta absoluta del directorio destino en el host remoto (ej: /opt/apps/dag_engine)"
|
||||
output: "JSON con files_transferred (int), total_size (string), ssh_alias (string), remote_dir (string)"
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "bash/functions/infra/rsync_deploy.sh"
|
||||
---
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```bash
|
||||
source bash/functions/infra/rsync_deploy.sh
|
||||
|
||||
# Deploy de una app al servidor de producción
|
||||
result=$(rsync_deploy "apps/dag_engine/" "prod-server" "/opt/apps/dag_engine")
|
||||
echo "$result"
|
||||
# {"files_transferred": 12, "total_size": "1.23 MB", "ssh_alias": "prod-server", "remote_dir": "/opt/apps/dag_engine"}
|
||||
|
||||
# Deploy con ruta absoluta local
|
||||
rsync_deploy "/home/lucas/fn_registry/apps/myapp/" "myserver" "/opt/myapp"
|
||||
```
|
||||
|
||||
## Notas
|
||||
|
||||
- Usa `rsync -avz --delete`: archivos borrados localmente se borran también en el remoto.
|
||||
- Antes del rsync crea el directorio remoto con `ssh mkdir -p` para evitar errores si no existe.
|
||||
- Archivos excluidos: `.git`, `operations.db*`, `*.exe`, `node_modules`, `.venv`, `__pycache__`, `build/`, `*.db-shm`, `*.db-wal`.
|
||||
- El JSON de salida va a stdout; los mensajes de progreso y errores van a stderr.
|
||||
- Exit code 1 si rsync falla o si el directorio local no existe.
|
||||
- El `ssh_alias` se resuelve con la configuración de `~/.ssh/config`, incluyendo host, user, identityfile y puerto.
|
||||
@@ -0,0 +1,77 @@
|
||||
#!/usr/bin/env bash
|
||||
# rsync_deploy — Sincroniza un directorio local a un host remoto via rsync+SSH
|
||||
set -euo pipefail
|
||||
|
||||
rsync_deploy() {
|
||||
local local_dir="$1"
|
||||
local ssh_alias="$2"
|
||||
local remote_dir="$3"
|
||||
|
||||
if [[ -z "$local_dir" || -z "$ssh_alias" || -z "$remote_dir" ]]; then
|
||||
echo "rsync_deploy: se requieren local_dir, ssh_alias y remote_dir" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [[ ! -d "$local_dir" ]]; then
|
||||
echo "rsync_deploy: directorio local '$local_dir' no existe" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Crear directorio remoto si no existe
|
||||
echo "rsync_deploy: verificando directorio remoto '$remote_dir' en '$ssh_alias'..." >&2
|
||||
if ! ssh "$ssh_alias" "mkdir -p '$remote_dir'" 2>&1; then
|
||||
echo "rsync_deploy: no se pudo crear el directorio remoto '$remote_dir' en '$ssh_alias'" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Ejecutar rsync y capturar salida para parsear estadísticas
|
||||
local rsync_output
|
||||
rsync_output=$(rsync -avz --delete \
|
||||
--exclude='.git' \
|
||||
--exclude='operations.db*' \
|
||||
--exclude='*.exe' \
|
||||
--exclude='node_modules' \
|
||||
--exclude='.venv' \
|
||||
--exclude='__pycache__' \
|
||||
--exclude='build/' \
|
||||
--exclude='*.db-shm' \
|
||||
--exclude='*.db-wal' \
|
||||
-e ssh \
|
||||
"$local_dir" \
|
||||
"${ssh_alias}:${remote_dir}" 2>&1) || {
|
||||
echo "rsync_deploy: rsync falló al sincronizar '$local_dir' → '${ssh_alias}:${remote_dir}'" >&2
|
||||
echo "$rsync_output" >&2
|
||||
return 1
|
||||
}
|
||||
|
||||
echo "$rsync_output" >&2
|
||||
|
||||
# Parsear número de archivos transferidos
|
||||
local files_transferred
|
||||
files_transferred=$(echo "$rsync_output" | grep -oP 'Number of regular files transferred: \K[0-9,]+' | tr -d ',' || echo "0")
|
||||
if [[ -z "$files_transferred" ]]; then
|
||||
# Intentar formato alternativo de rsync
|
||||
files_transferred=$(echo "$rsync_output" | grep -oP 'Number of files transferred: \K[0-9,]+' | tr -d ',' || echo "0")
|
||||
fi
|
||||
if [[ -z "$files_transferred" ]]; then
|
||||
files_transferred="0"
|
||||
fi
|
||||
|
||||
# Parsear tamaño total transferido
|
||||
local total_size
|
||||
total_size=$(echo "$rsync_output" | grep -oP 'Total transferred file size: \K[0-9,.]+ \w+' || echo "0 bytes")
|
||||
if [[ -z "$total_size" ]]; then
|
||||
total_size="0 bytes"
|
||||
fi
|
||||
|
||||
# Emitir JSON a stdout
|
||||
printf '{"files_transferred": %s, "total_size": "%s", "ssh_alias": "%s", "remote_dir": "%s"}\n' \
|
||||
"$files_transferred" \
|
||||
"$total_size" \
|
||||
"$ssh_alias" \
|
||||
"$remote_dir"
|
||||
}
|
||||
|
||||
if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then
|
||||
rsync_deploy "$@"
|
||||
fi
|
||||
Reference in New Issue
Block a user