Compare commits
18 Commits
stand-alon
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9fb80ef59f | ||
|
|
53ceb29bbe | ||
|
|
7862bb11b7 | ||
|
|
42b3d28505 | ||
|
|
658be24b7c | ||
|
|
a2fbbe5d44 | ||
|
|
e1447aca28 | ||
|
|
4755b392a3 | ||
|
|
445a540d54 | ||
|
|
1dd4b4d1d1 | ||
|
|
7bb7484ec8 | ||
|
|
940834d993 | ||
|
|
fa0aa808ac | ||
|
|
002764ea9a | ||
|
|
b4d7f7e10f | ||
|
|
029fbe5d26 | ||
|
|
fe16d01be7 | ||
|
|
7c4204c689 |
387
.woodpecker.yml
387
.woodpecker.yml
@@ -1,245 +1,158 @@
|
|||||||
---
|
|
||||||
kind: pipeline
|
|
||||||
type: docker
|
|
||||||
name: build-and-deploy
|
|
||||||
|
|
||||||
# Ce pipeline Woodpecker CI automatise le build et le déploiement de l'application statique
|
|
||||||
# Il se déclenche automatiquement sur chaque push vers les branches main ou stand-alone
|
|
||||||
|
|
||||||
steps:
|
|
||||||
# ============================================
|
|
||||||
# ÉTAPE 1: BUILD DE L'APPLICATION STATIQUE
|
|
||||||
# ============================================
|
|
||||||
# Cette étape construit l'application Next.js et génère les fichiers statiques HTML/CSS/JS
|
|
||||||
- name: build
|
|
||||||
# Image Docker utilisée : Node.js 20 sur Alpine Linux (légère et rapide)
|
|
||||||
image: node:20-alpine
|
|
||||||
|
|
||||||
# Variables d'environnement nécessaires pour le build
|
|
||||||
environment:
|
|
||||||
NODE_ENV: production # Mode production (optimisations activées)
|
|
||||||
HUSKY: 0 # Désactive Husky (git hooks) pour éviter les erreurs
|
|
||||||
HUSKY_SKIP_INSTALL: 1 # Skip l'installation de Husky
|
|
||||||
|
|
||||||
# Commandes exécutées dans l'ordre
|
|
||||||
commands:
|
|
||||||
# Installation des dépendances système nécessaires
|
|
||||||
# - git : pour cloner/récupérer des dépendances si nécessaire
|
|
||||||
# - python3 : requis pour les scripts de patch (patch_document.py, add_team_link.py)
|
|
||||||
- apk add --no-cache git python3
|
|
||||||
|
|
||||||
# Installation des dépendances Node.js de la racine du projet
|
|
||||||
# npm ci : Clean Install (supprime node_modules et réinstalle depuis package-lock.json)
|
|
||||||
# --legacy-peer-deps : Ignore les conflits de dépendances peer (compatibilité)
|
|
||||||
- npm ci --legacy-peer-deps
|
|
||||||
|
|
||||||
# Installation des dépendances de radar-app/ (le framework Next.js vendu)
|
|
||||||
# On se déplace dans le dossier radar-app pour installer ses dépendances
|
|
||||||
- cd radar-app
|
|
||||||
|
|
||||||
# Installation des dépendances de radar-app (y compris devDependencies)
|
|
||||||
# --include=dev : Inclut les dépendances de développement (tsx, eslint, etc.)
|
|
||||||
- npm ci --legacy-peer-deps --include=dev
|
|
||||||
|
|
||||||
# Build des icônes SVG en composants React TypeScript
|
|
||||||
# Ce script génère les composants Icons depuis les fichiers SVG dans src/icons/
|
|
||||||
- npm run build:icons
|
|
||||||
|
|
||||||
# Retour à la racine du projet
|
|
||||||
- cd ..
|
|
||||||
|
|
||||||
# Génération des données de visualisation équipe
|
|
||||||
# Ce script lit les profils dans docs/data/team/*.md et les technologies
|
|
||||||
# Il génère public/team-visualization-data.json utilisé par la page /team
|
|
||||||
- node scripts/generate-team-visualization-data.js
|
|
||||||
|
|
||||||
# Build de l'application complète
|
|
||||||
# Ce script (build-radar.js) :
|
|
||||||
# 1. Copie config-business.json vers radar-app/data/config.json
|
|
||||||
# 2. Copie radar-business/2025-01-15/ vers radar-app/data/radar/2025-01-15/
|
|
||||||
# 3. Copie public/* vers radar-app/public/
|
|
||||||
# 4. Applique les patches (team page, _document, Navigation)
|
|
||||||
# 5. Lance npm run build:data puis npm run build dans radar-app/
|
|
||||||
# 6. Copie radar-app/out/ vers build/ à la racine
|
|
||||||
- npm run build
|
|
||||||
|
|
||||||
# Vérification que le build a réussi
|
|
||||||
# Affiche les 10 premiers fichiers du dossier build/ pour vérifier la génération
|
|
||||||
- ls -la build/ | head -10
|
|
||||||
|
|
||||||
# Volumes temporaires pour accélérer les builds suivants
|
|
||||||
# Ces volumes persistent les node_modules entre les étapes pour éviter de réinstaller
|
|
||||||
volumes:
|
|
||||||
- name: node_modules_root
|
|
||||||
path: /app/node_modules
|
|
||||||
- name: node_modules_radar_app
|
|
||||||
path: /app/radar-app/node_modules
|
|
||||||
|
|
||||||
# ============================================
|
|
||||||
# ÉTAPE 2: DÉPLOIEMENT VIA RSYNC (OPTIONNEL)
|
|
||||||
# ============================================
|
|
||||||
# Cette étape synchronise les fichiers statiques vers un serveur web distant
|
|
||||||
# Utile si vous avez un serveur Nginx/Apache qui sert des fichiers statiques
|
|
||||||
- name: deploy-rsync
|
|
||||||
# Image Alpine (légère) avec rsync et SSH
|
|
||||||
image: alpine:latest
|
|
||||||
|
|
||||||
# Variables d'environnement récupérées depuis les secrets Woodpecker
|
|
||||||
environment:
|
|
||||||
DEPLOY_HOST:
|
|
||||||
from_secret: deploy_host # Exemple: techradar.example.com
|
|
||||||
DEPLOY_USER:
|
|
||||||
from_secret: deploy_user # Exemple: deploy
|
|
||||||
DEPLOY_PATH:
|
|
||||||
from_secret: deploy_path # Exemple: /var/www/techradar
|
|
||||||
DEPLOY_KEY:
|
|
||||||
from_secret: deploy_key # Clé privée SSH complète
|
|
||||||
|
|
||||||
commands:
|
|
||||||
# Installation des outils nécessaires
|
|
||||||
- apk add --no-cache openssh-client rsync
|
|
||||||
|
|
||||||
# Création du dossier .ssh pour les clés SSH
|
|
||||||
- mkdir -p ~/.ssh
|
|
||||||
|
|
||||||
# Écriture de la clé privée SSH dans le fichier id_rsa
|
|
||||||
# $$ est échappé pour éviter l'interpolation par le shell
|
|
||||||
- echo "$$DEPLOY_KEY" > ~/.ssh/id_rsa
|
|
||||||
|
|
||||||
# Protection de la clé privée (lecture/écriture uniquement pour le propriétaire)
|
|
||||||
- chmod 600 ~/.ssh/id_rsa
|
|
||||||
|
|
||||||
# Ajout de la clé publique du serveur dans known_hosts
|
|
||||||
# Évite la vérification manuelle lors de la première connexion
|
|
||||||
- ssh-keyscan -H $$DEPLOY_HOST >> ~/.ssh/known_hosts || true
|
|
||||||
|
|
||||||
# Synchronisation des fichiers
|
|
||||||
# rsync -avz : Archive mode, Verbose, Compression
|
|
||||||
# --delete : Supprime les fichiers sur le serveur qui n'existent plus localement
|
|
||||||
# build/ : Source (dossier build généré à l'étape précédente)
|
|
||||||
# $$DEPLOY_USER@$$DEPLOY_HOST:$$DEPLOY_PATH/ : Destination (serveur distant)
|
|
||||||
- rsync -avz --delete build/ $$DEPLOY_USER@$$DEPLOY_HOST:$$DEPLOY_PATH/
|
|
||||||
|
|
||||||
# Message de confirmation
|
|
||||||
- echo "✅ Déploiement terminé sur $$DEPLOY_HOST:$$DEPLOY_PATH"
|
|
||||||
|
|
||||||
# Conditions de déclenchement
|
|
||||||
# Cette étape ne s'exécute que si :
|
|
||||||
when:
|
when:
|
||||||
branch:
|
- branch:
|
||||||
- main # Sur la branche main
|
|
||||||
- stand-alone # Ou sur la branche stand-alone
|
|
||||||
event:
|
|
||||||
- push # Et uniquement sur un événement push (pas sur les PR)
|
|
||||||
|
|
||||||
# ============================================
|
|
||||||
# ÉTAPE 3: DÉPLOIEMENT DOCKER (RECOMMANDÉ)
|
|
||||||
# ============================================
|
|
||||||
# Cette étape build et déploie l'application via Docker
|
|
||||||
# Plus simple et reproductible que rsync
|
|
||||||
- name: deploy-docker
|
|
||||||
# Image Docker officielle avec docker-compose
|
|
||||||
image: docker:latest
|
|
||||||
|
|
||||||
# Configuration Docker
|
|
||||||
environment:
|
|
||||||
DOCKER_HOST: unix:///var/run/docker.sock # Socket Docker du serveur Woodpecker
|
|
||||||
|
|
||||||
commands:
|
|
||||||
# Installation de docker-compose
|
|
||||||
- apk add --no-cache docker-compose
|
|
||||||
|
|
||||||
# Build de l'image Docker sans cache
|
|
||||||
# --no-cache : Force un rebuild complet (garantit que tout est à jour)
|
|
||||||
# docker-compose.business.yml : Fichier de configuration Docker Compose
|
|
||||||
- docker compose -f docker-compose.business.yml build --no-cache
|
|
||||||
|
|
||||||
# Démarrage du conteneur en mode détaché
|
|
||||||
# -d : Détaché (en arrière-plan)
|
|
||||||
# up : Crée et démarre les conteneurs
|
|
||||||
- docker compose -f docker-compose.business.yml up -d
|
|
||||||
|
|
||||||
# Vérification que le conteneur tourne
|
|
||||||
# Affiche les conteneurs contenant "laplank-radar" ou un message si non trouvé
|
|
||||||
- docker ps | grep laplank-radar || echo "Conteneur non trouvé"
|
|
||||||
|
|
||||||
# Volume pour accéder au socket Docker du serveur
|
|
||||||
# Nécessaire pour que le conteneur puisse utiliser Docker
|
|
||||||
volumes:
|
|
||||||
- name: dockersock
|
|
||||||
path: /var/run/docker.sock
|
|
||||||
|
|
||||||
# Conditions de déclenchement (identique à deploy-rsync)
|
|
||||||
when:
|
|
||||||
branch:
|
|
||||||
- main
|
- main
|
||||||
- stand-alone
|
- stand-alone
|
||||||
event:
|
event: push
|
||||||
- push
|
|
||||||
|
|
||||||
# ============================================
|
steps:
|
||||||
# ÉTAPE 4: NOTIFICATION TELEGRAM
|
|
||||||
# ============================================
|
|
||||||
# Cette étape envoie une notification Telegram à la fin du pipeline
|
|
||||||
# Utile pour être informé immédiatement du résultat du build
|
|
||||||
- name: notify
|
|
||||||
# Plugin Drone/Woodpecker pour Telegram
|
|
||||||
# Compatible avec Woodpecker (fork de Drone)
|
|
||||||
image: appleboy/drone-telegram
|
|
||||||
|
|
||||||
# Configuration du plugin Telegram
|
# Etape 0 : Validation syntaxique du docker-compose
|
||||||
settings:
|
# Les vars CI (CI_REPO_OWNER, CI_COMMIT_BRANCH) sont injectees automatiquement par Woodpecker
|
||||||
token:
|
- name: validate
|
||||||
from_secret: telegram_token # Token du bot Telegram
|
image: docker:27-cli
|
||||||
to:
|
|
||||||
from_secret: telegram_chat_id_ajr # ID du chat Telegram (destinataire)
|
|
||||||
format: markdown # Format du message (Markdown supporté)
|
|
||||||
|
|
||||||
# Message personnalisé avec template Woodpecker
|
|
||||||
# {{#success build.status}} : Condition si le build a réussi
|
|
||||||
# {{repo.name}} : Nom du dépôt Git
|
|
||||||
# {{commit.branch}} : Branche du commit
|
|
||||||
# {{commit.message}} : Message du commit
|
|
||||||
# {{commit.author}} : Auteur du commit
|
|
||||||
# {{build.link}} : Lien vers le build dans Woodpecker
|
|
||||||
message: >
|
|
||||||
{{#success build.status}}
|
|
||||||
✅ Build réussi pour `{{repo.name}}` sur la branche `{{commit.branch}}`
|
|
||||||
📝 Commit: `{{commit.message}}`
|
|
||||||
👤 Auteur: {{commit.author}}
|
|
||||||
🔗 {{ build.link }}
|
|
||||||
{{else}}
|
|
||||||
❌ Build échoué pour `{{repo.name}}` sur la branche `{{commit.branch}}`
|
|
||||||
📝 Commit: `{{commit.message}}`
|
|
||||||
👤 Auteur: {{commit.author}}
|
|
||||||
🔗 {{ build.link }}
|
|
||||||
{{/success}}
|
|
||||||
|
|
||||||
# Conditions de déclenchement
|
|
||||||
# S'exécute toujours, que le build réussisse ou échoue
|
|
||||||
when:
|
|
||||||
status:
|
|
||||||
- success # Si le build réussit
|
|
||||||
- failure # Si le build échoue
|
|
||||||
|
|
||||||
# ============================================
|
|
||||||
# VOLUMES TEMPORAIRES
|
|
||||||
# ============================================
|
|
||||||
# Ces volumes sont créés temporairement pour chaque pipeline
|
|
||||||
# Ils permettent de partager des données entre les étapes
|
|
||||||
volumes:
|
volumes:
|
||||||
# Volume pour les node_modules de la racine
|
- /var/run/docker.sock:/var/run/docker.sock
|
||||||
# Persiste entre les étapes pour éviter de réinstaller
|
environment:
|
||||||
- name: node_modules_root
|
RADAR_DOMAIN: validate.example.com
|
||||||
temp: {} # Volume temporaire (supprimé à la fin du pipeline)
|
commands:
|
||||||
|
- |
|
||||||
|
export COMPOSE_PROJECT_NAME=$(printf '%s-%s-%s' "$CI_REPO_OWNER" "$CI_REPO_NAME" "$CI_COMMIT_BRANCH" | tr 'A-Z/' 'a-z-')
|
||||||
|
docker compose -f docker-compose.business.yml config --quiet
|
||||||
|
- echo "docker-compose.business.yml valide"
|
||||||
|
|
||||||
# Volume pour les node_modules de radar-app
|
# Etape 1 : Build de l'application statique
|
||||||
# Persiste entre les étapes pour éviter de réinstaller
|
- name: build
|
||||||
- name: node_modules_radar_app
|
image: node:20-alpine
|
||||||
temp: {}
|
environment:
|
||||||
|
NODE_ENV: production
|
||||||
|
HUSKY: 0
|
||||||
|
HUSKY_SKIP_INSTALL: 1
|
||||||
|
commands:
|
||||||
|
- apk add --no-cache git python3
|
||||||
|
- npm ci --legacy-peer-deps
|
||||||
|
- cd radar-app && npm ci --legacy-peer-deps --include=dev && npm run build:icons && cd ..
|
||||||
|
- node scripts/generate-team-visualization-data.js
|
||||||
|
- npm run build
|
||||||
|
- ls -la build/ | head -10
|
||||||
|
|
||||||
# Volume pour accéder au socket Docker du serveur
|
# Etape 2a : Ecriture du .env depuis les secrets
|
||||||
# Nécessaire pour que les conteneurs puissent utiliser Docker
|
# NOTE: from_secret et volumes: incompatibles dans le meme step (bug Woodpecker next)
|
||||||
- name: dockersock
|
- name: write-env
|
||||||
host:
|
image: alpine:3.20
|
||||||
path: /var/run/docker.sock # Chemin du socket Docker sur le serveur
|
environment:
|
||||||
|
RADAR_DOMAIN:
|
||||||
|
from_secret: radar_domain
|
||||||
|
commands:
|
||||||
|
- env | grep -E "^(RADAR_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)"
|
||||||
|
|
||||||
|
# Etape 2b : Deploiement sur sonic via Docker socket
|
||||||
|
# NOTE: from_secret et volumes: incompatibles — pas de secrets ici
|
||||||
|
- name: deploy
|
||||||
|
image: docker:27-cli
|
||||||
|
volumes:
|
||||||
|
- /var/run/docker.sock:/var/run/docker.sock
|
||||||
|
commands:
|
||||||
|
- docker compose --env-file .env.deploy -f docker-compose.business.yml build --no-cache
|
||||||
|
- docker compose --env-file .env.deploy -f docker-compose.business.yml up -d --remove-orphans
|
||||||
|
- docker compose --env-file .env.deploy -f docker-compose.business.yml ps
|
||||||
|
- |
|
||||||
|
DOMAIN=$(grep '^RADAR_DOMAIN=' .env.deploy | cut -d= -f2)
|
||||||
|
|
||||||
|
# --- Certificat TLS (acme.sh via sonic-acme-1) ---
|
||||||
|
# acme.sh est idempotent : skip si cert valide, renouvelle si proche expiration
|
||||||
|
# exit 0 = emis/renouvele, exit 2 = skip (domaine inchange), autres = erreur
|
||||||
|
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 "Cert TLS: /host/certs/$DOMAIN-cert.pem OK (acme exit $ACME_EXIT)"
|
||||||
|
|
||||||
|
# Etape 3a : Generation SBOM (Syft) — inventaire des dependances npm du workspace
|
||||||
|
# Scan du repertoire source (pas de docker socket = pas de bug volumes/CI-vars)
|
||||||
|
- name: sbom-generate
|
||||||
|
image: alpine:3.20
|
||||||
|
commands:
|
||||||
|
- apk add --no-cache curl
|
||||||
|
- curl -sSfL https://raw.githubusercontent.com/anchore/syft/main/install.sh | sh -s -- -b /usr/local/bin latest
|
||||||
|
- mkdir -p .reports
|
||||||
|
- syft dir:. --exclude './build' --exclude './radar-app/.next' --exclude './radar-app/out' -o cyclonedx-json --file .reports/sbom-radar.cyclonedx.json
|
||||||
|
- echo "SBOM genere $(wc -c < .reports/sbom-radar.cyclonedx.json) octets"
|
||||||
|
|
||||||
|
# Etape 3b : Scan CVE (Trivy) depuis le SBOM Syft
|
||||||
|
# Cache /home/syoul/trivy-cache evite ~200Mo de telechargement des DB CVE a chaque build
|
||||||
|
# Prerequis sur sonic : mkdir -p /home/syoul/trivy-cache
|
||||||
|
- name: sbom-scan
|
||||||
|
image: aquasec/trivy:latest
|
||||||
|
volumes:
|
||||||
|
- /home/syoul/trivy-cache:/root/.cache/trivy
|
||||||
|
commands:
|
||||||
|
- trivy sbom --format json --output .reports/trivy-radar.json .reports/sbom-radar.cyclonedx.json
|
||||||
|
- echo "Scan CVE termine"
|
||||||
|
|
||||||
|
# Etape 3c : Publication SBOM vers Dependency-Track (dtrack.syoul.fr)
|
||||||
|
# NOTE: from_secret et volumes: incompatibles — pas de volumes ici
|
||||||
|
- name: sbom-publish
|
||||||
|
image: alpine/curl:latest
|
||||||
|
environment:
|
||||||
|
DTRACK_TOKEN:
|
||||||
|
from_secret: dependency_track_token
|
||||||
|
commands:
|
||||||
|
- |
|
||||||
|
VERSION=$(date +%Y-%m-%d)-$(echo "$CI_COMMIT_SHA" | cut -c1-8)
|
||||||
|
HTTP=$(curl -s -o /tmp/dtrack-response.txt -w "%{http_code}" -X POST "https://dtrack.syoul.fr/api/v1/bom" \
|
||||||
|
-H "X-Api-Key: $DTRACK_TOKEN" \
|
||||||
|
-F "autoCreate=true" \
|
||||||
|
-F "projectName=techradardev-app" \
|
||||||
|
-F "projectVersion=$VERSION" \
|
||||||
|
-F "bom=@.reports/sbom-radar.cyclonedx.json")
|
||||||
|
echo "HTTP $HTTP : $(cat /tmp/dtrack-response.txt)"
|
||||||
|
[ "$HTTP" -ge 200 ] && [ "$HTTP" -lt 300 ] || exit 1
|
||||||
|
|
||||||
|
# Etape 4 : Healthcheck post-deploiement
|
||||||
|
- name: healthcheck
|
||||||
|
image: alpine:3.20
|
||||||
|
commands:
|
||||||
|
- apk add --no-cache --quiet curl
|
||||||
|
- |
|
||||||
|
DOMAIN=$(grep '^RADAR_DOMAIN=' .env.deploy | cut -d= -f2)
|
||||||
|
if [ -z "$DOMAIN" ]; then
|
||||||
|
echo "ERREUR: RADAR_DOMAIN non defini dans .env.deploy"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
TARGET="https://$DOMAIN"
|
||||||
|
echo "Healthcheck sur $TARGET (max 100s)..."
|
||||||
|
MAX=20
|
||||||
|
i=0
|
||||||
|
until [ $i -ge $MAX ]; do
|
||||||
|
CODE=$(curl -sSo /dev/null -w "%{http_code}" "$TARGET" 2>/dev/null)
|
||||||
|
echo "Tentative $((i+1))/$MAX - HTTP $CODE"
|
||||||
|
if [ "$CODE" = "200" ] || [ "$CODE" = "301" ] || [ "$CODE" = "302" ]; then
|
||||||
|
echo "Radar repond sur $TARGET"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
i=$((i+1))
|
||||||
|
sleep 5
|
||||||
|
done
|
||||||
|
echo "ERREUR: Radar ne repond pas apres 100 secondes"
|
||||||
|
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
|
||||||
|
|||||||
53
Readme.md
53
Readme.md
@@ -124,27 +124,27 @@ Accès à trois pages de stratégie depuis le header :
|
|||||||
|
|
||||||
```
|
```
|
||||||
TechradarDev/
|
TechradarDev/
|
||||||
├── radar-app/ # Framework Next.js vendu (stand-alone)
|
├── radar-business/ # ✏️ SOURCE DE VÉRITÉ — éditer ici
|
||||||
|
│ ├── 2025-01-15/ # Fichiers .md des technologies (38 blips)
|
||||||
|
│ ├── config-business.json # Configuration du radar (quadrants, rings)
|
||||||
|
│ └── FORMAT-BLIP.md # Format des métadonnées
|
||||||
|
├── data/ # ✏️ Données de build versionnées
|
||||||
|
│ └── team/ # Profils des 12 membres (*.md)
|
||||||
|
├── radar-app/ # ⚙️ Framework Next.js (ne pas éditer)
|
||||||
│ ├── src/ # Code source Next.js
|
│ ├── src/ # Code source Next.js
|
||||||
│ ├── data/ # Données du radar
|
│ └── data/ # Copie générée au build (ne pas éditer)
|
||||||
│ └── package.json # Next.js 16.1.6, React 19
|
├── radar/ # ⚠️ Dossier temporaire (généré par serve-business.sh)
|
||||||
├── radar-business/ # Contenu du radar business
|
│ └── ... # Ne jamais éditer ici — écrasé à chaque lancement
|
||||||
│ ├── 2025-01-15/ # Release actuelle (38 technologies)
|
|
||||||
│ └── config-business.json # Configuration du radar
|
|
||||||
├── docs/
|
|
||||||
│ ├── data/
|
|
||||||
│ │ └── team/ # Profils des 12 membres
|
|
||||||
│ └── app/ # Documentation technique
|
|
||||||
├── scripts/
|
├── scripts/
|
||||||
│ ├── build-radar.js # Script de build stand-alone
|
│ ├── build-radar.js # Script de build stand-alone
|
||||||
│ ├── serve-radar.js # Script de serve
|
│ ├── serve-radar.js # Script de serve
|
||||||
│ ├── generate-team-visualization-data.js # Génération données équipe
|
│ ├── generate-team-visualization-data.js
|
||||||
│ └── ...
|
│ └── ...
|
||||||
├── public/ # Fichiers statiques
|
├── public/ # Fichiers statiques
|
||||||
│ ├── team-block-script.js # Script page équipe
|
│ ├── team-block-script.js
|
||||||
│ └── team-visualization-data.json # Données visualisations
|
│ └── team-visualization-data.json # Généré par generate-team-visualization-data.js
|
||||||
├── Dockerfile.business # Dockerfile pour déploiement
|
├── Dockerfile.business
|
||||||
└── docker-compose.business.yml # Configuration Docker Compose
|
└── docker-compose.business.yml
|
||||||
```
|
```
|
||||||
|
|
||||||
## 🛠️ Scripts disponibles
|
## 🛠️ Scripts disponibles
|
||||||
@@ -158,6 +158,15 @@ TechradarDev/
|
|||||||
| `npm run extract-tech` | Extraction des technologies depuis la doc |
|
| `npm run extract-tech` | Extraction des technologies depuis la doc |
|
||||||
| `npm run analyze-business` | Analyse des métriques business |
|
| `npm run analyze-business` | Analyse des métriques business |
|
||||||
|
|
||||||
|
## ✏️ Où éditer le contenu
|
||||||
|
|
||||||
|
| Quoi | Dossier |
|
||||||
|
|---|---|
|
||||||
|
| Ajouter / modifier une technologie | `radar-business/2025-01-15/` |
|
||||||
|
| Ajouter / modifier un membre d'équipe | `data/team/` |
|
||||||
|
|
||||||
|
> ⚠️ Ne jamais éditer dans `radar/` (temporaire, écrasé au lancement) ni dans `radar-app/data/` (copie générée au build).
|
||||||
|
|
||||||
## 📝 Content Guidelines
|
## 📝 Content Guidelines
|
||||||
|
|
||||||
### Tags disponibles
|
### Tags disponibles
|
||||||
@@ -191,20 +200,14 @@ Voir `radar-business/FORMAT-BLIP.md` pour le format complet des métadonnées bu
|
|||||||
|
|
||||||
## 📚 Documentation
|
## 📚 Documentation
|
||||||
|
|
||||||
La documentation complète est disponible dans le dossier `docs/` :
|
La documentation technique est disponible dans `docs/app/` (non versionnée, locale uniquement) :
|
||||||
|
|
||||||
- [Architecture](./docs/app/architecture.md) - Architecture technique du projet
|
- Architecture, Configuration, Développement, Déploiement
|
||||||
- [Configuration](./docs/app/configuration.md) - Configuration du radar
|
- Guide Radar Business, Guide Page Équipe, Migration Next.js 16
|
||||||
- [Développement](./docs/app/developpement.md) - Guide de développement
|
|
||||||
- [Déploiement](./docs/app/deploiement.md) - Guide de déploiement
|
|
||||||
- [Contribution](./docs/app/contribution.md) - Guide de contribution
|
|
||||||
- [Guide Radar Business](./docs/app/guide-radar-business.md) - Guide spécifique au radar business
|
|
||||||
- [Guide Page Équipe](./docs/app/guide-page-equipe.md) - Documentation de la page équipe
|
|
||||||
- [Migration Next.js 16](./docs/app/migration-nextjs-16.md) - Notes de migration
|
|
||||||
|
|
||||||
## 🔄 État du projet
|
## 🔄 État du projet
|
||||||
|
|
||||||
- **Branche actuelle** : `stand-alone`
|
- **Branche principale** : `main`
|
||||||
- **Version** : 4.3.0
|
- **Version** : 4.3.0
|
||||||
- **Statut** : Projet stand-alone, indépendant du package externe `aoe_technology_radar`
|
- **Statut** : Projet stand-alone, indépendant du package externe `aoe_technology_radar`
|
||||||
- **Framework** : Code vendu dans `radar-app/` (basé sur aoe_technology_radar)
|
- **Framework** : Code vendu dans `radar-app/` (basé sur aoe_technology_radar)
|
||||||
|
|||||||
@@ -1,26 +1,34 @@
|
|||||||
version: '3.8'
|
# Convention de nommage : user-project-branch (ex: ajr-techradardev-main)
|
||||||
|
# Permet plusieurs instances en parallele (prod/test) sans collision
|
||||||
|
name: ${COMPOSE_PROJECT_NAME:-ajr-techradardev-main}
|
||||||
|
|
||||||
services:
|
services:
|
||||||
radar-business:
|
radar-business:
|
||||||
container_name: laplank-radar-technolologique
|
container_name: ${COMPOSE_PROJECT_NAME:-ajr-techradardev-main}-app
|
||||||
build:
|
build:
|
||||||
context: .
|
context: .
|
||||||
dockerfile: Dockerfile.business
|
dockerfile: Dockerfile.business
|
||||||
pull: true # Force le pull de l'image de base pour éviter le cache
|
pull: true
|
||||||
args:
|
args:
|
||||||
BUILD_DATE: "${BUILD_DATE:-$(date +%s)}"
|
BUILD_DATE: "${BUILD_DATE:-$(date +%s)}"
|
||||||
BUILD_VERSION: "${BUILD_VERSION:-dev}"
|
BUILD_VERSION: "${BUILD_VERSION:-dev}"
|
||||||
CACHE_BUST: "${CACHE_BUST:-$(date +%s%N)}" # Nanosecondes pour garantir l'unicité et forcer l'invalidation
|
CACHE_BUST: "${CACHE_BUST:-$(date +%s%N)}"
|
||||||
# Note: no_cache n'est pas supporté dans docker-compose
|
|
||||||
# Pour forcer le rebuild sans cache dans Portainer, utilisez l'option "Rebuild" avec "No cache" dans l'interface
|
|
||||||
# Si vous utilisez une image pré-bâtie, décommentez image et commentez build
|
|
||||||
# image: votre-registre/laplank-radar-business:latest
|
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
ports:
|
|
||||||
- "3006:3000" # Mappe le port 3006 de l'hôte vers le port 3000 du conteneur
|
|
||||||
environment:
|
environment:
|
||||||
- NODE_ENV=production
|
- NODE_ENV=production
|
||||||
# Optionnel : Persistance des logs si nécessaire
|
labels:
|
||||||
# volumes:
|
# Registrator lit l'IP du conteneur depuis le reseau "sonic" (-useIpFromNetwork sonic)
|
||||||
# - ./logs:/app/logs
|
# et enregistre le service dans Consul avec le tag urlprefix- -> Fabio route vers ce service
|
||||||
|
- SERVICE_3000_NAME=${COMPOSE_PROJECT_NAME:-ajr-techradardev-main}-app-3000
|
||||||
|
- SERVICE_3000_TAGS=urlprefix-${RADAR_DOMAIN}/*
|
||||||
|
- SERVICE_3000_CHECK_TCP=true
|
||||||
|
# sonic-acme-1 (acme-companion) emet le cert TLS et le copie dans /host/certs/
|
||||||
|
# Fabio le detecte automatiquement par SNI pour HTTPS
|
||||||
|
- LETSENCRYPT_HOST=${RADAR_DOMAIN}
|
||||||
|
networks:
|
||||||
|
- sonic
|
||||||
|
|
||||||
|
networks:
|
||||||
|
sonic:
|
||||||
|
# Reseau externe existant sur le serveur (partage avec Registrator/Consul/Fabio)
|
||||||
|
external: true
|
||||||
|
|||||||
1129
package-lock.json
generated
1129
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
1412
radar-app/package-lock.json
generated
1412
radar-app/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -33,6 +33,7 @@
|
|||||||
"marked": "^15.0.7",
|
"marked": "^15.0.7",
|
||||||
"marked-highlight": "^2.2.1",
|
"marked-highlight": "^2.2.1",
|
||||||
"next": "16.1.6",
|
"next": "16.1.6",
|
||||||
|
"postcss": "^8.5.3",
|
||||||
"postcss-nested": "^7.0.2",
|
"postcss-nested": "^7.0.2",
|
||||||
"postcss-preset-env": "^10.1.5",
|
"postcss-preset-env": "^10.1.5",
|
||||||
"prettier": "^3.5.3",
|
"prettier": "^3.5.3",
|
||||||
|
|||||||
@@ -358,7 +358,7 @@ ${metrics.emergingTechnologies.filter(t => t.differentiation === 'high').length
|
|||||||
// Main
|
// Main
|
||||||
function main() {
|
function main() {
|
||||||
const radarDir = path.join(__dirname, '../radar-business/2025-01-15');
|
const radarDir = path.join(__dirname, '../radar-business/2025-01-15');
|
||||||
const outputFile = path.join(__dirname, '../docs/data/analyse-strategique.md');
|
const outputFile = path.join(__dirname, '../data/analyse-strategique.md');
|
||||||
|
|
||||||
if (!fs.existsSync(radarDir)) {
|
if (!fs.existsSync(radarDir)) {
|
||||||
console.error(`Répertoire non trouvé: ${radarDir}`);
|
console.error(`Répertoire non trouvé: ${radarDir}`);
|
||||||
|
|||||||
@@ -164,7 +164,7 @@ try {
|
|||||||
// Modifier Navigation.tsx
|
// Modifier Navigation.tsx
|
||||||
const patchNavScript = path.join(CWD, 'docker', 'add_team_link.sh');
|
const patchNavScript = path.join(CWD, 'docker', 'add_team_link.sh');
|
||||||
if (fs.existsSync(patchNavScript)) {
|
if (fs.existsSync(patchNavScript)) {
|
||||||
execSync(`bash ${patchNavScript}`, {
|
execSync(`sh ${patchNavScript}`, {
|
||||||
cwd: CWD,
|
cwd: CWD,
|
||||||
stdio: 'inherit'
|
stdio: 'inherit'
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ const path = require('path');
|
|||||||
|
|
||||||
// Charger les compétences de l'équipe depuis les fichiers individuels
|
// Charger les compétences de l'équipe depuis les fichiers individuels
|
||||||
function loadTeamSkills() {
|
function loadTeamSkills() {
|
||||||
const teamDir = path.join(__dirname, '../docs/data/team');
|
const teamDir = path.join(__dirname, '../data/team');
|
||||||
const teamSkills = {};
|
const teamSkills = {};
|
||||||
|
|
||||||
if (!fs.existsSync(teamDir)) {
|
if (!fs.existsSync(teamDir)) {
|
||||||
@@ -291,7 +291,7 @@ function parseTechnologiesFile(filePath) {
|
|||||||
|
|
||||||
// Main
|
// Main
|
||||||
function main() {
|
function main() {
|
||||||
const techFile = path.join(__dirname, '../docs/data/technologies-duniter.md');
|
const techFile = path.join(__dirname, '../data/technologies-duniter.md');
|
||||||
const outputDir = path.join(__dirname, '../radar-business/2025-01-15');
|
const outputDir = path.join(__dirname, '../radar-business/2025-01-15');
|
||||||
|
|
||||||
if (!fs.existsSync(outputDir)) {
|
if (!fs.existsSync(outputDir)) {
|
||||||
|
|||||||
@@ -145,7 +145,7 @@ function loadTechnologies() {
|
|||||||
|
|
||||||
// Charger les membres de l'équipe (avec profils complets)
|
// Charger les membres de l'équipe (avec profils complets)
|
||||||
function loadTeamMembers() {
|
function loadTeamMembers() {
|
||||||
const teamDir = path.join(__dirname, '../docs/data/team');
|
const teamDir = path.join(__dirname, '../data/team');
|
||||||
const members = [];
|
const members = [];
|
||||||
|
|
||||||
if (!fs.existsSync(teamDir)) {
|
if (!fs.existsSync(teamDir)) {
|
||||||
|
|||||||
Reference in New Issue
Block a user