Añadidos arhcivos basicos de repos
This commit is contained in:
@@ -0,0 +1,245 @@
|
||||
---
|
||||
version: 1.0.0
|
||||
updated: 2026-03-11
|
||||
tags: [worktree, git, cleanup, automation]
|
||||
---
|
||||
|
||||
# Command: cleanup-worktrees
|
||||
|
||||
Elimina worktrees y sus ramas locales asociadas después de haber sido mergeadas. Puede limpiar un worktree específico o todos los worktrees en `worktrees/`.
|
||||
|
||||
**Flujo completo:**
|
||||
1. Validar argumentos (issue number o --all)
|
||||
2. Listar worktrees a eliminar
|
||||
3. Para cada worktree:
|
||||
- Verificar que la rama fue mergeada
|
||||
- Eliminar worktree
|
||||
- Eliminar rama local
|
||||
4. Reportar limpieza completada
|
||||
|
||||
## Inputs
|
||||
|
||||
Se necesita UNO de estos parámetros:
|
||||
|
||||
- `issue_number`: Número de issue (NNNN) para limpiar solo ese worktree
|
||||
- `--all`: Limpiar todos los worktrees en `worktrees/`
|
||||
|
||||
## Flujo obligatorio
|
||||
|
||||
### 1. Validar argumentos
|
||||
|
||||
Verificar que se proporcionó exactamente un argumento:
|
||||
|
||||
```bash
|
||||
if [ $# -eq 0 ]; then
|
||||
echo "Error: Debes especificar un número de issue o --all"
|
||||
echo "Uso: /workspace:cleanup-worktrees <issue_number>"
|
||||
echo " /workspace:cleanup-worktrees --all"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ $# -gt 1 ]; then
|
||||
echo "Error: Demasiados argumentos"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
ARG=$1
|
||||
```
|
||||
|
||||
### 2. Determinar worktrees a limpiar
|
||||
|
||||
**Si es issue específica:**
|
||||
|
||||
```bash
|
||||
if [[ "$ARG" =~ ^[0-9]{4}$ ]]; then
|
||||
ISSUE_NUM=$ARG
|
||||
WORKTREE_PATH="worktrees/issue-$ISSUE_NUM"
|
||||
|
||||
# Verificar que existe el worktree
|
||||
if [ ! -d "$WORKTREE_PATH" ]; then
|
||||
echo "Error: No existe el worktree $WORKTREE_PATH"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
WORKTREES=("$WORKTREE_PATH")
|
||||
fi
|
||||
```
|
||||
|
||||
**Si es --all:**
|
||||
|
||||
```bash
|
||||
if [ "$ARG" = "--all" ]; then
|
||||
# Listar todos los worktrees en worktrees/
|
||||
if [ ! -d "worktrees" ]; then
|
||||
echo "No hay directorio worktrees/ para limpiar"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Obtener lista de directorios en worktrees/
|
||||
WORKTREES=($(find worktrees -maxdepth 1 -type d -name "issue-*" | sort))
|
||||
|
||||
if [ ${#WORKTREES[@]} -eq 0 ]; then
|
||||
echo "No hay worktrees para limpiar"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "Se encontraron ${#WORKTREES[@]} worktrees para limpiar"
|
||||
fi
|
||||
```
|
||||
|
||||
**Si no es ninguno:**
|
||||
|
||||
```bash
|
||||
if [ -z "$WORKTREES" ]; then
|
||||
echo "Error: Argumento inválido '$ARG'"
|
||||
echo "Usa un número de issue (ej: 0003) o --all"
|
||||
exit 1
|
||||
fi
|
||||
```
|
||||
|
||||
### 3. Confirmar con el usuario
|
||||
|
||||
Mostrar lo que se va a eliminar:
|
||||
|
||||
```bash
|
||||
echo "Se eliminarán los siguientes worktrees:"
|
||||
for wt in "${WORKTREES[@]}"; do
|
||||
ISSUE_NUM=$(basename "$wt" | sed 's/issue-//')
|
||||
BRANCH="quick/fix-issue-$ISSUE_NUM"
|
||||
echo " - $wt (rama: $BRANCH)"
|
||||
done
|
||||
|
||||
echo ""
|
||||
read -p "¿Continuar? (y/N): " CONFIRM
|
||||
|
||||
if [[ ! "$CONFIRM" =~ ^[Yy]$ ]]; then
|
||||
echo "Operación cancelada"
|
||||
exit 0
|
||||
fi
|
||||
```
|
||||
|
||||
### 4. Limpiar cada worktree
|
||||
|
||||
Para cada worktree en la lista:
|
||||
|
||||
```bash
|
||||
for wt in "${WORKTREES[@]}"; do
|
||||
ISSUE_NUM=$(basename "$wt" | sed 's/issue-//')
|
||||
BRANCH="quick/fix-issue-$ISSUE_NUM"
|
||||
|
||||
echo ""
|
||||
echo "Limpiando issue $ISSUE_NUM..."
|
||||
|
||||
# Verificar que la rama fue mergeada (opcional, para seguridad)
|
||||
MERGED=$(git branch --merged main | grep -c "$BRANCH" || true)
|
||||
|
||||
if [ "$MERGED" -eq 0 ]; then
|
||||
echo "⚠️ Advertencia: La rama $BRANCH NO ha sido mergeada a main"
|
||||
read -p "¿Eliminar de todas formas? (y/N): " FORCE
|
||||
|
||||
if [[ ! "$FORCE" =~ ^[Yy]$ ]]; then
|
||||
echo "Saltando $wt"
|
||||
continue
|
||||
fi
|
||||
fi
|
||||
|
||||
# Eliminar worktree
|
||||
echo " → Eliminando worktree $wt..."
|
||||
git worktree remove "$wt" --force 2>/dev/null || {
|
||||
echo " ⚠️ No se pudo eliminar worktree, puede que ya esté eliminado"
|
||||
}
|
||||
|
||||
# Eliminar rama local
|
||||
echo " → Eliminando rama $BRANCH..."
|
||||
git branch -D "$BRANCH" 2>/dev/null || {
|
||||
echo " ⚠️ No se pudo eliminar rama, puede que ya esté eliminada"
|
||||
}
|
||||
|
||||
echo " ✓ Issue $ISSUE_NUM limpiada"
|
||||
done
|
||||
```
|
||||
|
||||
### 5. Reportar resultado
|
||||
|
||||
```bash
|
||||
echo ""
|
||||
echo "✓ Limpieza completada"
|
||||
echo ""
|
||||
echo "Worktrees restantes:"
|
||||
git worktree list | grep -v "(bare)" || echo " (ninguno)"
|
||||
```
|
||||
|
||||
## Ejemplos
|
||||
|
||||
**Ejemplo 1: Limpiar una issue específica**
|
||||
|
||||
```bash
|
||||
/workspace:cleanup-worktrees 0003
|
||||
```
|
||||
|
||||
**Output:**
|
||||
```
|
||||
Se eliminarán los siguientes worktrees:
|
||||
- worktrees/issue-0003 (rama: quick/fix-issue-0003)
|
||||
|
||||
¿Continuar? (y/N): y
|
||||
|
||||
Limpiando issue 0003...
|
||||
→ Eliminando worktree worktrees/issue-0003...
|
||||
→ Eliminando rama quick/fix-issue-0003...
|
||||
✓ Issue 0003 limpiada
|
||||
|
||||
✓ Limpieza completada
|
||||
|
||||
Worktrees restantes:
|
||||
(ninguno)
|
||||
```
|
||||
|
||||
**Ejemplo 2: Limpiar todos los worktrees**
|
||||
|
||||
```bash
|
||||
/workspace:cleanup-worktrees --all
|
||||
```
|
||||
|
||||
**Output:**
|
||||
```
|
||||
Se encontraron 3 worktrees para limpiar
|
||||
Se eliminarán los siguientes worktrees:
|
||||
- worktrees/issue-0003 (rama: quick/fix-issue-0003)
|
||||
- worktrees/issue-0006 (rama: quick/fix-issue-0006)
|
||||
- worktrees/issue-0008 (rama: quick/fix-issue-0008)
|
||||
|
||||
¿Continuar? (y/N): y
|
||||
|
||||
Limpiando issue 0003...
|
||||
→ Eliminando worktree worktrees/issue-0003...
|
||||
→ Eliminando rama quick/fix-issue-0003...
|
||||
✓ Issue 0003 limpiada
|
||||
|
||||
Limpiando issue 0006...
|
||||
→ Eliminando worktree worktrees/issue-0006...
|
||||
→ Eliminando rama quick/fix-issue-0006...
|
||||
✓ Issue 0006 limpiada
|
||||
|
||||
Limpiando issue 0008...
|
||||
→ Eliminando worktree worktrees/issue-0008...
|
||||
→ Eliminando rama quick/fix-issue-0008...
|
||||
✓ Issue 0008 limpiada
|
||||
|
||||
✓ Limpieza completada
|
||||
```
|
||||
|
||||
## Convenciones
|
||||
|
||||
- **Nomenclatura de worktrees:** Siempre `worktrees/issue-NNNN`
|
||||
- **Nomenclatura de ramas:** Siempre `quick/fix-issue-NNNN`
|
||||
- **Confirmación interactiva:** Siempre pedir confirmación antes de eliminar
|
||||
- **Verificación de merge:** Advertir si la rama no fue mergeada
|
||||
|
||||
## Reglas críticas
|
||||
|
||||
- **SIEMPRE verificar que la rama fue mergeada** antes de eliminar (advertir si no)
|
||||
- **NUNCA eliminar sin confirmación del usuario** (a menos que se agregue flag `--force` en futuro)
|
||||
- **SIEMPRE usar --force en git worktree remove** para evitar errores por cambios sin commit
|
||||
- **NUNCA fallar silenciosamente** - reportar cada paso con claridad
|
||||
- **SIEMPRE mostrar worktrees restantes** al final para confirmar estado
|
||||
@@ -0,0 +1,224 @@
|
||||
---
|
||||
version: 1.0.0
|
||||
updated: 2026-03-12
|
||||
tags: [workspace, gitea, repo, creation]
|
||||
---
|
||||
|
||||
# Command: create-repo
|
||||
|
||||
Crea un nuevo subrepo (child repository) en `workspaces/`, inicializándolo con la estructura core/shell/app, creando el repo en Gitea, y registrándolo en la base de datos local.
|
||||
|
||||
## Para el usuario
|
||||
|
||||
### Cuándo usar este comando
|
||||
|
||||
Usar cuando necesites crear un nuevo workspace (subrepo) dentro de Dataforge. El comando:
|
||||
|
||||
1. Solicita el nombre y descripción del repositorio
|
||||
2. Muestra un resumen de lo que se creará
|
||||
3. Pide confirmación antes de proceder
|
||||
4. Crea el workspace completo (local + Gitea + BD)
|
||||
|
||||
### Prerequisitos
|
||||
|
||||
- Issues 0006, 0007, 0008 completados (workspaces base)
|
||||
- Issue 0011a completado (comandos organizados)
|
||||
- Gitea configurado y accesible (`GITEA_URL` y `GITEA_TOKEN` en env)
|
||||
- Base de datos SQLite disponible
|
||||
|
||||
### Variables de entorno requeridas
|
||||
|
||||
```bash
|
||||
export GITEA_URL="https://gitea.example.com"
|
||||
export GITEA_TOKEN="tu-token-de-api"
|
||||
export DATAFORGE_REPOS="./workspaces" # opcional, default: ./workspaces
|
||||
```
|
||||
|
||||
### Ejemplo de uso
|
||||
|
||||
```bash
|
||||
/workspace:create-repo
|
||||
```
|
||||
|
||||
```
|
||||
Nombre del repositorio: my-etl-pipeline
|
||||
Descripción: ETL pipeline para datos de ventas
|
||||
Tipo (go/data/etl/api) [go]: etl
|
||||
¿Repositorio privado? (s/n) [n]: n
|
||||
|
||||
Resumen:
|
||||
Nombre: my-etl-pipeline
|
||||
Path local: ./workspaces/my-etl-pipeline
|
||||
Gitea: https://gitea.example.com/Bl4cksmith/my-etl-pipeline
|
||||
Módulo Go: gitea.../my-etl-pipeline
|
||||
Tipo: etl
|
||||
Privado: no
|
||||
|
||||
¿Crear repositorio? (s/n): s
|
||||
|
||||
✓ Directorio creado: ./workspaces/my-etl-pipeline
|
||||
✓ Templates generados (core/, shell/, app/, go.mod, main.go)
|
||||
✓ Git inicializado
|
||||
✓ Repositorio Gitea creado
|
||||
✓ Push inicial completado
|
||||
✓ Registrado en base de datos
|
||||
|
||||
Workspace creado: ./workspaces/my-etl-pipeline
|
||||
|
||||
Para trabajar en él:
|
||||
cd workspaces/my-etl-pipeline
|
||||
```
|
||||
|
||||
## Para Claude
|
||||
|
||||
### Precondiciones
|
||||
|
||||
Verificar antes de ejecutar:
|
||||
- [ ] Feature flag `workspace_commands` habilitado en `feature_flags.json`
|
||||
- [ ] Variables de entorno `GITEA_URL` y `GITEA_TOKEN` configuradas
|
||||
- [ ] Directorio `workspaces/` existe (o se puede crear)
|
||||
|
||||
### Flujo de implementación
|
||||
|
||||
#### Paso 1: Solicitar inputs interactivos
|
||||
|
||||
Preguntar al usuario:
|
||||
|
||||
1. **Nombre del repositorio** — debe ser URL-safe (lowercase, alfanumérico, guiones)
|
||||
- Validar con `core.ValidateRepoName()`
|
||||
- Ejemplos válidos: `my-api`, `etl-pipeline`, `data-processor`
|
||||
- Ejemplos inválidos: `My API`, `my_api`, `-test-`
|
||||
|
||||
2. **Descripción** — texto libre, puede estar vacío
|
||||
|
||||
3. **Tipo** — uno de: `go` (default), `data`, `etl`, `api`
|
||||
|
||||
4. **¿Privado?** — `s/n`, default `n`
|
||||
|
||||
#### Paso 2: Mostrar resumen y pedir confirmación
|
||||
|
||||
```
|
||||
Resumen:
|
||||
Nombre: <nombre>
|
||||
Path local: <DATAFORGE_REPOS>/<nombre>
|
||||
Gitea: <GITEA_URL>/Bl4cksmith/<nombre>
|
||||
Módulo Go: gitea-dgg044oo04woo4ggcsws4gk0.organic-machine.com/Bl4cksmith/dataforge/<nombre>
|
||||
Tipo: <tipo>
|
||||
Privado: <sí/no>
|
||||
|
||||
¿Crear repositorio? (s/n):
|
||||
```
|
||||
|
||||
Si el usuario responde `n`, cancelar sin hacer cambios.
|
||||
|
||||
#### Paso 3: Ejecutar creación vía `app.CreateWorkspaceCommand`
|
||||
|
||||
El flujo completo está implementado en `app/workspace_create.go`:
|
||||
|
||||
```go
|
||||
params := app.CreateWorkspaceParams{
|
||||
Name: nombre,
|
||||
Description: descripcion,
|
||||
Type: tipo,
|
||||
IsPrivate: privado,
|
||||
}
|
||||
result, err := app.CreateWorkspaceCommand(config, params)
|
||||
```
|
||||
|
||||
El comando hace automáticamente:
|
||||
1. Validar nombre
|
||||
2. Verificar que no existe en local ni en Gitea
|
||||
3. Crear estructura `core/shell/app/`
|
||||
4. Escribir templates (`go.mod`, `main.go`, `core/core.go`, `shell/shell.go`, `app/app.go`, `.gitignore`, `README.md`)
|
||||
5. `git init` + configurar usuario
|
||||
6. Crear repo en Gitea
|
||||
7. Configurar remote y push inicial
|
||||
8. Registrar en SQLite
|
||||
|
||||
**Rollback automático:** Si falla cualquier paso, limpia directorio local y elimina repo Gitea si se creó.
|
||||
|
||||
#### Paso 4: Mostrar resultado
|
||||
|
||||
```
|
||||
✓ Workspace creado exitosamente
|
||||
|
||||
Path local: <localPath>
|
||||
Gitea: <giteaURL>
|
||||
Módulo Go: <moduleName>
|
||||
|
||||
Para trabajar en él:
|
||||
cd <localPath>
|
||||
```
|
||||
|
||||
### Manejo de errores
|
||||
|
||||
| Error | Causa | Solución |
|
||||
|-------|-------|----------|
|
||||
| "nombre de repositorio inválido" | Nombre no cumple reglas Gitea | Usar solo lowercase, alfanumérico y guiones |
|
||||
| "ya existe un directorio" | Workspace ya creado localmente | Verificar `ls workspaces/` |
|
||||
| "ya existe el repositorio en Gitea" | Repo creado en Gitea anteriormente | Verificar en Gitea o usar nombre diferente |
|
||||
| "error al crear repositorio en Gitea" | Token inválido o sin permisos | Verificar `GITEA_TOKEN` |
|
||||
| "error al hacer push inicial" | Credenciales git no configuradas | Configurar git credentials |
|
||||
|
||||
### Feature flag
|
||||
|
||||
Este comando requiere que `workspace_commands` esté habilitado:
|
||||
|
||||
```json
|
||||
{
|
||||
"features": {
|
||||
"workspace_commands": {
|
||||
"enabled": true
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Si está deshabilitado, informar al usuario que debe habilitarlo en `feature_flags.json`.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Error: "nombre de repositorio inválido"
|
||||
|
||||
El nombre debe cumplir las reglas de Gitea:
|
||||
- Solo letras, números y guiones
|
||||
- No puede empezar o terminar con guión
|
||||
- Entre 2 y 100 caracteres
|
||||
|
||||
```bash
|
||||
# Válidos:
|
||||
my-api
|
||||
etl-pipeline-v2
|
||||
data-processor
|
||||
|
||||
# Inválidos:
|
||||
My API # espacios, mayúsculas
|
||||
my_api # guión bajo
|
||||
-test- # empieza/termina con guión
|
||||
```
|
||||
|
||||
### Error: "GITEA_URL o GITEA_TOKEN no configurados"
|
||||
|
||||
```bash
|
||||
export GITEA_URL="https://gitea.example.com"
|
||||
export GITEA_TOKEN="tu-token"
|
||||
# Luego ejecutar de nuevo
|
||||
/workspace:create-repo
|
||||
```
|
||||
|
||||
### Error: "ya existe un directorio en ./workspaces/<nombre>"
|
||||
|
||||
El workspace ya existe localmente. Opciones:
|
||||
1. Usar nombre diferente
|
||||
2. Eliminar directorio si fue creación fallida: `rm -rf workspaces/<nombre>`
|
||||
|
||||
### Error: "rollback falló"
|
||||
|
||||
En caso de rollback parcial, verificar manualmente:
|
||||
```bash
|
||||
# Ver estado local
|
||||
ls workspaces/
|
||||
|
||||
# Ver repos en Gitea (vía API o interfaz web)
|
||||
curl -H "Authorization: token $GITEA_TOKEN" $GITEA_URL/api/v1/user/repos
|
||||
```
|
||||
@@ -0,0 +1,272 @@
|
||||
---
|
||||
version: 1.0.0
|
||||
updated: 2026-03-12
|
||||
tags: [workspace, gitea, import, repos]
|
||||
---
|
||||
|
||||
# Command: import-repo
|
||||
|
||||
Importa repositorios existentes al sistema Dataforge: desde GitHub/GitLab/otra instancia Gitea (URL remota), o adoptando un repo local en `workspaces/` que aún no está en Gitea.
|
||||
|
||||
## Para el usuario
|
||||
|
||||
### Cuándo usar este comando
|
||||
|
||||
Usar cuando necesites incorporar un repositorio existente al sistema Dataforge:
|
||||
|
||||
- Tienes un repo en GitHub/GitLab y quieres migrarlo a Gitea
|
||||
- Tienes un repo en otra instancia de Gitea y quieres clonarlo a la tuya
|
||||
- Tienes un directorio en `workspaces/` con git, pero sin estar en Gitea
|
||||
- Quieres adoptar un proyecto legacy en el ecosistema Dataforge
|
||||
|
||||
### Prerequisitos
|
||||
|
||||
- Variables de entorno configuradas: `GITEA_URL` y `GITEA_TOKEN`
|
||||
- Acceso de lectura al repositorio origen (si es remoto)
|
||||
- Feature flag `workspace_commands` habilitado
|
||||
|
||||
### Variables de entorno requeridas
|
||||
|
||||
```bash
|
||||
export GITEA_URL="https://gitea.example.com"
|
||||
export GITEA_TOKEN="tu-token-de-api"
|
||||
export DATAFORGE_REPOS="./workspaces" # opcional, default: ./workspaces
|
||||
```
|
||||
|
||||
### Modos de importación
|
||||
|
||||
#### Caso 1: Importar desde URL remota
|
||||
|
||||
```bash
|
||||
/workspace:import-repo
|
||||
# Fuente: https://github.com/Bl4cksmith/analytics-pipeline
|
||||
# Destino en Gitea: analytics-pipeline (por defecto)
|
||||
```
|
||||
|
||||
Flujo interno:
|
||||
1. Crear repo vacío en Gitea
|
||||
2. Clonar origen con `git clone --mirror` (preserva historial completo)
|
||||
3. Push a Gitea con `git push --mirror`
|
||||
4. Clonar en `workspaces/` para desarrollo local
|
||||
5. Registrar en BD SQLite
|
||||
6. Limpiar temporales
|
||||
|
||||
#### Caso 2: Adoptar repo local
|
||||
|
||||
```bash
|
||||
/workspace:import-repo
|
||||
# Fuente: legacy-tool (nombre del dir en workspaces/)
|
||||
# Destino en Gitea: legacy-tool (por defecto)
|
||||
```
|
||||
|
||||
Flujo interno:
|
||||
1. Verificar que `workspaces/legacy-tool/.git` existe
|
||||
2. Crear repo vacío en Gitea
|
||||
3. Añadir Gitea como remote `gitea`
|
||||
4. Push de todos los branches y tags a Gitea
|
||||
5. Registrar en BD SQLite
|
||||
|
||||
---
|
||||
|
||||
## Para Claude
|
||||
|
||||
### Flujo interactivo
|
||||
|
||||
#### Paso 1: Solicitar fuente
|
||||
|
||||
```
|
||||
Fuente del repositorio:
|
||||
- URL remota (ej: https://github.com/user/repo)
|
||||
- Nombre local en workspaces/ (ej: legacy-tool)
|
||||
|
||||
¿Fuente?
|
||||
```
|
||||
|
||||
Leer la respuesta del usuario.
|
||||
|
||||
#### Paso 2: Detectar modo y analizar
|
||||
|
||||
Usar funciones del core Go:
|
||||
- `core.ValidateImportSource(source)` — validar que es segura
|
||||
- `core.DetectImportMode(source)` — detectar si es remota o local
|
||||
- `core.GenerateDestinationName(source)` — sugerir nombre de destino
|
||||
|
||||
Si es remota, informar al usuario:
|
||||
```
|
||||
✓ Modo: importar desde URL remota
|
||||
Fuente: https://github.com/user/analytics-pipeline
|
||||
Historia Git: se preservará completa (--mirror)
|
||||
```
|
||||
|
||||
Si es local, informar:
|
||||
```
|
||||
✓ Modo: adoptar repositorio local
|
||||
Directorio: workspaces/legacy-tool
|
||||
Verificando .git...
|
||||
```
|
||||
|
||||
Si es local, verificar que existe `workspaces/{source}/.git`. Si no existe, informar error:
|
||||
```
|
||||
✗ No se encontró .git en workspaces/legacy-tool
|
||||
¿Quizás quisiste usar la URL completa?
|
||||
```
|
||||
|
||||
#### Paso 3: Solicitar nombre de destino
|
||||
|
||||
```
|
||||
Nombre en Gitea (Enter para usar '{nombre-generado}'):
|
||||
```
|
||||
|
||||
Si el usuario presiona Enter, usar el nombre generado. Si escribe otro, validar con `core.ValidateRepoName`.
|
||||
|
||||
#### Paso 4: Verificar que no existe en Gitea
|
||||
|
||||
Llamar `GET /api/v1/repos/{owner}/{destName}`. Si ya existe:
|
||||
```
|
||||
✗ El repositorio {owner}/{destName} ya existe en Gitea.
|
||||
|
||||
Opciones:
|
||||
1. Usar otro nombre
|
||||
2. Cancelar
|
||||
```
|
||||
|
||||
#### Paso 5: Solicitar opciones adicionales
|
||||
|
||||
```
|
||||
¿Repositorio privado? (s/N):
|
||||
Descripción (opcional):
|
||||
```
|
||||
|
||||
#### Paso 6: Mostrar resumen y confirmar
|
||||
|
||||
```
|
||||
Resumen de importación:
|
||||
Fuente: {source}
|
||||
Destino Gitea: {gitea_url}/{owner}/{destName}
|
||||
Workspace local: workspaces/{destName}
|
||||
Tipo detectado: {tipo}
|
||||
Historia Git: se preservará completa
|
||||
Privado: {sí/no}
|
||||
|
||||
¿Importar repositorio? (s/N):
|
||||
```
|
||||
|
||||
Si el usuario responde "n" o Enter, cancelar:
|
||||
```
|
||||
Importación cancelada.
|
||||
```
|
||||
|
||||
#### Paso 7: Ejecutar importación
|
||||
|
||||
Llamar `app.ImportRepositoryCommand(config, params)` con los parámetros recogidos.
|
||||
|
||||
Mostrar progreso:
|
||||
```
|
||||
Ejecutando importación...
|
||||
✓ Creando repositorio en Gitea...
|
||||
✓ Clonando fuente...
|
||||
✓ Subiendo contenido a Gitea...
|
||||
✓ Clonando en workspaces/...
|
||||
✓ Registrando en base de datos...
|
||||
✓ Limpiando temporales...
|
||||
```
|
||||
|
||||
O para modo adopción local:
|
||||
```
|
||||
Ejecutando adopción...
|
||||
✓ Creando repositorio en Gitea...
|
||||
✓ Añadiendo remote 'gitea'...
|
||||
✓ Subiendo branches y tags...
|
||||
✓ Registrando en base de datos...
|
||||
```
|
||||
|
||||
#### Paso 8: Mostrar resultado
|
||||
|
||||
**Éxito:**
|
||||
```
|
||||
Repositorio importado exitosamente.
|
||||
|
||||
Workspace: workspaces/{destName}
|
||||
Gitea URL: {gitea_url}/{owner}/{destName}
|
||||
Tipo: {tipo}
|
||||
|
||||
Pasos siguientes:
|
||||
cd workspaces/{destName}
|
||||
# El repositorio está listo para usar
|
||||
|
||||
# Para sincronizar en otros nodos:
|
||||
/workspace:sync-repos
|
||||
```
|
||||
|
||||
**Error:**
|
||||
```
|
||||
✗ Error durante la importación: {mensaje de error}
|
||||
|
||||
Rollback completado: no quedaron cambios parciales.
|
||||
|
||||
Sugerencias:
|
||||
- Verifica que tienes acceso al repositorio origen
|
||||
- Verifica que GITEA_TOKEN tiene permisos de escritura
|
||||
- Verifica la conectividad de red
|
||||
```
|
||||
|
||||
### Manejo de errores comunes
|
||||
|
||||
#### Repo ya existe en Gitea
|
||||
- Detectar en Paso 4, antes de iniciar la operación
|
||||
- Ofrecer cambiar el nombre
|
||||
|
||||
#### Credenciales inválidas / sin acceso
|
||||
- Ocurre al intentar clonar repo privado sin credenciales
|
||||
- Mensaje: "Acceso denegado al repositorio origen. Verifica que tienes acceso de lectura."
|
||||
|
||||
#### Tamaño excesivo
|
||||
- Si la operación tarda más de 10 minutos, el comando tiene timeout
|
||||
- Mensaje: "Timeout: el repositorio puede ser muy grande. Intenta clonar manualmente."
|
||||
|
||||
#### Fallo de red
|
||||
- Puede ocurrir durante clone o push
|
||||
- Rollback automático: se elimina el repo creado en Gitea
|
||||
|
||||
#### .git no encontrado (modo local)
|
||||
- Verificar antes de crear repo en Gitea
|
||||
- Mensaje: "No se encontró .git en workspaces/{source}"
|
||||
|
||||
### Integración con el sistema
|
||||
|
||||
Después de una importación exitosa:
|
||||
- El repo está en Gitea (fuente de verdad)
|
||||
- El repo está en `workspaces/{name}` (desarrollo local)
|
||||
- El repo está en la BD SQLite (consultable con `/workspace:list-repos`)
|
||||
- Compatible con `/workspace:sync-repos` para sincronizar en otros nodos
|
||||
|
||||
### Troubleshooting
|
||||
|
||||
**Error: "fuente inválida"**
|
||||
- La fuente contiene caracteres especiales (`;`, `|`, etc.)
|
||||
- Verificar que la URL es correcta
|
||||
|
||||
**Error: "nombre de destino inválido"**
|
||||
- El nombre contiene caracteres no permitidos
|
||||
- Solo letras, números, guiones y puntos. Entre 2-100 caracteres.
|
||||
|
||||
**Error: "rollback falló"**
|
||||
- Estado inconsistente: repo puede existir en Gitea pero no localmente
|
||||
- Verificar manualmente: `GET /api/v1/repos/{owner}/{name}`
|
||||
- Si existe en Gitea pero no localmente, ejecutar `/workspace:sync-repos`
|
||||
|
||||
## Convenciones
|
||||
|
||||
- Confirmación obligatoria antes de ejecutar
|
||||
- Rollback automático si falla cualquier paso
|
||||
- Historia Git siempre preservada (--mirror para remotos, --all + --tags para locales)
|
||||
- El remote `gitea` se añade sin sobrescribir `origin` existente
|
||||
- Feature flag `workspace_commands` debe estar habilitado
|
||||
|
||||
## Referencias
|
||||
|
||||
- Issue 0011e: Especificación completa
|
||||
- `app.ImportRepositoryCommand`: Implementación Go
|
||||
- `core.ValidateImportSource`, `core.DetectImportMode`: Validación pura
|
||||
- `shell.CloneRemoteToTemp`, `shell.AdoptLocalRepo`: Operaciones I/O
|
||||
- Gitea API: GET /api/v1/repos/{owner}/{repo}
|
||||
@@ -0,0 +1,170 @@
|
||||
---
|
||||
version: 1.0.0
|
||||
updated: 2026-03-12
|
||||
tags: [workspace, list, repos]
|
||||
---
|
||||
|
||||
# Command: list-repos
|
||||
|
||||
Lista todos los workspaces registrados en la base de datos local con soporte de filtro y ordenamiento.
|
||||
|
||||
## Para el usuario
|
||||
|
||||
### Cuándo usar este comando
|
||||
|
||||
Usar para inspeccionar rápidamente los workspaces disponibles:
|
||||
|
||||
- Ver qué repos existen localmente
|
||||
- Verificar estado de sincronización con Gitea
|
||||
- Encontrar un workspace por nombre o descripción
|
||||
- Ver URLs de Gitea para clonar en otra máquina
|
||||
|
||||
### Prerequisitos
|
||||
|
||||
- Issue 0007 completado (base de datos SQLite)
|
||||
- Feature flag `workspace_commands` habilitado
|
||||
|
||||
### Sintaxis
|
||||
|
||||
```bash
|
||||
/workspace:list-repos
|
||||
/workspace:list-repos --filter <term>
|
||||
/workspace:list-repos --sort <field>
|
||||
```
|
||||
|
||||
### Ejemplos
|
||||
|
||||
```bash
|
||||
# Listar todos los workspaces
|
||||
/workspace:list-repos
|
||||
|
||||
# Filtrar por nombre o descripción
|
||||
/workspace:list-repos --filter pipeline
|
||||
|
||||
# Ordenar por nombre alfabético
|
||||
/workspace:list-repos --sort name
|
||||
|
||||
# Filtrar y ordenar combinados
|
||||
/workspace:list-repos --filter etl --sort name
|
||||
```
|
||||
|
||||
### Salida esperada
|
||||
|
||||
```
|
||||
Workspaces locales (4 repos):
|
||||
|
||||
┌────────────────────────┬─────────────────────────────┬────────────────┬─────────────┐
|
||||
│ Nombre │ Descripción │ Última act. │ Estado │
|
||||
├────────────────────────┼─────────────────────────────┼────────────────┼─────────────┤
|
||||
│ my-etl-pipeline │ ETL para datos ventas │ hace 2 días │ ✓ activo │
|
||||
│ data-pipeline-v2 │ Pipeline v2 mejorado │ hace 1 sem. │ ✓ activo │
|
||||
│ ml-training-service │ Servicio ML training │ hace 3 días │ ✓ activo │
|
||||
│ old-experiment │ Experimento temporal │ hace 2 meses │ ✓ activo │
|
||||
└────────────────────────┴─────────────────────────────┴────────────────┴─────────────┘
|
||||
|
||||
URLs Gitea:
|
||||
- my-etl-pipeline: https://gitea-xxx.organic-machine.com/Bl4cksmith/my-etl-pipeline
|
||||
- data-pipeline-v2: https://gitea-xxx.organic-machine.com/Bl4cksmith/data-pipeline-v2
|
||||
- ml-training-service: https://gitea-xxx.organic-machine.com/Bl4cksmith/ml-training-service
|
||||
- old-experiment: (no disponible en Gitea)
|
||||
|
||||
Para trabajar en un workspace:
|
||||
cd workspaces/<nombre>
|
||||
```
|
||||
|
||||
## Para Claude
|
||||
|
||||
### Precondiciones
|
||||
|
||||
Verificar antes de ejecutar:
|
||||
- [ ] Feature flag `workspace_commands` habilitado en `dev/feature_flags.json`
|
||||
- [ ] Base de datos inicializada (issue 0007 completado)
|
||||
|
||||
### Flujo de implementación
|
||||
|
||||
#### Paso 0: Verificar feature flag
|
||||
|
||||
Leer `dev/feature_flags.json`. Si `workspace_commands.enabled: false`, informar al usuario:
|
||||
|
||||
```
|
||||
El comando /list-repos requiere que el feature flag 'workspace_commands' esté habilitado.
|
||||
Editar dev/feature_flags.json y cambiar "enabled": true para workspace_commands.
|
||||
```
|
||||
|
||||
#### Paso 1: Detectar argumentos
|
||||
|
||||
Parsear los argumentos del usuario:
|
||||
- `--filter <term>` → filtrar por nombre/descripción (case-insensitive)
|
||||
- `--sort <field>` → ordenar por campo (`name` o `date`, default: `date`)
|
||||
|
||||
#### Paso 2: Ejecutar vía `app.ListWorkspacesCommand`
|
||||
|
||||
El flujo completo está en `app/workspace_list.go`:
|
||||
|
||||
```go
|
||||
// Listar todos:
|
||||
err := app.ListWorkspacesCommand(config, "", "date")
|
||||
|
||||
// Filtrar:
|
||||
err := app.ListWorkspacesCommand(config, "pipeline", "date")
|
||||
|
||||
// Filtrar y ordenar:
|
||||
err := app.ListWorkspacesCommand(config, "etl", "name")
|
||||
```
|
||||
|
||||
El comando hace automáticamente:
|
||||
1. Obtener workspaces de SQLite (`shell.ListWorkspacesDB`)
|
||||
2. Filtrar si hay `--filter` (`core.FilterWorkspaces` — función pura)
|
||||
3. Ordenar (`core.SortWorkspaces` — función pura)
|
||||
4. Formatear tabla ASCII (`core.FormatWorkspaceTable` — función pura)
|
||||
5. Mostrar resultado con URLs de Gitea al final
|
||||
|
||||
#### Paso 3: Si no hay workspaces
|
||||
|
||||
Si la BD está vacía o el filtro no coincide con nada:
|
||||
|
||||
```
|
||||
No hay workspaces registrados.
|
||||
```
|
||||
|
||||
O si había filtro:
|
||||
```
|
||||
Workspaces que coinciden con 'pipeline':
|
||||
|
||||
No hay workspaces registrados.
|
||||
```
|
||||
|
||||
Sugerir usar `/workspace:create-repo` para crear el primero o `/workspace:sync-repos` para importar desde Gitea.
|
||||
|
||||
### Decisiones de diseño
|
||||
|
||||
1. **Tabla ASCII con box-drawing**: Legible en cualquier terminal sin dependencias adicionales.
|
||||
2. **Filtro case-insensitive**: Busca en slug, nombre y descripción.
|
||||
3. **Ordenamiento por fecha por defecto**: Los más recientes primero (más útil en el día a día).
|
||||
4. **URLs Gitea al final**: Separadas de la tabla para no saturar visualmente.
|
||||
5. **Nombres truncados a 22 chars**: Evita tablas muy anchas.
|
||||
|
||||
### Manejo de errores
|
||||
|
||||
| Error | Causa | Solución |
|
||||
|-------|-------|----------|
|
||||
| "error al inicializar base de datos" | BD no creada | Ejecutar `dataforge init` |
|
||||
| "error al listar workspaces" | Corrupción de BD | Ejecutar `dataforge workspace rebuild-db` |
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### No aparecen workspaces
|
||||
|
||||
La BD puede estar vacía. Opciones:
|
||||
1. Crear un workspace: `/workspace:create-repo`
|
||||
2. Importar desde Gitea: `/workspace:sync-repos`
|
||||
|
||||
### El filtro no encuentra nada
|
||||
|
||||
El filtro busca en slug, nombre y descripción (case-insensitive). Verificar:
|
||||
- El término está bien escrito
|
||||
- El workspace existe: ejecutar `/workspace:list-repos` sin filtro
|
||||
|
||||
### Tabla con formato incorrecto
|
||||
|
||||
Puede ocurrir si la terminal es muy estrecha. Usar una terminal de al menos 80 columnas de ancho.
|
||||
@@ -0,0 +1,234 @@
|
||||
---
|
||||
version: 1.0.0
|
||||
updated: 2026-03-12
|
||||
tags: [workspace, gitea, sync, repos]
|
||||
---
|
||||
|
||||
# Command: sync-repos
|
||||
|
||||
Sincroniza workspaces locales con repositorios en Gitea: detecta repos nuevos en Gitea que no existen localmente, clona repos faltantes, actualiza metadata en la BD local, y detecta inconsistencias (huérfanos).
|
||||
|
||||
## Para el usuario
|
||||
|
||||
### Cuándo usar este comando
|
||||
|
||||
Usar cuando necesites sincronizar tu estado local con Gitea:
|
||||
|
||||
- Después de clonar el parent repo en una máquina nueva
|
||||
- Cuando otro nodo creó un workspace en Gitea
|
||||
- Para verificar consistencia entre local y Gitea
|
||||
- Después de ejecutar `/workspace:create-repo` en otra máquina
|
||||
|
||||
### Prerequisitos
|
||||
|
||||
- Issue 0011b completado (comando /create-repo)
|
||||
- Variables de entorno configuradas: `GITEA_URL` y `GITEA_TOKEN`
|
||||
- Feature flag `workspace_commands` habilitado
|
||||
|
||||
### Variables de entorno requeridas
|
||||
|
||||
```bash
|
||||
export GITEA_URL="https://gitea.example.com"
|
||||
export GITEA_TOKEN="tu-token-de-api"
|
||||
export DATAFORGE_REPOS="./workspaces" # opcional, default: ./workspaces
|
||||
```
|
||||
|
||||
### Ejemplo de uso
|
||||
|
||||
```bash
|
||||
# Sincronización completa (con confirmación interactiva)
|
||||
/workspace:sync-repos
|
||||
|
||||
# Solo análisis (dry-run)
|
||||
/workspace:sync-repos --dry-run
|
||||
```
|
||||
|
||||
### Salida esperada
|
||||
|
||||
```
|
||||
Analizando workspaces locales...
|
||||
Consultando repositorios en Gitea...
|
||||
|
||||
Plan de sincronización:
|
||||
|
||||
Repos a clonar (2) — existen en Gitea pero no localmente:
|
||||
+ data-pipeline-v2 (https://gitea.example.com/Bl4cksmith/data-pipeline-v2.git)
|
||||
+ ml-training-service (https://gitea.example.com/Bl4cksmith/ml-training-service.git)
|
||||
|
||||
Repos a actualizar metadata (1):
|
||||
~ my-etl-pipeline
|
||||
|
||||
Repos locales no en Gitea — huérfanos (1):
|
||||
? old-experiment
|
||||
|
||||
Total: 2 a clonar, 1 a actualizar, 1 huérfanos detectados
|
||||
|
||||
¿Ejecutar sincronización? (s/n): s
|
||||
|
||||
Clonando data-pipeline-v2...
|
||||
✓ data-pipeline-v2 clonado
|
||||
Clonando ml-training-service...
|
||||
✓ ml-training-service clonado
|
||||
⚠ old-experiment no está en Gitea (mantener local)
|
||||
|
||||
Sincronización completada.
|
||||
Clonados: 2
|
||||
Actualizados: 1
|
||||
Huérfanos: 1
|
||||
|
||||
Workspaces activos: 4
|
||||
```
|
||||
|
||||
## Para Claude
|
||||
|
||||
### Precondiciones
|
||||
|
||||
Verificar antes de ejecutar:
|
||||
- [ ] Feature flag `workspace_commands` habilitado en `feature_flags.json`
|
||||
- [ ] Variables de entorno `GITEA_URL` y `GITEA_TOKEN` configuradas
|
||||
- [ ] Issue 0011b completado
|
||||
|
||||
### Flujo de implementación
|
||||
|
||||
#### Paso 0: Verificar feature flag
|
||||
|
||||
Leer `feature_flags.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"features": {
|
||||
"workspace_commands": {
|
||||
"enabled": true
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Si `enabled: false`, informar al usuario y detenerse.
|
||||
|
||||
#### Paso 1: Detectar modo de ejecución
|
||||
|
||||
Determinar si el usuario pasó `--dry-run`:
|
||||
|
||||
- Con `--dry-run`: ejecutar solo análisis, no hacer cambios
|
||||
- Sin `--dry-run`: mostrar plan y pedir confirmación antes de ejecutar
|
||||
|
||||
#### Paso 2: Ejecutar análisis vía `app.SyncWorkspacesCommand`
|
||||
|
||||
El flujo completo está implementado en `app/workspace_sync.go`:
|
||||
|
||||
```go
|
||||
// Modo dry-run (solo análisis):
|
||||
err := app.SyncWorkspacesCommand(config, true)
|
||||
|
||||
// Modo ejecución completa:
|
||||
err := app.SyncWorkspacesCommand(config, false)
|
||||
```
|
||||
|
||||
El comando hace automáticamente:
|
||||
1. Obtener workspaces locales desde SQLite
|
||||
2. Consultar repos en Gitea (organización o usuario)
|
||||
3. Comparar con `core.CompareWorkspaces()` (función pura)
|
||||
4. Mostrar plan con ToClone, ToUpdate, Orphans
|
||||
5. Si no dry-run: clonar repos faltantes y actualizar metadata
|
||||
|
||||
#### Paso 3: Confirmación interactiva (si no --dry-run)
|
||||
|
||||
Después de mostrar el plan:
|
||||
|
||||
```
|
||||
¿Ejecutar sincronización? (s/n):
|
||||
```
|
||||
|
||||
- Si `n`: cancelar sin cambios
|
||||
- Si `s`: ejecutar sincronización completa
|
||||
|
||||
#### Paso 4: Mostrar resultado final
|
||||
|
||||
Mostrar resumen con conteos de clonados, actualizados, huérfanos.
|
||||
|
||||
### Decisiones de diseño importantes
|
||||
|
||||
1. **Huérfanos NO se eliminan**: Si un repo local no está en Gitea, solo se reporta como huérfano. NO se elimina automáticamente (podría ser un repo en desarrollo que no está pusheado aún).
|
||||
|
||||
2. **Dry-run por defecto** en análisis: El plan siempre se muestra antes de ejecutar, y siempre requiere confirmación.
|
||||
|
||||
3. **Confirmación obligatoria**: El comando SIEMPRE muestra el plan y pide confirmación antes de clonar o actualizar.
|
||||
|
||||
4. **Clonación secuencial**: Los repos se clonan uno a uno (no en paralelo por simplicidad).
|
||||
|
||||
### Manejo de errores
|
||||
|
||||
| Error | Causa | Solución |
|
||||
|-------|-------|----------|
|
||||
| "GITEA_URL y GITEA_TOKEN son requeridos" | Variables de entorno no configuradas | `export GITEA_URL=... GITEA_TOKEN=...` |
|
||||
| "error al obtener repositorios de Gitea" | Token inválido o Gitea inaccesible | Verificar token y conectividad |
|
||||
| "Error clonando <repo>" | URL incorrecta o permisos insuficientes | Verificar credenciales git y permisos |
|
||||
| "error al abrir base de datos" | BD no inicializada | Ejecutar `dataforge` para inicializar BD |
|
||||
|
||||
### Feature flag
|
||||
|
||||
Este comando requiere que `workspace_commands` esté habilitado:
|
||||
|
||||
```json
|
||||
{
|
||||
"features": {
|
||||
"workspace_commands": {
|
||||
"enabled": true
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Si está deshabilitado, informar al usuario:
|
||||
|
||||
```
|
||||
El comando /sync-repos requiere que el feature flag 'workspace_commands' esté habilitado.
|
||||
Editar feature_flags.json y cambiar "enabled": true para workspace_commands.
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Error: "GITEA_URL y GITEA_TOKEN son requeridos"
|
||||
|
||||
```bash
|
||||
export GITEA_URL="https://gitea.example.com"
|
||||
export GITEA_TOKEN="tu-token"
|
||||
# Luego ejecutar de nuevo
|
||||
/workspace:sync-repos
|
||||
```
|
||||
|
||||
### Error: "error al obtener repositorios de Gitea"
|
||||
|
||||
Verificar:
|
||||
1. Gitea accesible: `curl -H "Authorization: token $GITEA_TOKEN" $GITEA_URL/api/v1/user`
|
||||
2. Token tiene permisos de lectura de repositorios
|
||||
|
||||
### Error: "Error clonando <repo>"
|
||||
|
||||
Posibles causas:
|
||||
- Credenciales git no configuradas para HTTPS
|
||||
- Repo privado sin permisos de acceso
|
||||
- URL de clone incorrecta
|
||||
|
||||
Solución:
|
||||
```bash
|
||||
# Verificar credenciales git
|
||||
git config --global credential.helper store
|
||||
|
||||
# Verificar acceso manual
|
||||
git clone <clone_url>
|
||||
```
|
||||
|
||||
### Los huérfanos no desaparecen
|
||||
|
||||
Los huérfanos son repos locales que no están en Gitea. Opciones:
|
||||
1. Si el repo fue eliminado de Gitea: ejecutar `dataforge workspace clean <slug>`
|
||||
2. Si el repo debe estar en Gitea: pushear el repo a Gitea primero
|
||||
|
||||
### La BD local está desactualizada
|
||||
|
||||
Usar `RebuildDBCommand` para reconstruir desde cero:
|
||||
```bash
|
||||
dataforge rebuild-db
|
||||
```
|
||||
Reference in New Issue
Block a user