From 21f4facb31d576781b1221555c6a7e49ffe71b4f Mon Sep 17 00:00:00 2001 From: dmolinari Date: Sat, 14 Jun 2025 22:11:02 -0300 Subject: [PATCH] Ajustes de CI/CD. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fix: Run jobs on runner host to resolve network issues Reseteo de estado por fallas del deply. Reversión. Nuevo testeo de CI/CD. Reversión 2. Fix Reversión 3 Test con sh-runner Reversión antes de cambio de foco. Sin sh-runner. No puede resolver gitea. Debe usar el secreto REGISTRY_URL. Test con Gitea 1.21.11 Reversion 1.24.0 Va Este Final? A ver... Nuevo Cambio de Enfoque para CI/CD. Se intenta uso de Drone. Fix de yml para Drone. Retry .drone.yml Fix problema de indentación. Retry yml Va Retry mil Nuevo test Fix: Configure docker plugin for insecure registry Forzar la network. Retry 0142 Test Webhook Se agregan debug-network para verificar valores. Se agregan daemon_dns para poder resolver los dominios de nuget desde los contenedores de despliegue. Cambio de enfoque. Parametro para MTU (Maximum Transmission Unit) añadidos. Prueba: Usar el Socket de Docker del Host Fix indentación. Y Trusted en Drone. Linter: duplicate step name Todos los registros de contenedores (Docker Hub, Gitea, etc.) exigen que los nombres de las imágenes de Docker estén en minúsculas. El pipeline está intentando crear una imagen llamada dmolinari/GestionIntegralWeb-backend, pero la parte GestionIntegralWeb contiene mayúsculas. Esto ocurre porque la variable de Drone ${DRONE_REPO_NAME} toma el nombre directamente de Gitea, que en este caso es GestionIntegralWeb. La sintaxis es: ${VARIABLE,,}. Se añade la URL del registro al nombre del repo Cabio de Variable por valor directo. Retry 1806 Retry 1807 Webhook gitea Enable. Added: privileged: true Test con Kaniko Fix yml Retest Cambio de imagen kaniko Cambio por host --- .drone.yml | 82 ++++++++++++++++++++ Backend/GestionIntegral.Api/Dockerfile | 37 +++++++++ Backend/GestionIntegral.Api/appsettings.json | 2 +- Frontend/Dockerfile | 31 ++++++++ 4 files changed, 151 insertions(+), 1 deletion(-) create mode 100644 .drone.yml create mode 100644 Backend/GestionIntegral.Api/Dockerfile create mode 100644 Frontend/Dockerfile diff --git a/.drone.yml b/.drone.yml new file mode 100644 index 0000000..52b97f1 --- /dev/null +++ b/.drone.yml @@ -0,0 +1,82 @@ +kind: pipeline +type: docker +name: Build y Deploy + +trigger: + branch: + - main + event: + - push + +steps: +- name: build-and-publish-backend + image: plugins/kaniko + settings: + repo: host.docker.internal:5000/${DRONE_REPO_OWNER}/${DRONE_REPO_NAME,,}-backend + tags: + - latest + - ${DRONE_COMMIT_SHA:0:8} + dockerfile: Backend/GestionIntegral.Api/Dockerfile + context: . + username: + from_secret: GITEA_USER + password: + from_secret: ACTIONS_PAT + insecure: true + +- name: build-and-publish-frontend + image: plugins/kaniko + settings: + repo: host.docker.internal:5000/${DRONE_REPO_OWNER}/${DRONE_REPO_NAME,,}-frontend + tags: + - latest + - ${DRONE_COMMIT_SHA:0:8} + dockerfile: Frontend/Dockerfile + context: . + username: + from_secret: GITEA_USER + password: + from_secret: ACTIONS_PAT + insecure: true + depends_on: + - build-and-publish-backend + +- name: deploy-to-production + image: alpine:latest + environment: + SSH_KEY: + from_secret: PROD_SERVER_SSH_KEY + PROD_HOST: + from_secret: PROD_SERVER_HOST + PROD_USER: + from_secret: PROD_SERVER_USER + DB_PASSWORD: + from_secret: DB_SA_PASSWORD_SECRET + JWT_KEY: + from_secret: JWT_KEY_SECRET + REGISTRY: + from_secret: REGISTRY_URL + GITEA_USER: + from_secret: GITEA_USER + GITEA_PAT: + from_secret: ACTIONS_PAT + commands: + - apk add --no-cache openssh-client + - mkdir -p ~/.ssh + - echo "$SSH_KEY" > ~/.ssh/id_rsa + - chmod 600 ~/.ssh/id_rsa + - ssh-keyscan -H $PROD_HOST >> ~/.ssh/known_hosts + - | + ssh $PROD_USER@$PROD_HOST << 'EOF' + echo "--- CONECTADO AL SERVIDOR DE PRODUCCIÓN ---" + cd /opt/gestion-integral + export DB_SA_PASSWORD="${DB_PASSWORD}" + export JWT_KEY="${JWT_KEY}" + docker login ${REGISTRY} -u ${GITEA_USER} -p ${GITEA_PAT} + docker compose pull + docker compose up -d + docker image prune -af + echo "--- DESPLIEGUE COMPLETADO ---" + EOF + depends_on: + - build-and-publish-frontend \ No newline at end of file diff --git a/Backend/GestionIntegral.Api/Dockerfile b/Backend/GestionIntegral.Api/Dockerfile new file mode 100644 index 0000000..4a9f1db --- /dev/null +++ b/Backend/GestionIntegral.Api/Dockerfile @@ -0,0 +1,37 @@ +# --- Etapa 1: Build --- +# Usamos el SDK de .NET 9 (o el que corresponda) para compilar. +FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build +WORKDIR /src + +# Copiamos los archivos de proyecto (.sln y .csproj) y restauramos las dependencias. +# Esto es una optimización de caché de Docker. +COPY GestionIntegralWeb.sln . +COPY Backend/GestionIntegral.Api/GestionIntegral.Api.csproj Backend/GestionIntegral.Api/ + +# Restauramos los paquetes NuGet. +RUN dotnet restore "GestionIntegralWeb.sln" + +# Copiamos todo el resto del código fuente. +COPY . . + +# Nos movemos al directorio del proyecto y lo construimos en modo Release. +WORKDIR "/src/Backend/GestionIntegral.Api" +RUN dotnet build "GestionIntegral.Api.csproj" -c Release -o /app/build + +# --- Etapa 2: Publish --- +# Publicamos la aplicación, lo que genera los artefactos listos para producción. +FROM build AS publish +RUN dotnet publish "GestionIntegral.Api.csproj" -c Release -o /app/publish /p:UseAppHost=false + +# --- Etapa 3: Final --- +# Usamos la imagen de runtime de ASP.NET, que es mucho más ligera que el SDK. +FROM mcr.microsoft.com/dotnet/aspnet:9.0 +WORKDIR /app +COPY --from=publish /app/publish . + +# El puerto en el que la API escuchará DENTRO del contenedor. +# Usaremos 8080 para evitar conflictos si en el futuro corres algo en el puerto 80. +EXPOSE 8080 + +# El comando para iniciar la API cuando el contenedor arranque. +ENTRYPOINT ["dotnet", "GestionIntegral.Api.dll"] \ No newline at end of file diff --git a/Backend/GestionIntegral.Api/appsettings.json b/Backend/GestionIntegral.Api/appsettings.json index 2e51861..3fcf26c 100644 --- a/Backend/GestionIntegral.Api/appsettings.json +++ b/Backend/GestionIntegral.Api/appsettings.json @@ -6,7 +6,7 @@ } }, "Jwt": { - "Key": "badb1a38d221c9e23bcf70958840ca7f5a5dc54f2047dadf7ce45b578b5bc3e2", + "Key": "", "Issuer": "GestionIntegralApi", "Audience": "GestionIntegralClient", "DurationInHours": 8 diff --git a/Frontend/Dockerfile b/Frontend/Dockerfile new file mode 100644 index 0000000..81dfe7e --- /dev/null +++ b/Frontend/Dockerfile @@ -0,0 +1,31 @@ +# --- Etapa 1: Build --- +# Usamos una imagen de Node.js para construir los archivos estáticos de React. +FROM node:20-alpine AS build +WORKDIR /app + +# Copiamos los archivos de dependencias y las instalamos. +COPY package.json package-lock.json ./ +RUN npm install + +# Copiamos el resto del código del frontend. +COPY . . + +# Ejecutamos el script de build de Vite, que genera la carpeta 'dist'. +RUN npm run build + +# --- Etapa 2: Serve --- +# Usamos una imagen de Nginx súper ligera para servir los archivos estáticos. +FROM nginx:stable-alpine +WORKDIR /usr/share/nginx/html + +# Eliminamos el index.html por defecto de Nginx. +RUN rm -f index.html + +# Copiamos los archivos construidos desde la etapa anterior a la carpeta que Nginx sirve. +COPY --from=build /app/dist . + +# Nginx por defecto escucha en el puerto 80, así que lo exponemos. +EXPOSE 80 + +# Comando para iniciar Nginx. Esto asegura que se mantenga corriendo. +CMD ["nginx", "-g", "daemon off;"] \ No newline at end of file