1. run.sh: start/stop/restart/status/logs/build/test - PID file management in .pids/ - Colored output, health check wait, log files - Auto alembic migrate on start 2. DB Connector (replaces mock aiomysql): - MySQL: aiomysql (connect/query/dml/test) - PostgreSQL: asyncpg (connect/query/dml/test) - SQLite: sqlite3 via executor (connect/query/dml/test) - Unified interface: DBConnector.test_connection/execute_query/execute_dml - Connection test returns version + latency 3. GitLab CI/CD pipeline: - test: backend pytest + frontend build - build: Docker images (manual trigger) - deploy: SSH deploy (manual trigger) - Cache: pip + node_modules Tests: 128 passing, frontend build: OK
98 lines
2.4 KiB
YAML
98 lines
2.4 KiB
YAML
stages:
|
|
- test
|
|
- build
|
|
- deploy
|
|
|
|
variables:
|
|
PIP_CACHE_DIR: "$CI_PROJECT_DIR/.cache/pip"
|
|
|
|
# Cache
|
|
cache:
|
|
key: "${CI_COMMIT_REF_SLUG}"
|
|
paths:
|
|
- backend/venv/
|
|
- frontend/node_modules/
|
|
- .cache/pip/
|
|
|
|
# ===== Test Stage =====
|
|
|
|
backend-test:
|
|
stage: test
|
|
image: python:3.11-slim
|
|
before_script:
|
|
- cd backend
|
|
- python -m venv venv
|
|
- source venv/bin/activate
|
|
- pip install -r requirements.txt -q
|
|
script:
|
|
- source venv/bin/activate
|
|
- python -m pytest tests/ -v --tb=short --junitxml=report.xml
|
|
artifacts:
|
|
reports:
|
|
junit: backend/report.xml
|
|
when: always
|
|
rules:
|
|
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
|
|
- if: '$CI_COMMIT_BRANCH == "master"'
|
|
|
|
frontend-build:
|
|
stage: test
|
|
image: node:20-alpine
|
|
before_script:
|
|
- cd frontend
|
|
- npm ci
|
|
script:
|
|
- npm run build
|
|
artifacts:
|
|
paths:
|
|
- frontend/dist/
|
|
expire_in: 1 day
|
|
rules:
|
|
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
|
|
- if: '$CI_COMMIT_BRANCH == "master"'
|
|
|
|
# ===== Build Stage =====
|
|
|
|
docker-build:
|
|
stage: build
|
|
image: docker:24
|
|
services:
|
|
- docker:24-dind
|
|
variables:
|
|
DOCKER_TLS_CERTDIR: "/certs"
|
|
before_script:
|
|
- docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" "$CI_REGISTRY" 2>/dev/null || true
|
|
script:
|
|
- docker build -t $CI_REGISTRY_IMAGE/backend:$CI_COMMIT_SHORT_SHA ./backend
|
|
- docker build -t $CI_REGISTRY_IMAGE/frontend:$CI_COMMIT_SHORT_SHA ./frontend
|
|
- docker push $CI_REGISTRY_IMAGE/backend:$CI_COMMIT_SHORT_SHA 2>/dev/null || true
|
|
- docker push $CI_REGISTRY_IMAGE/frontend:$CI_COMMIT_SHORT_SHA 2>/dev/null || true
|
|
- echo "Build complete: $CI_COMMIT_SHORT_SHA"
|
|
rules:
|
|
- if: '$CI_COMMIT_BRANCH == "master"'
|
|
when: manual
|
|
tags:
|
|
- docker
|
|
|
|
# ===== Deploy Stage =====
|
|
|
|
deploy:
|
|
stage: deploy
|
|
image: alpine:3.19
|
|
before_script:
|
|
- apk add --no-cache openssh-client
|
|
- eval $(ssh-agent -s)
|
|
- echo "$DEPLOY_SSH_KEY" | tr -d '\r' | ssh-add -
|
|
- mkdir -p ~/.ssh && chmod 700 ~/.ssh
|
|
- echo "$DEPLOY_SSH_HOST_KEY" > ~/.ssh/known_hosts 2>/dev/null || true
|
|
script:
|
|
- ssh $DEPLOY_USER@$DEPLOY_HOST "cd $DEPLOY_PATH && docker compose pull && docker compose up -d && echo 'Deploy done'"
|
|
environment:
|
|
name: production
|
|
url: http://$DEPLOY_HOST
|
|
rules:
|
|
- if: '$CI_COMMIT_BRANCH == "master"'
|
|
when: manual
|
|
tags:
|
|
- deploy
|