diff --git a/.woodpecker.yml b/.woodpecker.yml index 481afd4..f2840e2 100644 --- a/.woodpecker.yml +++ b/.woodpecker.yml @@ -116,7 +116,7 @@ steps: SECRET_KEY: from_secret: secret_key commands: - - echo "DOMAIN=$APP_DOMAIN" > .env.deploy + - echo "APP_DOMAIN=$APP_DOMAIN" > .env.deploy - echo "POSTGRES_PASSWORD=$POSTGRES_PASSWORD" >> .env.deploy - echo "SECRET_KEY=$SECRET_KEY" >> .env.deploy - OWNER=$(echo "$CI_REPO_OWNER" | tr 'A-Z' 'a-z') && REPO=$(echo "$CI_REPO_NAME" | tr 'A-Z' 'a-z') && BRANCH=$(echo "$CI_COMMIT_BRANCH" | tr 'A-Z/' 'a-z-') && echo "COMPOSE_PROJECT_NAME=$OWNER-$REPO-$BRANCH" >> .env.deploy @@ -135,9 +135,9 @@ steps: [ -z "$VAL" ] && echo "FAIL: COMPOSE_PROJECT_NAME vide" && exit 1 echo "PASS: COMPOSE_PROJECT_NAME = $VAL" - | - VAL=$(grep '^DOMAIN=' .env.deploy | cut -d= -f2) - [ -z "$VAL" ] && echo "FAIL: DOMAIN vide" && exit 1 - echo "PASS: DOMAIN = $VAL" + VAL=$(grep '^APP_DOMAIN=' .env.deploy | cut -d= -f2) + [ -z "$VAL" ] && echo "FAIL: APP_DOMAIN vide" && exit 1 + echo "PASS: APP_DOMAIN = $VAL" # NOTE: volumes + pas de from_secret : compatible # Fabio/Traefik routing géré via labels du docker-compose.yml @@ -152,6 +152,24 @@ steps: - cp .env.deploy /opt/libredecision/.env - chmod 600 /opt/libredecision/.env - cp docker/docker-compose.yml /opt/libredecision/docker-compose.yml + # Arreter avant le challenge ACME : libere le webroot pour sonic-acme-1 + - cd /opt/libredecision && docker compose stop + - | + DOMAIN=$(grep '^APP_DOMAIN=' /opt/libredecision/.env | cut -d= -f2) + ACME_EXIT=0 + docker exec sonic-acme-1 /app/acme.sh \ + --home /etc/acme.sh \ + --issue -d "$DOMAIN" \ + --webroot /usr/share/nginx/html \ + --server letsencrypt \ + --accountemail support+acme@asycn.io || ACME_EXIT=$? + if [ "$ACME_EXIT" -ne 0 ] && [ "$ACME_EXIT" -ne 2 ]; then + echo "ERREUR: acme.sh a echoue (exit $ACME_EXIT)" + exit 1 + fi + docker exec sonic-acme-1 cp /etc/acme.sh/$DOMAIN/fullchain.cer /host/certs/$DOMAIN-cert.pem + docker exec sonic-acme-1 cp /etc/acme.sh/$DOMAIN/$DOMAIN.key /host/certs/$DOMAIN-key.pem + echo "TLS OK (acme exit $ACME_EXIT)" # Images construites localement dans la pipeline : pas de docker compose pull - cd /opt/libredecision && docker compose up -d --remove-orphans - cd /opt/libredecision && docker compose ps @@ -168,9 +186,9 @@ steps: - | PROJECT=$(grep '^COMPOSE_PROJECT_NAME=' /opt/libredecision/.env | cut -d= -f2) for SVC in backend frontend; do - STATUS=$(docker inspect --format '{{.State.Status}}' "$PROJECT-$SVC-1" 2>/dev/null || echo "absent") - echo "$PROJECT-$SVC-1 : $STATUS" - [ "$STATUS" = "running" ] || { echo "FAIL: $PROJECT-$SVC-1 non running"; exit 1; } + STATUS=$(docker inspect --format '{{.State.Status}}' "$PROJECT-$SVC" 2>/dev/null || echo "absent") + echo "$PROJECT-$SVC : $STATUS" + [ "$STATUS" = "running" ] || { echo "FAIL: $PROJECT-$SVC non running"; exit 1; } done echo "PASS: tous les containers running" @@ -181,7 +199,7 @@ steps: commands: - apk add --no-cache --quiet curl - | - SITE=$(grep '^DOMAIN=' .env.deploy | cut -d= -f2) + SITE=$(grep '^APP_DOMAIN=' .env.deploy | cut -d= -f2) TARGET="https://$SITE" echo "Healthcheck $TARGET..." MAX=60 diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 79bf97e..f70c531 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -1,13 +1,14 @@ -version: "3.9" +name: ${COMPOSE_PROJECT_NAME:-ehv-decision-main} services: postgres: image: postgres:16-alpine - restart: unless-stopped + container_name: ${COMPOSE_PROJECT_NAME:-ehv-decision-main}-postgres + restart: always environment: POSTGRES_DB: ${POSTGRES_DB:-libredecision} POSTGRES_USER: ${POSTGRES_USER:-libredecision} - POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-change-me-in-production} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} volumes: - postgres-data:/var/lib/postgresql/data healthcheck: @@ -18,63 +19,57 @@ services: start_period: 30s networks: - libredecision + # Pas de label SERVICE_* : postgres non exposé publiquement backend: image: libredecision-backend:latest - build: - context: ../ - dockerfile: docker/backend.Dockerfile - target: production - restart: unless-stopped + container_name: ${COMPOSE_PROJECT_NAME:-ehv-decision-main}-backend + restart: always depends_on: postgres: condition: service_healthy environment: - DATABASE_URL: postgresql+asyncpg://${POSTGRES_USER:-libredecision}:${POSTGRES_PASSWORD:-change-me-in-production}@postgres:5432/${POSTGRES_DB:-libredecision} - SECRET_KEY: ${SECRET_KEY:-change-me-in-production-with-a-real-secret-key} + DATABASE_URL: postgresql+asyncpg://${POSTGRES_USER:-libredecision}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB:-libredecision} + SECRET_KEY: ${SECRET_KEY} DEBUG: "false" - CORS_ORIGINS: '["https://${DOMAIN:-libredecision.org}"]' + CORS_ORIGINS: '["https://${APP_DOMAIN:-libredecision.org}"]' DUNITER_RPC_URL: ${DUNITER_RPC_URL:-wss://gdev.p2p.legal/ws} IPFS_API_URL: http://ipfs:5001 IPFS_GATEWAY_URL: http://ipfs:8080 labels: - - "traefik.enable=true" - - "traefik.http.routers.libredecision-api.rule=Host(`${DOMAIN:-libredecision.org}`) && PathPrefix(`/api`)" - - "traefik.http.routers.libredecision-api.entrypoints=websecure" - - "traefik.http.routers.libredecision-api.tls.certresolver=letsencrypt" - - "traefik.http.services.libredecision-api.loadbalancer.server.port=8002" + # Registrator enregistre dans Consul, Fabio route automatiquement + - SERVICE_8002_NAME=${COMPOSE_PROJECT_NAME:-ehv-decision-main}-backend-8002 + - SERVICE_8002_TAGS=urlprefix-${APP_DOMAIN:-libredecision.org}/api/* + # TCP : HTTP check échoue si le service redirige (301/302) + - SERVICE_8002_CHECK_TCP=true networks: - libredecision - - traefik + - sonic frontend: image: libredecision-frontend:latest - build: - context: ../ - dockerfile: docker/frontend.Dockerfile - target: production - restart: unless-stopped + container_name: ${COMPOSE_PROJECT_NAME:-ehv-decision-main}-frontend + restart: always depends_on: - backend environment: - NUXT_PUBLIC_API_BASE: https://${DOMAIN:-libredecision.org}/api/v1 + NUXT_PUBLIC_API_BASE: https://${APP_DOMAIN:-libredecision.org}/api/v1 labels: - - "traefik.enable=true" - - "traefik.http.routers.libredecision-front.rule=Host(`${DOMAIN:-libredecision.org}`)" - - "traefik.http.routers.libredecision-front.entrypoints=websecure" - - "traefik.http.routers.libredecision-front.tls.certresolver=letsencrypt" - - "traefik.http.services.libredecision-front.loadbalancer.server.port=3000" + - SERVICE_3000_NAME=${COMPOSE_PROJECT_NAME:-ehv-decision-main}-frontend-3000 + - SERVICE_3000_TAGS=urlprefix-${APP_DOMAIN:-libredecision.org}/* + - SERVICE_3000_CHECK_TCP=true networks: - - libredecision - - traefik + - sonic ipfs: image: ipfs/kubo:latest - restart: unless-stopped + container_name: ${COMPOSE_PROJECT_NAME:-ehv-decision-main}-ipfs + restart: always volumes: - ipfs-data:/data/ipfs networks: - libredecision + # Pas de label SERVICE_* : ipfs non exposé publiquement volumes: postgres-data: @@ -83,5 +78,5 @@ volumes: networks: libredecision: driver: bridge - traefik: + sonic: external: true