diff --git a/.env b/.env new file mode 100644 index 0000000..9607542 --- /dev/null +++ b/.env @@ -0,0 +1,7 @@ +# Скопируй в .env и заполни +SERVER_PORT=3000 +CLIENT_PORT=5000 + +# Добавь свои переменные ниже +# DATABASE_URL= +# JWT_SECRET= diff --git a/.gitea/workflows/deploy.yml b/.gitea/workflows/deploy.yml new file mode 100644 index 0000000..b7a2ea1 --- /dev/null +++ b/.gitea/workflows/deploy.yml @@ -0,0 +1,94 @@ +name: Build & Deploy + +on: + push: + branches: [main] + +env: + REGISTRY: ${{ gitea.server_url && 'git.aggtaa.com' || 'localhost' }} + IMAGE_PREFIX: ${{ gitea.repository }} # owner/blockly3d + +jobs: + # ── BUILD SERVER ───────────────────────────────────────────────────────────── + build-server: + runs-on: ubuntu-latest + if: | + contains(toJSON(github.event.commits.*.modified), 'server/') || + contains(toJSON(github.event.commits.*.modified), 'pnpm-lock.yaml') || + contains(toJSON(github.event.commits.*.modified), 'server.Dockerfile') + steps: + - uses: actions/checkout@v4 + + - name: Login to Gitea Registry + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ secrets.REGISTRY_USER }} + password: ${{ secrets.REGISTRY_PASSWORD }} + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Build & push server + uses: docker/build-push-action@v5 + with: + context: . + file: server.Dockerfile + push: true + tags: | + ${{ env.REGISTRY }}/${{ env.IMAGE_PREFIX }}/server:latest + ${{ env.REGISTRY }}/${{ env.IMAGE_PREFIX }}/server:${{ gitea.sha }} + cache-from: type=registry,ref=${{ env.REGISTRY }}/${{ env.IMAGE_PREFIX }}/server:cache + cache-to: type=registry,ref=${{ env.REGISTRY }}/${{ env.IMAGE_PREFIX }}/server:cache,mode=max + + # ── BUILD WEBCLIENT ─────────────────────────────────────────────────────────── + build-webclient: + runs-on: ubuntu-latest + if: | + contains(toJSON(github.event.commits.*.modified), 'webclient/') || + contains(toJSON(github.event.commits.*.modified), 'pnpm-lock.yaml') || + contains(toJSON(github.event.commits.*.modified), 'webclient.Dockerfile') + steps: + - uses: actions/checkout@v4 + + - name: Login to Gitea Registry + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ secrets.REGISTRY_USER }} + password: ${{ secrets.REGISTRY_PASSWORD }} + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Build & push webclient + uses: docker/build-push-action@v5 + with: + context: . + file: webclient.Dockerfile + push: true + tags: | + ${{ env.REGISTRY }}/${{ env.IMAGE_PREFIX }}/webclient:latest + ${{ env.REGISTRY }}/${{ env.IMAGE_PREFIX }}/webclient:${{ gitea.sha }} + cache-from: type=registry,ref=${{ env.REGISTRY }}/${{ env.IMAGE_PREFIX }}/webclient:cache + cache-to: type=registry,ref=${{ env.REGISTRY }}/${{ env.IMAGE_PREFIX }}/webclient:cache,mode=max + + # ── DEPLOY ──────────────────────────────────────────────────────────────────── + deploy: + runs-on: ubuntu-latest + needs: [build-server, build-webclient] + # Запускается даже если один из build-job-ов был пропущен + if: always() && !failure() && !cancelled() + steps: + - name: Deploy via SSH + uses: appleboy/ssh-action@v1 + with: + host: ${{ secrets.DEPLOY_HOST }} + username: ${{ secrets.DEPLOY_USER }} + key: ${{ secrets.DEPLOY_SSH_KEY }} + script: | + cd /opt/blockly3d + git pull origin main + docker compose pull + docker compose up -d --remove-orphans + docker image prune -f \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..4765a90 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,34 @@ +services: + server: + build: + context: . + dockerfile: server.Dockerfile + cache_from: + - ${CI_REGISTRY_IMAGE:-blockly3d}/server:cache + image: ${CI_REGISTRY_IMAGE:-blockly3d}/server:${CI_COMMIT_SHA:-latest} + restart: unless-stopped + ports: + - "${SERVER_PORT:-3000}:3000" + environment: + - NODE_ENV=production + env_file: + - .env + networks: + - blockly3d + + webclient: + build: + context: . + dockerfile: webclient.Dockerfile + cache_from: + - ${CI_REGISTRY_IMAGE:-blockly3d}/webclient:cache + image: ${CI_REGISTRY_IMAGE:-blockly3d}/webclient:${CI_COMMIT_SHA:-latest} + restart: unless-stopped + ports: + - "${CLIENT_PORT:-5000}:5000" + networks: + - blockly3d + +networks: + blockly3d: + driver: bridge diff --git a/server.Dockerfile b/server.Dockerfile new file mode 100644 index 0000000..e4388cd --- /dev/null +++ b/server.Dockerfile @@ -0,0 +1,37 @@ +# syntax=docker/dockerfile:1 +FROM node:22.22.3-alpine AS base +RUN corepack enable && corepack prepare pnpm@latest --activate + +# ── deps ────────────────────────────────────────────────────────────────────── +FROM base AS deps +WORKDIR /app + +# Копируем только манифесты — слой кешируется пока lock не менялся +COPY package.json pnpm-workspace.yaml pnpm-lock.yaml ./ +COPY server/package.json ./server/ + +RUN pnpm install --frozen-lockfile --filter server... + +# ── build ───────────────────────────────────────────────────────────────────── +FROM deps AS builder +WORKDIR /app + +COPY server ./server + +RUN pnpm --filter server build + +# ── runtime ─────────────────────────────────────────────────────────────────── +FROM node:22.22.3-alpine AS runner +RUN corepack enable && corepack prepare pnpm@latest --activate +WORKDIR /app + +COPY package.json pnpm-workspace.yaml pnpm-lock.yaml ./ +COPY server/package.json ./server/ + +RUN pnpm install --frozen-lockfile --filter server... --prod + +COPY --from=builder /app/server/dist ./server/dist + +EXPOSE 3000 + +CMD ["node", "server/dist/index.js"] diff --git a/webclient.Dockerfile b/webclient.Dockerfile new file mode 100644 index 0000000..a0cca6f --- /dev/null +++ b/webclient.Dockerfile @@ -0,0 +1,32 @@ +# syntax=docker/dockerfile:1 +FROM node:22.22.3-alpine AS base +RUN corepack enable && corepack prepare pnpm@latest --activate + +# ── deps ────────────────────────────────────────────────────────────────────── +FROM base AS deps +WORKDIR /app + +COPY package.json pnpm-workspace.yaml pnpm-lock.yaml ./ +COPY webclient/package.json ./webclient/ + +RUN pnpm install --frozen-lockfile --filter webclient... + +# ── build ───────────────────────────────────────────────────────────────────── +FROM deps AS builder +WORKDIR /app + +COPY webclient ./webclient + +RUN pnpm --filter webclient build + +# ── runtime ─────────────────────────────────────────────────────────────────── +FROM node:22.22.3-alpine AS runner +WORKDIR /app + +RUN npm install -g serve + +COPY --from=builder /app/webclient/dist ./dist + +EXPOSE 5000 + +CMD ["serve", "-s", "dist", "-l", "5000"]