didi-lot1-ai/ai_platform/modules/domain_check/.gitlab-ci.yml

325 lines
8.6 KiB
YAML

# ===========================================
# Domain Check API - GitLab CI/CD Pipeline
# ===========================================
# Registry: <registry-didi> (GitLab Container Registry)
# Runner: Main Docker Runner (10.11.10.102) - tags: docker, ci, cd
# ===========================================
stages:
- lint
- security
- test
- build
- release
- deploy
variables:
DOCKER_DRIVER: overlay2
DOCKER_TLS_CERTDIR: "/certs"
# GitLab CI provides these automatically:
# CI_REGISTRY = <registry-didi>
# CI_REGISTRY_IMAGE = didiai-domain-check
# CI_REGISTRY_USER / CI_REGISTRY_PASSWORD = auto-generated JWT
# Cache pip packages
cache:
key: ${CI_COMMIT_REF_SLUG}
paths:
- .cache/pip
# ===========================================
# LINT Stage
# ===========================================
lint:python:
stage: lint
image: python:3.10-slim
tags:
- docker
before_script:
- pip install --cache-dir .cache/pip flake8 black isort
script:
- flake8 api/app --max-line-length=120 --ignore=E501,W503 || true
- black --check api/app || true
- isort --check-only api/app || true
allow_failure: true
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- if: $CI_COMMIT_BRANCH
lint:dockerfile:
stage: lint
image: hadolint/hadolint:latest-debian
tags:
- docker
script:
- hadolint api/Dockerfile || true
allow_failure: true
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- if: $CI_COMMIT_BRANCH
# ===========================================
# SECURITY Stage (SAST)
# ===========================================
sast:bandit:
stage: security
image: python:3.10-slim
tags:
- docker
before_script:
- pip install --cache-dir .cache/pip bandit
script:
- bandit -r api/app -f json -o bandit-report.json || true
- bandit -r api/app -f txt || true
artifacts:
paths:
- bandit-report.json
when: always
expire_in: 1 week
allow_failure: true
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- if: $CI_COMMIT_BRANCH
sast:safety:
stage: security
image: python:3.10-slim
tags:
- docker
before_script:
- pip install --cache-dir .cache/pip safety
script:
- cd api && safety check -r requirements.txt --json > ../safety-report.json || true
- cd api && safety check -r requirements.txt || true
artifacts:
paths:
- safety-report.json
when: always
expire_in: 1 week
allow_failure: true
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- if: $CI_COMMIT_BRANCH
sast:trivy:
stage: security
image:
name: aquasec/trivy:latest
entrypoint: [""]
tags:
- docker
script:
- trivy fs --exit-code 0 --severity HIGH,CRITICAL --format json -o trivy-report.json . || true
- trivy fs --exit-code 0 --severity HIGH,CRITICAL . || true
artifacts:
paths:
- trivy-report.json
when: always
expire_in: 1 week
allow_failure: true
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- if: $CI_COMMIT_BRANCH
# ===========================================
# TEST Stage
# ===========================================
test:api:
stage: test
image: python:3.10-slim
tags:
- docker
services:
- postgres:15-alpine
- redis:7-alpine
variables:
POSTGRES_DB: test_domain_check
POSTGRES_USER: test_user
POSTGRES_PASSWORD: test_password
DATABASE_URL: postgresql://test_user:test_password@postgres:5432/test_domain_check
REDIS_URL: redis://redis:6379/0
FLASK_ENV: testing
before_script:
- cd api
- pip install --cache-dir ../.cache/pip -r requirements.txt
script:
- python -c "from app import create_app; app = create_app(); print('App created successfully')"
allow_failure: true
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- if: $CI_COMMIT_BRANCH
# ===========================================
# BUILD Stage - Push to GitLab Container Registry
# ===========================================
build:docker:
stage: build
image: docker:24-dind
tags:
- docker
- ci
services:
- docker:24-dind
before_script:
# Login to GitLab Container Registry (auto credentials)
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
script:
- echo "Building image $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG"
- docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG -t $CI_REGISTRY_IMAGE:latest ./api
- docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG
- docker push $CI_REGISTRY_IMAGE:latest
- echo "Image pushed to $CI_REGISTRY_IMAGE"
rules:
- if: $CI_COMMIT_BRANCH == "main"
- if: $CI_COMMIT_BRANCH == "develop"
# ===========================================
# RELEASE Stage - Create deployment bundle
# ===========================================
release:bundle:
stage: release
image: alpine:latest
tags:
- docker
script:
- apk add --no-cache tar gzip
- mkdir -p release-bundle/deploy
# Copy deployment files
- cp docker-compose.yml release-bundle/deploy/
- cp deploy.sh release-bundle/deploy/ 2>/dev/null || echo "#!/bin/bash" > release-bundle/deploy/deploy.sh
- cp .env.example release-bundle/deploy/ 2>/dev/null || cp .env release-bundle/deploy/env.example 2>/dev/null || true
# Create IMAGE.txt with registry path
- echo "$CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG" > release-bundle/IMAGE.txt
- echo "$CI_REGISTRY_IMAGE:latest" >> release-bundle/IMAGE.txt
# Create restore script
- |
cat > release-bundle/restore.sh << 'RESTORE_EOF'
#!/bin/bash
set -e
echo "=== Domain Check API - Restore ==="
IMAGE=$(head -1 IMAGE.txt)
echo "Pulling image: $IMAGE"
docker pull $IMAGE
echo "Starting services..."
cd deploy
docker compose up -d
echo "Done! Check: docker compose ps"
RESTORE_EOF
- chmod +x release-bundle/restore.sh release-bundle/deploy/deploy.sh
# Create tarball
- tar -czvf release-bundle-${CI_COMMIT_SHORT_SHA}.tgz release-bundle/
- ls -la release-bundle-*.tgz
artifacts:
paths:
- release-bundle-*.tgz
expire_in: 30 days
rules:
- if: $CI_COMMIT_BRANCH == "main"
# ===========================================
# DEPLOY Stage - Deploy to server via SSH
# ===========================================
deploy:dev:
stage: deploy
image: alpine:latest
tags:
- docker
- cd
before_script:
- apk add --no-cache openssh-client
- eval $(ssh-agent -s)
- echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add -
- mkdir -p ~/.ssh
- chmod 700 ~/.ssh
- echo "$SSH_KNOWN_HOSTS" >> ~/.ssh/known_hosts
- chmod 644 ~/.ssh/known_hosts
script:
- |
ssh $DEPLOY_USER@$DEPLOY_HOST << ENDSSH
set -e
echo "=== Deploying Domain Check API ==="
cd /home/admin365/domain-check
# Pull latest code
git pull origin main
# Login to GitLab registry
docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
# Pull latest image
docker pull $CI_REGISTRY_IMAGE:latest || echo "Pull failed, building locally"
# Restart services
docker compose down
docker compose up -d --build
# Health check
sleep 10
curl -s http://localhost:51000/health || echo "Health check pending..."
docker compose ps
echo "=== Deploy complete ==="
ENDSSH
environment:
name: development
url: http://domain-check-api:11000
rules:
- if: $CI_COMMIT_BRANCH == "main"
when: manual
allow_failure: true
deploy:auto:
stage: deploy
image: alpine:latest
tags:
- docker
- cd
before_script:
- apk add --no-cache openssh-client
- eval $(ssh-agent -s)
- echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add -
- mkdir -p ~/.ssh
- chmod 700 ~/.ssh
- echo "$SSH_KNOWN_HOSTS" >> ~/.ssh/known_hosts
- chmod 644 ~/.ssh/known_hosts
script:
- |
ssh $DEPLOY_USER@$DEPLOY_HOST << ENDSSH
set -e
cd /home/admin365/domain-check
git pull origin main
docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY || true
docker pull $CI_REGISTRY_IMAGE:latest || true
docker compose up -d --build
docker compose ps
ENDSSH
environment:
name: development
url: http://domain-check-api:11000
rules:
- if: $CI_COMMIT_BRANCH == "main"
allow_failure: true
# ===========================================
# Cleanup
# ===========================================
cleanup:images:
stage: .post
image: docker:24-dind
tags:
- docker
services:
- docker:24-dind
script:
- docker image prune -f
when: always
allow_failure: true
rules:
- if: $CI_COMMIT_BRANCH == "main"