diff --git a/.forgejo/workflows/deploy.yaml b/.forgejo/workflows/deploy.yaml index c666f7a..77ea3a5 100644 --- a/.forgejo/workflows/deploy.yaml +++ b/.forgejo/workflows/deploy.yaml @@ -1,4 +1,4 @@ -name: Deploy Backend (Dev) +name: Deploy Stack (Dev) on: push: @@ -6,11 +6,13 @@ on: - dev paths: - 'backend/**' + - 'backoffice/**' jobs: - # Job 1: Run Backend Tests + # Job 1: Testes do Backend (só roda se houver alteração no /backend) test-backend: runs-on: docker + if: contains(github.event.head_commit.modified, 'backend/') || contains(github.event.head_commit.added, 'backend/') steps: - name: Checkout code uses: https://github.com/actions/checkout@v4 @@ -32,12 +34,14 @@ jobs: go test -v ./internal/services/... go test -v ./internal/core/usecases/... - # Job 2: Deploy (only if tests pass) - deploy-backend-dev: + # Job 2: Deploy Inteligente + deploy-dev: runs-on: docker - needs: test-backend + # O deploy depende dos testes apenas se houver mudanças no backend + needs: [test-backend] + if: always() && (needs.test-backend.result == 'success' || needs.test-backend.result == 'skipped') steps: - - name: Executar Deploy via SSH na Apolo (Ambiente Dev) + - name: Executar Deploy via SSH na Apolo uses: https://github.com/appleboy/ssh-action@v1.0.3 with: host: ${{ secrets.HOST }} @@ -45,25 +49,23 @@ jobs: key: ${{ secrets.SSH_KEY }} port: ${{ secrets.PORT || 22 }} script: | - # 1. Verifica se o diretório existe, senão cria e faz o clone inicial - if [ ! -d "/mnt/data/gohorsejobs" ]; then - sudo mkdir -p /mnt/data/gohorsejobs - sudo chown $USER:$USER /mnt/data/gohorsejobs - git clone https://forgejo-gru.rede5.com.br/rede5/gohorsejobs.git /mnt/data/gohorsejobs - fi - cd /mnt/data/gohorsejobs - - # 2. Atualiza o código na branch dev git fetch origin git checkout dev git pull origin dev - # 3. Build da imagem com o prefixo localhost/ para evitar erro de short-name resolution - podman build -t localhost/gohorsejobs-backend-dev:latest ./backend + # DEPLOY DO BACKEND (Se houver alterações) + if git diff --name-only HEAD@{1} HEAD | grep -q "^backend/"; then + echo "Detectadas mudanças no Backend. Iniciando Build..." + podman build -t localhost/gohorsejobs-backend-dev:latest ./backend + sudo systemctl restart gohorsejobs-backend-dev + fi + + # DEPLOY DO BACKOFFICE (Se houver alterações) + if git diff --name-only HEAD@{1} HEAD | grep -q "^backoffice/"; then + echo "Detectadas mudanças no Backoffice. Iniciando Build..." + podman build -t localhost/gohorsejobs-backoffice-dev:latest ./backoffice + sudo systemctl restart gohorsejobs-backoffice-dev + fi - # 4. Reinicia o serviço via Systemd (Quadlet) - sudo systemctl restart gohorsejobs-backend-dev - - # 5. Limpeza de imagens antigas para economizar espaço na VPS podman image prune -f \ No newline at end of file diff --git a/backoffice/Dockerfile b/backoffice/Dockerfile index c2c7df0..8ca23a4 100644 --- a/backoffice/Dockerfile +++ b/backoffice/Dockerfile @@ -1,61 +1,81 @@ # ============================================================================= -# GoHorse Backoffice - Ultra-Optimized Dockerfile with pnpm +# GoHorse Backoffice - Optimized Dockerfile with pnpm + BuildKit caching # Target: < 200MB final image # ============================================================================= +# syntax=docker/dockerfile:1 -# Stage 1: Base with pnpm +# Stage 1: Base with pnpm (latest stable) FROM mirror.gcr.io/library/node:20-alpine AS base -RUN corepack enable && corepack prepare pnpm@9.15.4 --activate + +# Enable corepack and activate pnpm +RUN corepack enable && corepack prepare pnpm@latest --activate + +# libc6-compat for native module compatibility RUN apk add --no-cache libc6-compat -# Stage 2: Install dependencies +# Set pnpm store location for caching +ENV PNPM_HOME="/pnpm" +ENV PATH="$PNPM_HOME:$PATH" + +# ============================================================================= +# Stage 2: Install dependencies with BuildKit cache +# ============================================================================= FROM base AS deps WORKDIR /app -COPY package.json pnpm-lock.yaml ./ -RUN pnpm install --no-frozen-lockfile -# Stage 3: Build +# Copy only package files first (better layer caching) +COPY package.json pnpm-lock.yaml ./ + +# Use BuildKit cache mount for pnpm store - dramatically speeds up rebuilds +RUN --mount=type=cache,id=pnpm,target=/pnpm/store \ + pnpm install --frozen-lockfile + +# ============================================================================= +# Stage 3: Build application +# ============================================================================= FROM base AS builder WORKDIR /app + +# Copy dependencies from deps stage COPY --from=deps /app/node_modules ./node_modules + +# Copy source files COPY package.json pnpm-lock.yaml ./ COPY tsconfig*.json nest-cli.json ./ COPY src ./src -# Build the application +# Build the NestJS application RUN pnpm build -# Prune dev dependencies for production -RUN pnpm prune --prod +# Prune dev dependencies for smaller production image +RUN --mount=type=cache,id=pnpm,target=/pnpm/store \ + pnpm prune --prod # ============================================================================= # Stage 4: Production - Minimal runtime # ============================================================================= FROM mirror.gcr.io/library/node:20-alpine AS production -# Security: non-root user +# Security: create non-root user RUN addgroup -g 1001 -S nodejs && \ - adduser -S nestjs -u 1001 -G nodejs + adduser -S nestjs -u 1001 -G nodejs WORKDIR /app -# Copy only production artifacts +# Copy only production artifacts (with proper ownership) COPY --from=builder --chown=nestjs:nodejs /app/dist ./dist COPY --from=builder --chown=nestjs:nodejs /app/node_modules ./node_modules COPY --from=builder --chown=nestjs:nodejs /app/package.json ./ -# Environment +# Environment configuration ENV NODE_ENV=production ENV BACKOFFICE_PORT=3001 ENV BACKOFFICE_HOST=0.0.0.0 -# Switch to non-root user +# Switch to non-root user for security USER nestjs EXPOSE 3001 -# Health check -HEALTHCHECK --interval=30s --timeout=3s --start-period=10s --retries=3 \ - CMD node -e "const http = require('http'); http.get('http://localhost:3001/health', (r) => process.exit(r.statusCode === 200 ? 0 : 1)).on('error', () => process.exit(1))" - +# Start application CMD ["node", "dist/main.js"]