feat(cpp,bash): app_about + Settings submenu, ensure_repo_synced pipeline

cpp/core: nuevo modulo app_about — ventana About con project/version/desc,
componible via about_window_set_info() en el init de la app y rendererizada
automaticamente por fn::run_app al final de cada frame.

app_menubar: el item top-level "Settings..." pasa a ser un BeginMenu
"Settings" con dos subitems: "Settings..." (existente) y "About..." (nuevo).

bash/infra: nueva pipeline ensure_repo_synced que compone gitea_create_repo
y gitea_push_directory para garantizar repo Gitea existente + sync de un
directorio local en una sola llamada idempotente.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-28 22:05:31 +02:00
parent dc5e3266ed
commit 424e913566
9 changed files with 327 additions and 7 deletions
@@ -0,0 +1,60 @@
---
name: ensure_repo_synced
kind: pipeline
lang: bash
domain: infra
version: "1.0.0"
purity: impure
signature: "ensure_repo_synced(directory: string, owner?: string, repo_name?: string, branch?: string, commit_msg?: string) -> void"
description: "Garantiza que un directorio tenga repo Gitea y este sincronizado. Crea el repo remoto si no existe, inicializa .git si falta, commitea cambios pendientes y pushea a origin. Idempotente."
tags: [gitea, git, sync, repo, pipeline, infra]
uses_functions:
- gitea_create_repo_bash_infra
- gitea_push_directory_bash_infra
uses_types: []
returns: []
returns_optional: false
error_type: "error_go_core"
imports: []
params:
- name: directory
desc: "ruta del directorio local que se quiere sincronizar"
- name: owner
desc: "owner Gitea (org o user); default 'dataforge'"
- name: repo_name
desc: "nombre del repo Gitea; default basename del directorio"
- name: branch
desc: "rama destino del push; default 'master'"
- name: commit_msg
desc: "mensaje de commit usado si hay cambios pendientes; default 'chore: sync from fn_registry'"
output: "vacio — efectos en Gitea: repo creado (si no existia) y rama push-eada con los contenidos del directorio"
tested: false
tests: []
test_file_path: ""
file_path: "bash/functions/infra/ensure_repo_synced.sh"
---
# ensure_repo_synced
Pipeline bash que compone `gitea_create_repo` + `gitea_push_directory` para garantizar que un directorio local este sincronizado con un repo Gitea, creando el repo si no existe.
## Cuando usarlo
Cuando se quiere sincronizar a Gitea un directorio que **podria** no tener todavia repo remoto creado, sin tener que comprobarlo manualmente. Idempotente: se puede llamar repetidamente sin romper nada.
## Variables de entorno requeridas
- `GITEA_URL` — base URL del Gitea (ej. `https://gitea.example.com`)
- `GITEA_TOKEN` — token de API con permisos de creacion de repos y push
## Ejemplo
```bash
source bash/functions/infra/ensure_repo_synced.sh
# Sincronizar app local a dataforge/<basename>
ensure_repo_synced apps/mi_app
# Sincronizar con owner/repo custom y rama main
ensure_repo_synced apps/mi_app dataforge mi_app main "feat: initial sync"
```
+82
View File
@@ -0,0 +1,82 @@
#!/usr/bin/env bash
# ensure_repo_synced — Garantiza que un directorio tenga repo Gitea y este
# sincronizado: crea el repo remoto si no existe, inicializa .git si falta,
# commitea cambios pendientes y pushea a origin.
#
# Pipeline que compone gitea_create_repo + gitea_push_directory.
#
# Uso:
# ensure_repo_synced <directory> [owner] [repo_name] [branch] [commit_msg]
#
# Defaults:
# owner = dataforge
# repo_name = basename(directory)
# branch = master
# commit_msg = "chore: sync from fn_registry"
ensure_repo_synced() {
local directory="$1"
local owner="${2:-dataforge}"
local repo_name="$3"
local branch="${4:-master}"
local commit_msg="${5:-chore: sync from fn_registry}"
if [[ -z "$directory" ]]; then
echo "ensure_repo_synced: se requiere directory" >&2
return 1
fi
if [[ ! -d "$directory" ]]; then
echo "ensure_repo_synced: directorio '$directory' no existe" >&2
return 1
fi
if [[ -z "$repo_name" ]]; then
repo_name=$(basename "$(realpath "$directory")")
fi
if [[ -z "${GITEA_URL:-}" || -z "${GITEA_TOKEN:-}" ]]; then
echo "ensure_repo_synced: GITEA_URL y GITEA_TOKEN deben estar seteados" >&2
return 1
fi
local script_dir
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck source=./gitea_create_repo.sh
source "$script_dir/gitea_create_repo.sh"
# shellcheck source=./gitea_push_directory.sh
source "$script_dir/gitea_push_directory.sh"
echo "ensure_repo_synced: $directory$owner/$repo_name [$branch]" >&2
# 1. Asegurar repo remoto en Gitea (idempotente — 409 si existe)
if ! gitea_create_repo "$owner" "$repo_name" "false" "Synced from fn_registry" >/dev/null; then
echo "ensure_repo_synced: fallo creando/comprobando repo remoto" >&2
return 1
fi
# 2. Inicializar .git, commitear y pushear
# gitea_push_directory ya hace commit con su mensaje por defecto. Para
# respetar commit_msg custom, hacemos commit aqui antes si hay cambios.
if [[ -d "$directory/.git" ]]; then
local status
status=$(git -C "$directory" status --porcelain)
if [[ -n "$status" ]]; then
echo "ensure_repo_synced: commiteando cambios con mensaje: $commit_msg" >&2
git -C "$directory" add -A
git -C "$directory" \
-c user.email="agent@fn-registry" -c user.name="fn-registry agent" \
commit -m "$commit_msg"
fi
fi
if ! gitea_push_directory "$directory" "$owner" "$repo_name" "$branch"; then
echo "ensure_repo_synced: fallo en gitea_push_directory" >&2
return 1
fi
echo "ensure_repo_synced: ok '$owner/$repo_name'" >&2
}
# Si se invoca como script (no source), ejecutar la funcion.
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
ensure_repo_synced "$@"
fi