кодекс вайб деплой
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful

This commit is contained in:
dv 2026-04-15 09:53:15 +03:00
parent f81021d2d0
commit 8cb0c58004
2 changed files with 47 additions and 58 deletions

View file

@ -1,21 +1,13 @@
# ───────────────────────────────────────────────────────────────────────────── # Woodpecker CI/CD
# Woodpecker CI Pipeline
# #
# Шаги: # Required secrets:
# 1. docker-build — сборка и пуш образа (push в main) # forgejo_host - registry host (example: git.example.com)
# 2. docker-build-tag — сборка и пуш образа с semver-тегом (git tag v*) # forgejo_image - full image path (example: git.example.com/user/intradesk-monitor)
# 3. deploy — деплой на сервер (выполняется на агенте role=work_woodpecker) # forgejo_username - registry username
# # forgejo_token - registry token with package:write
# Секреты (Settings → Secrets репозитория):
# forgejo_host — хост реестра, например git.example.com
# forgejo_image — полный путь образа, например git.example.com/user/intradesk-monitor
# forgejo_username — логин (обычно = gitea.actor)
# forgejo_token — токен с правом package:write
# ─────────────────────────────────────────────────────────────────────────────
steps: steps:
# Build and push on main branch updates.
# ── 1а. Сборка при push в main ─────────────────────────────────────────────
- name: docker-build - name: docker-build
image: woodpeckerci/plugin-docker-buildx image: woodpeckerci/plugin-docker-buildx
settings: settings:
@ -29,51 +21,45 @@ steps:
from_secret: forgejo_token from_secret: forgejo_token
tags: tags:
- latest - latest
- "sha-${CI_COMMIT_SHA:0:8}" - "sha-${CI_COMMIT_SHA}"
# Берём inline-кэш из latest; ARG GIT_COMMIT теперь в конце Dockerfile — кэш не сбивается
cache_from: cache_from:
from_secret: forgejo_image from_secret: forgejo_image
# mode=max кэширует все промежуточные слои (apk, npm install) в отдельный манифест
cache_to:
from_secret: forgejo_image_cache # значение: <registry>/<repo>:buildcache
rewrite_timestamp: false rewrite_timestamp: false
build_args: build_args:
- "GIT_COMMIT=${CI_COMMIT_SHA:0:8}" - "GIT_COMMIT=${CI_COMMIT_SHA}"
- "BUILDKIT_INLINE_CACHE=1" - "BUILDKIT_INLINE_CACHE=1"
when: when:
- event: push - event: push
branch: main branch: main
# ── 1б. Сборка при git tag v* (semver) ──────────────────────────────────── # Build and push on Git tag (release) events.
- name: docker-build-tag - name: docker-build-tag
image: woodpeckerci/plugin-docker-buildx image: woodpeckerci/plugin-docker-buildx
settings: settings:
registry: registry:
from_secret: forgejo_host from_secret: forgejo_host
repo: repo:
from_secret: forgejo_image_2 from_secret: forgejo_image
username: username:
from_secret: forgejo_username from_secret: forgejo_username
password: password:
from_secret: forgejo_token from_secret: forgejo_token
tags: tags:
- "${CI_COMMIT_TAG}" # v1.2.3 - "${CI_COMMIT_TAG}"
- latest - latest
- "sha-${CI_COMMIT_SHA:0:8}" # всегда — нужно для отката - "sha-${CI_COMMIT_SHA}"
cache_from: cache_from:
from_secret: forgejo_image # значение: git.example.com/user/intradesk-monitor:latest from_secret: forgejo_image
rewrite_timestamp: false
build_args: build_args:
- "GIT_COMMIT=${CI_COMMIT_SHA:0:8}" - "GIT_COMMIT=${CI_COMMIT_SHA}"
- "BUILDKIT_INLINE_CACHE=1" - "BUILDKIT_INLINE_CACHE=1"
when: when:
- event: tag - event: tag
# ── 2. Деплой на продакшн-сервер ────────────────────────────────────────── # Deploy for both push(main) and tag events.
# Выполняется на Woodpecker-агенте с меткой role=work_woodpecker.
# Требует: репозиторий помечен «trusted» в настройках Woodpecker-сервера
# (иначе монтирование volumes запрещено).
- name: deploy - name: deploy
image: docker:cli image: docker:27-cli
volumes: volumes:
- /var/run/docker.sock:/var/run/docker.sock - /var/run/docker.sock:/var/run/docker.sock
- /opt/intra_monitor:/deploy - /opt/intra_monitor:/deploy
@ -82,10 +68,8 @@ steps:
set -e set -e
cd /deploy cd /deploy
# Проверяем наличие .env на сервере
test -f .env || (echo "Missing /opt/intra_monitor/.env on deploy host" && exit 1) test -f .env || (echo "Missing /opt/intra_monitor/.env on deploy host" && exit 1)
# Сохраняем ID текущего образа (для отката)
CONTAINER=$(docker compose ps -q intradesk-monitor 2>/dev/null | head -1) CONTAINER=$(docker compose ps -q intradesk-monitor 2>/dev/null | head -1)
if [ -n "$CONTAINER" ]; then if [ -n "$CONTAINER" ]; then
OLD_IMAGE=$(docker inspect --format='{{.Image}}' "$CONTAINER") OLD_IMAGE=$(docker inspect --format='{{.Image}}' "$CONTAINER")
@ -93,16 +77,14 @@ steps:
echo "Snapshot for rollback: $OLD_IMAGE" echo "Snapshot for rollback: $OLD_IMAGE"
fi fi
# Удаляем legacy-контейнер со статичным именем, чтобы избежать name conflict
if docker ps -a --format '{{.Names}}' | grep -Eq '^intradesk-monitor$'; then if docker ps -a --format '{{.Names}}' | grep -Eq '^intradesk-monitor$'; then
docker rm -f intradesk-monitor || true docker rm -f intradesk-monitor || true
fi fi
# Обновляем образ и перезапускаем docker compose -p intradesk-monitor pull intradesk-monitor
docker compose -p intradesk-monitor pull
docker compose -p intradesk-monitor up -d --remove-orphans docker compose -p intradesk-monitor up -d --remove-orphans
echo "Deployed: ${CI_COMMIT_SHA:0:8} (${CI_COMMIT_MESSAGE})" echo "Deployed commit: ${CI_COMMIT_SHA} tag: ${CI_COMMIT_TAG}"
labels: labels:
role: work_woodpecker role: work_woodpecker
when: when:

View file

@ -1,35 +1,42 @@
FROM node:20-alpine # syntax=docker/dockerfile:1.7
FROM node:20-alpine AS deps
WORKDIR /app WORKDIR /app
ENV NODE_ENV=production # Build dependencies for native Node modules (better-sqlite3).
ENV LANG=C.UTF-8
ENV LC_ALL=C.UTF-8
# Зависимости для better-sqlite3 (нужна компиляция)
# Слой кэшируется пока не меняется базовый образ
RUN apk add --no-cache python3 make g++ RUN apk add --no-cache python3 make g++
COPY package*.json ./ COPY package*.json ./
RUN npm install --omit=dev --no-audit --no-fund \ # Use npm cache between builds; fallback to npm install when lock file is absent.
&& npm cache clean --force RUN --mount=type=cache,target=/root/.npm \
if [ -f package-lock.json ]; then \
npm ci --omit=dev --no-audit --no-fund; \
else \
npm install --omit=dev --no-audit --no-fund; \
fi
# Код приложения — копируется после зависимостей FROM node:20-alpine AS runtime
COPY server.js ./ WORKDIR /app
COPY refresh-service.js ./
COPY public/ ./public/
# Build-аргумент объявлен ПОСЛЕ тяжёлых слоёв — не сбивает кэш при каждом коммите ENV NODE_ENV=production \
ARG GIT_COMMIT=unknown LANG=C.UTF-8 \
ENV GIT_COMMIT=${GIT_COMMIT} LC_ALL=C.UTF-8 \
PORT=3000 \
DB_PATH=/data/intradesk.db
# Runtime libs required by native modules.
RUN apk add --no-cache libstdc++
COPY --from=deps /app/node_modules ./node_modules
COPY server.js refresh-service.js ./
COPY public ./public
# Папка для базы данных
RUN mkdir -p /data RUN mkdir -p /data
EXPOSE 3000 ARG GIT_COMMIT=unknown
LABEL org.opencontainers.image.revision=$GIT_COMMIT
ENV PORT=3000 EXPOSE 3000
ENV DB_PATH=/data/intradesk.db
HEALTHCHECK --interval=30s --timeout=5s --start-period=15s --retries=3 \ HEALTHCHECK --interval=30s --timeout=5s --start-period=15s --retries=3 \
CMD node -e "const http=require('http');const req=http.get('http://127.0.0.1:3000/healthz',res=>process.exit(res.statusCode===200?0:1));req.on('error',()=>process.exit(1));" CMD node -e "const http=require('http');const req=http.get('http://127.0.0.1:3000/healthz',res=>process.exit(res.statusCode===200?0:1));req.on('error',()=>process.exit(1));"