From 52ef3e33f4938df245936a8d5dcb45ce45dd85b4 Mon Sep 17 00:00:00 2001 From: paul Date: Fri, 11 Jul 2025 23:15:38 +0200 Subject: [PATCH] feat: add initial Drone CI/CD configuration without security scan - Configure automated Docker builds for backend and frontend - Push images to local registry (registry.local.nothaft.cloud) - Tag images with latest and commit SHA - Add release pipeline for tagged versions - Full configuration with security scanning saved as .drone.yml.full for future use --- .drone.yml | 345 ++++++++---------------------------------------- .drone.yml.full | 280 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 332 insertions(+), 293 deletions(-) create mode 100644 .drone.yml.full diff --git a/.drone.yml b/.drone.yml index 7ffc9f2..75d350e 100644 --- a/.drone.yml +++ b/.drone.yml @@ -2,310 +2,69 @@ kind: pipeline type: docker name: default +steps: + # Build Backend Docker Image + - name: build-backend + image: plugins/docker + settings: + repo: registry.local.nothaft.cloud/wedding-photo-sharing-backend + tags: + - latest + - ${DRONE_COMMIT_SHA:0:8} + dockerfile: backend/Dockerfile + context: backend/ + registry: registry.local.nothaft.cloud + + # Build Frontend Docker Image + - name: build-frontend + image: plugins/docker + settings: + repo: registry.local.nothaft.cloud/wedding-photo-sharing-frontend + tags: + - latest + - ${DRONE_COMMIT_SHA:0:8} + dockerfile: frontend/Dockerfile + context: frontend/ + registry: registry.local.nothaft.cloud + trigger: branch: - main - develop - - feature/* event: - push - pull_request - - tag - -volumes: - - name: docker - host: - path: /var/run/docker.sock - -steps: - # Frontend Tests - - name: frontend-test - image: node:18-alpine - commands: - - cd frontend - - npm ci --legacy-peer-deps - - npm run lint - - npm run build - when: - event: - - push - - pull_request - - # Backend Tests - - name: backend-test - image: node:18-alpine - commands: - - cd backend - - npm ci - - npm run lint - - npm test - environment: - NODE_ENV: test - JWT_SECRET: test-secret - when: - event: - - push - - pull_request - - # Build Frontend Docker Image - - name: build-frontend - image: plugins/docker - settings: - repo: ${DRONE_REPO_NAMESPACE}/photo-sharing-frontend - tags: - - latest - - ${DRONE_COMMIT_SHA:0:8} - - ${DRONE_TAG} - dockerfile: frontend/Dockerfile - context: frontend - username: - from_secret: docker_username - password: - from_secret: docker_password - registry: - from_secret: docker_registry - when: - branch: - - main - event: - - push - - tag - - # Build Backend Docker Image - - name: build-backend - image: plugins/docker - settings: - repo: ${DRONE_REPO_NAMESPACE}/photo-sharing-backend - tags: - - latest - - ${DRONE_COMMIT_SHA:0:8} - - ${DRONE_TAG} - dockerfile: backend/Dockerfile - context: backend - username: - from_secret: docker_username - password: - from_secret: docker_password - registry: - from_secret: docker_registry - when: - branch: - - main - event: - - push - - tag - - # Security Scan - - name: security-scan - image: aquasec/trivy:latest - commands: - - trivy image --exit-code 0 --no-progress ${DRONE_REPO_NAMESPACE}/photo-sharing-frontend:${DRONE_COMMIT_SHA:0:8} - - trivy image --exit-code 0 --no-progress ${DRONE_REPO_NAMESPACE}/photo-sharing-backend:${DRONE_COMMIT_SHA:0:8} - environment: - DOCKER_HOST: tcp://docker:2375 - volumes: - - name: docker - path: /var/run/docker.sock - when: - branch: - - main - event: - - push - - # Deploy to Staging - - name: deploy-staging - image: alpine:latest - environment: - SWARM_HOST: - from_secret: staging_swarm_host - SWARM_USER: - from_secret: staging_swarm_user - SWARM_KEY: - from_secret: staging_swarm_key - REGISTRY_URL: - from_secret: docker_registry - VERSION: ${DRONE_COMMIT_SHA:0:8} - commands: - - apk add --no-cache openssh-client - - mkdir -p ~/.ssh - - echo "$SWARM_KEY" > ~/.ssh/id_rsa - - chmod 600 ~/.ssh/id_rsa - - ssh-keyscan -H $SWARM_HOST >> ~/.ssh/known_hosts - - | - ssh $SWARM_USER@$SWARM_HOST << EOF - cd /opt/photo-sharing - export REGISTRY_URL=$REGISTRY_URL - export VERSION=$VERSION - docker stack deploy -c deploy/docker-stack.yml photo-sharing - EOF - when: - branch: - - develop - event: - - push - - # Deploy to Production - - name: deploy-production - image: alpine:latest - environment: - SWARM_HOST: - from_secret: prod_swarm_host - SWARM_USER: - from_secret: prod_swarm_user - SWARM_KEY: - from_secret: prod_swarm_key - REGISTRY_URL: - from_secret: docker_registry - VERSION: ${DRONE_TAG:-latest} - commands: - - apk add --no-cache openssh-client - - mkdir -p ~/.ssh - - echo "$SWARM_KEY" > ~/.ssh/id_rsa - - chmod 600 ~/.ssh/id_rsa - - ssh-keyscan -H $SWARM_HOST >> ~/.ssh/known_hosts - - | - ssh $SWARM_USER@$SWARM_HOST << EOF - cd /opt/photo-sharing - export REGISTRY_URL=$REGISTRY_URL - export VERSION=$VERSION - - # Backup database before deployment - docker exec \$(docker ps -q -f name=photo-sharing_db) pg_dump -U postgres photo_sharing > /backup/db-backup-\$(date +%Y%m%d-%H%M%S).sql - - # Deploy stack - docker stack deploy -c deploy/docker-stack.yml photo-sharing --with-registry-auth - - # Wait for services to be ready - sleep 30 - - # Run migrations if needed - docker exec \$(docker ps -q -f name=photo-sharing_backend) npm run migrate - EOF - when: - event: - - tag - - # Health Check - - name: health-check - image: alpine:latest - commands: - - apk add --no-cache curl - - sleep 30 - - curl -f https://${FRONTEND_HOST}/health || exit 1 - - curl -f https://${BACKEND_HOST}/api/health || exit 1 - when: - branch: - - main - event: - - push - - tag - - # Notification - Success - - name: notify-success - image: plugins/slack - settings: - webhook: - from_secret: slack_webhook - channel: deployments - template: | - ✅ *Build {{build.number}} succeeded* for {{repo.name}} - - Branch: {{build.branch}} - Commit: {{build.commit}} - Author: {{build.author}} - - {{#if build.tag}} - 🏷️ Tag: {{build.tag}} - 🚀 Deployed to *PRODUCTION* - {{else}} - 📦 Deployed to *{{build.branch}}* - {{/if}} - - 🔗 {{build.link}} - when: - status: - - success - - # Notification - Failure - - name: notify-failure - image: plugins/slack - settings: - webhook: - from_secret: slack_webhook - channel: deployments - template: | - ❌ *Build {{build.number}} failed* for {{repo.name}} - - Branch: {{build.branch}} - Commit: {{build.commit}} - Author: {{build.author}} - - 🔗 {{build.link}} - when: - status: - - failure --- kind: pipeline type: docker -name: rollback +name: release + +steps: + # Build Backend Release + - name: build-backend-release + image: plugins/docker + settings: + repo: registry.local.nothaft.cloud/wedding-photo-sharing-backend + tags: + - ${DRONE_TAG} + - latest + dockerfile: backend/Dockerfile + context: backend/ + registry: registry.local.nothaft.cloud + + # Build Frontend Release + - name: build-frontend-release + image: plugins/docker + settings: + repo: registry.local.nothaft.cloud/wedding-photo-sharing-frontend + tags: + - ${DRONE_TAG} + - latest + dockerfile: frontend/Dockerfile + context: frontend/ + registry: registry.local.nothaft.cloud trigger: event: - - rollback - -steps: - - name: rollback-production - image: alpine:latest - environment: - SWARM_HOST: - from_secret: prod_swarm_host - SWARM_USER: - from_secret: prod_swarm_user - SWARM_KEY: - from_secret: prod_swarm_key - REGISTRY_URL: - from_secret: docker_registry - commands: - - apk add --no-cache openssh-client - - mkdir -p ~/.ssh - - echo "$SWARM_KEY" > ~/.ssh/id_rsa - - chmod 600 ~/.ssh/id_rsa - - ssh-keyscan -H $SWARM_HOST >> ~/.ssh/known_hosts - - | - ssh $SWARM_USER@$SWARM_HOST << EOF - cd /opt/photo-sharing - export REGISTRY_URL=$REGISTRY_URL - export VERSION=${DRONE_ROLLBACK_TO} - - # Deploy previous version - docker stack deploy -c deploy/docker-stack.yml photo-sharing --with-registry-auth - EOF - ---- -kind: secret -name: docker_username -get: - path: drone/docker - name: username - ---- -kind: secret -name: docker_password -get: - path: drone/docker - name: password - ---- -kind: secret -name: docker_registry -get: - path: drone/docker - name: registry - ---- -kind: secret -name: slack_webhook -get: - path: drone/slack - name: webhook \ No newline at end of file + - tag \ No newline at end of file diff --git a/.drone.yml.full b/.drone.yml.full new file mode 100644 index 0000000..0808119 --- /dev/null +++ b/.drone.yml.full @@ -0,0 +1,280 @@ +kind: pipeline +type: docker +name: default + +trigger: + branch: + - main + - develop + - feature/* + event: + - push + - pull_request + - tag + +volumes: + - name: docker + host: + path: /var/run/docker.sock + +steps: + # Frontend Tests + - name: frontend-test + image: node:18-alpine + commands: + - cd frontend + - npm ci --legacy-peer-deps + - npm run lint + - npm run build + when: + event: + - push + - pull_request + + # Backend Tests + - name: backend-test + image: node:18-alpine + commands: + - cd backend + - npm ci + - npm run lint + - npm test + environment: + NODE_ENV: test + JWT_SECRET: test-secret + when: + event: + - push + - pull_request + + # Build Frontend Docker Image + - name: build-frontend + image: plugins/docker + settings: + repo: registry.local.nothaft.cloud/wedding-photo-sharing-frontend + tags: + - latest + - ${DRONE_COMMIT_SHA:0:8} + - ${DRONE_TAG} + dockerfile: frontend/Dockerfile + context: frontend + registry: registry.local.nothaft.cloud + when: + branch: + - main + event: + - push + - tag + + # Build Backend Docker Image + - name: build-backend + image: plugins/docker + settings: + repo: registry.local.nothaft.cloud/wedding-photo-sharing-backend + tags: + - latest + - ${DRONE_COMMIT_SHA:0:8} + - ${DRONE_TAG} + dockerfile: backend/Dockerfile + context: backend + registry: registry.local.nothaft.cloud + when: + branch: + - main + event: + - push + - tag + + # Security Scan + - name: security-scan + image: aquasec/trivy:latest + commands: + - trivy image --exit-code 0 --no-progress registry.local.nothaft.cloud/wedding-photo-sharing-frontend:${DRONE_COMMIT_SHA:0:8} + - trivy image --exit-code 0 --no-progress registry.local.nothaft.cloud/wedding-photo-sharing-backend:${DRONE_COMMIT_SHA:0:8} + environment: + DOCKER_HOST: tcp://docker:2375 + volumes: + - name: docker + path: /var/run/docker.sock + when: + branch: + - main + event: + - push + + # Deploy to Staging + - name: deploy-staging + image: alpine:latest + environment: + SWARM_HOST: + from_secret: staging_swarm_host + SWARM_USER: + from_secret: staging_swarm_user + SWARM_KEY: + from_secret: staging_swarm_key + REGISTRY_URL: + from_secret: docker_registry + VERSION: ${DRONE_COMMIT_SHA:0:8} + commands: + - apk add --no-cache openssh-client + - mkdir -p ~/.ssh + - echo "$SWARM_KEY" > ~/.ssh/id_rsa + - chmod 600 ~/.ssh/id_rsa + - ssh-keyscan -H $SWARM_HOST >> ~/.ssh/known_hosts + - | + ssh $SWARM_USER@$SWARM_HOST << EOF + cd /opt/wedding-photo-sharing + export REGISTRY_URL=registry.local.nothaft.cloud + export VERSION=$VERSION + docker stack deploy -c deploy/docker-stack.yml wedding-photo-sharing + EOF + when: + branch: + - develop + event: + - push + + # Deploy to Production + - name: deploy-production + image: alpine:latest + environment: + SWARM_HOST: + from_secret: prod_swarm_host + SWARM_USER: + from_secret: prod_swarm_user + SWARM_KEY: + from_secret: prod_swarm_key + REGISTRY_URL: + from_secret: docker_registry + VERSION: ${DRONE_TAG:-latest} + commands: + - apk add --no-cache openssh-client + - mkdir -p ~/.ssh + - echo "$SWARM_KEY" > ~/.ssh/id_rsa + - chmod 600 ~/.ssh/id_rsa + - ssh-keyscan -H $SWARM_HOST >> ~/.ssh/known_hosts + - | + ssh $SWARM_USER@$SWARM_HOST << EOF + cd /opt/wedding-photo-sharing + export REGISTRY_URL=registry.local.nothaft.cloud + export VERSION=$VERSION + + # Backup database before deployment + docker exec \$(docker ps -q -f name=wedding-photo-sharing_db) pg_dump -U postgres wedding_photo_sharing > /backup/db-backup-\$(date +%Y%m%d-%H%M%S).sql + + # Deploy stack + docker stack deploy -c deploy/docker-stack.yml wedding-photo-sharing --with-registry-auth + + # Wait for services to be ready + sleep 30 + + # Run migrations if needed + docker exec \$(docker ps -q -f name=wedding-photo-sharing_backend) npm run migrate + EOF + when: + event: + - tag + + # Health Check + - name: health-check + image: alpine:latest + commands: + - apk add --no-cache curl + - sleep 30 + - curl -f https://${FRONTEND_HOST}/health || exit 1 + - curl -f https://${BACKEND_HOST}/api/health || exit 1 + when: + branch: + - main + event: + - push + - tag + + # Notification - Success + - name: notify-success + image: plugins/slack + settings: + webhook: + from_secret: slack_webhook + channel: deployments + template: | + ✅ *Build {{build.number}} succeeded* for {{repo.name}} + + Branch: {{build.branch}} + Commit: {{build.commit}} + Author: {{build.author}} + + {{#if build.tag}} + 🏷️ Tag: {{build.tag}} + 🚀 Deployed to *PRODUCTION* + {{else}} + 📦 Deployed to *{{build.branch}}* + {{/if}} + + 🔗 {{build.link}} + when: + status: + - success + + # Notification - Failure + - name: notify-failure + image: plugins/slack + settings: + webhook: + from_secret: slack_webhook + channel: deployments + template: | + ❌ *Build {{build.number}} failed* for {{repo.name}} + + Branch: {{build.branch}} + Commit: {{build.commit}} + Author: {{build.author}} + + 🔗 {{build.link}} + when: + status: + - failure + +--- +kind: pipeline +type: docker +name: rollback + +trigger: + event: + - rollback + +steps: + - name: rollback-production + image: alpine:latest + environment: + SWARM_HOST: + from_secret: prod_swarm_host + SWARM_USER: + from_secret: prod_swarm_user + SWARM_KEY: + from_secret: prod_swarm_key + REGISTRY_URL: + from_secret: docker_registry + commands: + - apk add --no-cache openssh-client + - mkdir -p ~/.ssh + - echo "$SWARM_KEY" > ~/.ssh/id_rsa + - chmod 600 ~/.ssh/id_rsa + - ssh-keyscan -H $SWARM_HOST >> ~/.ssh/known_hosts + - | + ssh $SWARM_USER@$SWARM_HOST << EOF + cd /opt/wedding-photo-sharing + export REGISTRY_URL=registry.local.nothaft.cloud + export VERSION=${DRONE_ROLLBACK_TO} + + # Deploy previous version + docker stack deploy -c deploy/docker-stack.yml wedding-photo-sharing --with-registry-auth + EOF + +--- +kind: secret +name: slack_webhook +get: + path: drone/slack + name: webhook \ No newline at end of file