Herramientas de usuario

Herramientas del sitio


clase:daw:daw-proyecto:tema03

Diferencias

Muestra las diferencias entre dos versiones de la página.

Enlace a la vista de comparación

Ambos lados, revisión anteriorRevisión previa
clase:daw:daw-proyecto:tema03 [2026/10/14 13:10] – «Fábrica» pasa a «arnés» / ingeniería de arneses (harness engineering, dicho en el tema 1); el día de trabajo en impersonal, sin Lorenzo; ejemplos con <alumno> claudeclase:daw:daw-proyecto:tema03 [2026/10/14 14:08] (actual) – Tema nuevo «2. IA y agentes»: los temas 2 a 7 pasan a ser 3 a 8, con sus referencias cruzadas renumeradas claude
Línea 1: Línea 1:
-====== 3. Docker en el proyecto ======+====== 3. Git en el proyecto ======
  
-Objetivo del tema: cada worktree tiene **su propia aplicación completa funcionando** (base de datos, backend y frontend), sin chocar con las de los demás worktrees. Al acabar, ''/git-flow start'' deja el worktree con sus puertos asignados y ''./app.sh start'' levanta la aplicación del issue con datos.+Objetivo del tema: que nadie vuelva a escribir a mano un comando de git del día a día. Al acabar, todo lo de git pasa por la skill ''/git-flow''.
  
-Lo que ya sabéis y aquí no se repite: imágenes y contenedores ([[clase:daw:daw:1eval:tema03|Despliegue 3]]), Dockerfile ([[clase:daw:daw:1eval:tema04|Despliegue 4]]), Docker Compose ([[clase:daw:daw:1eval:tema04.b|Despliegue 4.b]]) y redes y nginx-proxy ([[clase:daw:daw:1eval:tema04.c|Despliegue 4.c]]). Automatizar Maven y Node: [[clase:daw:proyecto:1eval:tema03|Proyecto 3]].+Lo que ya sabéis y aquí no se repite:
  
-===== Tres contenedores =====+  * Git básico, ''.gitignore'' y el formato de los mensajes de commit ''type(#issue):título'': [[clase:daw:daw:1eval:tema01#mensajes_de_commit|Despliegue 1]]. 
 +  * Ramas, los tipos de merge, conflictos y, sobre todo, **worktrees** y por qué uno por rama cuando se trabaja con agentes: [[clase:daw:daw:1eval:tema02#worktrees|Despliegue 2]]. 
 +  * Scrum y los tableros de GitHub: [[clase:daw:proyecto:1eval:tema04#scrum_en_github|Proyecto 4]].
  
-La aplicación son tres contenedores, uno por caja del diagrama C4 de contenedores:+===== Git en Windows =====
  
-^ Servicio ^ Imagen ^ Qué hace ^ +En Linux y Mac, git viene con bash. En Windows, el instalador de Git ([[https://gitforwindows.org/|Git for Windows]]) trae también **Git Bash**, un bash completo con los comandos de Linux (''ls'', ''cat'', ''grep''...). Hace falta porque el script de este proyecto (''app.sh'') es de bash, y así los mismos ficheros valen para todos. Dos pantallas del instalador importan:
-| ''db'' | ''mariadb'' | La base de datos. Solo accesible desde el backend | +
-| ''backend'' | Construida: Maven → JRE | El JAR de Spring Boot en el puerto 8080 | +
-| ''frontend'' | Construida: Angular → Nginx | Sirve el Angular compilado y hace de proxy de ''/api'' al backend |+
  
-El navegador solo habla con ''frontend''. Todo lo que empieza por ''/api'' lo reenvía Nginx al backend, así el frontend no necesita saber en qué puerto está el backend y no hay problemas de CORS.+{{:clase:daw:daw-proyecto:git-setup-bash.png?500|}}
  
-Los dos Dockerfile son **multi-stage** y los escribe la IA. Lo único que hay que vigilar:+Elegir **«Use Git and optional Unix tools from the Command Prompt»**: git y los comandos de Linux quedan disponibles desde cualquier terminal (Git Bash, ''cmd'' y PowerShell), que es lo que necesita el agente para ejecutar ''./app.sh start'' o un ''ls''. El aviso de que tapa el ''find'' y el ''sort'' de Windows no afecta a nada de lo que se hace aquí.
  
-<sxh bash;title:backend/Dockerfile> +{{:clase:daw:daw-proyecto:git-setup-pull.png?500|}}
-FROM maven:3.9-eclipse-temurin-25 AS build +
-WORKDIR /app +
-COPY pom.xml . +
-RUN --mount=type=cache,target=/root/.m2 mvn -q dependency:go-offline +
-COPY src ./src +
-RUN --mount=type=cache,target=/root/.m2 mvn -q -DskipTests package+
  
-FROM eclipse-temurin:25-jre +Elegir **«Only ever fast-forward»**. En este módulo nunca se hace ''git pull'' (abajo, en las reglas para la IA), pero si alguien lo hace por costumbre, con esta opción el ''pull'' falla cuando las historias han divergido en vez de crear un commit de merge sin avisar. Es lo mismo que hace la skill con ''git merge --ff-only''. En Linux y Mac se consigue con ''git config --global pull.ff only''.
-RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/* +
-COPY --from=build /app/target/*.jar /app.jar +
-ENTRYPOINT ["java", "-jar", "/app.jar"] +
-</sxh>+
  
-El ''curl'' es para el ''healthcheck'' de abajo. El ''--mount=type=cache'' sobre ''.m2'' es lo que hace que reconstruir la imagen tarde segundos y no minutos: sin él, cada ''build'' vuelve a descargar todas las dependencias.+===== GitHub CLI: gh =====
  
-<sxh bash;title:frontend/Dockerfile> +''gh'' es la línea de comandos de GitHub. Es lo que usa la skill ''/git-flow'' para leer el issue (''gh issue view N'') y lo que se usa en el tema 7 para mirar los workflows, así que tiene que estar instalado y con sesión iniciada en el equipo de cada uno.
-FROM node:22 AS build +
-WORKDIR /app +
-COPY package*.json . +
-RUN npm ci +
-COPY . . +
-RUN npx ng build --configuration production+
  
-FROM nginx:alpine +  * **Windows**: ''winget install GitHub.cli'' y cerrar y abrir la terminal. 
-COPY nginx.conf /etc/nginx/conf.d/default.conf +  * **Linux (Ubuntu, Debian, Mint)**: ''sudo apt install gh''. 
-COPY --from=build /app/dist/frontend/browser /usr/share/nginx/html+  * **Mac**: ''brew install gh''. 
 + 
 +Después, iniciar sesión una vez, eligiendo //GitHub.com//, //HTTPS// y autenticar en el navegador: 
 + 
 +<sxh bash> 
 +gh auth login 
 +gh auth status      # tiene que decir «Logged in to github.com»
 </sxh> </sxh>
  
-<sxh plain;title:frontend/nginx.conf> +Con eso ''gh'' guarda también la credencial de git para HTTPS: el ''push'' deja de pedir usuario y contraseña, sin configurar nada más.
-server { +
-    listen 80; +
-    root /usr/share/nginx/html;+
  
-    location /api/ { +===== El repositorio y el agente =====
-        proxy_pass http://backend:8080/api/; +
-    }+
  
-    location / { +Antes de la primera skill hay que dejar el repositorio del tema 1 preparado para trabajar con el agente: su estructura, sus dos ramas, las instrucciones que lee el agente al empezar y los permisos que tiene.
-        try_files $uri $uri/ /index.html; +
-    } +
-} +
-</sxh>+
  
-El ''try_files'' es obligatorio en una aplicación Angular: sin él, recargar el navegador en ''/clientes/12'' da un 404 de Nginx.+==== El repositorio del proyecto ====
  
-===== Producción y desarrollo con los mismos ficheros =====+Un único repositorio en GitHub, creado desde cero por cada alumno, con el backend y el frontend dentro:
  
-Docker Compose carga ''compose.yaml'' y, si existe, ''compose.override.yaml'' encima. Eso se usa así:+<code> 
 +mi-proyecto/ 
 +├── backend/                 <- Spring Boot (Maven) 
 +├── frontend/                <- Angular 
 +├── compose.yaml             <- la aplicación en Docker (tema 4) 
 +├── compose.override.yaml    <- lo que cambia en desarrollo (tema 4) 
 +├── app.sh                   <- start, stop, status y logs de la aplicación (tema 4) 
 +├── .kiro/                   <- la spec de cada issue y el steering (tema 5) 
 +├── docs/                    <- detalle para el agente, si hace falta 
 +├── AGENTS.md                <- instrucciones para el agente 
 +├── CLAUDE.md                <- solo contiene @AGENTS.md 
 +├── .claude/ 
 +│   ├── skills/              <- las skills del proyecto 
 +│   └── settings.json        <- permisos del agente, compartidos 
 +└── .github/workflows/       <- CI y despliegue (tema 7) 
 +</code> 
 + 
 +La estructura interna de ''backend/'' y ''frontend/'' es la de [[clase:daw:proyecto:1eval:tema02#estructura_de_carpetas|Proyecto 2]].
  
-  * ''compose.yaml'' **es producción**: imágenes construidas, la base de datos en una red interna sin puertos publicados, el frontend en la red de nginx-proxy con su ''VIRTUAL_HOST''. +<note important>El esqueleto del backend nace **sin JPA ni Flyway**. Spring Boot con JPA no arranca si no encuentra una base de datos, y la base de datos llega con Docker en el tema 4. Hasta entonces, el backend es el ''@RestController'' del tema 1 que responde a ''/api/hola''.</note>
-  * ''compose.override.yaml'' **cambia lo de desarrollo**. Está en el repositorio y Compose lo carga solo. En el servidor, el workflow de despliegue (tema 6) fija ''COMPOSE_FILE=compose.yaml'' y el override no se aplica.+
  
-<sxh yaml;title:compose.yaml> +=== Las dos ramas ===
-services: +
-  db: +
-    image: mariadb:11 +
-    environment: +
-      MARIADB_DATABASE: proyecto +
-      MARIADB_USER: proyecto +
-      MARIADB_PASSWORD: ${DB_PASSWORD} +
-      MARIADB_ROOT_PASSWORD: ${DB_PASSWORD} +
-    volumes: +
-      - db-data:/var/lib/mysql +
-    networks: +
-      - interna +
-    healthcheck: +
-      test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"] +
-      interval: 5s +
-      retries: 20+
  
-  backend: +El proyecto tiene solo dos ramas oficiales:
-    build: ./backend +
-    environment: +
-      SPRING_DATASOURCE_URL: jdbc:mariadb://db:3306/proyecto +
-      SPRING_DATASOURCE_USERNAME: proyecto +
-      SPRING_DATASOURCE_PASSWORD: ${DB_PASSWORD} +
-    depends_on: +
-      db: +
-        condition: service_healthy +
-    networks: +
-      - interna +
-    healthcheck: +
-      test: ["CMD", "curl", "-f", "http://localhost:8080/api/hola"] +
-      interval: 5s +
-      retries: 20+
  
-  frontend: +  * ''develop'': donde se integra el trabajo. Es la rama **por defecto** del repositorio en GitHub, y hace falta que lo sea: GitHub solo cierra un issue desde un commit cuando ese commit llega a la rama por defecto (ver abajo). 
-    build: ./frontend +  * ''main'': lo que hay en producción. Nunca se trabaja en ella; solo recibe lo que ya está en ''develop'' (tema 7).
-    environment: +
-      VIRTUAL_HOST: <alumno>.proyectosdaw.cip.fpmislata.com +
-    depends_on: +
-      backend: +
-        condition: service_healthy +
-    networks: +
-      - interna +
-      - proxy+
  
-volumes: +Se crean las dos en el primer commit y se cambia la rama por defecto en GitHub (//Settings → General → Default branch//) o con ''gh'':
-  db-data:+
  
-networks: +<sxh bash> 
-  interna: +git switch -c develop 
-  proxy: +git push -u origin develop 
-    external: true +gh repo edit --default-branch develop
-    name: nginx-proxy-network+
 </sxh> </sxh>
  
-Los ''healthcheck'' con ''depends_on: condition: service_healthy'' hacen que el backend no arranque hasta que MariaDB acepte conexiones, y el frontend hasta que el backend responda. Sin eso, el backend muere al arrancar porque la base de datos aún no está lista.+==== Instrucciones para el agente: AGENTS.md ====
  
-<sxh yaml;title:compose.override.yaml> +''AGENTS.md'' es el fichero que el agente lee al empezar cada conversación (qué es y cómo se escribe: [[cursos:sdd:03-opencode#agentsmdlas_instrucciones_fijas_del_proyecto|AGENTS.md en OpenCode]]). Claude Code lee ''CLAUDE.md'', así que ''CLAUDE.md'' contiene una sola línea que importa al otro:
-services: +
-  db: +
-    ports: +
-      - "${DB_PORT:-3306}:3306"+
  
-  backend: +<sxh plain;title:CLAUDE.md> 
-    ports: +@AGENTS.md
-      - "${BACKEND_PORT:-8080}:8080" +
-    environment: +
-      SPRING_PROFILES_ACTIVE: ${PERFIL:-datos-prueba} +
- +
-  frontend: +
-    build: +
-      target: build +
-    command: npx ng serve --host 0.0.0.0 --proxy-config proxy.conf.json +
-    volumes: +
-      - ./frontend:/app +
-      - /app/node_modules +
-    ports: +
-      - "${FRONTEND_PORT:-4200}:4200"+
 </sxh> </sxh>
  
-Lo que cambia en desarrollo:+Así los dos agentes leen exactamente lo mismo y solo hay un fichero que mantener.
  
-  * **Se publican los puertos**, para entrar desde el navegador y para que Maven y Playwright lleguen a la base de datos y al frontend desde el host. +Lo que va dentro, y nada más:
-  * **El frontend pasa a ''ng serve''** con la carpeta local ''frontend/'' montada dentro: al guardar un fichero, el navegador se recarga. ''proxy.conf.json'' manda ''/api'' a ''%%http://backend:8080%%''. El volumen anónimo sobre ''node_modules'' evita que los módulos instalados en la imagen se tapen con los del host. +
-  * **El backend no tiene recarga en caliente**: cada cambio en Java es ''docker compose up --build backend''. Con la caché de Maven son unos segundos. Y no pasa nada. +
-  * **Datos de prueba** activados (abajo).+
  
-<note important>La red ''nginx-proxy-network'' es externa: en el servidor la crea nginx-proxy, pero en el portátil hay que crearla una vez, ''docker network create nginx-proxy-network'', o Compose se niega a arrancar. ''app.sh start'' lo hace si no existe.</note>+  * **Qué es el proyecto**, en dos líneas. 
 +  * **Cómo se compila, arranca y prueba**: los comandos exactos. En el tema 4 se añade dónde corren Maven y Playwright. 
 +  * **Reglas del proyecto** que el agente tiene que respetar siempre: las ramas, que git se maneja con ''/git-flow'', que ''.kiro/specs/'' es histórico (tema 5).
  
-===== Un compose por worktree =====+<note tip>Corto. ''AGENTS.md'' viaja en **todas** las peticiones, así que cada línea de relleno se paga en cada prompt. Lo que sea largo (una explicación de la arquitectura, un procedimiento) va en ''docs/'' y ''AGENTS.md'' dice cuándo leerlo. Es la **divulgación progresiva**: el agente carga el detalle solo cuando lo necesita.</note>
  
-Dos worktrees con el mismo ''compose.yaml'' crearían contenedores con el mismo nombre y pedirían los mismos puertos. Lo resuelve un fichero ''.env'' **en cada worktree**, ignorado por git, que Compose lee solo:+==== Skills en este proyecto ====
  
-<sxh bash;title:.env> +Qué es una skill y cómo se escribe está en el [[.:tema02#skills|tema 2]]. La primera skill del proyecto, ''/git-flow'', se escribe en este tema; lo que hay que saber además para este proyecto: 
-COMPOSE_PROJECT_NAME=mi-proyecto-12-alta-clientes + 
-DB_PORT=33112 +  * **Dónde van**: en ''.claude/skills/<nombre>/SKILL.md''. OpenCode también lee esa carpeta, así que hay **una sola copia** para los dos agentes. 
-BACKEND_PORT=8112 +  * **Dos tipos**. Las **de tarea** se invocan escribiendo ''/nombre'' y hacen algo (''/git-flow'', ''/test''). Las **de conocimiento** no se invocan: el agente las carga solo cuando la tarea encaja con su ''description'' (''convenciones-backend''). Las dos se escriben igual; la diferencia está en la descripción y en que las de tarea tienen un procedimiento. 
-FRONTEND_PORT=4212 +  * **Un subcomando por llamada**: todas las skills de tarea del proyecto reciben familia, sustantivo y verbo, como en el tema 1 (''/git-flow start 12'', ''/test e2e run''). Dentro de ''SKILL.md'' los argumentos llegan en ''$ARGUMENTS'' y la skill hace una cosa u otra según la primera palabra. 
-DB_PASSWORD=dev+  * **''allowed-tools''**: en la cabecera de la skill, la lista blanca de lo que puede ejecutar. Una skill de git que solo puede lanzar ''git'' y ''gh'' no puede borrar una carpeta por error aunque se confunda: 
 + 
 +<sxh yaml> 
 +--- 
 +name: estado 
 +description: Resume el estado del proyecto. Usar cuando se pida qué hay pendiente, cómo va el repositorio o al empezar a trabajar. 
 +allowed-tools: Bash(git status:*), Bash(git worktree list:*), Bash(gh issue list:*) 
 +---
 </sxh> </sxh>
  
-  * ''COMPOSE_PROJECT_NAME'' es el prefijo de contenedores, redes y volúmenes de ese worktree. Dos proyectos distintos no se tocan. +''allowed-tools'' lo entiende Claude Code; OpenCode lo ignora y aplica sus propios permisos (abajo).
-  * Los puertos los busca ''/git-flow start'' al crear el worktree: prueba a partir de una base y se queda con los libres. +
-  * Las variables llevan valor por defecto, ''${BACKEND_PORT:-8080}'', para cuando no hay ''.env'': en CI (tema 6).+
  
-''/git-flow finish'' termina con ''docker compose down -v'': se van los contenedores **y el volumen** de la base de datos de ese worktree. No queda nada.+==== Permisos del agente: lo básico ====
  
-===== La primera tabla =====+El agente pide permiso antes de ejecutar comandos o escribir ficheros. Aceptar uno a uno no es sostenible, y aceptar todo a ciegas tampoco. Lo justo para trabajar:
  
-Hasta ahora el backend no tiene base de datos. Se añaden ahora, con un prompt directo al agente (el flujo de trabajo de verdad llega en el tema 4; esto es solo para tener algo que ver):+  * **Claude Code**: ''/permissions'' muestra lo permitido. Lo que se permita para el proyecto se guarda en ''.claude/settings.json'', que va al repositorio, para que valga en todos los worktrees:
  
-  * Dependencias ''spring-boot-starter-data-jpa'', ''mariadb-java-client'' y ''flyway-mysql''. +<sxh json;title:.claude/settings.json> 
-  * Una migración ''%%backend/src/main/resources/db/migration/V1__clientes.sql%%'' con una tabla. +{ 
-  * Un ''GET /api/clientes'' que la lista y una página de Angular que la muestra.+  "permissions": { 
 +    "allow": [ 
 +      "Bash(git status:*)", "Bash(git diff:*)", "Bash(git log:*)", 
 +      "Bash(gh issue list:*)", "Bash(gh issue view:*)", 
 +      "Bash(mvn:*)", "Bash(npm:*)", "Bash(npx:*)", 
 +      "Bash(docker compose:*)" 
 +    ], 
 +    "deny": ["Bash(git push --force:*)", "Bash(rm -rf:*)"] 
 +  } 
 +} 
 +</sxh>
  
-El esquema **siempre** va en migraciones de Flyway, ''%%V<n>__descripcion.sql%%'', y nunca con ''ddl-auto''. Así cada worktree, cada runner de CI y el servidor construyen la misma base de datos desde cero.+  * **OpenCode**: en ''opencode.json'' en la raíz del proyecto, la clave ''permission''.
  
-===== Datos de prueba =====+En el tema 5 se completa la lista con lo que haga falta para programar.
  
-Cada worktree arranca con la base de datos vacía: no hay nada que ver en la aplicación ni se pueden escribir tests E2E que dependan de que exista un cliente. Los datos de prueba se separan del esquema: 
  
-<code> +===== Issues y tablero ===== 
-backend/src/main/resources/db/ + 
-├── migration/            <- esquema: V1__clientes.sql, V2__pedidos.sql ... +Todo trabajo empieza por un **issue**. El issue es la unidad: de él sale la rama, su número va en los commits y al fusionar se cierra solo. 
-└── datos-prueba/         <- datos: R__clientes.sql, R__pedidos.sql ... + 
-</code>+El tablero es un **GitHub Project** del repositorio con tres columnas: //Todo//, //In progress// y //Done//. De sus automatizaciones (//Workflows// del proyecto) solo hace falta una, que viene activada de serie: **issue cerrado → Done**. El resto se hace a mano: al crear el issue se añade al tablero, y al empezar a trabajarlo se arrastra a //In progress//. Automatizar eso por línea de comandos no compensa. 
 + 
 +Etiquetas: ''feature'' y ''bug''. No hacen falta más.
  
-Los de ''datos-prueba/'' son migraciones **repetibles** de Flyway (''%%R__%%''): se ejecutan después de todas las versionadas, y como la base de datos de un worktree siempre nace vacía, se ejecutan una vez. Flyway solo mira esa carpeta cuando está activo el perfil ''datos-prueba'':+Los issues se crean desde la web o con ''gh'':
  
-<sxh plain;title:application-datos-prueba.properties> +<sxh bash> 
-spring.flyway.locations=classpath:db/migration,classpath:db/datos-prueba+gh issue create --title "Alta de clientes" --label feature --body "Formulario de alta con nombre, NIF y email." 
 +gh issue list 
 +gh issue view 12
 </sxh> </sxh>
  
-  * En desarrollo el perfil lo activa ''compose.override.yaml''. Para probar la aplicación vacía: ''PERFIL=default docker compose up -d --build backend''. +===== Las ramas de este proyecto =====
-  * En producción ''compose.yaml'' no activa el perfil: los datos de prueba **nunca llegan al servidor**. +
-  * Para volver al estado inicial (antes de pasar los E2E, por ejemplo): ''docker compose down -v && docker compose up -d''.+
  
-Cada entidad nueva trae su ''%%R__<entidad>.sql%%'' con unos cuantos registros; el steering de cc-sdd (tema 5) obliga a que cada CRUD lo traiga.+El modelo de ramas de Despliegue 2 tiene ''develop'', ''release'' y ''master''. Aquí, al ser un proyecto individual con un solo entorno, se recorta a dos:
  
-===== El script app.sh =====+  * ''develop'': la integración. Solo existe de verdad **en local**, en la carpeta principal del repositorio; en GitHub está porque es la rama por defecto y es donde se cierran los issues. Nunca se despliega. 
 +  * ''main'': producción. Cada push a ''main'' despliega en el servidor (tema 7). Nunca se trabaja en ella.
  
-Operar la aplicación es siempre lo mismo y no necesita ninguna inteligencia, así que no es una skill: es un script de bash en la raíz del repositorio, con cuatro subcomandos y la gramática del tema 1. Bash está en los tres sistemas (en Windows lo trae Git, tema 2). Y es el mismo script que se usa en el servidor (tema 6): no hay una forma de operar en local y otra en producción.+Y por cada issue, una **rama de trabajo** en su propio worktree:
  
-<sxh bash;title:app.sh> +^ Cosa ^ Nombre ^ Ejemplo ^ 
-#!/bin/bash +| Rama | ''<alumno>-<N>-<slug>'' | ''<alumno>-12-alta-clientes'' | 
-# La aplicación de este worktree: start | stop [-v] | status | logs +| Worktree | ''../<repo>-wt-<N>-<slug>'' | ''../mi-proyecto-wt-12-alta-clientes'' |
-set -e +
-cd "$(dirname "$0")" +
-case "$1" in +
-  start) +
-    docker network inspect nginx-proxy-network >/dev/null 2>&1 || docker network create nginx-proxy-network +
-    docker compose up -d --build --wait +
-    [ -f .env ] && . ./.env +
-    echo "Aplicación en http://localhost:${FRONTEND_PORT:-4200}" ;; +
-  stop)   shift; docker compose down "$@" ;; +
-  status) docker compose ps ;; +
-  logs)   docker compose logs --tail=200 ;; +
-  *)      echo "uso: $0 start | stop [-v] | status | logs"; exit 1 ;; +
-esac +
-</sxh>+
  
-  * ''./app.sh start'' vale para todo: la primera vez, después de cambiar el backend (reconstruye la imagen y la vuelve a levantar) y al día siguiente tras un ''stop''. ''--wait'' espera a los ''healthcheck'', así que cuando escribe la URL la aplicación ya responde. +El número del issue en el nombre de la rama hace que la skill sepa siempre a qué issue pertenece, sin preguntarlo.
-  * ''./app.sh stop'' para los contenedores y conserva la base de datos. ''./app.sh stop -v'' la borra también: es lo que hace ''/git-flow finish'' al cerrar el issue, y lo que se hace para volver a los datos de prueba iniciales. +
-  * ''./app.sh status'' y ''./app.sh logs'' son lo primero que se mira cuando algo no responde. +
-  * Trabaja sobre la carpeta en la que está y lee su ''.env'', así que cada worktree opera el suyo. En el servidor no hay ''.env'': valen los valores por defecto.+
  
-<note>Los scripts se guardan con finales de línea de Linux o bash en Windows no los entiende. Una línea en ''.gitattributes'' lo garantiza para todos: ''*.sh text eol=lf''.</note>+**Sin pull requests.** En un proyecto individual nadie va a revisar el PR, así que es un trámite que no aporta nada: la rama se fusiona en ''develop'' **en local** y se hace push. La revisión del código la hará la IA antes de fusionar (tema 6).
  
-===== Dónde corren Maven y los tests =====+===== El ciclo de una rama =====
  
-Las imágenes son de **ejecución**: el backend es un JRE sin Maven y el frontend es Nginx. Así que compilar y pasar tests se hace **en el host**, que para eso están instalados el JDK, Maven y Node:+  - **Iniciar**: desde la carpeta principal (siempre en ''develop'', actualizada), crear la rama y su worktree. 
 +  - **Trabajar** en el worktree. Commits pequeños con ''type(#N):título''. 
 +  - **Actualizar**: si ''develop'' ha avanzado (otro issue que se ha terminado antes), traer ''develop'' a la rama con ''git merge develop''. Los conflictos se resuelven en la rama, nunca en ''develop''. 
 +  - **Finalizar**: fusionar en ''develop'' con ''--squash'': todos los commits de la rama se funden en uno con el mensaje ''feat(#N): título''. Push de ''develop''. Borrar el worktree y la rama. 
 +  - **Publicar**: cuando hay algo que sacar, ''develop'' → ''main'' con ''--ff-only'' y push.
  
-^ Qué ^ Dónde ^ Contra qué ^ +Por qué ''--squash'' hacia ''develop'' y ''--ff-only'' hacia ''main'' está en [[clase:daw:daw:1eval:tema02#usos_de_merge|Usos de merge]].
-| ''mvn test'' (unitarios y ArchUnit) | ''backend/'' del worktree | Nada: no necesitan la aplicación levantada | +
-| ''npm run test:api'' (API) | ''frontend/'' del worktree | ''%%http://localhost:$FRONTEND_PORT/api%%'' | +
-| ''npm run test:e2e'' (E2E) | ''frontend/'' del worktree | ''%%http://localhost:$FRONTEND_PORT%%'' |+
  
-Son los tres comandos de tests del proyecto. Maven y npm funcionan igual en Linux, Windows y Mac, y son lo que ejecutan el alumno, el agente y CI (tema 6). ''mvn test'' forma parte del build y corre en cada compilación. Los de API y los E2E **no forman parte del build**: se lanzan aparte, cuando se quiere, con los otros dos comandos. Los dos son de Playwright y atacan la aplicación levantada del worktree (tema 5). En el tema 5 la skill ''/test'' los envuelve con la gramática del tema 1 (''/test api run'') y explica lo que sale en rojo; CI los ejecuta tal cual.+==== El commit que cierra el issue ====
  
-==== npm run ====+GitHub cierra un issue automáticamente cuando llega a la **rama por defecto** un commit cuyo mensaje contiene ''Closes #N''. Por eso ''develop'' es la rama por defecto, y por eso el commit de la fusión lleva el ''Closes'' en el cuerpo:
  
-''npm run <nombre>'' ejecuta un comando guardado en la sección ''scripts'' del ''package.json''. Es la forma de tener en el proyecto los comandos largos con un nombre corto, sin depender del sistema operativo: npm los ejecuta desde la carpeta del ''package.json'' y con los programas de ''node_modules/.bin'' en el PATH, así que ''playwright'' se escribe sin ''npx''.+<code> 
 +feat(#12): Alta de clientes
  
-<sxh json;title:frontend/package.json> +Closes #12 
-"scripts": { +</code> 
-  "start": "ng serve", + 
-  "build": "ng build", +Al hacer push de ''develop'', el issue 12 se cierra y el tablero lo pasa a //Done//. No hay que tocar nada en la web. 
-  "test:api": "docker compose down -v && docker compose up -d --wait && playwright test --project=api", + 
-  "test:e2e": "docker compose down -v && docker compose up -d --wait && playwright test --project=e2e" +===== Reglas para que la IA toque git ===== 
-} + 
-</sxh>+Una skill que ejecuta git puede hacer daño si improvisa. Estas reglas van en la skill y no se negocian: 
 + 
 +  * **Nunca ''git pull''**. Siempre ''git fetch --prune'' y ''git merge --ff-only origin/<rama>''. Así, si las historias han divergido, falla en vez de hacer un merge sorpresa. 
 +  * **Nunca ''--force''**, ni ''--no-verify'', ni ''stash'', ni borrar worktrees con ''--force''. 
 +  * **Nunca resolver un conflicto**. Si un merge da conflicto, la skill **se para**, enseña ''git status'' y dice qué comandos tiene que ejecutar el alumno. Resolver conflictos es trabajo humano. 
 +  * **La carpeta principal siempre en ''develop''** y limpia. Si no lo está, la skill se para antes de hacer nada. 
 +  * **Una operación por invocación**: ''/git-flow start 12'', no «start y luego finish». 
 +  * **''allowed-tools'' como lista blanca**: solo ''git'' y ''gh''. Nada de ''rm'', nada de escribir ficheros del proyecto. 
 + 
 +===== La skill /git-flow ===== 
 + 
 +Se parte de la skill que ya enlaza Despliegue 2, {{ :clase:daw:daw:1eval:git-flow-skill.md |git-flow-skill.md}}, que es casi lo que hace falta. Se adapta con estos cambios: 
 + 
 +  * Quitar ''release'', ''bajar'' y ''subir'' y poner las operaciones en inglés, con la gramática del tema 1. Quedan **cinco**: 
 + 
 +^ Operación ^ Qué hace ^ 
 +| ''start N'' | Lee el issue ''N'' con ''gh issue view'', deriva el slug del título, crea la rama ''<alumno>-<N>-<slug>'' desde ''develop'' actualizada y su worktree hermano, y hace push de la rama | 
 +| ''commit'' | Mira ''git status'' y ''git diff'' del worktree actual y **agrupa los cambios por intención**, no por fichero: un commit por cosa hecha, cada uno con ''type(#N):título''. El ''N'' lo saca del nombre de la rama. Enseña los commits que va a hacer antes de hacerlos | 
 +| ''update'' | Trae ''develop'' a la rama del worktree actual | 
 +| ''finish'' | ''update'', ''--squash'' en ''develop'' con el mensaje ''feat(#N): <título del issue>'' y ''Closes #N'' en el cuerpo, push de ''develop'', borrar rama remota, worktree y rama local | 
 +| ''publish'' | ''develop'' → ''main'' con ''--ff-only'' y push de ''main'' | 
 + 
 +  * El nombre del alumno para las ramas se lee de ''AGENTS.md'' (una línea: «las ramas empiezan por ''<alumno>-''»). 
 +  * Añadir ''Bash(gh issue view:*)'' a ''allowed-tools''. 
 +  * ''finish'' y ''update'' deducen la rama del worktree en el que se ejecutan, como ya hace la skill original.
  
-Los dos scripts resetean la base de datos del worktree, esperan a que la aplicación esté sana y pasan los tests de un proyecto de Playwright (''api'' o ''e2e'', tema 5). El ''&&'' lo entienden igual ''cmd'' y bash. ''npm run'' sin nombre lista los scripts que hay. ''start'' y ''build'' ya los trae Angular: son los que ejecutan ''npm start'' y ''npm run build''.+En el tema 4 ''start'' crece para escribir el ''.env'' con los puertos del worktree y ''finish'' para parar los contenedores, y en el tema 6 ''finish'' comprueba antes de fusionar que las tareas, la revisión y los tests están en orden.
  
-Esto va en ''AGENTS.md'' tal cual, porque es lo que el agente ejecuta para comprobar su trabajo. Si no lo sabe, intentará ''mvn test'' dentro del contenedor y fallará, o ni probará.+<note>''AGENTS.md'' tiene que decir desde ahora: «git se maneja solo con ''/git-flow''; no ejecutes comandos de git que cambien el repositorio fuera de esa skill». Si no, el agente hará ''git commit'' por su cuenta a mitad de una tarea.</note>
  
 ===== Ejercicios ===== ===== Ejercicios =====
 +Todos los ejercicios se hacen en el repositorio del tema 1. Desde el ejercicio 5, ningún comando de git se escribe a mano.
  
 ==== Ejercicio 1 ==== ==== Ejercicio 1 ====
-  * Crea un issue «Aplicación en Docker» e inícialo con ''/git-flow''. +  * Instala ''gh'', inicia sesión y comprueba con ''gh auth status'' y ''gh repo view'' sobre tu repositorio que funciona. 
-  * Pide al agente los dos ''Dockerfile'', ''compose.yaml'', ''compose.override.yaml'', ''frontend/nginx.conf'' y ''frontend/proxy.conf.json'' siguiendo este tema. Revisa que el backend tiene la caché de ''.m2'', que ''db'' no publica puertos en ''compose.yaml'' y que ''nginx.conf'' tiene el ''try_files''. +  * Escribe ''AGENTS.md'' con: qué es el proyecto, cómo se arranca cada parte y cómo se compila. 
-  * Añade ''.env'' al ''.gitignore'' y crea uno a mano en el worktree con ''COMPOSE_PROJECT_NAME'', los tres puertos y ''DB_PASSWORD''. +  * Crea ''CLAUDE.md'' con una sola línea: ''@AGENTS.md''. 
-  * ''docker network create nginx-proxy-network'' y ''docker compose up -d --build''. Comprueba con ''docker compose ps'' que los tres servicios están //healthy//. +  * Abre una conversación nueva en Claude Code y otra en OpenCode y pregunta al agente cómo se arranca el backend. Tiene que responder con lo que has escrito.
-  * Entra en ''%%http://localhost:<FRONTEND_PORT>%%'' y comprueba que se ve el ''/api/hola''.+
  
 ==== Ejercicio 2 ==== ==== Ejercicio 2 ====
-  * En el mismo worktree, pide al agente que añada JPA, MariaDB y Flyway, la migración ''%%V1__clientes.sql%%'', un ''GET /api/clientes'' y una página de Angular que liste los clientes. +  * Crea la rama ''develop'' desde ''main'' y súbela. 
-  * Crea ''application-datos-prueba.properties'' y ''db/datos-prueba/%%R__clientes.sql%%'' con cinco clientes. +  * Ponla como rama por defecto en GitHub con ''gh repo edit --default-branch develop''. 
-  * ''docker compose up -d --build backend'' y comprueba que la lista muestra los cinco clientes. +  * Comprueba en la web del repositorio que al entrar se ve ''develop''.
-  * Arranca con ''PERFIL=default'' tras un ''down -v'' y comprueba que la lista sale vacía. Vuelve a dejarlo con datos. +
-  * Cambia un texto de la página de Angular, guarda y comprueba que el navegador se recarga solo.+
  
 ==== Ejercicio 3 ==== ==== Ejercicio 3 ====
-  * Añade a ''application.properties'' la URL con ''${DB_PORT:3306}'' y ejecuta ''mvn test'' desde ''backend/''. Tiene que pasar sin nada levantado. +  * Escribe la skill ''/estado'' en ''.claude/skills/estado/SKILL.md'': resume ''git status'', ''git worktree list'' y los issues abiertos con ''gh issue list''. 
-  * Escribe en ''AGENTS.md'': que la aplicación se opera con ''./app.sh start | stop | status | logs'', que los tests se lanzan en el host con ''mvn test'', ''npm run test:api'' y ''npm run test:e2e'', y que el esquema va siempre en migraciones. +  * Pon en ''allowed-tools'' solo esos tres comandos. 
-  * ''/git-flow commit'' y ''/git-flow finish''.+  * Pruébala en Claude Code y en OpenCode. 
 +  * Crea ''.claude/settings.json'' con los permisos básicos de este tema y comprueba que ''/estado'' ya no pide permiso. 
 +  * Haz commit de todo y push a ''develop''.
  
 ==== Ejercicio 4 ==== ==== Ejercicio 4 ====
-  * Escribe ''app.sh'' y el ''.gitattributes''. Pruébalos: arrancar, cambiar un texto del backend, volver a arrancar y ver el cambio; parar, arrancar y ver que los datos siguen; parar con ''-v'', arrancar y ver los datos de prueba de nuevo. +  * Crea el tablero del repositorio (GitHub Project) con las columnas //Todo//, //In progress// y //Done//. 
-  * Amplía ''/git-flow'': ''start'' escribe el ''.env'' del worktree buscando puertos libres y dice cómo arrancar; ''finish'' ejecuta ''./app.sh stop -v'' antes de borrar el worktree. +  * Comprueba en los //Workflows// del proyecto que está activo «Item closed → Done». 
-  * Finaliza el issue.+  * Crea las etiquetas ''feature'' y ''bug''. 
 +  * Crea con ''gh issue create'' dos issues: «Página de inicio con el nombre del proyecto» y «README con cómo arrancar». Añádelos al tablero.
  
 ==== Ejercicio 5 ==== ==== Ejercicio 5 ====
-  * Crea dos issues cualesquiera, inícialos y arranca los dos con ''./app.sh start''. Comprueba que cada worktree tiene su ''.env'' con puertos distintos y que las dos aplicaciones están levantadas a la vez (''docker ps''). +  * Descarga {{ :clase:daw:daw:1eval:git-flow-skill.md |git-flow-skill.md}} y guárdala como ''.claude/skills/git-flow/SKILL.md''. 
-  * Cambia los datos de prueba en uno de los dos y comprueba que el otro no se entera. +  * Pide al agente que la adapte a este proyecto: quitar ''release'', ''bajar'' y ''subir''; renombrar las operaciones a ''start'', ''commit'', ''update'', ''finish'' y ''publish''; que ''start'' reciba el número del issue, lo lea con ''gh issue view'' y nombre la rama ''<alumno>-<N>-<slug>''; que ''finish'' ponga ''Closes #N'' en el cuerpo del commit; y que ''publish'' lleve ''develop'' a ''main'' con ''--ff-only''. 
-  * Finaliza los dos y comprueba con ''docker ps -a'' y ''docker volume ls'' que no queda nada de ellos.+  * Pide que añada la operación ''commit'': agrupar los cambios por intención y proponer los commits antes de hacerlos. 
 +  * Añade a ''AGENTS.md'' el nombre con el que empiezan tus ramas y la regla «git solo con ''/git-flow''». 
 +  * Lee la skill entera y comprueba que ''allowed-tools'' solo tiene ''git'' y ''gh''. 
 + 
 +==== Ejercicio 6 ==== 
 +  * ''/git-flow start'' con el primer issue y, sin cerrarlo, ''/git-flow start'' con el segundo. Comprueba con ''git worktree list'' que hay dos worktrees hermanos. 
 +  * Pasa los dos issues a //In progress// en el tablero. 
 +  * En el primer worktree pide al agente la página de inicio; en el segundo, el README. 
 +  * En cada worktree, ''/git-flow commit''. Mira los mensajes: tienen que llevar ''type(#N):título''. 
 +  * ''/git-flow finish'' en el primero. Comprueba en GitHub que el issue está cerrado y en //Done// y que el worktree y la rama han desaparecido. 
 +  * ''/git-flow update'' en el segundo y después ''/git-flow finish''. 
 + 
 +==== Ejercicio 7 ==== 
 +  * ''/git-flow publish''. Comprueba que ''main'' tiene los mismos commits que ''develop''. 
 +  * Con la carpeta principal en ''develop'', modifica un fichero sin hacer commit y lanza ''/git-flow start'' con un issue nuevo. La skill tiene que pararse y decirte por qué. Deshaz el cambio.
  
clase/daw/daw-proyecto/tema03.1791976215.txt.gz · Última modificación: por claude