325 lines
8.6 KiB
YAML
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"
|