first commit: add woodpecker et compose
This commit is contained in:
107
.woodpecker.yml
Normal file
107
.woodpecker.yml
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
when:
|
||||||
|
- branch: main
|
||||||
|
event: push
|
||||||
|
|
||||||
|
steps:
|
||||||
|
|
||||||
|
# Etape 1 : Ecriture du .env.deploy depuis les secrets
|
||||||
|
# NOTE: ne pas utiliser ${VAR} dans commands (bug Woodpecker next), utiliser env | grep
|
||||||
|
# NOTE: from_secret et volumes incompatibles dans le meme step (bug Woodpecker next)
|
||||||
|
- name: write-env
|
||||||
|
image: alpine:3.20
|
||||||
|
environment:
|
||||||
|
DTRACK_DOMAIN:
|
||||||
|
from_secret: dtrack_domain
|
||||||
|
commands:
|
||||||
|
- env | grep -E "^(DTRACK_DOMAIN)=" > .env.deploy
|
||||||
|
# COMPOSE_PROJECT_NAME : convention user-project-branch, genere depuis les vars CI
|
||||||
|
- 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
|
||||||
|
- echo "Fichier .env.deploy cree ($(wc -c < .env.deploy) octets)"
|
||||||
|
|
||||||
|
# TEST write-env : valide le contenu du .env.deploy
|
||||||
|
- name: test-env
|
||||||
|
image: alpine:3.20
|
||||||
|
commands:
|
||||||
|
- |
|
||||||
|
if [ ! -f .env.deploy ]; then
|
||||||
|
echo "FAIL: .env.deploy introuvable dans le workspace"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "PASS: .env.deploy present"
|
||||||
|
- |
|
||||||
|
for KEY in COMPOSE_PROJECT_NAME DTRACK_DOMAIN; do
|
||||||
|
VAL=$(grep "^${KEY}=" .env.deploy | cut -d= -f2)
|
||||||
|
if [ -z "$VAL" ]; then
|
||||||
|
echo "FAIL: $KEY manquant ou vide dans .env.deploy"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "PASS: $KEY = $VAL"
|
||||||
|
done
|
||||||
|
|
||||||
|
# Etape 2 : Deploiement sur sonic via Docker socket
|
||||||
|
# L'agent Woodpecker tourne sur sonic — pas de SSH requis
|
||||||
|
- name: deploy
|
||||||
|
image: docker:27-cli
|
||||||
|
volumes:
|
||||||
|
- /var/run/docker.sock:/var/run/docker.sock
|
||||||
|
- /opt/dtrack:/opt/dtrack
|
||||||
|
commands:
|
||||||
|
- cp .env.deploy /opt/dtrack/.env
|
||||||
|
- chmod 600 /opt/dtrack/.env
|
||||||
|
- cp docker-compose.yml /opt/dtrack/docker-compose.yml
|
||||||
|
- cd /opt/dtrack && docker compose pull
|
||||||
|
- cd /opt/dtrack && docker compose up -d --remove-orphans
|
||||||
|
- cd /opt/dtrack && docker compose ps
|
||||||
|
|
||||||
|
# TEST deploy : verifie que les conteneurs sont running
|
||||||
|
- name: test-deploy
|
||||||
|
image: docker:27-cli
|
||||||
|
volumes:
|
||||||
|
- /var/run/docker.sock:/var/run/docker.sock
|
||||||
|
commands:
|
||||||
|
- |
|
||||||
|
PROJECT=$(grep '^COMPOSE_PROJECT_NAME=' /opt/dtrack/.env | cut -d= -f2)
|
||||||
|
for CONTAINER in apiserver frontend; do
|
||||||
|
STATUS=$(docker inspect --format '{{.State.Status}}' "${PROJECT}-${CONTAINER}" 2>/dev/null || echo "absent")
|
||||||
|
echo "${PROJECT}-${CONTAINER} : $STATUS"
|
||||||
|
[ "$STATUS" = "running" ] || { echo "FAIL: ${CONTAINER} non running"; exit 1; }
|
||||||
|
echo "PASS: ${CONTAINER} running"
|
||||||
|
done
|
||||||
|
|
||||||
|
# Etape 3 : Healthcheck HTTP sur l'apiserver
|
||||||
|
- name: healthcheck
|
||||||
|
image: alpine:3.20
|
||||||
|
commands:
|
||||||
|
- apk add --no-cache --quiet curl
|
||||||
|
- |
|
||||||
|
DOMAIN=$(grep '^DTRACK_DOMAIN=' .env.deploy | cut -d= -f2)
|
||||||
|
if [ -z "$DOMAIN" ]; then
|
||||||
|
echo "ERREUR: DTRACK_DOMAIN non defini dans .env.deploy"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
TARGET="https://$DOMAIN/api/version"
|
||||||
|
echo "Healthcheck sur $TARGET (max 2 minutes)..."
|
||||||
|
MAX=12
|
||||||
|
i=0
|
||||||
|
until [ $i -ge $MAX ]; do
|
||||||
|
RESPONSE=$(curl -sf "$TARGET" 2>/dev/null)
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
echo "PASS: apiserver repond"
|
||||||
|
echo "version: $RESPONSE"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
i=$((i+1))
|
||||||
|
echo "Tentative $i/$MAX - retry dans 10s"
|
||||||
|
sleep 10
|
||||||
|
done
|
||||||
|
echo "ERREUR: apiserver ne repond pas apres 2 minutes"
|
||||||
|
exit 1
|
||||||
|
|
||||||
|
# Notification en cas d'echec
|
||||||
|
- name: notify-failure
|
||||||
|
image: alpine:3.20
|
||||||
|
commands:
|
||||||
|
- 'echo "ECHEC pipeline #$CI_BUILD_NUMBER sur commit $CI_COMMIT_SHA"'
|
||||||
|
- 'echo "Branche: $CI_COMMIT_BRANCH"'
|
||||||
|
when:
|
||||||
|
- status: failure
|
||||||
62
docker-compose.yml
Normal file
62
docker-compose.yml
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
# Convention de nommage : user-project-branch (ex: syoul-dtrack-main)
|
||||||
|
# Permet plusieurs instances en parallele (prod/staging) sans collision
|
||||||
|
name: ${COMPOSE_PROJECT_NAME:-syoul-dtrack-main}
|
||||||
|
|
||||||
|
services:
|
||||||
|
apiserver:
|
||||||
|
image: dependencytrack/apiserver:4.12
|
||||||
|
container_name: ${COMPOSE_PROJECT_NAME:-syoul-dtrack-main}-apiserver
|
||||||
|
restart: always
|
||||||
|
environment:
|
||||||
|
# Stockage H2 interne — suffisant pour une team, pas de PostgreSQL requis
|
||||||
|
ALPINE_DATABASE_MODE: internal
|
||||||
|
ALPINE_DATA_DIRECTORY: /data
|
||||||
|
volumes:
|
||||||
|
- dtrack_data:/data
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "wget", "-qO-", "http://localhost:8080/api/version"]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 10s
|
||||||
|
retries: 5
|
||||||
|
start_period: 120s # ~60-90s au premier démarrage
|
||||||
|
networks:
|
||||||
|
- dtrack-net
|
||||||
|
- sonic
|
||||||
|
labels:
|
||||||
|
# Registrator enregistre le service dans Consul via le reseau "sonic"
|
||||||
|
# Fabio route /api/* vers l'apiserver (prioritaire sur le catch-all frontend)
|
||||||
|
- SERVICE_8080_NAME=${SERVICE_8080_APISERVER_NAME:-${COMPOSE_PROJECT_NAME}-apiserver-8080}
|
||||||
|
- SERVICE_8080_TAGS=${SERVICE_8080_APISERVER_TAGS:-urlprefix-${DTRACK_DOMAIN}/api/*}
|
||||||
|
- SERVICE_8080_CHECK_TCP=true
|
||||||
|
# sonic-acme-1 emet le cert TLS detecte automatiquement par Fabio via SNI
|
||||||
|
- LETSENCRYPT_HOST=${DTRACK_DOMAIN}
|
||||||
|
|
||||||
|
frontend:
|
||||||
|
image: dependencytrack/frontend:4.12
|
||||||
|
container_name: ${COMPOSE_PROJECT_NAME:-syoul-dtrack-main}-frontend
|
||||||
|
restart: always
|
||||||
|
environment:
|
||||||
|
# URL de l'apiserver vue depuis le navigateur (HTTPS public)
|
||||||
|
API_BASE_URL: https://${DTRACK_DOMAIN}
|
||||||
|
depends_on:
|
||||||
|
apiserver:
|
||||||
|
condition: service_healthy
|
||||||
|
networks:
|
||||||
|
- dtrack-net
|
||||||
|
- sonic
|
||||||
|
labels:
|
||||||
|
# Catch-all /* : doit etre enregistre apres /api/* pour que Fabio priorise l'apiserver
|
||||||
|
- SERVICE_8080_NAME=${SERVICE_8080_FRONTEND_NAME:-${COMPOSE_PROJECT_NAME}-frontend-8080}
|
||||||
|
- SERVICE_8080_TAGS=${SERVICE_8080_FRONTEND_TAGS:-urlprefix-${DTRACK_DOMAIN}/*}
|
||||||
|
- SERVICE_8080_CHECK_TCP=true
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
dtrack_data:
|
||||||
|
|
||||||
|
networks:
|
||||||
|
dtrack-net:
|
||||||
|
# Reseau interne isole apiserver <-> frontend
|
||||||
|
driver: bridge
|
||||||
|
sonic:
|
||||||
|
# Reseau externe partage avec Registrator/Consul/Fabio
|
||||||
|
external: true
|
||||||
Reference in New Issue
Block a user