Skip to content
This repository was archived by the owner on Jan 28, 2026. It is now read-only.

Commit 1e44eba

Browse files
committed
feat: Migrate from core repository
1 parent d7ada1d commit 1e44eba

148 files changed

Lines changed: 37603 additions & 0 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.dockerignore

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
node_modules
2+
dist
3+
.git
4+
.env
5+
*.log
6+
npm-debug.log*
7+
yarn-debug.log*
8+
yarn-error.log*
9+

.github/workflows/deploy.yml

Lines changed: 335 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,335 @@
1+
name: Build and Deploy
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
workflow_dispatch:
8+
9+
env:
10+
NODE_VERSION: '20'
11+
IMAGE_NAME: ${{ secrets.DOCKER_IMAGE_NAME || 'atom-dbro-backend' }}
12+
13+
jobs:
14+
build:
15+
name: Build and Export Docker Image
16+
runs-on: ubuntu-latest
17+
18+
steps:
19+
- name: Checkout code
20+
uses: actions/checkout@v4
21+
22+
- name: Set up Docker Buildx
23+
uses: docker/setup-buildx-action@v3
24+
25+
- name: Build Docker image
26+
uses: docker/build-push-action@v5
27+
with:
28+
context: .
29+
file: ./Dockerfile
30+
push: false
31+
load: true
32+
tags: ${{ env.IMAGE_NAME }}:latest
33+
no-cache: true
34+
35+
- name: Verify image was built
36+
run: |
37+
if ! docker images | grep -q "${{ env.IMAGE_NAME }}.*latest"; then
38+
echo "❌ ERROR: Docker image was not built successfully"
39+
exit 1
40+
fi
41+
echo "✅ Docker image built successfully: ${{ env.IMAGE_NAME }}:latest"
42+
docker images | grep "${{ env.IMAGE_NAME }}"
43+
44+
- name: Export Docker image to tar.gz
45+
run: |
46+
echo "📦 Exporting Docker image to tar.gz..."
47+
docker save ${{ env.IMAGE_NAME }}:latest | gzip > image.tar.gz
48+
49+
echo "📊 Image file info:"
50+
ls -lh image.tar.gz
51+
IMAGE_SIZE=$(du -h image.tar.gz | cut -f1)
52+
echo "Image size: $IMAGE_SIZE"
53+
54+
# Проверяем, что файл создан и не пустой
55+
if [ ! -f image.tar.gz ] || [ ! -s image.tar.gz ]; then
56+
echo "❌ ERROR: Failed to export Docker image"
57+
exit 1
58+
fi
59+
60+
echo "✅ Docker image exported successfully"
61+
62+
- name: Upload Docker image artifact
63+
uses: actions/upload-artifact@v4
64+
with:
65+
name: docker-image
66+
path: image.tar.gz
67+
retention-days: 1
68+
69+
transfer:
70+
name: Transfer Image to Server
71+
needs: build
72+
runs-on: ubuntu-latest
73+
if: github.ref == 'refs/heads/main'
74+
75+
steps:
76+
- name: Download Docker image artifact
77+
uses: actions/download-artifact@v4
78+
with:
79+
name: docker-image
80+
path: ./
81+
82+
- name: Set up SSH
83+
uses: webfactory/ssh-agent@v0.9.0
84+
with:
85+
ssh-private-key: ${{ secrets.DEPLOY_SSH_KEY }}
86+
87+
- name: Validate SSH connection
88+
run: |
89+
SSH_PORT="${DEPLOY_SSH_PORT:-22}"
90+
ssh -o StrictHostKeyChecking=no -p "$SSH_PORT" ${{ secrets.DEPLOY_USER }}@${{ secrets.DEPLOY_HOST }} \
91+
"echo 'SSH connection successful' && hostname"
92+
93+
- name: Transfer Docker image to server
94+
run: |
95+
SSH_PORT="${DEPLOY_SSH_PORT:-22}"
96+
97+
echo "📦 Transferring Docker image to server..."
98+
echo "📊 Image file size: $(du -h image.tar.gz | cut -f1)"
99+
100+
# Передаем образ на сервер
101+
scp -o StrictHostKeyChecking=no -P "$SSH_PORT" image.tar.gz ${{ secrets.DEPLOY_USER }}@${{ secrets.DEPLOY_HOST }}:/tmp/
102+
103+
# Проверяем, что файл успешно передан
104+
ssh -o StrictHostKeyChecking=no -p "$SSH_PORT" ${{ secrets.DEPLOY_USER }}@${{ secrets.DEPLOY_HOST }} \
105+
"if [ -f /tmp/image.tar.gz ]; then echo '✅ Image file transferred successfully'; ls -lh /tmp/image.tar.gz; else echo '❌ ERROR: Image file not found on server'; exit 1; fi"
106+
107+
echo "✅ Docker image transferred successfully"
108+
env:
109+
DEPLOY_SSH_PORT: ${{ secrets.DEPLOY_SSH_PORT || '22' }}
110+
111+
deploy:
112+
name: Deploy Application
113+
needs: transfer
114+
runs-on: ubuntu-latest
115+
if: github.ref == 'refs/heads/main'
116+
117+
steps:
118+
- name: Set up SSH
119+
uses: webfactory/ssh-agent@v0.9.0
120+
with:
121+
ssh-private-key: ${{ secrets.DEPLOY_SSH_KEY }}
122+
123+
- name: Deploy application
124+
run: |
125+
SSH_PORT="${DEPLOY_SSH_PORT:-22}"
126+
ssh -o StrictHostKeyChecking=no -p "$SSH_PORT" ${{ secrets.DEPLOY_USER }}@${{ secrets.DEPLOY_HOST }} "export PROJECT_DIR_VALUE='$PROJECT_DIR_VALUE' CONTAINER_NAME='$CONTAINER_NAME' IMAGE_NAME='$IMAGE_NAME' SERVICE_NAME='$SERVICE_NAME' COMPOSE_PROJECT_NAME='$COMPOSE_PROJECT_NAME'; bash -s" << 'REMOTE_SCRIPT'
127+
set -e
128+
129+
IMAGE_NAME="${IMAGE_NAME:-atom-dbro-backend}"
130+
PROJECT_DIR="$PROJECT_DIR_VALUE"
131+
CONTAINER_NAME="${CONTAINER_NAME:-atom-dbro-app}"
132+
SERVICE_NAME="${SERVICE_NAME:-app}"
133+
COMPOSE_PROJECT_NAME="${COMPOSE_PROJECT_NAME:-}"
134+
135+
# Проверяем, что переменная PROJECT_DIR установлена
136+
if [ -z "$PROJECT_DIR" ]; then
137+
echo "❌ ERROR: PROJECT_DIR is not set"
138+
exit 1
139+
fi
140+
141+
# Переходим в директорию проекта
142+
echo "📁 Changing to project directory: $PROJECT_DIR"
143+
cd "$PROJECT_DIR" || {
144+
echo "❌ ERROR: Failed to change to project directory: $PROJECT_DIR"
145+
exit 1
146+
}
147+
148+
# Проверяем, что docker-compose.yml существует
149+
if [ ! -f "docker-compose.yml" ]; then
150+
echo "❌ ERROR: docker-compose.yml not found in $PROJECT_DIR"
151+
echo "Current directory: $(pwd)"
152+
echo "Files in directory:"
153+
ls -la || true
154+
exit 1
155+
fi
156+
157+
echo "✅ Found docker-compose.yml in $(pwd)"
158+
159+
# Загружаем образ в Docker
160+
echo "📥 Importing Docker image from tar.gz..."
161+
if [ ! -f /tmp/image.tar.gz ]; then
162+
echo "❌ ERROR: Image file not found: /tmp/image.tar.gz"
163+
exit 1
164+
fi
165+
166+
echo "📊 Image file size: $(du -h /tmp/image.tar.gz | cut -f1)"
167+
docker load -i /tmp/image.tar.gz
168+
169+
# Проверяем, что образ загружен
170+
if ! docker images | grep -q "$IMAGE_NAME.*latest"; then
171+
echo "❌ ERROR: Failed to import Docker image"
172+
exit 1
173+
fi
174+
175+
echo "✅ Docker image imported successfully"
176+
177+
# Удаляем временный файл
178+
rm -f /tmp/image.tar.gz
179+
echo "🧹 Cleaned up temporary image file"
180+
181+
# Очищаем старые версии образа перед деплоем
182+
echo "🧹 Removing old image versions (if any)..."
183+
docker images "$IMAGE_NAME" --format "{{.Repository}}:{{.Tag}} {{.ID}}" | \
184+
grep -v "latest" | \
185+
awk '{print $2}' | \
186+
xargs -r docker rmi -f || echo "No old image versions to remove"
187+
188+
# Создание необходимых сетей Docker (если не существуют)
189+
echo "🌐 Ensuring Docker networks exist..."
190+
docker network create atom-external-network 2>/dev/null || echo "Network atom-external-network already exists"
191+
docker network create atom-internal-network 2>/dev/null || echo "Network atom-internal-network already exists"
192+
193+
# Перезапуск только контейнера приложения с новым образом
194+
echo "🔄 Restarting application container with new image..."
195+
echo "Working directory: $(pwd)"
196+
echo "Container name: $CONTAINER_NAME"
197+
echo "Service name: $SERVICE_NAME"
198+
echo "Docker image: $IMAGE_NAME:latest"
199+
200+
# Убеждаемся, что мы в правильной директории
201+
if [ "$(pwd)" != "$PROJECT_DIR" ]; then
202+
echo "⚠️ Warning: Not in project directory, changing to $PROJECT_DIR"
203+
cd "$PROJECT_DIR" || exit 1
204+
fi
205+
206+
# Проверяем, что docker-compose.yml существует
207+
COMPOSE_FILE="$PROJECT_DIR/docker-compose.yml"
208+
if [ ! -f "$COMPOSE_FILE" ]; then
209+
echo "❌ ERROR: docker-compose.yml not found at $COMPOSE_FILE"
210+
exit 1
211+
fi
212+
213+
# Проверяем, что docker-compose.yml содержит указанный сервис
214+
if ! grep -q "^ $SERVICE_NAME:" "$COMPOSE_FILE"; then
215+
echo "❌ ERROR: Service '$SERVICE_NAME' not found in docker-compose.yml"
216+
echo "Available services:"
217+
grep -E "^ [a-zA-Z-]+:" "$COMPOSE_FILE" || echo "No services found"
218+
exit 1
219+
fi
220+
221+
# Устанавливаем переменные для docker-compose
222+
export DOCKER_IMAGE="$IMAGE_NAME:latest"
223+
echo "✅ DOCKER_IMAGE environment variable set to: $DOCKER_IMAGE"
224+
225+
# Устанавливаем имя проекта (стек) для docker-compose, если указано
226+
if [ -n "$COMPOSE_PROJECT_NAME" ]; then
227+
export COMPOSE_PROJECT_NAME="$COMPOSE_PROJECT_NAME"
228+
echo "✅ COMPOSE_PROJECT_NAME set to: $COMPOSE_PROJECT_NAME"
229+
else
230+
echo "ℹ️ COMPOSE_PROJECT_NAME not set, using default (directory name)"
231+
fi
232+
233+
# Останавливаем и удаляем только контейнер приложения (если существует)
234+
if docker ps -a --format "{{.Names}}" | grep -q "^${CONTAINER_NAME}$"; then
235+
echo "🛑 Stopping existing container: $CONTAINER_NAME"
236+
docker stop "$CONTAINER_NAME" 2>/dev/null || true
237+
echo "🗑️ Removing existing container: $CONTAINER_NAME"
238+
docker rm "$CONTAINER_NAME" 2>/dev/null || true
239+
else
240+
echo "ℹ️ Container $CONTAINER_NAME does not exist, will be created"
241+
fi
242+
243+
# Проверяем, что образ существует
244+
if ! docker images | grep -q "$IMAGE_NAME.*latest"; then
245+
echo "❌ ERROR: Docker image $IMAGE_NAME:latest not found"
246+
echo "Available images:"
247+
docker images | head -10
248+
exit 1
249+
fi
250+
251+
# Запускаем только указанный сервис с новым образом
252+
# --force-recreate: пересоздает контейнер даже если конфигурация не изменилась
253+
# --no-deps: не запускает зависимости
254+
# --pull never: не пытается скачать образ (он уже загружен)
255+
# -p или --project-name: явно указываем имя проекта (стек)
256+
echo "▶️ Starting service '$SERVICE_NAME' with docker compose..."
257+
258+
if [ -n "$COMPOSE_PROJECT_NAME" ]; then
259+
echo "Command: docker compose -f '$COMPOSE_FILE' -p '$COMPOSE_PROJECT_NAME' up -d --force-recreate --no-deps --pull never '$SERVICE_NAME'"
260+
docker compose -f "$COMPOSE_FILE" -p "$COMPOSE_PROJECT_NAME" up -d --force-recreate --no-deps --pull never "$SERVICE_NAME"
261+
else
262+
echo "Command: docker compose -f '$COMPOSE_FILE' up -d --force-recreate --no-deps --pull never '$SERVICE_NAME'"
263+
docker compose -f "$COMPOSE_FILE" up -d --force-recreate --no-deps --pull never "$SERVICE_NAME"
264+
fi
265+
266+
# Проверяем, что контейнер запустился
267+
if ! docker ps --format "{{.Names}}" | grep -q "^${CONTAINER_NAME}$"; then
268+
echo "❌ ERROR: Container $CONTAINER_NAME failed to start"
269+
echo "Container status:"
270+
docker ps -a | grep "$CONTAINER_NAME" || echo "Container not found"
271+
exit 1
272+
fi
273+
274+
echo "✅ Container $CONTAINER_NAME started successfully"
275+
276+
# Ожидание готовности контейнера
277+
echo "⏳ Waiting for application container to be ready..."
278+
MAX_CONTAINER_WAIT_ATTEMPTS=30
279+
CONTAINER_WAIT_ATTEMPT=0
280+
CONTAINER_READY=false
281+
282+
while [ $CONTAINER_WAIT_ATTEMPT -lt $MAX_CONTAINER_WAIT_ATTEMPTS ]; do
283+
# Проверяем, что контейнер запущен и работает
284+
if docker ps | grep -q "$CONTAINER_NAME" && docker exec "$CONTAINER_NAME" echo "Container is ready" > /dev/null 2>&1; then
285+
CONTAINER_READY=true
286+
echo "✅ Container is ready and accepting commands"
287+
break
288+
fi
289+
CONTAINER_WAIT_ATTEMPT=$((CONTAINER_WAIT_ATTEMPT + 1))
290+
if [ $CONTAINER_WAIT_ATTEMPT -lt $MAX_CONTAINER_WAIT_ATTEMPTS ]; then
291+
echo "Waiting for container to be ready... ($CONTAINER_WAIT_ATTEMPT/$MAX_CONTAINER_WAIT_ATTEMPTS)"
292+
sleep 2
293+
fi
294+
done
295+
296+
if [ "$CONTAINER_READY" != true ]; then
297+
echo "❌ Application container failed to start or is not ready"
298+
echo "📋 Container status:"
299+
docker ps -a | grep "$CONTAINER_NAME" || echo "Container not found"
300+
echo "📋 Container logs:"
301+
docker logs "$CONTAINER_NAME" --tail 50 || true
302+
exit 1
303+
fi
304+
305+
# Даем приложению время на полный запуск
306+
echo "⏳ Waiting for application to fully start (10 seconds)..."
307+
sleep 10
308+
309+
# Очистка неиспользуемых Docker ресурсов (для экономии места)
310+
echo "🧹 Cleaning up unused Docker resources..."
311+
echo "📊 Disk usage before cleanup:"
312+
df -h / | tail -1 || true
313+
314+
# Удаляем старые версии образа (если есть)
315+
docker images "$IMAGE_NAME" --format "{{.Repository}}:{{.Tag}} {{.ID}}" | \
316+
grep -v "latest" | \
317+
awk '{print $2}' | \
318+
xargs -r docker rmi -f || true
319+
320+
# Полная очистка неиспользуемых ресурсов (без volumes для безопасности)
321+
# Удаляет: остановленные контейнеры, неиспользуемые образы, build cache, сети
322+
docker system prune -a -f || echo "⚠️ Warning: Some resources could not be cleaned up"
323+
324+
echo "📊 Disk usage after cleanup:"
325+
df -h / | tail -1 || true
326+
327+
echo "✅ Deployment completed successfully!"
328+
REMOTE_SCRIPT
329+
env:
330+
DEPLOY_SSH_PORT: ${{ secrets.DEPLOY_SSH_PORT || '22' }}
331+
PROJECT_DIR_VALUE: ${{ secrets.DEPLOY_PROJECT_PATH }}
332+
CONTAINER_NAME: ${{ secrets.DEPLOY_CONTAINER_NAME || 'atom-dbro-app' }}
333+
SERVICE_NAME: ${{ secrets.DEPLOY_SERVICE_NAME || 'app' }}
334+
IMAGE_NAME: ${{ env.IMAGE_NAME }}
335+
COMPOSE_PROJECT_NAME: ${{ secrets.DEPLOY_COMPOSE_PROJECT_NAME || '' }}

.gitignore

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# Nestjs specific
2+
/dist
3+
/node_modules
4+
/build
5+
/tmp
6+
7+
# Logs
8+
logs
9+
*.log
10+
npm-debug.log*
11+
pnpm-debug.log*
12+
yarn-debug.log*
13+
yarn-error.log*
14+
lerna-debug.log*
15+
16+
# dotenv environment variable files
17+
.env
18+
.env.development
19+
.env.test
20+
.env.production
21+
22+
# temp directory
23+
.temp
24+
.tmp
25+
26+
# Drizzle
27+
# Игнорируем только временные файлы, но коммитим миграции
28+
drizzle/.kit

0 commit comments

Comments
 (0)