285 lines
9.2 KiB
YAML
285 lines
9.2 KiB
YAML
stages:
|
|
- test
|
|
- build
|
|
- deploy
|
|
|
|
variables:
|
|
PIP_CACHE_DIR: "$CI_PROJECT_DIR/.cache/pip"
|
|
CI_PYTHON_PACKAGES_DIR: "$CI_PROJECT_DIR/.ci-packages"
|
|
APP_IMAGE_REPOSITORY: "$CI_REGISTRY_IMAGE/app"
|
|
ASTERISK_IMAGE_REPOSITORY: "$CI_REGISTRY_IMAGE/asterisk"
|
|
APP_IMAGE_TAG: "$CI_COMMIT_SHA"
|
|
ASTERISK_IMAGE_TAG: "$CI_COMMIT_SHA"
|
|
APP_IMAGE: "$CI_REGISTRY_IMAGE/app:$CI_COMMIT_SHA"
|
|
ASTERISK_IMAGE: "$CI_REGISTRY_IMAGE/asterisk:$CI_COMMIT_SHA"
|
|
DOCKER_TLS_CERTDIR: "/certs"
|
|
DOCKER_HOST: "tcp://docker:2376"
|
|
DOCKER_TLS_VERIFY: "1"
|
|
DOCKER_CERT_PATH: "$DOCKER_TLS_CERTDIR/client"
|
|
PYTHON_BIN: "python3"
|
|
CI_POSTGRES_ADMIN_URL: ""
|
|
|
|
.python_job:
|
|
tags:
|
|
- crm-shell
|
|
before_script:
|
|
- rm -rf "$CI_PYTHON_PACKAGES_DIR"
|
|
- mkdir -p "$CI_PYTHON_PACKAGES_DIR"
|
|
- $PYTHON_BIN -m pip install --target "$CI_PYTHON_PACKAGES_DIR" -r requirements.txt
|
|
- export PYTHONPATH="$CI_PYTHON_PACKAGES_DIR${PYTHONPATH:+:$PYTHONPATH}"
|
|
|
|
test:
|
|
extends: .python_job
|
|
stage: test
|
|
script:
|
|
- $PYTHON_BIN -m pytest -q
|
|
|
|
postgres-readiness:
|
|
extends: .python_job
|
|
stage: test
|
|
rules:
|
|
- if: '$ENABLE_POSTGRES_READINESS == "1" && $CI_POSTGRES_ADMIN_URL'
|
|
- when: never
|
|
variables:
|
|
SCHEMA_MANAGEMENT_MODE: migrations
|
|
CC_DATA_DIR: .testdata_pg
|
|
EVENT_BUS_ENABLED: "0"
|
|
APP_TOKEN_SECRET: ci-secret
|
|
ALLOW_LEGACY_HEADER_AUTH: "1"
|
|
TELEGRAM_BOT_ENABLED: "0"
|
|
WHATSAPP_OUTBOUND_ENABLED: "0"
|
|
AI_PROVIDER: stub
|
|
AI_TELEGRAM_ENABLED: "0"
|
|
AI_WHATSAPP_ENABLED: "0"
|
|
AI_VOICE_ENABLED: "0"
|
|
script:
|
|
- |
|
|
export CI_PG_DB="mvpcc_ci_${CI_PIPELINE_IID}_${CI_JOB_ID}"
|
|
$PYTHON_BIN - <<'PY'
|
|
import os
|
|
import psycopg
|
|
|
|
admin_url = os.environ["CI_POSTGRES_ADMIN_URL"]
|
|
db_name = os.environ["CI_PG_DB"]
|
|
|
|
with psycopg.connect(admin_url, autocommit=True) as conn:
|
|
with conn.cursor() as cur:
|
|
cur.execute(f'DROP DATABASE IF EXISTS "{db_name}" WITH (FORCE)')
|
|
cur.execute(f'CREATE DATABASE "{db_name}"')
|
|
PY
|
|
- export DATABASE_URL="postgresql://postgres@127.0.0.1:5433/${CI_PG_DB}"
|
|
- $PYTHON_BIN scripts/migrate_core_db.py
|
|
- $PYTHON_BIN -m pytest -q tests/test_postgres_readiness.py
|
|
after_script:
|
|
- |
|
|
export CI_PG_DB="mvpcc_ci_${CI_PIPELINE_IID}_${CI_JOB_ID}"
|
|
$PYTHON_BIN - <<'PY'
|
|
import os
|
|
import psycopg
|
|
|
|
admin_url = os.environ["CI_POSTGRES_ADMIN_URL"]
|
|
db_name = os.environ["CI_PG_DB"]
|
|
|
|
with psycopg.connect(admin_url, autocommit=True) as conn:
|
|
with conn.cursor() as cur:
|
|
cur.execute(f'DROP DATABASE IF EXISTS "{db_name}" WITH (FORCE)')
|
|
PY
|
|
|
|
.docker_build:
|
|
tags:
|
|
- call-center-prod
|
|
variables:
|
|
DOCKER_HOST: ""
|
|
DOCKER_TLS_VERIFY: ""
|
|
DOCKER_CERT_PATH: ""
|
|
DOCKER_TLS_CERTDIR: ""
|
|
image: docker:27
|
|
services:
|
|
- name: docker:27-dind
|
|
alias: docker
|
|
before_script:
|
|
- command -v docker >/dev/null
|
|
- echo "$CI_REGISTRY_PASSWORD" | docker login "$CI_REGISTRY" -u "$CI_REGISTRY_USER" --password-stdin
|
|
rules:
|
|
- if: '$ENABLE_REGISTRY_BUILD == "1" && $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
|
|
- if: '$ENABLE_REGISTRY_BUILD == "1" && $CI_COMMIT_TAG'
|
|
- when: never
|
|
|
|
build-app-image:
|
|
extends: .docker_build
|
|
stage: build
|
|
needs:
|
|
- test
|
|
- job: postgres-readiness
|
|
optional: true
|
|
script:
|
|
- docker build -t "$APP_IMAGE" .
|
|
- docker push "$APP_IMAGE"
|
|
- |
|
|
if [ "$CI_COMMIT_BRANCH" = "$CI_DEFAULT_BRANCH" ]; then
|
|
docker tag "$APP_IMAGE" "$APP_IMAGE_REPOSITORY:latest"
|
|
docker push "$APP_IMAGE_REPOSITORY:latest"
|
|
fi
|
|
- |
|
|
if [ -n "$CI_COMMIT_TAG" ]; then
|
|
docker tag "$APP_IMAGE" "$APP_IMAGE_REPOSITORY:$CI_COMMIT_TAG"
|
|
docker push "$APP_IMAGE_REPOSITORY:$CI_COMMIT_TAG"
|
|
fi
|
|
|
|
build-asterisk-image:
|
|
extends: .docker_build
|
|
stage: build
|
|
needs:
|
|
- test
|
|
- job: postgres-readiness
|
|
optional: true
|
|
script:
|
|
- docker build -f deployment/asterisk/Dockerfile -t "$ASTERISK_IMAGE" .
|
|
- docker push "$ASTERISK_IMAGE"
|
|
- |
|
|
if [ "$CI_COMMIT_BRANCH" = "$CI_DEFAULT_BRANCH" ]; then
|
|
docker tag "$ASTERISK_IMAGE" "$ASTERISK_IMAGE_REPOSITORY:latest"
|
|
docker push "$ASTERISK_IMAGE_REPOSITORY:latest"
|
|
fi
|
|
- |
|
|
if [ -n "$CI_COMMIT_TAG" ]; then
|
|
docker tag "$ASTERISK_IMAGE" "$ASTERISK_IMAGE_REPOSITORY:$CI_COMMIT_TAG"
|
|
docker push "$ASTERISK_IMAGE_REPOSITORY:$CI_COMMIT_TAG"
|
|
fi
|
|
|
|
deploy-production:
|
|
stage: deploy
|
|
tags:
|
|
- call-center-prod
|
|
image: alpine:3.21
|
|
needs:
|
|
- build-app-image
|
|
- build-asterisk-image
|
|
environment:
|
|
name: production
|
|
before_script:
|
|
- command -v ssh >/dev/null
|
|
- command -v rsync >/dev/null
|
|
- mkdir -p ~/.ssh
|
|
- chmod 700 ~/.ssh
|
|
- printf '%s\n' "$DEPLOY_SSH_PRIVATE_KEY" > ~/.ssh/id_ed25519
|
|
- chmod 600 ~/.ssh/id_ed25519
|
|
- ssh-keyscan -H "$DEPLOY_HOST" >> ~/.ssh/known_hosts
|
|
script:
|
|
- test -n "$DEPLOY_HOST"
|
|
- test -n "$DEPLOY_USER"
|
|
- test -n "$DEPLOY_PATH"
|
|
- ssh "$DEPLOY_USER@$DEPLOY_HOST" "mkdir -p '$DEPLOY_PATH/deployment'"
|
|
- rsync -av deployment/ "$DEPLOY_USER@$DEPLOY_HOST:$DEPLOY_PATH/deployment/"
|
|
- |
|
|
ssh "$DEPLOY_USER@$DEPLOY_HOST" \
|
|
"mkdir -p '$DEPLOY_PATH/.data_local/generated_ivr_yandex/ivr' '$DEPLOY_PATH/.data_local/recordings' '$DEPLOY_PATH/.asterisk_assets/ivr'"
|
|
- |
|
|
if [ -d .asterisk_assets/ ]; then
|
|
rsync -av .asterisk_assets/ "$DEPLOY_USER@$DEPLOY_HOST:$DEPLOY_PATH/.asterisk_assets/"
|
|
fi
|
|
- |
|
|
if [ -d .asterisk_assets/ivr/ ]; then
|
|
rsync -av --delete .asterisk_assets/ivr/ "$DEPLOY_USER@$DEPLOY_HOST:$DEPLOY_PATH/.data_local/generated_ivr_yandex/ivr/"
|
|
fi
|
|
- |
|
|
ssh "$DEPLOY_USER@$DEPLOY_HOST" \
|
|
"printf 'APP_IMAGE=%s\nASTERISK_IMAGE=%s\n' '$APP_IMAGE' '$ASTERISK_IMAGE' > '$DEPLOY_PATH/deployment/.env.images'"
|
|
- |
|
|
ssh "$DEPLOY_USER@$DEPLOY_HOST" \
|
|
"echo '$CI_REGISTRY_PASSWORD' | docker login '$CI_REGISTRY' -u '$CI_REGISTRY_USER' --password-stdin && \
|
|
cd '$DEPLOY_PATH/deployment' && \
|
|
docker compose --env-file .env.images -f docker-compose.server.registry.yml pull && \
|
|
docker compose --env-file .env.images -f docker-compose.server.registry.yml up -d && \
|
|
docker compose --env-file .env.images -f docker-compose.asterisk.server.registry.yml pull && \
|
|
docker compose --env-file .env.images -f docker-compose.asterisk.server.registry.yml up -d"
|
|
rules:
|
|
- if: '$ENABLE_REGISTRY_BUILD == "1" && $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
|
|
when: manual
|
|
- when: never
|
|
allow_failure: false
|
|
|
|
deploy-production-source:
|
|
stage: deploy
|
|
tags:
|
|
- call-center-prod
|
|
needs:
|
|
- test
|
|
- job: postgres-readiness
|
|
optional: true
|
|
variables:
|
|
DOCKER_HOST: ""
|
|
DOCKER_TLS_VERIFY: ""
|
|
DOCKER_CERT_PATH: ""
|
|
DOCKER_TLS_CERTDIR: ""
|
|
DEPLOY_DIR: /home/gitlab-runner/deploy/call-center
|
|
APP_IMAGE_NAME: call-center-app
|
|
HEALTHCHECK_URL: http://127.0.0.1:8080/health
|
|
environment:
|
|
name: production
|
|
before_script:
|
|
- command -v rsync >/dev/null
|
|
- command -v docker >/dev/null
|
|
- command -v python3 >/dev/null
|
|
script:
|
|
- bash scripts/deploy_gitlab.sh
|
|
rules:
|
|
- if: '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
|
|
- if: '$CI_PIPELINE_SOURCE == "web" && $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
|
|
allow_failure: false
|
|
|
|
hotfix-analyst-assets:
|
|
stage: deploy
|
|
tags:
|
|
- call-center-prod
|
|
variables:
|
|
DOCKER_HOST: ""
|
|
DOCKER_TLS_VERIFY: ""
|
|
DOCKER_CERT_PATH: ""
|
|
DOCKER_TLS_CERTDIR: ""
|
|
needs:
|
|
- test
|
|
script:
|
|
- command -v docker >/dev/null
|
|
- |
|
|
GATEWAY_CONTAINER="$(docker ps --format '{{.Names}}' | grep -E '^(call-center-app|call-center-api-gateway($|[-_]))' | head -n1)"
|
|
test -n "$GATEWAY_CONTAINER"
|
|
echo "Using gateway container: $GATEWAY_CONTAINER"
|
|
docker exec "$GATEWAY_CONTAINER" sh -lc 'mkdir -p /app/gateway /app/ui/analyst /app/ui/operator'
|
|
docker cp gateway/app.py "$GATEWAY_CONTAINER:/app/gateway/app.py"
|
|
docker cp ui/analyst/index.html "$GATEWAY_CONTAINER:/app/ui/analyst/index.html"
|
|
docker cp ui/analyst/app.js "$GATEWAY_CONTAINER:/app/ui/analyst/app.js"
|
|
docker cp ui/analyst/mock-analytics.json "$GATEWAY_CONTAINER:/app/ui/analyst/mock-analytics.json"
|
|
docker cp ui/operator/styles.css "$GATEWAY_CONTAINER:/app/ui/operator/styles.css"
|
|
docker restart "$GATEWAY_CONTAINER"
|
|
rules:
|
|
- if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
|
|
when: manual
|
|
- when: never
|
|
allow_failure: false
|
|
|
|
prune-demo-data-production:
|
|
stage: deploy
|
|
tags:
|
|
- call-center-prod
|
|
needs:
|
|
- deploy-production-source
|
|
variables:
|
|
DOCKER_HOST: ""
|
|
DOCKER_TLS_VERIFY: ""
|
|
DOCKER_CERT_PATH: ""
|
|
DOCKER_TLS_CERTDIR: ""
|
|
DEPLOY_DIR: /home/gitlab-runner/deploy/call-center
|
|
KEEP_LATEST_INTERACTIONS: "6"
|
|
script:
|
|
- command -v python3 >/dev/null
|
|
- test -d "$DEPLOY_DIR"
|
|
- test -f "$DEPLOY_DIR/.env.production"
|
|
- mkdir -p "$DEPLOY_DIR/.db_backups"
|
|
- python3 scripts/prune_demo_interactions.py --env-file "$DEPLOY_DIR/.env.production" --base-dir "$DEPLOY_DIR" --backup-dir "$DEPLOY_DIR/.db_backups" --keep-latest-interactions "$KEEP_LATEST_INTERACTIONS"
|
|
rules:
|
|
- if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
|
|
when: manual
|
|
- when: never
|
|
allow_failure: false
|