═══════════════════════════════════════════════════════════════════════════
[LIVRE] GUIDE COMPLET NEXUS REPOSITORY MANAGER
VERSION FINALE - PARTIES 1 À 4
═══════════════════════════════════════════════════════════════════════════

[OBJECTIF] Guide ultra-détaillé de Nexus Repository Manager
[GRAPHIQUE] 14 841 lignes | 345 KB | 30-36 heures de lecture
[EDIT] Inspiré du guide Flask (structure pédagogique éprouvée)
[CALENDRIER] Version : Janvier 2026
[TROPHEE] De débutant à expert Nexus

═══════════════════════════════════════════════════════════════════════════
[DOCS] TABLE DES MATIÈRES COMPLÈTE - 17 CHAPITRES
═══════════════════════════════════════════════════════════════════════════

[LIVRE] PARTIE 1 : FONDAMENTAUX (Chapitres 0-4)
[TEMPS] Temps estimé : 6-8 heures | Pages : ~80

  CHAPITRE 0 : INTRODUCTION À NEXUS
  ──────────────────────────────────
  [OK] Qu'est-ce que Nexus Repository Manager ?
  [OK] Problèmes résolus (cache, contrôle, sécurité)
  [OK] Nexus OSS vs Pro (comparaison détaillée)
  [OK] Concepts fondamentaux (Repositories, Artifacts, Blob Stores)
  [OK] Architecture (client -> Nexus -> remote)
  [OK] Cas d'usage concrets (Java, JavaScript, Docker, Python)
  [OK] Bénéfices mesurables (80-90% réduction temps build)

  CHAPITRE 1 : INSTALLATION ET CONFIGURATION
  ───────────────────────────────────────────
  [OK] Prérequis système (2-4 CPU, 4-16GB RAM, 20-1000GB disque)
  [OK] Installation Docker (recommandée) - 3 minutes
  [OK] Installation binaire (Linux, Windows)
  [OK] Configuration initiale et wizard
  [OK] Interface Web (navigation complète)
  [OK] Configuration mémoire JVM
  [OK] Best practices sécurité initiale
  [NOTE] EXERCICE 1 : Installation complète (30-45 min)

  CHAPITRE 2 : REPOSITORIES ET FORMATS
  ─────────────────────────────────────
  [OK] Types de repositories (Hosted, Proxy, Group)
  [OK] Flux de résolution artifacts (3-step lookup)
  [OK] Repositories par défaut (Maven, npm, NuGet)
  [OK] Créer repository Hosted (releases/snapshots)
  [OK] Créer repository Proxy (Maven Central, npmjs)
  [OK] Créer repository Group (agrégation multi-repos)
  [OK] Configuration repositories Docker (ports 8082-8084)
  [OK] Repositories Raw (fichiers génériques)
  [OK] Blob stores (file system, S3, Azure, GCS)
  [NOTE] EXERCICE 2 : Infrastructure complète (45-60 min)

  CHAPITRE 3 : GESTION DES ARTIFACTS
  ───────────────────────────────────
  [OK] Anatomie artifact Maven (GroupId:ArtifactId:Version)
  [OK] Structure sur disque (conventions Maven)
  [OK] Versioning sémantique (MAJOR.MINOR.PATCH)
  [OK] Upload via UI (simple et multi-assets)
  [OK] Upload via CLI (curl, Maven deploy, Gradle publish)
  [OK] Recherche artifacts (UI browse, API REST)
  [OK] Téléchargement (wget, curl, clients Maven/Gradle)
  [OK] Suppression artifacts (précautions immuabilité)
  [OK] Cleanup Policies (automatisation >30j)
  [OK] Metadata et checksums (MD5, SHA1, SHA256)
  [NOTE] EXERCICE 3 : Cycle complet artifact (30 min)

  CHAPITRE 4 : UTILISATEURS, RÔLES ET PERMISSIONS
  ────────────────────────────────────────────────
  [OK] Modèle RBAC (User -> Role -> Privilege -> Action)
  [OK] Utilisateurs par défaut (admin, anonymous)
  [OK] Créer utilisateurs locaux (john.doe, jenkins-user)
  [OK] Rôles built-in (nx-admin, nx-anonymous, nx-deployment)
  [OK] Comprendre privileges (repository-view-*, admin-*)
  [OK] Créer rôles personnalisés (developer, deployer, reader)
  [OK] Intégration LDAP/Active Directory (SSO)
  [OK] Configuration realms (ordre authentification)
  [OK] API Tokens (génération base64, utilisation)
  [OK] Best practices sécurité (least privilege)
  [NOTE] EXERCICE 4 : Sécurité équipe 10 personnes (60 min)


[LIVRE] PARTIE 2 : INTÉGRATION BUILD TOOLS (Chapitres 5-8)
[TEMPS] Temps estimé : 6-8 heures | Pages : ~75

  CHAPITRE 5 : INTÉGRATION MAVEN COMPLÈTE
  ────────────────────────────────────────
  [OK] Maven sans Nexus (problèmes : lenteur, dépendance Internet)
  [OK] Maven avec Nexus (80% réduction temps build)
  [OK] Configuration pom.xml (<repositories>, <distributionManagement>)
  [OK] Configuration settings.xml (<servers>, <mirrors>, <profiles>)
  [OK] Télécharger dépendances (cache transparent)
  [OK] Déployer artifacts (mvn deploy)
  [OK] Gestion snapshots (-SNAPSHOT) vs releases
  [OK] Sécuriser credentials (encryption Maven, variables env, tokens)
  [OK] Résolution dépendances (nearest definition, transitives)
  [OK] Troubleshooting (401, 404, redeploy forbidden, cache corrompu)
  [OK] Logs debug Maven (-X flag)
  [NOTE] EXERCICE 5 : Projet Maven complet (60-90 min)

  CHAPITRE 6 : INTÉGRATION GRADLE
  ────────────────────────────────
  [OK] Gradle vs Maven (comparaison détaillée)
  [OK] Prérequis Gradle (installation, wrapper)
  [OK] Configuration build.gradle (Groovy DSL)
  [OK] Configuration build.gradle.kts (Kotlin DSL type-safe)
  [OK] Repositories { maven { url } }
  [OK] Publishing (plugin maven-publish)
  [OK] Configuration gradle.properties (credentials externes)
  [OK] Configuration init.gradle (global machine)
  [OK] Multi-projets Gradle (subprojects, dependencies)
  [OK] Build cache (local et remote Nexus Pro)
  [OK] Troubleshooting Gradle-Nexus
  [NOTE] EXERCICE 6 : Projet Gradle multi-modules (60-90 min)

  CHAPITRE 7 : PROXY ET CACHE AVANCÉ
  ───────────────────────────────────
  [OK] Comprendre cache Nexus (3 niveaux : client, Nexus, remote)
  [OK] Flux de cache (98% économie bande passante)
  [OK] Configuration proxy repository (Maven Central, npmjs)
  [OK] Maximum component age (-1 pour releases, 1440 pour snapshots)
  [OK] Maximum metadata age (contrôle re-vérification)
  [OK] Negative cache (404 caching, évite requêtes inutiles)
  [OK] Auto-blocking (remote unavailable temporairement)
  [OK] Stratégies par type (releases immuables vs snapshots changeants)
  [OK] Cleanup et maintenance (tasks automatiques)
  [OK] Tasks custom (cron expressions)
  [OK] Performance tuning (JVM, blob stores multiples)
  [OK] Cloud blob storage (S3, Azure, lifecycle policies)
  [OK] Monitoring cache (stats, API)

  CHAPITRE 8 : SNAPSHOTS VS RELEASES - STRATÉGIES
  ────────────────────────────────────────────────
  [OK] Snapshot vs Release (différences fondamentales)
  [OK] Caractéristiques snapshots (mutable, timestamp 20241218.103045-1)
  [OK] Caractéristiques releases (immutable, permanent)
  [OK] Cycle de vie artifact (dev -> RC -> release -> maintenance)
  [OK] Semantic Versioning (1.0.0 -> 1.0.1 -> 1.1.0 -> 2.0.0)
  [OK] Qualifiers (-SNAPSHOT, -RC1, -alpha, -beta, -M1)
  [OK] Workflow standard (timeline exemple 6 semaines)
  [OK] Bonnes pratiques (snapshots dev, releases prod)
  [OK] Anti-patterns (re-deploy release, snapshot en prod)
  [OK] Configuration Nexus (Disable redeploy releases)


[LIVRE] PARTIE 3 : FORMATS AVANCÉS (Chapitres 9-13)
[TEMPS] Temps estimé : 8-10 heures | Pages : ~95

  CHAPITRE 9 : DOCKER REGISTRY NEXUS
  ───────────────────────────────────
  [OK] Pourquoi Docker registry privé ? (rate limits Docker Hub)
  [OK] Architecture Docker Nexus (ports dédiés 8082-8084)
  [OK] Créer repositories Docker (hosted, proxy Docker Hub, group)
  [OK] Configuration ports (8082 hosted, 8083 proxy, 8084 group)
  [OK] Configurer Docker client (daemon.json insecure-registries)
  [OK] Login Docker (docker login localhost:8082)
  [OK] Push images (docker push localhost:8082/my-app:1.0.0)
  [OK] Tag format (<host>:<port>/<name>:<version>)
  [OK] Pull images (hosted et proxy cache)
  [OK] Gestion tags (latest, stable, semver, build numbers)
  [OK] Docker avec SSL/TLS (Nginx reverse proxy, Let's Encrypt)
  [OK] Cleanup images (policies, old layers)
  [OK] Docker Compose avec Nexus
  [NOTE] EXERCICE 9 : Pipeline Docker Node.js (60-90 min)

  CHAPITRE 10 : NPM REGISTRY PRIVÉ
  ─────────────────────────────────
  [OK] Pourquoi npm registry privé ? (packages privés gratuits)
  [OK] Repositories npm (hosted, proxy npmjs.org, group)
  [OK] Configuration .npmrc (user-level, project-level)
  [OK] Configuration par scope (@company/* -> npm-hosted)
  [OK] npm login (credentials storage)
  [OK] Installer packages (cache transparent npmjs.org)
  [OK] Créer package npm (package.json, publishConfig)
  [OK] Publier package (npm publish)
  [OK] Utiliser package publié (npm install @company/my-package)
  [OK] Versioning npm (npm version patch/minor/major)
  [OK] Update packages (npm update, npm outdated)
  [OK] Scopes organisations (@company/* namespace)
  [NOTE] EXERCICE 10 : Library @company/string-utils (45-60 min)

  CHAPITRE 11 : PyPI REPOSITORY PYTHON
  ─────────────────────────────────────
  [OK] Pourquoi PyPI privé ? (packages privés, cache PyPI.org)
  [OK] Repositories PyPI (hosted, proxy pypi.org, group)
  [OK] Configuration pip.conf (index-url, trusted-host)
  [OK] Configuration via environnement (PIP_INDEX_URL)
  [OK] Installer packages (pip install depuis Nexus)
  [OK] requirements.txt (gestion dépendances projet)
  [OK] Virtual environments (venv isolation)
  [OK] Créer package Python (setup.py, pyproject.toml moderne)
  [OK] Builder package (python -m build -> tar.gz + whl)
  [OK] Configuration .pypirc (repository credentials)
  [OK] Publier avec Twine (twine upload -r nexus dist/*)
  [OK] Utiliser package publié (pip install depuis hosted)
  [OK] Versioning Python (SemVer 1.0.0)
  [NOTE] EXERCICE 11 : Package math-helpers (60 min)

  CHAPITRE 12 : RAW REPOSITORY - FICHIERS GÉNÉRIQUES
  ───────────────────────────────────────────────────
  [OK] Qu'est-ce que Raw ? (stockage universel)
  [OK] Cas d'usage (scripts, configs, assets, binaries, backups)
  [OK] Créer Raw repository (hosted, proxy, group)
  [OK] Content Disposition (Inline affichage vs Attachment téléchargement)
  [OK] Upload via UI (directory + filename)
  [OK] Upload via curl (--upload-file)
  [OK] Upload multiple fichiers (script bash loop)
  [OK] Upload directory complet (script Python recursive)
  [OK] Télécharger (wget, curl, browser direct)
  [OK] API REST (lister, rechercher assets)
  [OK] Organisation fichiers (structure /scripts/, /configs/, /assets/)
  [OK] Cas pratiques : Bootstrap scripts, Config management, CDN interne, Binaries distribution, Backup storage
  [NOTE] EXERCICE 12 : Système déploiement scripts (45-60 min)

  CHAPITRE 13 : AUTRES FORMATS (NuGet, Helm, APT, YUM)
  ─────────────────────────────────────────────────────
  [OK] NuGet (.NET packages)
    - Repositories (hosted, proxy nuget.org, group)
    - Configuration NuGet client (nuget sources Add)
    - Installer (dotnet add package)
    - Publier (dotnet nuget push)
  [OK] Helm (Kubernetes charts)
    - Repositories Helm
    - Configuration (helm repo add nexus-hosted)
    - Installer charts (helm install my-release)
    - Publier (helm push, curl upload)
  [OK] APT (Debian/Ubuntu packages)
    - Repository APT (distribution focal/jammy)
    - Configuration client (/etc/apt/sources.list.d/)
    - GPG signing
    - Publier .deb packages
  [OK] YUM (RHEL/CentOS packages)
    - Repository YUM
    - Configuration (/etc/yum.repos.d/)
    - Publier .rpm packages
  [OK] Formats additionnels (20+ formats supportés) :
    - rubygems (Ruby), go (Go modules), cocoapods (iOS)
    - conan (C/C++), conda (Anaconda), p2 (Eclipse)
    - gitlfs (Git LFS), bower (frontend obsolète)
  [OK] Choisir bon format (decision tree par langage)
  [OK] Combinaisons courantes (full-stack, microservices)


[LIVRE] PARTIE 4 : ADMINISTRATION ET PRODUCTION (Chapitres 14-17)
[TEMPS] Temps estimé : 10-12 heures | Pages : ~95

  CHAPITRE 14 : BACKUP ET RESTAURATION
  ─────────────────────────────────────
  [OK] Données critiques Nexus (db/, blobs/, etc/)
  [OK] Priorités backup (CRITIQUE vs IMPORTANT vs OPTIONNEL)
  [OK] Stratégies backup (full, incremental, differential, hybride)
  [OK] Planning backup (full hebdo dimanche, incremental quotidien)
  [OK] Backup manuel (arrêt Nexus, rsync, tar.gz)
  [OK] Backup à chaud (task "Export databases for backup")
  [OK] Script backup automatisé (bash complet avec cleanup)
  [OK] Automatisation cron (0 3 * * *)
  [OK] Backup cloud (AWS S3, Azure Blob, Google Cloud Storage)
  [OK] Lifecycle policies cloud (Glacier après 30j)
  [OK] Restauration complète (script restore-nexus.sh)
  [OK] Restauration partielle (DB seulement)
  [OK] Tests restauration (mensuel obligatoire)
  [OK] Script test automatisé (Docker container test)
  [OK] Rapport test (RTO actuel vs target)
  [OK] Disaster Recovery Plan (DRP complet)
  [OK] Scénarios (serveur down, DB corrompu, perte complète, blob corrompu)
  [OK] RTO/RPO (objectifs 30min/24h)
  [OK] Procédures détaillées (step-by-step)
  [OK] Contacts urgence (escalation L1->L2->L3->L4)
  [OK] Post-mortem (template incident report)
  [NOTE] EXERCICE 14 : Backup/restore complet (2-3h)

  CHAPITRE 15 : MONITORING ET LOGS
  ─────────────────────────────────
  [OK] Métriques critiques (uptime, CPU, memory, disk, throughput)
  [OK] KPIs Nexus (response time <200ms, cache hit ratio)
  [OK] Thresholds alertes (WARNING >70%, CRITICAL >90%)
  [OK] Fichiers logs (nexus.log, request.log, audit.log)
  [OK] Log levels (TRACE, DEBUG, INFO, WARN, ERROR)
  [OK] Configurer log level (UI System -> Logging)
  [OK] Analyser logs (grep patterns, awk scripts)
  [OK] Rotation logs (daily, 90 jours retention)
  [OK] JMX Exporter (Nexus OSS metrics /metrics)
  [OK] Installer Prometheus (Docker Compose)
  [OK] Configuration Prometheus (scrape_configs)
  [OK] Queries Prometheus (CPU, memory, GC, HTTP)
  [OK] Dashboards Grafana (8 panels recommandés)
  [OK] Data source Grafana (Prometheus connection)
  [OK] Export/import dashboard JSON
  [OK] Alertes Prometheus (rules file)
  [OK] Alertmanager (email, Slack notifications)
  [OK] Alertes Grafana (alternative/complément)
  [NOTE] EXERCICE 15 : Stack monitoring complète (2-3h)

  CHAPITRE 16 : SÉCURITÉ AVANCÉE
  ───────────────────────────────
  [OK] Hardening checklist (15 points essentiels)
  [OK] Password policy (12+ chars, complexité, rotation 90j)
  [OK] Désactiver anonymous (production obligatoire)
  [OK] Minimal privileges (rôle developer exemple)
  [OK] Rotation API tokens (procédure)
  [OK] Audit logging (activer complet, 365j retention)
  [OK] Regular updates (process upgrade)
  [OK] SSL/TLS configuration
    - Méthode 1 : Reverse proxy Nginx (recommandée)
    - Let's Encrypt (certbot gratuit, auto-renewal)
    - Certificat commercial ou auto-signé
    - Security headers (HSTS, X-Content-Type-Options)
    - Méthode 2 : SSL direct Nexus (keystore.jks)
  [OK] Firewall configuration
    - UFW (Ubuntu) - deny all, allow minimum
    - firewalld (CentOS/RHEL)
    - iptables (avancé, rate limiting DDoS)
  [OK] Network segmentation
    - Zones DMZ / Application / Management
    - Firewall inter-zones
  [OK] Compliance et audit
    - SOC 2 (cloud services)
    - HIPAA (santé)
    - PCI-DSS (paiements)
    - RGPD (données personnelles EU)
    - Configuration compliance Nexus
    - Audit report automatisé (script bash)
  [OK] Vulnerability scanning
    - Scanner Nexus (CVE database check)
    - Scanner dependencies (OWASP Dependency Check)
    - Scanner Docker images (Trivy)
    - Nexus IQ Server (PRO)
    - Automated scanning CI/CD
  [NOTE] EXERCICE 16 : Hardening production-ready (3-4h)

  CHAPITRE 17 : PERFORMANCE TUNING ET OPTIMISATION
  ─────────────────────────────────────────────────
  [OK] Identifier bottlenecks (CPU, memory, disk I/O, network)
  [OK] Métriques performance (response time, throughput, concurrent users)
  [OK] Outils diagnostic (System Info, JMX, logs, Prometheus)
  [OK] Symptômes courants (UI lente, timeouts, high CPU)
  [OK] JVM tuning
    - Configuration par défaut (2703MB heap)
    - Recommandations (4GB small, 8GB medium, 16GB large, 32GB enterprise)
    - Règle 50-75% RAM disponible
    - Appliquer config (Docker INSTALL4J_ADD_VM_PARAMS, nexus.vmoptions)
    - Garbage Collector G1GC (tuning, logs)
    - Heap dump OOM (analysis Eclipse MAT)
  [OK] Database optimization
    - Compaction régulière (task weekly)
    - Rebuild index (si recherche lente)
    - Backup/restore DB (si corruption)
  [OK] Blob store optimization
    - Stratégie SSD pour hot data
    - Cloud storage S3 (tiers Standard/IA/Glacier)
    - Multiple blob stores (I/O parallèle)
  [OK] Caching strategies
    - 3 niveaux cache (client, Nexus memory, blob store)
    - Optimiser proxy cache (component age -1, metadata age)
    - Warm cache preloading (script top 100 artifacts)
  [OK] Load balancing
    - Architecture HA (2+ Nexus, shared blob store NFS/S3)
    - Nginx load balancer (least_conn, ip_hash sticky sessions)
    - HAProxy alternative (balance leastconn, health checks)
  [OK] Load testing
    - Pourquoi ? (identifier limites, valider config)
    - Outils (JMeter, Gatling, Locust, k6)
    - JMeter test plan (100 users, 10 minutes)
    - k6 script (ramp up/down, thresholds)
    - Analyser résultats (p95, error rate, throughput, breaking point)
  [NOTE] EXERCICE 17 : Performance optimization (3-4h)


═══════════════════════════════════════════════════════════════════════════
[GRAPHIQUE] STATISTIQUES DU GUIDE COMPLET
═══════════════════════════════════════════════════════════════════════════

[NOTE] Contenu :
  • 14 841 lignes de code et documentation
  • 17 chapitres ultra-détaillés (4 parties)
  • 11 exercices pratiques complets avec validation
  • 100+ exemples de code prêts à l'emploi
  • 200+ snippets de configuration
  • 345 KB de contenu technique

[OBJECTIF] Formats couverts :
  • Maven (Java/JVM ecosystem)
  • Gradle (build system Groovy/Kotlin)
  • Docker (containers OCI)
  • npm (JavaScript/Node.js/TypeScript)
  • PyPI (Python packages)
  • NuGet (.NET/C#)
  • Helm (Kubernetes charts)
  • APT/YUM (Linux packages)
  • Raw (fichiers génériques)
  • 20+ autres formats (Go, Ruby, Conan, etc.)

[OUTILS] Technologies intégrées :
  • Maven (settings.xml, pom.xml, deploy)
  • Gradle (Groovy DSL, Kotlin DSL, publishing)
  • Docker (Dockerfile, docker-compose, registry)
  • npm (package.json, .npmrc, scopes)
  • Python (pip, Twine, pyproject.toml, venv)
  • CI/CD (Jenkins, GitLab CI, GitHub Actions)
  • Monitoring (Prometheus, Grafana, JMX)
  • Security (SSL/TLS, Firewall, LDAP, Compliance)
  • Performance (JVM tuning, load balancing)

[TEMPS] Temps de lecture complet :
  • Partie 1 : 6-8 heures (Fondamentaux)
  • Partie 2 : 6-8 heures (Maven/Gradle)
  • Partie 3 : 8-10 heures (Formats avancés)
  • Partie 4 : 10-12 heures (Administration)
  • TOTAL : 30-36 heures (4-5 jours intensifs)


═══════════════════════════════════════════════════════════════════════════
[COURS] PÉDAGOGIE ET APPROCHE
═══════════════════════════════════════════════════════════════════════════

[DOCS] Méthode "Comment ? Pourquoi ? Quand ?"
  • Explications conceptuelles avant technique
  • Exemples concrets du monde réel
  • Comparaisons "Sans Nexus" vs "Avec Nexus"
  • Diagrammes ASCII art (flux, architecture)

[OK] Bonnes pratiques systématiques
  • Security best practices (SSL, RBAC, audit)
  • Production-ready patterns (HA, backup, monitoring)
  • Performance optimization (JVM, cache, load balancing)
  • Compliance guidance (SOC2, HIPAA, RGPD)

[X] Anti-patterns documentés
  • Erreurs courantes à éviter (re-deploy release, snapshot prod)
  • Troubleshooting détaillé (401, 404, cache corrompu)
  • Solutions concrètes avec exemples

[CLE] Points clés récapitulatifs
  • Résumés de fin de chapitre
  • Validation des acquis
  • Références croisées

[NOTE] Exercices pratiques validés
  • Cahier des charges détaillé (SCÉNARIO -> ÉTAPES -> VALIDATION)
  • Code fourni complet
  • Critères de validation clairs
  • Temps estimé réaliste


═══════════════════════════════════════════════════════════════════════════
[RAPIDE] PARCOURS D'APPRENTISSAGE RECOMMANDÉS
═══════════════════════════════════════════════════════════════════════════

[OBJECTIF] DÉBUTANT (Découverte Nexus)
  Semaine 1 : Partie 1 complète (Chapitres 0-4)
    - Installation en local
    - Faire tous les exercices
    - Créer compte utilisateurs
  
  Semaine 2 : Partie 2 (Chapitres 5-6 selon stack)
    - Si Java -> Focus Maven
    - Si JavaScript -> Focus Gradle + npm (Partie 3 Ch10)
    - Intégrer dans projet existant
  
  Semaine 3 : Pratique et consolidation
    - Projet personnel complet
    - Docker registry si pertinent
    - Documentation interne équipe

[OBJECTIF] DÉVELOPPEUR (Intégration projets)
  Jour 1 : Survol Partie 1 (refresh concepts)
  Jour 2-3 : Partie 2 (Maven ou Gradle selon stack)
  Jour 4 : Partie 3 (Docker + npm/PyPI selon besoins)
  Jour 5 : Intégration CI/CD et tests

[OBJECTIF] DEVOPS/SYSADMIN (Administration production)
  Semaine 1 : Parties 1-2 (fondamentaux + intégration)
  Semaine 2 : Partie 3 (tous formats pour équipes multiples)
  Semaine 3 : Partie 4 (backup, monitoring, sécurité, performance)
  Semaine 4 : Mise en production
    - HA setup
    - Disaster recovery plan
    - Monitoring stack
    - Load testing

[OBJECTIF] ARCHITECTE (Design système)
  Focus : Architecture et stratégie
    - Partie 1 : Chapitres 0-2 (concepts, architecture)
    - Partie 2 : Chapitre 7-8 (cache, versioning)
    - Partie 3 : Tous chapitres (vue d'ensemble formats)
    - Partie 4 : Tous chapitres (production considerations)
  
  Livrables :
    - Architecture diagram
    - Capacity planning
    - Disaster recovery strategy
    - Security compliance plan

[OBJECTIF] RÉFÉRENCE RAPIDE
  Utilisation : Index par problème
    - "Comment configurer Maven ?" -> Chapitre 5
    - "Docker registry setup ?" -> Chapitre 9
    - "Backup strategy ?" -> Chapitre 14
    - "Performance slow ?" -> Chapitre 17
    - "Security hardening ?" -> Chapitre 16


═══════════════════════════════════════════════════════════════════════════
[MILITARY_MEDAL] CERTIFICATION NEXUS (Suggérée)
═══════════════════════════════════════════════════════════════════════════

Après ce guide, vous êtes prêt pour :
  [OK] Sonatype Nexus Repository Manager Certification (si disponible)
  [OK] Rôle Nexus Administrator dans votre organisation
  [OK] Architect solutions avec Nexus comme pierre angulaire
  [OK] Former autres développeurs/DevOps sur Nexus

Compétences maîtrisées :
  [OK] Installation et configuration Nexus (Docker, binaire)
  [OK] Gestion repositories tous formats majeurs
  [OK] Sécurité (RBAC, LDAP, SSL/TLS, compliance)
  [OK] Intégration CI/CD (Maven, Gradle, Docker, npm, Python)
  [OK] Administration (backup, monitoring, logs)
  [OK] Performance optimization (JVM, cache, load balancing)
  [OK] Disaster recovery planning
  [OK] Troubleshooting avancé


═══════════════════════════════════════════════════════════════════════════
[TEL] À PROPOS DU GUIDE
═══════════════════════════════════════════════════════════════════════════

[CALENDRIER] Version : Janvier 2026
[LABEL] Nexus : Version 3.x (OSS Open Source)
[GUIDE] Structure inspirée : Guide Flask 11 000+ lignes
[OBJECTIF] Objectif : Formation complète débutant -> expert
[UTILISATEURS] Public : Développeurs, DevOps, SysAdmins, Architectes
[SCALES] Licence : Guide éducatif libre

[ATTENTION] Note sur versions :
  • Guide couvre Nexus OSS (version gratuite)
  • Fonctionnalités Nexus Pro mentionnées et marquées [PRO]
  • Concepts généraux applicables toutes versions 3.x
  • Vérifier release notes pour nouvelles versions

[LIEN] Ressources officielles :
  • Documentation : https://help.sonatype.com/repomanager3
  • Community : https://community.sonatype.com
  • Release Notes : https://help.sonatype.com/repomanager3/product-information/release-notes


═══════════════════════════════════════════════════════════════════════════
[BRAVO] FÉLICITATIONS !
═══════════════════════════════════════════════════════════════════════════

Vous détenez maintenant le guide Nexus Repository Manager le plus complet
en français, couvrant de l'installation basique jusqu'à l'administration
production avec haute disponibilité, monitoring avancé et sécurité durcie.

17 chapitres | 14 841 lignes | 345 KB | 30-36 heures de contenu

Vous êtes prêt pour :
  [OK] Installer et configurer Nexus pour votre organisation
  [OK] Intégrer avec tous vos projets (Java, JS, Python, Docker, etc.)
  [OK] Administrer Nexus en production (backup, monitoring, sécurité)
  [OK] Optimiser performance pour 1000+ utilisateurs
  [OK] Former vos équipes sur Nexus

[RAPIDE] Bon apprentissage et excellente maîtrise de Nexus ! [RAPIDE]


═══════════════════════════════════════════════════════════════════════════
[IDEE] AIDE-MÉMOIRE COMMANDES ESSENTIELLES
═══════════════════════════════════════════════════════════════════════════

# Docker Nexus
docker run -d --name nexus -p 8081:8081 -v nexus-data:/nexus-data sonatype/nexus3
docker logs -f nexus
docker exec nexus cat /nexus-data/admin.password

# Maven
mvn clean install                    # Build avec dépendances depuis Nexus
mvn deploy                           # Deploy vers Nexus
mvn dependency:tree                  # Voir arbre dépendances
mvn clean install -U                 # Force update snapshots

# Gradle
./gradlew build                      # Build projet
./gradlew publish                    # Publish vers Nexus
./gradlew dependencies               # Voir dépendances

# Docker Registry
docker login localhost:8082
docker tag my-app:1.0.0 localhost:8082/my-app:1.0.0
docker push localhost:8082/my-app:1.0.0
docker pull localhost:8082/my-app:1.0.0

# npm
npm login --registry=http://localhost:8081/repository/npm-hosted/
npm publish --registry=http://localhost:8081/repository/npm-hosted/
npm install @company/package

# Python
pip install requests                 # Depuis Nexus PyPI
twine upload -r nexus dist/*         # Publish package

# Backup
tar -czf nexus-backup-$(date +%Y-%m-%d).tar.gz /opt/sonatype-work/nexus3/
aws s3 cp nexus-backup.tar.gz s3://backups/

# Monitoring
curl http://localhost:9090/metrics   # JMX Exporter metrics
curl -u admin:admin123 http://localhost:8081/service/rest/v1/status

# Security
sudo ufw allow 443/tcp               # Firewall HTTPS
sudo certbot --nginx -d nexus.company.com  # Let's Encrypt SSL

# Performance
# Augmenter heap : -Xms8g -Xmx8g -XX:MaxDirectMemorySize=8g
k6 run load-test.js                  # Load testing

═══════════════════════════════════════════════════════════════════════════
* FIN DU GUIDE COMPLET *
═══════════════════════════════════════════════════════════════════════════
# ============================================================================
# [LIVRE] NEXUS REPOSITORY MANAGER - GUIDE ULTRA-DÉTAILLÉ POUR DÉBUTANTS
# ============================================================================
#
# [OBJECTIF] GUIDE COMPLET POUR MAÎTRISER NEXUS DE ZÉRO À EXPERT
#
# Ce guide est organisé en 4 parties progressives :
#
# PARTIE 1 : FONDAMENTAUX (nexus_partie1.txt)
# - Chapitre 0 : Introduction à Nexus
# - Chapitre 1 : Installation et Configuration
# - Chapitre 2 : Repositories et Formats
# - Chapitre 3 : Gestion des Artifacts
# - Chapitre 4 : Utilisateurs et Permissions
#
# PARTIE 2 : INTÉGRATION MAVEN/GRADLE (nexus_partie2.txt)
# - Chapitre 5 : Intégration Maven
# - Chapitre 6 : Intégration Gradle
# - Chapitre 7 : Proxy et Cache
# - Chapitre 8 : Snapshots vs Releases
#
# PARTIE 3 : FORMATS AVANCÉS (nexus_partie3.txt)
# - Chapitre 9 : Docker Registry
# - Chapitre 10 : npm Registry
# - Chapitre 11 : PyPI Repository
# - Chapitre 12 : Raw Repository
# - Chapitre 13 : Autres Formats (NuGet, Helm, etc.)
#
# PARTIE 4 : PRODUCTION ET ADMINISTRATION (nexus_partie4.txt)
# - Chapitre 14 : Backup et Restore
# - Chapitre 15 : Monitoring et Logs
# - Chapitre 16 : Sécurité Avancée
# - Chapitre 17 : Performance et Optimisation
# - Chapitre 18 : CI/CD Integration
# - Chapitre 19 : Haute Disponibilité
# - Chapitre 20 : Best Practices
#
# [TEMPS] TEMPS DE LECTURE TOTAL : ~20-25 heures
# [DOCS] PRÉREQUIS : Notions de base en développement logiciel
#
# [IDEE] COMMENT UTILISER CE GUIDE :
# 1. Lisez les parties dans l'ordre
# 2. Testez TOUS les exemples
# 3. Faites les exercices pratiques
# 4. Créez vos propres configurations
#
# ============================================================================

"""
[OBJECTIF] PHILOSOPHIE DE CE GUIDE

COMMENT ? -> Explications pas à pas
POURQUOI ? -> Raisons et contexte
QUAND ? -> Cas d'usage concrets
PRATIQUE -> Exemples réels et exercices

Ce guide vise à être VOTRE SEULE RÉFÉRENCE Nexus !
"""

# ============================================================================
# [NOTE] CONVENTIONS UTILISÉES DANS CE GUIDE
# ============================================================================

"""
[IDEE] Information importante
[REFLEXION] Question / Réflexion
[OK] Bonne pratique
[X] Mauvaise pratique
[ATTENTION] Attention / Avertissement
[CLE] Point clé à retenir
[COURS] Exercice pratique
[DOCS] Résumé
[OBJECTIF] Objectif
[TEMPS] Temps estimé
[RAPIDE] Prêt pour la suite
"""


# ============================================================================
# [GUIDE] CHAPITRE 0 : INTRODUCTION À NEXUS REPOSITORY MANAGER
# ============================================================================

"""
[OBJECTIF] OBJECTIFS D'APPRENTISSAGE

À la fin de ce chapitre, vous saurez :
[OK] Ce qu'est Nexus Repository Manager
[OK] Pourquoi utiliser un gestionnaire de repository
[OK] Différence entre Nexus OSS et Pro
[OK] Concepts clés (artifacts, repositories, etc.)
[OK] Cas d'usage concrets
"""


# ----------------------------------------------------------------------------
# [REFLEXION] QU'EST-CE QUE NEXUS REPOSITORY MANAGER ?
# ----------------------------------------------------------------------------

"""
DÉFINITION SIMPLE

Nexus Repository Manager = Gestionnaire de dépôts d'artifacts
- Stocke et gère vos bibliothèques/packages
- Proxy pour repositories externes
- Cache local pour accélérer les builds
- Sécurise l'accès aux artifacts


[IDEE] ARTIFACT = QUOI ?

Artifact = Fichier produit par votre build
Exemples :
- JAR/WAR (Java)
- Docker image
- npm package (JavaScript)
- wheel/tar.gz (Python)
- DLL/EXE (C#/.NET)


ANALOGIE SIMPLE [CONSTRUCTION]

Imaginez une bibliothèque municipale :

SANS NEXUS (Bibliothèque sans organisation) :
[DOCS] Livres éparpillés partout
[X] Impossible de trouver un livre
[X] Pas de contrôle d'accès
[X] Chacun doit acheter ses propres livres

AVEC NEXUS (Bibliothèque bien organisée) :
[OK] Catalogue centralisé
[OK] Système de classement (repositories)
[OK] Carte de bibliothèque (authentification)
[OK] Prêt contrôlé (permissions)
[OK] Livres partagés (cache)


COMPOSANTS DE BASE DE NEXUS

Nexus vous donne :
[OK] Repository Manager (stockage)
[OK] Proxy Repository (cache Maven Central, npm, etc.)
[OK] Hosted Repository (vos artifacts privés)
[OK] Group Repository (agrégation)
[OK] Système de sécurité (users, roles, privileges)
[OK] REST API (automation)
[OK] Interface Web (UI conviviale)


[IDEE] POURQUOI "REPOSITORY MANAGER" ?

Repository Manager ≠ Git Repository

Git Repository :
- Stocke CODE SOURCE (.java, .js, .py)
- Versionning du code
- Collaboration développeurs

Nexus Repository Manager :
- Stocke BINAIRES/PACKAGES (.jar, .war, .zip)
- Cache des dépendances
- Distribution d'artifacts
"""


# ----------------------------------------------------------------------------
# [RECHERCHE] PROBLÈME : DÉVELOPPEMENT SANS NEXUS
# ----------------------------------------------------------------------------

"""
SCÉNARIO : ÉQUIPE DE 10 DÉVELOPPEURS

Sans Nexus Repository Manager :
"""

# [X] Chaque développeur télécharge depuis Maven Central
# Build.gradle ou pom.xml
"""
dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web:3.2.0'
    implementation 'com.fasterxml.jackson.core:jackson-databind:2.15.0'
    // ... 50+ dépendances
}
"""

# Que se passe-t-il ?
"""
Développeur 1 -> gradle build -> Télécharge 200 MB depuis Maven Central
Développeur 2 -> gradle build -> Télécharge 200 MB depuis Maven Central
Développeur 3 -> gradle build -> Télécharge 200 MB depuis Maven Central
...
Développeur 10 -> gradle build -> Télécharge 200 MB depuis Maven Central

CI Server -> gradle build -> Télécharge 200 MB depuis Maven Central (à chaque build!)

TOTAL : 2+ GB téléchargés
TEMPS : 5-10 minutes par build
BANDE PASSANTE : Saturée
"""

"""
[X] PROBLÈMES MAJEURS

1. BANDE PASSANTE GASPILLÉE
   - Même artifacts téléchargés 100+ fois
   - Facture internet élevée
   - Builds lents

2. DÉPENDANCE EXTERNE
   - Maven Central down -> Impossible de builder
   - npm registry slow -> Builds bloqués
   - Aucun contrôle

3. PAS DE SÉCURITÉ
   - N'importe qui peut ajouter une dépendance
   - Risque de packages malveillants
   - Pas d'audit

4. PAS DE VERSIONNING INTERNE
   - Artifacts privés non partagés
   - Chacun compile localement
   - Pas de binaires centralisés

5. COMPLIANCE IMPOSSIBLE
   - Quelle version de quelle lib ?
   - Licence compatible ?
   - Vulnérabilités connues ?


[OK] SOLUTION : NEXUS REPOSITORY MANAGER

Avec Nexus :
"""

# Nexus comme proxy
"""
Développeur 1 -> gradle build -> Nexus (cache Maven Central) -> 200 MB
Développeur 2 -> gradle build -> Nexus (déjà en cache) -> 0 MB Internet
Développeur 3 -> gradle build -> Nexus (déjà en cache) -> 0 MB Internet
...
CI Server -> gradle build -> Nexus (déjà en cache) -> 0 MB Internet

TOTAL : 200 MB téléchargés (1 fois)
TEMPS : 30 secondes par build
BANDE PASSANTE : Économisée
"""

"""
[OK] AVANTAGES NEXUS

1. CACHE LOCAL [SAUVEGARDE]
   Artifact téléchargé 1 fois -> Réutilisé infiniment
   
2. DISPONIBILITÉ [VERROUILLE]
   Maven Central down -> Nexus sert depuis le cache
   Build continue à fonctionner
   
3. SÉCURITÉ [SECURITE]
   Contrôle qui peut télécharger quoi
   Scan de vulnérabilités
   Audit trail
   
4. ARTIFACTS PRIVÉS [ENTREPRISE]
   Stocker vos propres libs
   Partage entre équipes
   Versioning centralisé
   
5. PERFORMANCE [RAPIDE]
   Builds 10x plus rapides
   Réseau local vs Internet
   
6. COMPLIANCE [LISTE]
   Traçabilité complète
   Gestion des licences
   Rapports d'usage
"""


# ----------------------------------------------------------------------------
# 🆚 NEXUS OSS vs NEXUS PRO
# ----------------------------------------------------------------------------

"""
NEXUS REPOSITORY OSS (Open Source)
──────────────────────────────────

[ARGENT] GRATUIT
[PACKAGE] Formats supportés :
   - Maven (Java)
   - npm (JavaScript)
   - Docker
   - PyPI (Python)
   - NuGet (.NET)
   - Helm (Kubernetes)
   - Raw (n'importe quoi)
   - Et 20+ autres formats

[OK] Features incluses :
   - Repository Proxy
   - Repository Hosted
   - Repository Group
   - User Management
   - Role-Based Access Control (RBAC)
   - REST API
   - LDAP/Active Directory
   - Cleanup Policies
   - Content Selectors

[X] Limitations :
   - Pas de support officiel
   - Pas de High Availability
   - Pas de features Pro (voir ci-dessous)


NEXUS REPOSITORY PRO (Payant)
─────────────────────────────

[ARGENT] PAYANT (licence annuelle)
[PACKAGE] Tous les formats OSS +
   - Formats propriétaires additionnels

[OK] Features PRO :
   - High Availability (HA)
   - Disaster Recovery
   - Support technique Sonatype
   - Repository Health Check
   - Firewall (block malicious components)
   - Staging/Build Promotion
   - Tag-Based Component Metadata
   - Auditing avancé
   - Smart Proxy

[OBJECTIF] QUAND CHOISIR PRO ?

OSS suffisant si :
[OK] Petite/moyenne équipe
[OK] Pas de contraintes HA critiques
[OK] Budget limité
[OK] Besoin standard

PRO nécessaire si :
[OK] Grande entreprise
[OK] Besoin HA (uptime 99.9%+)
[OK] Support officiel requis
[OK] Compliance stricte
[OK] Scan sécurité automatique


COMPARAISON TABLEAU
───────────────────

┌─────────────────────┬─────────┬─────────┐
│ Feature             │   OSS   │   PRO   │
├─────────────────────┼─────────┼─────────┤
│ Prix                │  Gratuit│  $$$    │
│ Formats (Maven,npm) │    [OK]   │    [OK]   │
│ Docker Registry     │    [OK]   │    [OK]   │
│ RBAC                │    [OK]   │    [OK]   │
│ LDAP/AD             │    [OK]   │    [OK]   │
│ REST API            │    [OK]   │    [OK]   │
│ Support             │    [X]   │    [OK]   │
│ High Availability   │    [X]   │    [OK]   │
│ Firewall            │    [X]   │    [OK]   │
│ Health Check        │    [X]   │    [OK]   │
│ Staging             │    [X]   │    [OK]   │
└─────────────────────┴─────────┴─────────┘

[IDEE] CE GUIDE SE CONCENTRE SUR NEXUS OSS
Car gratuit et amplement suffisant pour 95% des cas
"""


# ----------------------------------------------------------------------------
# [DOCS] CONCEPTS CLÉS DE NEXUS
# ----------------------------------------------------------------------------

"""
1. REPOSITORY (Dépôt)
────────────────────

Repository = Emplacement de stockage pour artifacts

3 TYPES :

a) HOSTED REPOSITORY (Dépôt hébergé)
   - Stocke VOS artifacts
   - Vous uploadez dedans
   - Exemple : mes-libs-java, mes-docker-images
   
b) PROXY REPOSITORY (Dépôt proxy)
   - Cache d'un repository externe
   - Télécharge et stocke localement
   - Exemple : maven-central-proxy, npmjs-proxy
   
c) GROUP REPOSITORY (Dépôt groupe)
   - Agrégation de plusieurs repositories
   - Point d'accès unique
   - Exemple : maven-public (hosted + proxy)


ANALOGIE [PACKAGE]

Hosted Repository = Votre propre entrepôt
   Vous stockez vos propres produits

Proxy Repository = Distributeur local d'un fournisseur
   Cache des produits d'Amazon localement

Group Repository = Centre commercial
   Agrège votre entrepôt + distributeurs


2. ARTIFACT (Artefact)
─────────────────────

Artifact = Fichier binaire versionné

Composants d'un artifact :
- GroupId (organisation)
- ArtifactId (nom du projet)
- Version
- Type/Extension (.jar, .war, .zip)
- Classifier (optionnel : javadoc, sources)

Exemple Maven :
com.example:my-app:1.0.0:jar
│           │       │     │
│           │       │     └─ Type
│           │       └─────── Version
│           └─────────────── ArtifactId
└─────────────────────────── GroupId


3. BLOB STORE
────────────

Blob Store = Stockage physique sur disque

Types :
- File (système de fichiers)
- S3 (AWS S3)
- Azure Blob Storage
- Google Cloud Storage

[IDEE] Repository utilise un Blob Store pour stocker artifacts


4. FORMAT
────────

Format = Type de package/artifact

Exemples :
- maven2 (Java)
- npm (JavaScript)
- docker (Containers)
- pypi (Python)
- nuget (.NET)
- raw (n'importe quoi)

Chaque format a :
- Structure spécifique
- Métadonnées propres
- API client dédiée


5. COMPONENT
───────────

Component = Ensemble d'artifacts liés

Exemple :
Component "my-app 1.0.0" contient :
- my-app-1.0.0.jar
- my-app-1.0.0-sources.jar
- my-app-1.0.0-javadoc.jar
- my-app-1.0.0.pom


6. PRIVILEGE & ROLE
──────────────────

Privilege = Permission granulaire
   Exemples : nx-repository-view-*-*-read, nx-repository-view-*-*-add

Role = Groupe de privileges
   Exemples : nx-admin, nx-anonymous, developer-role

User = Utilisateur avec un ou plusieurs roles


7. CONTENT SELECTOR
──────────────────

Content Selector = Filtre sur les artifacts

Exemples :
- Seulement groupe com.mycompany
- Seulement versions RELEASE
- Exclure versions SNAPSHOT
"""


# ----------------------------------------------------------------------------
# [OBJECTIF] CAS D'USAGE CONCRETS
# ----------------------------------------------------------------------------

"""
CAS 1 : ACCÉLÉRER LES BUILDS JAVA
─────────────────────────────────

AVANT (Sans Nexus) :
- Build Maven/Gradle -> 5 minutes
- Télécharge depuis Maven Central à chaque fois
- CI pipelines lents

APRÈS (Avec Nexus) :
- Build Maven/Gradle -> 30 secondes
- Cache local Nexus
- CI pipelines rapides

Configuration :
1. Créer proxy repository pour Maven Central
2. Configurer pom.xml/build.gradle pour utiliser Nexus
3. Premiers builds remplissent le cache
4. Builds suivants ultra-rapides


CAS 2 : PARTAGER BIBLIOTHÈQUES INTERNES
───────────────────────────────────────

PROBLÈME :
Équipe développe "common-utils.jar"
-> Comment le partager entre projets ?

SANS Nexus :
[X] Copier-coller le JAR (version hell)
[X] Compiler localement à chaque fois
[X] Pas de versioning

AVEC Nexus :
[OK] Upload common-utils-1.0.0.jar dans Nexus
[OK] Autres projets le récupèrent via Maven/Gradle
[OK] Versioning automatique
[OK] Mise à jour facile (1.0.1, 1.1.0, etc.)


CAS 3 : DOCKER REGISTRY PRIVÉ
─────────────────────────────

PROBLÈME :
Stocker images Docker privées

SANS Nexus :
[X] Docker Hub public (pas de privé gratuit)
[X] Payant pour registries privés
[X] Dépendance externe

AVEC Nexus :
[OK] Docker registry privé gratuit
[OK] Sur votre infrastructure
[OK] Contrôle total
[OK] Sécurité

Configuration :
1. Créer Docker hosted repository
2. docker login nexus.mycompany.com:8082
3. docker push nexus.mycompany.com:8082/my-app:1.0


CAS 4 : ISOLATION RÉSEAU (AIR-GAPPED)
────────────────────────────────────

CONTEXTE :
Environnement sans accès Internet (sécurité)

PROBLÈME :
Comment obtenir dépendances Maven/npm ?

SOLUTION Nexus :
1. Nexus dans zone avec Internet
2. Proxy et cache toutes les dépendances
3. Export blob store
4. Import dans Nexus air-gapped
5. Développeurs utilisent Nexus local


CAS 5 : COMPLIANCE ET AUDIT
──────────────────────────

BESOIN :
- Savoir quelle lib utilisée où
- Identifier vulnérabilités
- Vérifier licences

AVEC Nexus :
[OK] Historique complet des téléchargements
[OK] Rapport d'usage par projet
[OK] Scan de vulnérabilités (Pro)
[OK] Gestion des licences
"""


# ----------------------------------------------------------------------------
# [CONSTRUCTION] ARCHITECTURE NEXUS
# ----------------------------------------------------------------------------

"""
ARCHITECTURE SIMPLIFIÉE

┌──────────────────────────────────────────────────┐
│            UTILISATEURS / SYSTÈMES               │
│  Développeurs    CI/CD    Maven/Gradle  Docker  │
└────────────┬──────────┬──────────┬──────────────┘
             │          │          │
             [BLACK_DOWN-POINTING_TRIANGLE]          [BLACK_DOWN-POINTING_TRIANGLE]          [BLACK_DOWN-POINTING_TRIANGLE]
┌────────────────────────────────────────────────┐
│              NEXUS WEB UI / REST API           │
│  http://nexus.company.com:8081                 │
└────────────┬───────────────────────────────────┘
             │
             [BLACK_DOWN-POINTING_TRIANGLE]
┌────────────────────────────────────────────────┐
│          NEXUS APPLICATION LAYER               │
│  - Authentification                            │
│  - Authorization (RBAC)                        │
│  - Repository Management                       │
│  - Format Handlers (Maven, npm, Docker, etc.)  │
└────────────┬───────────────────────────────────┘
             │
             [BLACK_DOWN-POINTING_TRIANGLE]
┌────────────────────────────────────────────────┐
│              REPOSITORY LAYER                  │
│  ┌──────────┐  ┌──────────┐  ┌──────────┐    │
│  │  Hosted  │  │  Proxy   │  │  Group   │    │
│  │  Repos   │  │  Repos   │  │  Repos   │    │
│  └──────────┘  └──────────┘  └──────────┘    │
└────────────┬───────────────────────────────────┘
             │
             [BLACK_DOWN-POINTING_TRIANGLE]
┌────────────────────────────────────────────────┐
│               BLOB STORES                      │
│  ┌──────────────────────────────────────────┐ │
│  │  File System / S3 / Azure / GCS          │ │
│  │  /sonatype-work/nexus3/blobs/           │ │
│  └──────────────────────────────────────────┘ │
└────────────────────────────────────────────────┘


FLUX DE REQUÊTE

Exemple : gradle build télécharge spring-boot-3.2.0.jar

1. Gradle -> GET http://nexus:8081/repository/maven-public/org/springframework/boot/spring-boot/3.2.0/spring-boot-3.2.0.jar

2. Nexus reçoit requête
   ├─ Authentifie utilisateur
   ├─ Vérifie permissions (peut-il lire maven-public ?)
   └─ Si OK, continue

3. maven-public = Group Repository
   ├─ Cherche dans maven-releases (hosted)
   ├─ Pas trouvé
   ├─ Cherche dans maven-central-proxy (proxy)
   └─ Trouvé en cache -> Retourne

4. Si pas en cache :
   ├─ Télécharge depuis Maven Central
   ├─ Stocke dans blob store
   ├─ Retourne à Gradle
   └─ Prochaine requête servie depuis cache
"""


# ----------------------------------------------------------------------------
# [LISTE] QUAND UTILISER NEXUS ?
# ----------------------------------------------------------------------------

"""
[OK] UTILISER NEXUS QUAND :

1. ÉQUIPE > 3 DÉVELOPPEURS
   Partage d'artifacts nécessaire

2. BUILDS FRÉQUENTS
   CI/CD avec builds toutes les heures/minutes
   
3. DÉPENDANCES EXTERNES
   Projet utilise Maven Central, npm, PyPI, etc.
   
4. ARTIFACTS PRIVÉS
   Besoin de stocker vos propres libs
   
5. ENVIRONNEMENT D'ENTREPRISE
   Sécurité, audit, compliance requis
   
6. MULTI-PROJETS
   Plusieurs projets partageant des libs
   
7. BANDE PASSANTE LIMITÉE
   Connexion Internet lente/coûteuse


[X] NE PAS UTILISER NEXUS QUAND :

1. PROJET SOLO SIMPLE
   Un seul dev, projet simple, peu de dépendances
   
2. PAS DE DÉPENDANCES EXTERNES
   Tout codé from scratch (rare)
   
3. RESSOURCES LIMITÉES
   Pas de serveur pour héberger Nexus
   
4. PROTOTYPAGE RAPIDE
   POC court terme, jetable


ALTERNATIVES À NEXUS

JFrog Artifactory
   - Équivalent commercial
   - Plus de features out-of-the-box
   - Plus cher

GitHub Packages
   - Gratuit pour public repos
   - Intégré à GitHub
   - Limité aux formats GitHub supporte

GitLab Package Registry
   - Gratuit
   - Intégré à GitLab
   - Bon pour petites équipes

CloudRepo / Packagecloud
   - SaaS
   - Pas d'infra à gérer
   - Coût mensuel
"""


# ----------------------------------------------------------------------------
# [GRAPHIQUE] STATISTIQUES ET CHIFFRES
# ----------------------------------------------------------------------------

"""
GAINS MESURABLES AVEC NEXUS

TEMPS DE BUILD
──────────────
Sans Nexus : 5-10 minutes (téléchargement dépendances)
Avec Nexus : 30-60 secondes (cache local)
-> 80-90% de réduction


BANDE PASSANTE
──────────────
Équipe de 10 devs, 5 builds/jour :
Sans Nexus : 200 MB × 10 × 5 = 10 GB/jour
Avec Nexus : 200 MB (première fois) + négligeable = ~200 MB/jour
-> 98% de réduction


DISPONIBILITÉ
─────────────
Sans Nexus : Dépend de Maven Central uptime (~99%)
Avec Nexus : Cache local -> 99.9%+ uptime
-> Builds non bloqués si Internet down


SÉCURITÉ
────────
Sans Nexus : 0 contrôle sur les packages téléchargés
Avec Nexus : Audit complet, scan vulnérabilités, RBAC
-> Risque divisé par 10


FORMATS SUPPORTÉS (Nexus OSS 3.x)
──────────────────────────────────

[OK] Maven 2/3 (Java)
[OK] npm (JavaScript/Node.js)
[OK] Docker
[OK] PyPI (Python)
[OK] NuGet (.NET)
[OK] RubyGems (Ruby)
[OK] Helm (Kubernetes)
[OK] APT (Debian/Ubuntu)
[OK] YUM (RedHat/CentOS)
[OK] Go
[OK] Composer (PHP)
[OK] CPAN (Perl)
[OK] CocoaPods (iOS)
[OK] Conda (Python scientifique)
[OK] p2 (Eclipse)
[OK] R (Statistiques)
[OK] Raw (n'importe quoi)
[OK] Et 10+ autres
"""


# ----------------------------------------------------------------------------
# [COURS] EXERCICE DE RÉFLEXION
# ----------------------------------------------------------------------------

"""
AVANT DE CONTINUER, RÉPONDEZ À CES QUESTIONS :

1. Votre projet a-t-il besoin de Nexus ?
   [ ] Oui
   [ ] Non
   [ ] Pas sûr
   
2. Quels formats utilisez-vous ?
   [ ] Maven (Java)
   [ ] npm (JavaScript)
   [ ] Docker
   [ ] PyPI (Python)
   [ ] Autre : __________
   
3. Combien de développeurs dans votre équipe ?
   [ ] 1-3 (petit)
   [ ] 4-10 (moyen)
   [ ] 10+ (grand)
   
4. Avez-vous des artifacts privés à partager ?
   [ ] Oui
   [ ] Non
   [ ] Bientôt
   
5. Vos builds sont-ils lents (>2 minutes) ?
   [ ] Oui -> Nexus peut aider !
   [ ] Non
   [ ] Variable

Si vous avez répondu Oui à 2+ questions -> Nexus est pertinent !
"""


# ----------------------------------------------------------------------------
# [DOCS] RÉCAPITULATIF CHAPITRE 0
# ----------------------------------------------------------------------------

"""
CE QUE VOUS AVEZ APPRIS

[OK] Nexus = Gestionnaire de repositories d'artifacts
[OK] 3 types de repositories : Hosted, Proxy, Group
[OK] Résout : performance, disponibilité, sécurité
[OK] Nexus OSS = Gratuit, suffisant pour la plupart
[OK] Nexus Pro = Payant, HA, support, features avancées
[OK] Supporte 20+ formats (Maven, npm, Docker, etc.)


[CLE] POINTS CLÉS

1. Nexus ≠ Git
   Git stocke code source
   Nexus stocke binaires/packages

2. Cache local = Builds rapides
   Télécharge 1 fois, réutilise infiniment

3. Repository types :
   Hosted = Vos artifacts
   Proxy = Cache d'external
   Group = Agrégation

4. Artifact = Binaire versionné
   GroupId:ArtifactId:Version


[OBJECTIF] AVANT DE CONTINUER

Assurez-vous de comprendre :
[OK] Différence Hosted vs Proxy vs Group
[OK] Pourquoi Nexus accélère les builds
[OK] Quels formats vous intéressent


-> PROCHAINE ÉTAPE : Chapitre 1 - Installation !

Vous allez apprendre :
- Installer Nexus (Docker, binaire)
- Premier démarrage
- Configuration initiale
- Créer premier repository

Prêt ? C'est parti ! [RAPIDE]
"""


# ============================================================================
# [GUIDE] CHAPITRE 1 : INSTALLATION ET CONFIGURATION
# ============================================================================

"""
[OBJECTIF] OBJECTIFS D'APPRENTISSAGE

À la fin de ce chapitre, vous saurez :
[OK] Installer Nexus Repository OSS
[OK] Différentes méthodes d'installation
[OK] Configuration initiale
[OK] Accéder à l'interface Web
[OK] Changer le mot de passe admin
[OK] Configuration de base
"""


# ----------------------------------------------------------------------------
# [CODE] PRÉREQUIS SYSTÈME
# ----------------------------------------------------------------------------

"""
CONFIGURATION MINIMALE
─────────────────────

CPU : 2 cores
RAM : 4 GB (recommandé 8 GB)
Disque : 20 GB minimum (dépend de l'usage)
OS : Windows, Linux, macOS
Java : OpenJDK 8 ou 11 (inclus dans Docker)


CONFIGURATION RECOMMANDÉE (Production)
──────────────────────────────────────

CPU : 4+ cores
RAM : 8-16 GB
Disque : 100+ GB SSD
OS : Linux (Ubuntu 20.04+, CentOS 8+, RHEL 8+)
Java : OpenJDK 11


ESTIMATION ESPACE DISQUE
───────────────────────

Petit projet (1-5 devs) : 20-50 GB
Moyen projet (5-20 devs) : 50-200 GB
Grand projet (20+ devs) : 200 GB - 1 TB+

[IDEE] Prévoir croissance : Artifacts s'accumulent !
"""


# ----------------------------------------------------------------------------
# [DOCKER] MÉTHODE 1 : INSTALLATION DOCKER (RECOMMANDÉE)
# ----------------------------------------------------------------------------

"""
POURQUOI DOCKER ?

[OK] Installation rapide (5 minutes)
[OK] Pas de configuration Java
[OK] Isolation complète
[OK] Facile à upgrader
[OK] Portable
[OK] Idéal pour dev et test


PRÉREQUIS
────────

Docker installé et running :
"""

# Vérifier Docker
docker --version
# Docker version 24.0.0 ou supérieur

"""
ÉTAPE 1 : CRÉER VOLUME POUR DONNÉES
───────────────────────────────────

[IDEE] POURQUOI UN VOLUME ?

Sans volume :
[X] Données perdues si container supprimé
[X] Pas de persistence

Avec volume :
[OK] Données persistantes
[OK] Container supprimable/recréable
[OK] Backup facile
"""

# Créer volume Docker
docker volume create --name nexus-data

# Vérifier
docker volume ls
# DRIVER    VOLUME NAME
# local     nexus-data

"""
ÉTAPE 2 : LANCER CONTENEUR NEXUS
────────────────────────────────
"""

# Version simple (développement)
docker run -d \
  --name nexus \
  -p 8081:8081 \
  -v nexus-data:/nexus-data \
  sonatype/nexus3

"""
[IDEE] DÉCRYPTAGE COMMANDE

docker run                        Lancer un container
  -d                             Détaché (background)
  --name nexus                   Nom du container
  -p 8081:8081                   Port mapping (host:container)
  -v nexus-data:/nexus-data      Volume mount
  sonatype/nexus3                Image Docker officielle


VARIANTES UTILES
───────────────
"""

# Avec variables d'environnement
docker run -d \
  --name nexus \
  -p 8081:8081 \
  -v nexus-data:/nexus-data \
  -e INSTALL4J_ADD_VM_PARAMS="-Xms2703m -Xmx2703m -XX:MaxDirectMemorySize=2703m" \
  sonatype/nexus3

# Avec port Docker registry (pour héberger images Docker)
docker run -d \
  --name nexus \
  -p 8081:8081 \
  -p 8082:8082 \
  -p 8083:8083 \
  -v nexus-data:/nexus-data \
  sonatype/nexus3

"""
[IDEE] PORTS EXPLIQUÉS

8081 : Interface Web Nexus
8082 : Docker registry (hosted)
8083 : Docker registry (group)

Vous pouvez mapper d'autres ports selon besoins


ÉTAPE 3 : VÉRIFIER DÉMARRAGE
────────────────────────────

Nexus prend 2-3 minutes à démarrer
"""

# Voir les logs
docker logs -f nexus

# Sortie attendue :
"""
-------------------------------------------------

Started Sonatype Nexus OSS 3.x.x

-------------------------------------------------
"""

# Vérifier statut
docker ps

# Sortie :
"""
CONTAINER ID   IMAGE             STATUS          PORTS                    NAMES
abc123...      sonatype/nexus3   Up 2 minutes    0.0.0.0:8081->8081/tcp   nexus
"""

"""
ÉTAPE 4 : RÉCUPÉRER MOT DE PASSE INITIAL
────────────────────────────────────────

[IDEE] SÉCURITÉ

Nexus génère un mot de passe admin aléatoire
Stocké dans /nexus-data/admin.password
"""

# Méthode 1 : Via Docker
docker exec nexus cat /nexus-data/admin.password

# Sortie : e1b2c3d4-5678-90ab-cdef-1234567890ab

# Méthode 2 : Si volume local
cat /var/lib/docker/volumes/nexus-data/_data/admin.password

"""
ÉTAPE 5 : ACCÉDER À L'INTERFACE WEB
───────────────────────────────────
"""

# Ouvrir navigateur :
http://localhost:8081

# Ou si serveur distant :
http://<IP_SERVEUR>:8081

"""
[IDEE] PREMIÈRE CONNEXION

1. Page d'accueil Nexus s'affiche
2. Cliquer "Sign in" (en haut à droite)
3. Username : admin
4. Password : (celui récupéré à l'étape 4)
5. Wizard de configuration s'affiche
"""


# ----------------------------------------------------------------------------
# [PACKAGE] MÉTHODE 2 : INSTALLATION BINAIRE (Linux/Windows)
# ----------------------------------------------------------------------------

"""
QUAND UTILISER ?

[OK] Pas de Docker disponible
[OK] Installation sur serveur bare-metal
[OK] Intégration avec systemd
[OK] Performance maximale


INSTALLATION LINUX (Ubuntu/Debian)
──────────────────────────────────

ÉTAPE 1 : Installer Java
"""

# Ubuntu/Debian
sudo apt update
sudo apt install openjdk-11-jdk -y

# Vérifier
java -version
# openjdk version "11.0.x"

"""
ÉTAPE 2 : Télécharger Nexus
"""

# Créer répertoire
sudo mkdir -p /opt/nexus
cd /opt

# Télécharger dernière version
NEXUS_VERSION=3.63.0-01  # Vérifier dernière version sur sonatype.com
wget https://download.sonatype.com/nexus/3/nexus-${NEXUS_VERSION}-unix.tar.gz

# Extraire
sudo tar -xvf nexus-${NEXUS_VERSION}-unix.tar.gz

# Renommer pour simplicité
sudo mv nexus-${NEXUS_VERSION} nexus
sudo mv sonatype-work /opt/

"""
[IDEE] STRUCTURE INSTALLÉE

/opt/nexus/                # Application Nexus
/opt/sonatype-work/        # Données et configuration


ÉTAPE 3 : Créer utilisateur Nexus
"""

# Créer utilisateur système
sudo adduser --system --no-create-home --group nexus

# Donner ownership
sudo chown -R nexus:nexus /opt/nexus
sudo chown -R nexus:nexus /opt/sonatype-work

"""
ÉTAPE 4 : Configurer Nexus
"""

# Éditer fichier de configuration
sudo nano /opt/nexus/bin/nexus.rc

# Ajouter :
run_as_user="nexus"

# Sauvegarder et quitter (Ctrl+X, Y, Enter)

"""
ÉTAPE 5 : Configurer comme service systemd
"""

# Créer fichier service
sudo nano /etc/systemd/system/nexus.service

# Contenu :
"""
[Unit]
Description=Nexus Repository Manager
After=network.target

[Service]
Type=forking
LimitNOFILE=65536
User=nexus
Group=nexus
ExecStart=/opt/nexus/bin/nexus start
ExecStop=/opt/nexus/bin/nexus stop
Restart=on-abort

[Install]
WantedBy=multi-user.target
"""

# Recharger systemd
sudo systemctl daemon-reload

# Activer au démarrage
sudo systemctl enable nexus

# Démarrer Nexus
sudo systemctl start nexus

# Vérifier statut
sudo systemctl status nexus

"""
ÉTAPE 6 : Vérifier logs
"""

# Logs Nexus
tail -f /opt/sonatype-work/nexus3/log/nexus.log

# Attendre message :
# Started Sonatype Nexus OSS

"""
ÉTAPE 7 : Récupérer mot de passe
"""

cat /opt/sonatype-work/nexus3/admin.password

"""
ÉTAPE 8 : Accéder interface
"""

# http://localhost:8081 ou http://<IP>:8081


"""
INSTALLATION WINDOWS
───────────────────

ÉTAPE 1 : Installer Java
- Télécharger OpenJDK 11 depuis https://adoptium.net/
- Installer
- Vérifier : java -version

ÉTAPE 2 : Télécharger Nexus
- https://www.sonatype.com/products/nexus-repository
- Télécharger nexus-3.x.x-win64.zip
- Extraire dans C:\nexus

ÉTAPE 3 : Lancer Nexus
- Ouvrir PowerShell en Administrateur
- cd C:\nexus\bin
- .\nexus.exe /run

ÉTAPE 4 : Installer comme service (optionnel)
- .\nexus.exe /install
- Démarrer service : net start nexus

ÉTAPE 5 : Accéder
- http://localhost:8081
"""


# ----------------------------------------------------------------------------
# [OUTIL] CONFIGURATION INITIALE (WIZARD)
# ----------------------------------------------------------------------------

"""
APRÈS PREMIÈRE CONNEXION
────────────────────────

Un wizard de configuration apparaît


ÉTAPE 1 : CHANGER MOT DE PASSE ADMIN
────────────────────────────────────

1. Entrer mot de passe actuel (celui du fichier admin.password)
2. Entrer nouveau mot de passe
3. Confirmer nouveau mot de passe

[IDEE] RÈGLES MOT DE PASSE

[OK] Au moins 8 caractères
[OK] Mélange majuscules/minuscules
[OK] Au moins 1 chiffre
[OK] Caractère spécial recommandé

Exemple bon : Admin@Nexus2024
Exemple mauvais : admin123


ÉTAPE 2 : CONFIGURER ACCÈS ANONYME
──────────────────────────────────

Question : "Enable anonymous access?"

[ ] Enable (permet téléchargement sans authentification)
[x] Disable (nécessite login pour tout)

[IDEE] RECOMMANDATION

Développement : Enable (pratique)
Production : Disable (sécurité)

Vous pouvez changer plus tard !


ÉTAPE 3 : AMÉLIORATION PRODUIT
─────────────────────────────

Question : "Help Sonatype improve Nexus?"

[ ] Send anonymous usage statistics

À votre choix, pas d'impact fonctionnel


ÉTAPE 4 : TERMINER
─────────────────

Cliquer "Finish"

-> Wizard terminé !
-> Interface principale Nexus s'affiche
"""


# ----------------------------------------------------------------------------
# [DESIGN] DÉCOUVERTE INTERFACE WEB
# ----------------------------------------------------------------------------

"""
LAYOUT PRINCIPAL
───────────────

┌─────────────────────────────────────────────────┐
│  [NEXUS LOGO]    Browse  Search      [Sign Out] │  Header
├─────────────────────────────────────────────────┤
│ ┌─────────────┐                                 │
│ │             │                                 │
│ │  Sidebar    │       Main Content              │
│ │  Menu       │       Area                      │
│ │             │                                 │
│ │  Browse     │                                 │
│ │  Upload     │                                 │
│ │  Security   │                                 │
│ │  System     │                                 │
│ │             │                                 │
│ └─────────────┘                                 │
└─────────────────────────────────────────────────┘


MENU PRINCIPAL (Sidebar)
───────────────────────

[RECHERCHE] Browse
   - Components (artifacts stockés)
   - Assets (fichiers individuels)
   
[PACKAGE] Repositories
   - Liste des repositories
   - Créer nouveau repository
   
[SORTIE] Upload
   - Upload manuel d'artifacts
   
[UTILISATEURS] Security
   - Users (utilisateurs)
   - Roles (rôles)
   - Privileges (permissions)
   - LDAP
   - Realms
   
[CONFIG] System
   - Blob Stores
   - Repositories
   - Capabilities
   - Tasks
   - Support
   
[GRAPHIQUE] Repository Health Check (Pro)
   - Scan vulnérabilités


NAVIGATION RAPIDE
────────────────

[RECHERCHE] Search (en haut) :
   Chercher artifacts par nom, groupe, version


PREMIÈRE EXPLORATION
───────────────────

1. Cliquer "Browse" -> "maven-central"
   Voir cache proxy Maven Central (vide pour l'instant)

2. Cliquer "Security" -> "Users"
   Voir utilisateurs (admin + anonymous)

3. Cliquer "System" -> "Blob Stores"
   Voir stockage par défaut
"""


# ----------------------------------------------------------------------------
# [SECURISE] CONFIGURATION SÉCURITÉ DE BASE
# ----------------------------------------------------------------------------

"""
BONNES PRATIQUES SÉCURITÉ
────────────────────────

1. CHANGER MOT DE PASSE ADMIN
   [OK] Fait lors du wizard

2. DÉSACTIVER ACCÈS ANONYME (Production)
"""

# Settings -> Security -> Anonymous Access
# Décocher "Allow anonymous users to access the server"
# Save

"""
3. CRÉER UTILISATEURS SPÉCIFIQUES
   [X] Ne jamais partager compte admin !
"""

# Security -> Users -> Create local user
# - ID : john.doe
# - First Name : John
# - Last Name : Doe
# - Email : john@company.com
# - Status : Active
# - Roles : nx-admin (ou role personnalisé)
# - Password : (générer fort)

"""
4. CONFIGURER FIREWALL

Si Nexus sur serveur, ouvrir seulement ports nécessaires :
- 8081 (Web UI)
- 8082-8083 (Docker registry si utilisé)

Bloquer tout le reste !


5. HTTPS (Production obligatoire)

Configurer reverse proxy (Nginx/Apache) avec SSL
OU
Configurer SSL directement dans Nexus (Jetty)

-> Voir Chapitre 16 pour config détaillée
"""


# ----------------------------------------------------------------------------
# [NOTE] CONFIGURATION SYSTÈME DE BASE
# ----------------------------------------------------------------------------

"""
AJUSTER MÉMOIRE JVM
──────────────────

Par défaut, Nexus alloue 2703 MB RAM

Pour changer (si vous avez plus de RAM) :


DOCKER :
"""

docker run -d \
  --name nexus \
  -p 8081:8081 \
  -v nexus-data:/nexus-data \
  -e INSTALL4J_ADD_VM_PARAMS="-Xms4096m -Xmx4096m -XX:MaxDirectMemorySize=4096m" \
  sonatype/nexus3

"""
BINAIRE LINUX :
"""

# Éditer fichier
sudo nano /opt/nexus/bin/nexus.vmoptions

# Modifier lignes :
-Xms4096m
-Xmx4096m
-XX:MaxDirectMemorySize=4096m

# Redémarrer
sudo systemctl restart nexus

"""
[IDEE] FORMULE MÉMOIRE

RAM Disponible = 8 GB
-> Allouer 50-75% à Nexus

Exemple :
8 GB total -> 4 GB Nexus
16 GB total -> 8-10 GB Nexus


CONFIGURER DATA DIRECTORY (Optionnel)
─────────────────────────────────────

Par défaut : /nexus-data (Docker) ou /opt/sonatype-work (binaire)

Pour changer :
"""

# Créer nouveau dossier
sudo mkdir -p /data/nexus
sudo chown nexus:nexus /data/nexus

# Éditer properties
sudo nano /opt/nexus/bin/nexus.properties

# Modifier ligne :
application-data=/data/nexus

# Redémarrer
sudo systemctl restart nexus


# ----------------------------------------------------------------------------
# [COURS] EXERCICE PRATIQUE 1 : INSTALLATION COMPLÈTE
# ----------------------------------------------------------------------------

"""
OBJECTIF : Installer Nexus et créer premier utilisateur


ÉTAPES :

1. Installer Nexus (Docker OU Binaire)
   [OK] Suivre instructions ci-dessus
   [OK] Vérifier accès http://localhost:8081

2. Configuration initiale
   [OK] Changer mot de passe admin
   [OK] Configurer accès anonyme (désactivé)

3. Créer utilisateur développeur
   [OK] ID : dev-user
   [OK] Role : nx-anonymous (lecture seule pour commencer)
   [OK] Tester connexion avec ce user

4. Explorer interface
   [OK] Browse repositories par défaut
   [OK] Regarder Blob Stores
   [OK] Voir Tasks système

5. Vérifier santé système
   [OK] System -> Support -> System Information
   [OK] Noter version Nexus
   [OK] Noter mémoire allouée


VALIDATION :

[OK] Nexus accessible sur port 8081
[OK] Login admin fonctionne (nouveau password)
[OK] Login dev-user fonctionne
[OK] Repositories par défaut visibles
[OK] Aucune erreur dans logs


TEMPS ESTIMÉ : 30-45 minutes
"""


# ----------------------------------------------------------------------------
# [DOCS] RÉCAPITULATIF CHAPITRE 1
# ----------------------------------------------------------------------------

"""
CE QUE VOUS AVEZ APPRIS

[OK] Installer Nexus avec Docker (méthode recommandée)
[OK] Installer Nexus en binaire (Linux/Windows)
[OK] Récupérer mot de passe initial
[OK] Wizard de configuration
[OK] Changer mot de passe admin
[OK] Interface Web Nexus
[OK] Configuration sécurité de base
[OK] Ajustement mémoire JVM


[CLE] POINTS CLÉS

1. Docker = Installation la plus simple
   docker run + volume = Done !

2. Mot de passe admin dans admin.password
   À changer immédiatement

3. Port 8081 par défaut
   Configurable si besoin

4. 2-3 minutes pour démarrer
   Vérifier logs : "Started Sonatype Nexus"

5. Interface Web intuitive
   Browse, Security, System


[OBJECTIF] AVANT DE CONTINUER

Assurez-vous :
[OK] Nexus installé et accessible
[OK] Login admin fonctionne
[OK] Interface Web familière
[OK] Pas d'erreurs dans logs


-> PROCHAINE ÉTAPE : Chapitre 2 - Repositories !

Vous allez apprendre :
- Types de repositories (Hosted, Proxy, Group)
- Créer votre premier repository
- Formats supportés (Maven, npm, Docker, etc.)
- Configurer repositories

Prêt pour la pratique ! [RAPIDE]
"""


# ============================================================================
# FIN DE NEXUS_PARTIE1.TXT
# CONTINUEZ AVEC nexus_partie2.txt
# ============================================================================
# ============================================================================
# [LIVRE] NEXUS REPOSITORY MANAGER - PARTIE 1 (SUITE)
# CHAPITRES 2, 3 ET 4
# ============================================================================


# ============================================================================
# [GUIDE] CHAPITRE 2 : REPOSITORIES ET FORMATS
# ============================================================================

"""
[OBJECTIF] OBJECTIFS D'APPRENTISSAGE

À la fin de ce chapitre, vous saurez :
[OK] Créer différents types de repositories
[OK] Comprendre Hosted vs Proxy vs Group
[OK] Configurer un repository Maven
[OK] Configurer un repository npm
[OK] Configurer un repository Docker
[OK] Gérer blob stores
[OK] Bonnes pratiques repositories
"""


# ----------------------------------------------------------------------------
# [RECHERCHE] RAPPEL : TYPES DE REPOSITORIES
# ----------------------------------------------------------------------------

"""
LES 3 TYPES FONDAMENTAUX

┌─────────────────────────────────────────────────┐
│          1. HOSTED REPOSITORY                   │
│  Stocke VOS artifacts                           │
│  Vous uploadez dedans                           │
│  Exemple : mes-releases, mes-snapshots          │
└─────────────────────────────────────────────────┘
              v Upload
         [Vos artifacts]


┌─────────────────────────────────────────────────┐
│          2. PROXY REPOSITORY                    │
│  Cache d'un repository externe                  │
│  Télécharge et stocke localement                │
│  Exemple : maven-central-proxy                  │
└─────────────────────────────────────────────────┘
              v Cache depuis
       [Maven Central, npm, etc.]


┌─────────────────────────────────────────────────┐
│          3. GROUP REPOSITORY                    │
│  Agrégation de plusieurs repositories           │
│  Point d'accès unique                           │
│  Exemple : maven-public = hosted + proxy        │
└─────────────────────────────────────────────────┘
              v Agrège
    [Hosted Repos + Proxy Repos]


FLUX TYPIQUE
───────────

Developer/Build Tool
       v
Group Repository (maven-public)
       v
  Cherche dans :
    1. Hosted (vos libs)
    2. Proxy (Maven Central cache)
       v
  Si pas en cache -> Télécharge depuis remote
  Si en cache -> Retourne immédiatement
"""


# ----------------------------------------------------------------------------
# [PACKAGE] REPOSITORIES PAR DÉFAUT
# ----------------------------------------------------------------------------

"""
APRÈS INSTALLATION, NEXUS CRÉE AUTOMATIQUEMENT
──────────────────────────────────────────────

Format MAVEN :
├── maven-central (proxy -> Maven Central)
├── maven-releases (hosted pour vos releases)
├── maven-snapshots (hosted pour vos snapshots)
└── maven-public (group = releases + snapshots + central)

Format NPM :
├── npm-proxy (proxy -> npmjs.org)
├── npm-hosted (vos packages npm)
└── npm-group (aggregation)

Format NUGET :
├── nuget.org-proxy (proxy -> nuget.org)
└── nuget-hosted (vos packages .NET)


[IDEE] CONVENTION DE NOMMAGE

Proxy repositories : <format>-<remote>
  Exemple : maven-central, npm-proxy

Hosted repositories : <format>-<type>
  Exemple : maven-releases, docker-hosted

Group repositories : <format>-<purpose>
  Exemple : maven-public, npm-group


VOIR LES REPOSITORIES
─────────────────────

Interface Web :
1. Sidebar -> [CONFIG] (roue dentée) -> Repositories
2. Liste de tous les repositories
3. Type affiché : hosted/proxy/group
4. Format affiché : maven2/npm/docker/etc.
"""


# ----------------------------------------------------------------------------
# + CRÉER REPOSITORY HOSTED (Vos artifacts)
# ----------------------------------------------------------------------------

"""
CAS D'USAGE : Stocker vos bibliothèques internes

Exemple : Équipe développe "common-utils" library
-> Besoin de la partager entre projets
-> Créer repository hosted


ÉTAPE PAR ÉTAPE : CRÉER MAVEN HOSTED
────────────────────────────────────
"""

# 1. Aller dans Repositories
# Sidebar -> [CONFIG] -> Repositories -> Create repository

# 2. Sélectionner format
# Cliquer sur "maven2 (hosted)"

# 3. Configuration
"""
Name : my-maven-releases
   [IDEE] Nom unique, sans espaces
   Convention : <projet>-<format>-<type>

Online : [OK] (checked)
   [OK] = Repository actif
   [X] = Repository désactivé (maintenance)

Version policy : Release
   Options :
   - Release : Seulement versions stables (1.0.0, 2.1.3)
   - Snapshot : Seulement versions dev (1.0.0-SNAPSHOT)
   - Mixed : Les deux (pas recommandé)
   
   [IDEE] RÈGLE D'OR : Repository dédié par type
   [OK] my-maven-releases (Release)
   [OK] my-maven-snapshots (Snapshot)
   [X] my-maven-mixed (les deux)

Layout policy : Strict
   - Strict : Force structure Maven standard
   - Permissive : Accepte variations
   
   [IDEE] Toujours laisser Strict

Content Disposition : Inline
   - Inline : Fichiers affichés dans navigateur
   - Attachment : Force téléchargement

Blob store : default
   Stockage physique (voir plus bas)

Strict Content Type Validation : [OK]
   Vérifie que fichier correspond à son type MIME

Deployment policy : Allow redeploy
   Options :
   - Allow redeploy : Peut remplacer artifact existant
   - Disable redeploy : Une fois uploadé = immuable
   - Read-only : Pas d'upload du tout
   
   [IDEE] RECOMMANDATIONS :
   Releases : Disable redeploy (immutabilité)
   Snapshots : Allow redeploy (développement)
"""

# 4. Cliquer "Create repository"

# [OK] Repository créé !
# Accessible à : http://nexus:8081/repository/my-maven-releases/


"""
EXEMPLE COMPLET : CRÉER REPOSITORY MAVEN SNAPSHOTS
──────────────────────────────────────────────────
"""

# Configuration recommandée
"""
Name : my-maven-snapshots

Version policy : Snapshot
   Accepte seulement versions -SNAPSHOT

Deployment policy : Allow redeploy
   Snapshots peuvent être re-uploadés

Cleanup Policies : (optionnel)
   Supprimer automatiquement vieux snapshots
   Exemple : Garder seulement 30 derniers jours
"""


# ----------------------------------------------------------------------------
# [SYNC] CRÉER REPOSITORY PROXY (Cache externe)
# ----------------------------------------------------------------------------

"""
CAS D'USAGE : Accélérer builds en cachant Maven Central

Sans proxy :
[X] Chaque build télécharge depuis Maven Central
[X] 5-10 minutes par build
[X] Dépendance Internet

Avec proxy :
[OK] Premier build télécharge et cache
[OK] Builds suivants ultra-rapides (cache local)
[OK] Fonctionne même si Maven Central down


CRÉER PROXY MAVEN CENTRAL
─────────────────────────
"""

# 1. Create repository -> maven2 (proxy)

# 2. Configuration
"""
Name : maven-central-proxy

Remote storage : https://repo1.maven.org/maven2/
   [IDEE] URL du repository distant à proxifier
   
   Repositories populaires :
   Maven Central : https://repo1.maven.org/maven2/
   JCenter (obsolète) : https://jcenter.bintray.com
   Spring : https://repo.spring.io/release
   Google : https://maven.google.com

Version policy : Release
   Maven Central = releases seulement

Layout policy : Strict

Blob store : default

Use the proxy server : (si vous avez proxy d'entreprise)
   Configurer proxy HTTP pour sortir d'Internet

Auto blocking enabled : [OK]
   Si remote indisponible, Nexus le marque "blocked"
   Évite de surcharger un remote down

Maximum component age : 1440 (minutes = 24h)
   Combien de temps garder metadata en cache
   
Maximum metadata age : -1 (illimité)
   -1 = Jamais re-vérifier (performances max)
   1440 = Re-vérifier metadata après 24h

Negative Cache : Enabled
   TTL : 1440
   Cache les "404 Not Found" pour éviter re-requêtes
"""

# 3. Créer


"""
CRÉER PROXY NPM
──────────────

Pour JavaScript/Node.js projects
"""

# Create repository -> npm (proxy)

"""
Name : npmjs-proxy

Remote storage : https://registry.npmjs.org
   Registry npm officiel

Remove non-cataloged : [X]
   Laisser décoché pour npm

Remove quarantined : [X]
"""


"""
CRÉER PROXY PYPI
───────────────

Pour Python projects
"""

# Create repository -> pypi (proxy)

"""
Name : pypi-proxy

Remote storage : https://pypi.org
   Python Package Index officiel
"""


"""
[IDEE] TESTER PROXY REPOSITORY

Après création, le proxy est vide (pas encore de cache)

Pour le remplir :
1. Configurer Maven/Gradle pour utiliser Nexus (voir Chapitre 5)
2. Faire un build
3. Artifacts sont téléchargés et cachés
4. Browse -> maven-central-proxy -> Voir artifacts cachés
"""


# ----------------------------------------------------------------------------
# [LIEN] CRÉER REPOSITORY GROUP (Agrégation)
# ----------------------------------------------------------------------------

"""
CAS D'USAGE : Point d'accès unique

Au lieu de configurer :
- maven-releases
- maven-snapshots  
- maven-central-proxy

Dans pom.xml -> Configurer seulement :
- maven-public (group qui agrège les 3)


CRÉER GROUP REPOSITORY
─────────────────────
"""

# Create repository -> maven2 (group)

"""
Name : maven-public

Member repositories : (ordre important!)
   [x] my-maven-releases        (1er = priorité haute)
   [x] my-maven-snapshots        (2ème)
   [x] maven-central-proxy       (3ème = fallback)
   
[IDEE] ORDRE D'AGRÉGATION

Quand artifact demandé :
1. Cherche dans my-maven-releases
2. Si pas trouvé -> my-maven-snapshots
3. Si pas trouvé -> maven-central-proxy
4. Si toujours pas trouvé -> 404


[IDEE] POURQUOI CET ORDRE ?

Vos releases (hosted) en premier :
-> Si vous avez version custom, elle est servie
-> Évite de télécharger depuis Maven Central si exists localement

Proxy en dernier :
-> Fallback pour dépendances externes


Blob store : default
"""


"""
EXEMPLE : GROUP NPM
──────────────────
"""

# Create repository -> npm (group)

"""
Name : npm-all

Members :
   [x] npm-hosted
   [x] npmjs-proxy
"""


# ----------------------------------------------------------------------------
# [SAUVEGARDE] BLOB STORES
# ----------------------------------------------------------------------------

"""
BLOB STORE = STOCKAGE PHYSIQUE

Repository (logique) -> Blob Store (physique)

Par défaut : 1 blob store "default"
Stocke : /nexus-data/blobs/default/


POURQUOI PLUSIEURS BLOB STORES ?
────────────────────────────────

1. SÉPARER PAR TYPE
   blob-maven : Repositories Maven
   blob-docker : Repositories Docker
   blob-npm : Repositories npm
   
   [IDEE] Facilite backup, quotas, monitoring

2. SÉPARER PAR DISQUE
   SSD rapide : Artifacts fréquemment utilisés
   HDD lent : Archives, vieux snapshots
   
3. CLOUD STORAGE
   S3 (AWS)
   Azure Blob Storage
   Google Cloud Storage


CRÉER BLOB STORE
───────────────
"""

# Sidebar -> System -> Blob Stores -> Create blob store

"""
Type : File
   - File : Système de fichiers local
   - S3 : AWS S3
   - Azure : Azure Blob Storage
   - Google : Google Cloud Storage

Name : blob-maven

Path : /nexus-data/blobs/blob-maven
   Chemin sur disque
   
State : Started
   
Soft Quota : (optionnel)
   Type : Space remaining
   Limit : 10 GB
   
   [IDEE] Alerte si espace restant < limite
   N'empêche PAS d'écrire
"""


"""
MIGRER REPOSITORY VERS AUTRE BLOB STORE
───────────────────────────────────────

[ATTENTION] Impossible directement via UI !

Solutions :
1. Créer nouveau repository avec nouveau blob store
2. Copier artifacts (via API ou manuellement)
3. Supprimer ancien repository

OU

Utiliser scripts Groovy (avancé)
"""


# ----------------------------------------------------------------------------
# [DOCKER] REPOSITORIES DOCKER (CAS PARTICULIER)
# ----------------------------------------------------------------------------

"""
DOCKER = FORMAT SPÉCIAL

Nécessite :
- Port dédié (pas 8081)
- Configuration supplémentaire
- HTTP ou HTTPS


CRÉER DOCKER HOSTED REPOSITORY
──────────────────────────────
"""

# Create repository -> docker (hosted)

"""
Name : docker-hosted

HTTP : 8082
   [IDEE] Port dédié pour ce registry
   Docker n'utilise pas /repository/docker-hosted
   Docker utilise http://nexus:8082
   
   [ATTENTION] Port doit être mappé si Docker Nexus :
   docker run -p 8082:8082 ...

HTTPS : (laisser vide si pas configuré)
   Nécessite certificat SSL

Allow anonymous docker pull : [OK]
   Permet docker pull sans login
   
Enable Docker V1 API : [X]
   V1 obsolète, toujours utiliser V2

Blob store : default

Deployment policy : Allow redeploy
   Permet push même tag (latest)
"""

# [OK] Repository créé !
# Accessible à : http://nexus:8082 (pas 8081!)


"""
CRÉER DOCKER PROXY (Docker Hub)
───────────────────────────────
"""

# Create repository -> docker (proxy)

"""
Name : dockerhub-proxy

HTTP : 8083

Remote storage : https://registry-1.docker.io
   Docker Hub officiel

Docker Index : Use Docker Hub
   Utiliser index Docker Hub pour recherche

Allow anonymous docker pull : [OK]
"""


"""
CRÉER DOCKER GROUP
─────────────────
"""

# Create repository -> docker (group)

"""
Name : docker-all

HTTP : 8084

Members :
   [x] docker-hosted
   [x] dockerhub-proxy
"""


"""
UTILISER DOCKER REGISTRY NEXUS
──────────────────────────────
"""

# Configuration Docker client

# Login
docker login nexus.company.com:8082
# Username : admin
# Password : <votre-password>

# Tag image
docker tag my-app:latest nexus.company.com:8082/my-app:latest

# Push
docker push nexus.company.com:8082/my-app:latest

# Pull
docker pull nexus.company.com:8082/my-app:latest


"""
[IDEE] DOCKER AVEC HTTP (Non-SSL)

Docker nécessite HTTPS par défaut

Pour utiliser HTTP (développement uniquement!) :
"""

# Éditer /etc/docker/daemon.json (Linux)
# ou Docker Desktop Settings (Windows/Mac)

{
  "insecure-registries": ["nexus.company.com:8082"]
}

# Redémarrer Docker
sudo systemctl restart docker


# ----------------------------------------------------------------------------
# [LISTE] REPOSITORIES RAW (Fichiers quelconques)
# ----------------------------------------------------------------------------

"""
RAW REPOSITORY = Stockage générique

Peut stocker N'IMPORTE QUOI :
- Scripts (shell, python, etc.)
- Fichiers de configuration
- Assets (images, fonts, etc.)
- Backups
- Documentation


CRÉER RAW HOSTED
───────────────
"""

# Create repository -> raw (hosted)

"""
Name : raw-hosted

Content Disposition : Attachment
   Force téléchargement navigateur
"""


"""
UTILISER RAW REPOSITORY
──────────────────────

Upload via UI :
"""

# 1. Browse -> raw-hosted
# 2. Upload component
# 3. Sélectionner fichiers
# 4. Upload

"""
Upload via curl :
"""

curl -v -u admin:admin123 \
  --upload-file mon-script.sh \
  http://nexus:8081/repository/raw-hosted/scripts/mon-script.sh

"""
Télécharger :
"""

curl http://nexus:8081/repository/raw-hosted/scripts/mon-script.sh \
  -o mon-script.sh

# Ou directement dans navigateur
# http://nexus:8081/repository/raw-hosted/scripts/mon-script.sh


# ----------------------------------------------------------------------------
# [COURS] EXERCICE PRATIQUE 2 : CRÉER ENSEMBLE COMPLET
# ----------------------------------------------------------------------------

"""
OBJECTIF : Créer infrastructure complète pour projet Java


CAHIER DES CHARGES :

1. Repositories Maven :
   [ ] Hosted pour releases (my-releases)
   [ ] Hosted pour snapshots (my-snapshots)
   [ ] Proxy pour Maven Central (central-proxy)
   [ ] Group agrégant les 3 (maven-all)

2. Repositories npm :
   [ ] Hosted pour packages internes (npm-internal)
   [ ] Proxy pour npmjs.org (npm-proxy)
   [ ] Group (npm-all)

3. Repositories Docker :
   [ ] Hosted sur port 8082 (docker-private)
   [ ] Proxy Docker Hub sur port 8083 (dockerhub-proxy)
   [ ] Group sur port 8084 (docker-all)

4. Repository Raw :
   [ ] Pour scripts et assets (raw-files)


ÉTAPES :

1. Créer my-releases
   - Type : maven2 (hosted)
   - Version policy : Release
   - Deployment : Disable redeploy

2. Créer my-snapshots
   - Type : maven2 (hosted)
   - Version policy : Snapshot
   - Deployment : Allow redeploy

3. Créer central-proxy
   - Type : maven2 (proxy)
   - Remote : https://repo1.maven.org/maven2/

4. Créer maven-all
   - Type : maven2 (group)
   - Members : my-releases, my-snapshots, central-proxy

5. Répéter pour npm (étapes similaires)

6. Créer Docker repositories avec ports dédiés

7. Créer raw-files


VALIDATION :

[OK] 11 repositories créés au total
[OK] Groups contiennent bons members
[OK] Docker repositories ont ports corrects
[OK] Tout en status "Online"


TEMPS ESTIMÉ : 45-60 minutes
"""


# ----------------------------------------------------------------------------
# [DOCS] RÉCAPITULATIF CHAPITRE 2
# ----------------------------------------------------------------------------

"""
CE QUE VOUS AVEZ APPRIS

[OK] 3 types de repositories : Hosted, Proxy, Group
[OK] Créer repository de chaque type
[OK] Configurer repositories Maven
[OK] Configurer repositories npm
[OK] Configurer repositories Docker (ports dédiés)
[OK] Repositories Raw pour fichiers génériques
[OK] Blob stores pour stockage physique


[CLE] POINTS CLÉS

1. Hosted = Vos artifacts
   my-releases, my-snapshots

2. Proxy = Cache externe
   maven-central-proxy, npm-proxy

3. Group = Agrégation (point unique)
   maven-public, npm-all

4. Docker nécessite ports dédiés
   8082, 8083, etc. (pas 8081)

5. Ordre des members dans Group = important
   Hosted d'abord, Proxy ensuite

6. Blob store = Stockage physique
   Repository = Logique


[OBJECTIF] BONNES PRATIQUES

[OK] Repository dédié par type (Release/Snapshot)
[OK] Nommage cohérent (<format>-<type>)
[OK] Disable redeploy pour releases
[OK] Allow redeploy pour snapshots
[OK] Groupe pour simplifier configuration clients


-> PROCHAINE ÉTAPE : Chapitre 3 - Gestion Artifacts !

Vous allez apprendre :
- Upload artifacts manuellement
- Upload via Maven/Gradle
- Rechercher artifacts
- Télécharger artifacts
- Supprimer artifacts
- Cleanup policies

Passons à la pratique ! [RAPIDE]
"""


# ============================================================================
# [GUIDE] CHAPITRE 3 : GESTION DES ARTIFACTS
# ============================================================================

"""
[OBJECTIF] OBJECTIFS D'APPRENTISSAGE

À la fin de ce chapitre, vous saurez :
[OK] Comprendre structure d'un artifact
[OK] Upload artifacts via UI
[OK] Upload artifacts via CLI
[OK] Rechercher et filtrer artifacts
[OK] Télécharger artifacts
[OK] Supprimer artifacts
[OK] Configurer Cleanup Policies
[OK] Gérer métadonnées
"""


# ----------------------------------------------------------------------------
# [PACKAGE] ANATOMIE D'UN ARTIFACT
# ----------------------------------------------------------------------------

"""
COMPOSANTS D'UN ARTIFACT MAVEN
──────────────────────────────

Artifact complet Maven :
com.example:my-library:1.0.0:jar

Décomposition :
┌─────────────────────────────────────────────┐
│  GroupId    : com.example                   │
│              (organisation/domaine)         │
├─────────────────────────────────────────────┤
│  ArtifactId : my-library                    │
│              (nom du projet)                │
├─────────────────────────────────────────────┤
│  Version    : 1.0.0                         │
│              (numéro de version)            │
├─────────────────────────────────────────────┤
│  Type       : jar                           │
│              (packaging: jar, war, pom, etc)│
├─────────────────────────────────────────────┤
│  Classifier : (optionnel)                   │
│              javadoc, sources, tests        │
└─────────────────────────────────────────────┘


EXEMPLE COMPLET :

Component : my-library 1.0.0
├── my-library-1.0.0.jar          (artifact principal)
├── my-library-1.0.0.pom          (métadonnées Maven)
├── my-library-1.0.0-sources.jar  (code source)
├── my-library-1.0.0-javadoc.jar  (documentation)
└── checksums
    ├── my-library-1.0.0.jar.md5
    ├── my-library-1.0.0.jar.sha1
    └── ...


STRUCTURE SUR DISQUE (Repository Maven)
───────────────────────────────────────

repository/
└── com/
    └── example/
        └── my-library/
            ├── 1.0.0/
            │   ├── my-library-1.0.0.jar
            │   ├── my-library-1.0.0.pom
            │   ├── my-library-1.0.0-sources.jar
            │   └── my-library-1.0.0.jar.sha1
            ├── 1.0.1/
            │   └── ...
            └── maven-metadata.xml


PATH = groupId (. -> /) + artifactId + version + fichiers

com.example.my-library
└-> com/example/my-library/1.0.0/my-library-1.0.0.jar


VERSIONING SEMANTIQUE
─────────────────────

Format : MAJOR.MINOR.PATCH[-QUALIFIER]

Exemples :
1.0.0          Release stable
1.0.1          Patch (bugfix)
1.1.0          Minor (nouvelle feature)
2.0.0          Major (breaking change)
1.0.0-SNAPSHOT Snapshot (développement)
1.0.0-RC1      Release Candidate
1.0.0-alpha    Alpha version


[IDEE] SNAPSHOT vs RELEASE

SNAPSHOT :
- Version en développement
- Peut être re-uploadée
- Maven/Gradle vérifie nouvelles versions
- Exemple : 1.0.0-SNAPSHOT

RELEASE :
- Version stable
- Immuable (ne doit pas changer)
- Téléchargée une fois et cachée
- Exemple : 1.0.0
"""


# ----------------------------------------------------------------------------
# ^ UPLOAD ARTIFACTS VIA UI
# ----------------------------------------------------------------------------

"""
MÉTHODE 1 : UPLOAD COMPOSANT COMPLET
────────────────────────────────────

Cas : Upload library Java avec sources et javadoc
"""

# 1. Naviguer vers repository
# Browse -> Upload

# 2. Sélectionner repository
# Repository : my-maven-releases

# 3. Remplir informations

"""
Group : com.example
Artifact : my-library
Version : 1.0.0
Packaging : jar

Component Assets:
└─ my-library-1.0.0.jar        <- Artifact principal
   Extension : jar
   Classifier : (vide)
   
└─ my-library-1.0.0-sources.jar <- Code source
   Extension : jar
   Classifier : sources
   
└─ my-library-1.0.0-javadoc.jar <- Documentation
   Extension : jar
   Classifier : javadoc

Generate a POM file : [OK]
   Nexus génère automatiquement le .pom
"""

# 4. Cliquer "Upload"

# [OK] Artifact uploadé !


"""
MÉTHODE 2 : UPLOAD FICHIER UNIQUE
─────────────────────────────────

Cas : Upload rapide sans sources/javadoc
"""

# Browse -> Upload -> my-maven-releases

"""
Group : com.example
Artifact : simple-lib
Version : 1.0.0
Packaging : jar

Asset 1:
File : simple-lib-1.0.0.jar
Extension : jar

Generate POM : [OK]
"""

# Upload


"""
UPLOAD NPM PACKAGE
─────────────────

Pour packages npm (.tgz)
"""

# Browse -> Upload -> npm-hosted

"""
Sélectionner fichier .tgz :
my-package-1.0.0.tgz
"""

# Upload

# Nexus extrait automatiquement :
# - Nom du package (depuis package.json)
# - Version
# - Métadonnées


"""
UPLOAD DOCKER IMAGE
──────────────────

[X] Pas via UI !
Docker images s'uploadent via docker push

Voir configuration Docker au Chapitre 2
"""

# docker push nexus:8082/my-app:1.0.0


"""
UPLOAD RAW FILES
───────────────

Pour scripts, configs, assets
"""

# Browse -> Upload -> raw-hosted

"""
Directory : /scripts/
Filename : deploy.sh

Asset :
File : deploy.sh
"""

# Upload

# Accessible à :
# http://nexus:8081/repository/raw-hosted/scripts/deploy.sh


# ----------------------------------------------------------------------------
# [CODE] UPLOAD ARTIFACTS VIA CLI
# ----------------------------------------------------------------------------

"""
UPLOAD AVEC CURL
───────────────

Format Maven
"""

# Upload JAR
curl -v -u admin:admin123 \
  --upload-file my-library-1.0.0.jar \
  http://nexus:8081/repository/my-maven-releases/com/example/my-library/1.0.0/my-library-1.0.0.jar

# Upload POM
curl -v -u admin:admin123 \
  --upload-file my-library-1.0.0.pom \
  http://nexus:8081/repository/my-maven-releases/com/example/my-library/1.0.0/my-library-1.0.0.pom


"""
UPLOAD AVEC MAVEN
────────────────

Configuration pom.xml
"""

<project>
  <distributionManagement>
    <repository>
      <id>nexus-releases</id>
      <url>http://nexus:8081/repository/my-maven-releases/</url>
    </repository>
    <snapshotRepository>
      <id>nexus-snapshots</id>
      <url>http://nexus:8081/repository/my-maven-snapshots/</url>
    </snapshotRepository>
  </distributionManagement>
</project>

# Configuration ~/.m2/settings.xml

"""
<settings>
  <servers>
    <server>
      <id>nexus-releases</id>
      <username>admin</username>
      <password>admin123</password>
    </server>
    <server>
      <id>nexus-snapshots</id>
      <username>admin</username>
      <password>admin123</password>
    </server>
  </servers>
</settings>
"""

# Deploy vers Nexus
mvn clean deploy

"""
[IDEE] QUE FAIT mvn deploy ?

1. Compile le projet
2. Package (crée .jar)
3. Génère POM
4. Upload vers repository Nexus configuré
5. Upload sources (si activé)
6. Upload javadoc (si activé)
"""


"""
UPLOAD AVEC GRADLE
─────────────────

Configuration build.gradle
"""

plugins {
    id 'java'
    id 'maven-publish'
}

publishing {
    publications {
        maven(MavenPublication) {
            from components.java
            groupId = 'com.example'
            artifactId = 'my-library'
            version = '1.0.0'
        }
    }
    
    repositories {
        maven {
            name = 'nexus'
            url = 'http://nexus:8081/repository/my-maven-releases/'
            credentials {
                username = 'admin'
                password = 'admin123'
            }
        }
    }
}

# Publish vers Nexus
# gradle publish


"""
UPLOAD NPM PACKAGE
─────────────────

Configuration .npmrc
"""

# .npmrc (à la racine du projet)
registry=http://nexus:8081/repository/npm-hosted/
email=admin@example.com
always-auth=true

# Login
npm login --registry=http://nexus:8081/repository/npm-hosted/
# Username: admin
# Password: admin123

# Publish
npm publish --registry=http://nexus:8081/repository/npm-hosted/


"""
UPLOAD PYTHON PACKAGE
────────────────────

Configuration .pypirc
"""

# ~/.pypirc
"""
[distutils]
index-servers =
    nexus

[nexus]
repository: http://nexus:8081/repository/pypi-hosted/
username: admin
password: admin123
"""

# Upload avec twine
pip install twine

# Build package
python setup.py sdist bdist_wheel

# Upload
twine upload -r nexus dist/*


# ----------------------------------------------------------------------------
# [RECHERCHE] RECHERCHER ARTIFACTS
# ----------------------------------------------------------------------------

"""
RECHERCHE VIA UI
───────────────

Méthode 1 : Barre de recherche (en haut)
"""

# Taper : my-library
# Résultats affichent :
# - Nom du component
# - Version
# - Repository
# - Format


"""
Méthode 2 : Browse
"""

# Browse -> Browse
# Sélectionner repository : my-maven-releases
# Navigation hiérarchique :
#   com/
#   └── example/
#       └── my-library/
#           └── 1.0.0/


"""
Méthode 3 : Search avancée
"""

# Browse -> Search

"""
Filtres disponibles :
- Keyword : my-library
- Format : maven2
- Repository : my-maven-releases
- Group : com.example
- Name : my-library
- Version : 1.0.0
"""


"""
RECHERCHE VIA API REST
─────────────────────
"""

# Rechercher par nom
curl -u admin:admin123 \
  "http://nexus:8081/service/rest/v1/search?name=my-library"

# Rechercher par coordonnées Maven
curl -u admin:admin123 \
  "http://nexus:8081/service/rest/v1/search?maven.groupId=com.example&maven.artifactId=my-library"

# Rechercher par repository
curl -u admin:admin123 \
  "http://nexus:8081/service/rest/v1/search?repository=my-maven-releases"


# Réponse JSON
"""
{
  "items": [
    {
      "id": "bXktbWF2ZW4tcmVsZWFzZXM6Y...",
      "repository": "my-maven-releases",
      "format": "maven2",
      "group": "com.example",
      "name": "my-library",
      "version": "1.0.0",
      "assets": [
        {
          "downloadUrl": "http://nexus:8081/repository/my-maven-releases/com/example/my-library/1.0.0/my-library-1.0.0.jar",
          "path": "com/example/my-library/1.0.0/my-library-1.0.0.jar",
          "id": "...",
          "repository": "my-maven-releases",
          "format": "maven2",
          "checksum": {
            "sha1": "abc123...",
            "md5": "def456..."
          }
        }
      ]
    }
  ]
}
"""


# ----------------------------------------------------------------------------
# v TÉLÉCHARGER ARTIFACTS
# ----------------------------------------------------------------------------

"""
TÉLÉCHARGER VIA UI
─────────────────
"""

# 1. Rechercher artifact
# Search -> my-library

# 2. Cliquer sur component
# Vue détaillée affiche assets

# 3. Cliquer sur asset (ex: my-library-1.0.0.jar)
# Fichier téléchargé


"""
TÉLÉCHARGER VIA CURL
───────────────────
"""

# URL directe
curl -o my-library.jar \
  http://nexus:8081/repository/my-maven-releases/com/example/my-library/1.0.0/my-library-1.0.0.jar

# Avec authentification (si repository privé)
curl -u admin:admin123 -o my-library.jar \
  http://nexus:8081/repository/my-maven-releases/com/example/my-library/1.0.0/my-library-1.0.0.jar


"""
TÉLÉCHARGER VIA WGET
───────────────────
"""

wget http://nexus:8081/repository/my-maven-releases/com/example/my-library/1.0.0/my-library-1.0.0.jar


"""
TÉLÉCHARGER VIA MAVEN
────────────────────

Maven télécharge automatiquement depuis Nexus
quand configuré dans pom.xml (voir Chapitre 5)
"""

# pom.xml
<dependency>
  <groupId>com.example</groupId>
  <artifactId>my-library</artifactId>
  <version>1.0.0</version>
</dependency>

# mvn install
# Maven télécharge depuis Nexus et place dans ~/.m2/repository


# ----------------------------------------------------------------------------
# [SUPPRIMER] SUPPRIMER ARTIFACTS
# ----------------------------------------------------------------------------

"""
SUPPRIMER VIA UI
───────────────
"""

# 1. Rechercher artifact
# Search -> my-library 1.0.0

# 2. Sélectionner component

# 3. Cliquer "Delete component"

# 4. Confirmer

# [ATTENTION] SUPPRESSION DÉFINITIVE !
# Pas de corbeille, pas de restauration


"""
SUPPRIMER VIA API
────────────────
"""

# 1. Récupérer ID du component
curl -u admin:admin123 \
  "http://nexus:8081/service/rest/v1/search?name=my-library" \
  | jq '.items[0].id'

# ID : bXktbWF2ZW4tcmVsZWFzZXM6Y29tL...

# 2. Supprimer par ID
curl -u admin:admin123 -X DELETE \
  "http://nexus:8081/service/rest/v1/components/bXktbWF2ZW4tcmVsZWFzZXM6Y29tL..."


"""
[IDEE] BONNES PRATIQUES SUPPRESSION

[X] Ne JAMAIS supprimer releases utilisées en production
[X] Ne supprimer que si vraiment inutilisable

[OK] Supprimer vieux snapshots (automatiser avec Cleanup)
[OK] Supprimer releases bugguées (communiquer avant!)
[OK] Garder historique des releases stables
"""


# ----------------------------------------------------------------------------
# [NETTOYAGE] CLEANUP POLICIES
# ----------------------------------------------------------------------------

"""
CLEANUP POLICY = Suppression automatique

Cas d'usage :
- Supprimer vieux snapshots (> 30 jours)
- Supprimer versions non utilisées
- Libérer espace disque


CRÉER CLEANUP POLICY
───────────────────
"""

# Sidebar -> System -> Cleanup Policies -> Create Cleanup Policy

"""
Name : delete-old-snapshots

Format : maven2
   Politique s'applique seulement à repos Maven

Cleanup Criteria:

Published Before : 30 days
   Supprimer artifacts publiés il y a > 30 jours
   
Last Downloaded Before : (vide)
   Optionnel : Supprimer si pas téléchargé depuis X jours

Asset Name Matcher : .*-SNAPSHOT.*
   Regex : Seulement artifacts SNAPSHOT
   
   [IDEE] REGEX UTILES :
   .*-SNAPSHOT.*     -> Tous snapshots
   .*-RC.*           -> Release candidates
   .*-alpha.*        -> Versions alpha
"""

# Create


"""
APPLIQUER CLEANUP POLICY À REPOSITORY
─────────────────────────────────────
"""

# Repositories -> my-maven-snapshots -> Edit

"""
Cleanup Policies:
[x] delete-old-snapshots
"""

# Save


"""
EXÉCUTER CLEANUP MANUELLEMENT
─────────────────────────────

Par défaut, cleanup s'exécute automatiquement (nuit)

Pour forcer immédiatement :
"""

# System -> Tasks -> Create task

"""
Type : Admin - Compact blob store

Blob store : default

Schedule : Manual (pour test)
"""

# Create -> Run

# Puis :
# System -> Tasks -> Create task
# Type : Repository - Delete unused components and assets
# Repository : my-maven-snapshots
# Run


"""
[IDEE] CLEANUP FLOW

1. Cleanup Policy identifie artifacts à supprimer
2. Nexus marque comme "à supprimer"
3. Task "Compact blob store" libère espace disque réel

[ATTENTION] Les deux tasks sont nécessaires !
"""


"""
EXEMPLES CLEANUP POLICIES
─────────────────────────

1. Supprimer snapshots > 30 jours
"""
Published Before: 30
Asset Name Matcher: .*-SNAPSHOT.*

"""
2. Garder seulement 5 dernières versions
"""
Component Usage: Releases
Last Downloaded Before: 90
# -> Supprimer releases pas téléchargées depuis 90j

"""
3. Supprimer Docker images old
"""
Published Before: 60
# -> Images Docker > 60 jours


# ----------------------------------------------------------------------------
# [COURS] EXERCICE PRATIQUE 3 : CYCLE COMPLET ARTIFACT
# ----------------------------------------------------------------------------

"""
OBJECTIF : Upload, rechercher, télécharger, supprimer un artifact


ÉTAPES :

1. Créer artifact factice
   [ ] Créer fichier dummy-lib-1.0.0.jar (vide OK)
   [ ] echo "test" > dummy-lib-1.0.0.jar

2. Upload via UI
   [ ] Browse -> Upload -> my-maven-releases
   [ ] Group : com.test
   [ ] Artifact : dummy-lib
   [ ] Version : 1.0.0
   [ ] Upload JAR

3. Vérifier upload
   [ ] Search -> dummy-lib
   [ ] Vérifier présence
   [ ] Noter download URL

4. Télécharger via curl
   [ ] curl -o downloaded.jar <URL>
   [ ] Vérifier fichier téléchargé

5. Upload version 2.0.0
   [ ] Même artifact, version 2.0.0
   [ ] Search -> Vérifier 2 versions

6. Créer cleanup policy
   [ ] Name : delete-test-artifacts
   [ ] Asset Name Matcher : .*dummy.*
   [ ] Published Before : 0 (immédiat)

7. Appliquer et exécuter
   [ ] Appliquer policy à my-maven-releases
   [ ] Exécuter cleanup task
   [ ] Vérifier suppression


VALIDATION :

[OK] Artifact uploadé visible
[OK] Téléchargement fonctionne
[OK] 2 versions coexistent
[OK] Cleanup supprime artifacts


TEMPS ESTIMÉ : 30 minutes
"""


# ----------------------------------------------------------------------------
# [DOCS] RÉCAPITULATIF CHAPITRE 3
# ----------------------------------------------------------------------------

"""
CE QUE VOUS AVEZ APPRIS

[OK] Structure artifact Maven (GroupId:ArtifactId:Version)
[OK] Upload via UI (simple et complet)
[OK] Upload via CLI (curl, Maven, Gradle, npm)
[OK] Recherche (UI, API REST)
[OK] Téléchargement (UI, curl, wget)
[OK] Suppression (UI, API)
[OK] Cleanup Policies (automatisation)


[CLE] POINTS CLÉS

1. Artifact Maven = GroupId:ArtifactId:Version:Type
   com.example:my-lib:1.0.0:jar

2. Upload UI = Pratique pour test
   Production = Automatiser avec Maven/Gradle

3. Recherche API REST = Automation possible
   Intégration CI/CD

4. Cleanup Policies = Espace disque
   Snapshots > 30j, vieilles releases

5. Suppression définitive
   Pas de corbeille !


[OBJECTIF] BONNES PRATIQUES

[OK] Versionner correctement (SemVer)
[OK] Snapshots pour dev, Releases pour prod
[OK] Cleanup automatique des snapshots
[OK] Ne jamais supprimer releases en prod
[OK] Documenter artifacts (POM, README)


-> PROCHAINE ÉTAPE : Chapitre 4 - Utilisateurs et Permissions !

Vous allez apprendre :
- Créer utilisateurs
- Gérer rôles
- Configurer permissions granulaires
- LDAP/Active Directory
- Tokens d'API

Sécurisons Nexus ! [VERROUILLE]
"""


# ============================================================================
# FIN DE NEXUS_PARTIE1_SUITE.TXT
# CONTINUEZ AVEC CHAPITRE 4 OU nexus_partie2.txt
# ============================================================================
# ============================================================================
# [LIVRE] NEXUS REPOSITORY MANAGER - PARTIE 1 (FIN)
# CHAPITRE 4 : UTILISATEURS ET PERMISSIONS
# ============================================================================


# ============================================================================
# [GUIDE] CHAPITRE 4 : UTILISATEURS, RÔLES ET PERMISSIONS
# ============================================================================

"""
[OBJECTIF] OBJECTIFS D'APPRENTISSAGE

À la fin de ce chapitre, vous saurez :
[OK] Créer et gérer utilisateurs
[OK] Comprendre le système de rôles
[OK] Configurer permissions granulaires
[OK] Créer rôles personnalisés
[OK] Intégrer LDAP/Active Directory
[OK] Utiliser API tokens
[OK] Configurer realms
[OK] Best practices sécurité
"""


# ----------------------------------------------------------------------------
# [SECURISE] MODÈLE DE SÉCURITÉ NEXUS
# ----------------------------------------------------------------------------

"""
ARCHITECTURE SÉCURITÉ

┌─────────────────────────────────────────────────┐
│                    USER                         │
│  (john.doe, alice, bob)                         │
└────────────┬────────────────────────────────────┘
             │ a un ou plusieurs
             v
┌─────────────────────────────────────────────────┐
│                    ROLE                         │
│  (developer, admin, deployer)                   │
└────────────┬────────────────────────────────────┘
             │ contient un ou plusieurs
             v
┌─────────────────────────────────────────────────┐
│                 PRIVILEGE                       │
│  (nx-repository-view-*-*-read)                  │
└────────────┬────────────────────────────────────┘
             │ permet
             v
┌─────────────────────────────────────────────────┐
│                   ACTION                        │
│  (read, browse, edit, add, delete)              │
└─────────────────────────────────────────────────┘


FLUX D'AUTORISATION

1. User john.doe se connecte
2. Nexus charge ses roles : developer, deployer
3. Nexus agrège tous privileges de ces roles
4. User essaie d'uploader artifact dans my-releases
5. Nexus vérifie si john.doe a privilege nx-repository-view-maven2-my-releases-add
6. Si oui -> Autoriser
7. Si non -> Refuser (403 Forbidden)


PRINCIPES RBAC (Role-Based Access Control)

User (Qui ?)
v
Role (Quel groupe ?)
v
Privilege (Quelle permission ?)
v
Resource (Sur quoi ?)
v
Action (Faire quoi ?)
"""


# ----------------------------------------------------------------------------
# [UTILISATEUR] UTILISATEURS PAR DÉFAUT
# ----------------------------------------------------------------------------

"""
APRÈS INSTALLATION
─────────────────

Nexus crée 2 utilisateurs :

1. ADMIN
   Username : admin
   Password : (dans admin.password)
   Roles : nx-admin
   Permissions : TOUT
   
   [IDEE] SUPER-UTILISATEUR
   Peut tout faire, tout voir, tout modifier

2. ANONYMOUS
   Username : anonymous
   Password : (aucun)
   Roles : nx-anonymous
   Permissions : Lecture publique
   
   [IDEE] UTILISATEUR PUBLIC
   Représente utilisateurs non authentifiés
   Désactivable (recommandé en prod)


VOIR UTILISATEURS
────────────────
"""

# Sidebar -> Security -> Users

# Liste affichée :
"""
┌──────────────┬─────────────┬────────────┬─────────┐
│ User ID      │ First Name  │ Last Name  │ Status  │
├──────────────┼─────────────┼────────────┼─────────┤
│ admin        │ Admin       │ User       │ Active  │
│ anonymous    │ Anonymous   │ User       │ Active  │
└──────────────┴─────────────┴────────────┴─────────┘
"""


# ----------------------------------------------------------------------------
# + CRÉER UTILISATEUR LOCAL
# ----------------------------------------------------------------------------

"""
CAS D'USAGE : Créer compte pour développeur


ÉTAPE PAR ÉTAPE
──────────────
"""

# Security -> Users -> Create local user

"""
CONFIGURATION

ID : john.doe
   [IDEE] Identifiant unique
   Convention : prenom.nom ou username
   Pas d'espaces, caractères spéciaux limités

First Name : John
   Prénom (optionnel mais recommandé)

Last Name : Doe
   Nom de famille

Email : john.doe@company.com
   Email de contact
   Utilisé pour notifications (si configurées)

Status : Active
   - Active : Peut se connecter
   - Disabled : Compte désactivé (temporaire)
   
   [IDEE] Désactiver plutôt que supprimer
   Permet de réactiver si besoin

Password : (générer un mot de passe fort)
   Minimum 8 caractères recommandé
   Voir politique de password ci-dessous

Confirm password : (répéter)

Roles :
   [ ] nx-admin              (Administrateur complet)
   [ ] nx-anonymous          (Lecture publique)
   [x] nx-deployment         (Upload artifacts)
   [ ] (rôles personnalisés)
   
   [IDEE] PRINCIPE DU MOINDRE PRIVILÈGE
   Donner seulement permissions nécessaires
   
   Pour développeur standard :
   [OK] Lire repositories
   [OK] Télécharger artifacts
   [X] Créer repositories
   [X] Gérer utilisateurs
"""

# Create

# [OK] Utilisateur créé !


"""
POLITIQUE DE MOT DE PASSE
─────────────────────────

Par défaut, Nexus n'impose pas de politique stricte

Pour renforcer (PRO uniquement) :
- Longueur minimale
- Complexité (majuscules, chiffres, spéciaux)
- Expiration (90 jours)
- Historique (pas les 5 derniers)


BONNES PRATIQUES PASSWORD

[OK] Au moins 12 caractères
[OK] Majuscules + minuscules + chiffres + spéciaux
[OK] Pas de mots du dictionnaire
[OK] Unique par utilisateur
[OK] Rotation régulière (90-180 jours)

Exemples :
[X] admin123
[X] password
[X] john2024
[OK] J0hn@N3xu$2024!
[OK] MyC0mpl3x_P@ssw0rd
"""


"""
EXEMPLES UTILISATEURS À CRÉER
─────────────────────────────

1. Développeur (john.doe)
   Roles : developer-role (custom)
   Permissions : Lire et télécharger

2. Déployeur CI/CD (jenkins-user)
   Roles : deployer-role (custom)
   Permissions : Upload artifacts

3. Administrateur secondaire (jane.admin)
   Roles : admin-role (custom)
   Permissions : Tout sauf gérer users

4. Lecteur externe (client-reader)
   Roles : reader-role (custom)
   Permissions : Lecture seulement releases publiques
"""


# ----------------------------------------------------------------------------
# [SCENARIO] RÔLES PAR DÉFAUT
# ----------------------------------------------------------------------------

"""
RÔLES BUILT-IN NEXUS
────────────────────

1. nx-admin
   Description : Full administrator
   Privileges : nx-all (tout)
   
   [IDEE] SUPER-ADMIN
   Peut tout faire, y compris :
   - Créer/supprimer repositories
   - Gérer utilisateurs
   - Configurer système
   - Voir tous logs

2. nx-anonymous
   Description : Anonymous role
   Privileges :
   - nx-search-read
   - nx-repository-view-*-*-read
   - nx-repository-view-*-*-browse
   
   [IDEE] ACCÈS PUBLIC LECTURE
   Peut :
   - Chercher artifacts
   - Lire repositories
   - Télécharger artifacts
   Ne peut pas :
   - Upload
   - Delete
   - Configurer

3. nx-deployment
   Description : Deployment role
   Privileges :
   - Tout de nx-anonymous
   - nx-repository-view-*-*-add
   - nx-repository-view-*-*-edit
   
   [IDEE] RÔLE CI/CD
   Pour comptes Jenkins, GitLab CI, etc.
   Peut uploader artifacts


VOIR RÔLES
─────────
"""

# Security -> Roles

# Liste :
"""
┌────────────────┬──────────────────────────┬─────────┐
│ ID             │ Name                     │ Source  │
├────────────────┼──────────────────────────┼─────────┤
│ nx-admin       │ nx-admin                 │ default │
│ nx-anonymous   │ nx-anonymous             │ default │
│ nx-deployment  │ Nexus Deployment Role    │ default │
└────────────────┴──────────────────────────┴─────────┘
"""


"""
DÉTAILS D'UN RÔLE
────────────────

Cliquer sur nx-deployment -> Voir :

Role ID : nx-deployment
Role name : Nexus Deployment Role
Role description : Deployment Role
Privileges :
  - nx-repository-view-*-*-add
  - nx-repository-view-*-*-browse
  - nx-repository-view-*-*-edit
  - nx-repository-view-*-*-read
  - nx-search-read
"""


# ----------------------------------------------------------------------------
# [CLE] COMPRENDRE LES PRIVILEGES
# ----------------------------------------------------------------------------

"""
PRIVILEGE = PERMISSION GRANULAIRE

Format : nx-<domain>-<action>

Exemples :
nx-search-read               -> Chercher artifacts
nx-repository-view-*-*-read  -> Lire tous repositories
nx-repository-admin-*-*-*    -> Administrer repositories


TYPES DE PRIVILEGES
──────────────────

1. REPOSITORY VIEW PRIVILEGES
   Format : nx-repository-view-<format>-<repository>-<action>
   
   Exemples :
   nx-repository-view-maven2-*-read
   └─ Lire tous repositories maven2
   
   nx-repository-view-maven2-my-releases-add
   └─ Upload dans my-releases (maven)
   
   nx-repository-view-docker-docker-hosted-browse
   └─ Browse repository docker-hosted

2. REPOSITORY ADMIN PRIVILEGES
   Gérer repositories (créer, modifier, supprimer)
   
   nx-repository-admin-*-*-*
   └─ Administrer tous repositories
   
   nx-repository-admin-maven2-my-releases-*
   └─ Administrer seulement my-releases

3. SEARCH PRIVILEGES
   nx-search-read
   └─ Utiliser fonction de recherche

4. USER PRIVILEGES
   nx-users-all
   └─ Gérer utilisateurs
   
   nx-users-read
   └─ Voir utilisateurs

5. SCRIPT PRIVILEGES
   nx-script-*-*
   └─ Exécuter scripts Groovy


WILDCARDS (*)
────────────

* = Tout

nx-repository-view-*-*-*
└─ Tous formats, tous repos, toutes actions

nx-repository-view-maven2-*-read
└─ Tous repos maven2, action read

nx-repository-view-*-my-releases-*
└─ Tous formats, repo my-releases, toutes actions


ACTIONS COMMUNES
───────────────

read    -> Lire/télécharger artifacts
browse  -> Naviguer dans repository
edit    -> Modifier metadata
add     -> Upload nouveaux artifacts
delete  -> Supprimer artifacts


VOIR PRIVILEGES
──────────────
"""

# Security -> Privileges

# Filtrer par type : Repository View
# Liste très longue (100+ privileges)


# ----------------------------------------------------------------------------
# [DESIGN] CRÉER RÔLE PERSONNALISÉ
# ----------------------------------------------------------------------------

"""
CAS D'USAGE : Rôle "Developer"

Besoin :
[OK] Lire tous repositories Maven
[OK] Télécharger artifacts
[OK] Uploader dans snapshots seulement
[X] Pas toucher releases
[X] Pas admin


ÉTAPE PAR ÉTAPE
──────────────
"""

# Security -> Roles -> Create role

"""
Type : Nexus role
   - Nexus role : Rôle local Nexus
   - External role : Rôle LDAP/AD (voir plus bas)

Role ID : developer-role
   Identifiant unique

Role name : Developer Role
   Nom affiché

Role description : 
   "Développeurs : lecture tous repos, upload snapshots"

Privileges : (sélectionner)
   [x] nx-search-read
   [x] nx-repository-view-maven2-*-read
   [x] nx-repository-view-maven2-*-browse
   [x] nx-repository-view-maven2-my-snapshots-add
   [x] nx-repository-view-maven2-my-snapshots-edit
   [x] nx-repository-view-npm-*-read
   [x] nx-repository-view-npm-*-browse

Roles : (optionnel - héritage)
   Peut contenir d'autres rôles
   Exemple : developer-role contient nx-anonymous
"""

# Create

# [OK] Rôle créé !


"""
ASSIGNER RÔLE À UTILISATEUR
──────────────────────────
"""

# Security -> Users -> john.doe -> Edit

"""
Roles :
   [x] developer-role
"""

# Save


"""
AUTRES EXEMPLES DE RÔLES
───────────────────────

1. DEPLOYER-ROLE (CI/CD)
"""

Privileges:
  [x] nx-repository-view-*-*-read
  [x] nx-repository-view-*-*-browse
  [x] nx-repository-view-maven2-my-releases-add
  [x] nx-repository-view-maven2-my-snapshots-add
  [x] nx-repository-view-docker-docker-hosted-add

# Pour Jenkins, GitLab CI
# Peut uploader partout

"""
2. READER-ROLE (Lecture seule)
"""

Privileges:
  [x] nx-search-read
  [x] nx-repository-view-*-*-read
  [x] nx-repository-view-*-*-browse

# Clients externes, consultants
# Aucun upload, seulement téléchargement

"""
3. RELEASE-MANAGER-ROLE
"""

Privileges:
  [x] nx-repository-view-*-*-read
  [x] nx-repository-view-*-*-browse
  [x] nx-repository-view-maven2-my-releases-add
  [x] nx-repository-view-maven2-my-releases-edit
  [x] nx-repository-view-maven2-my-releases-delete

# Gère releases seulement
# Pas de snapshots


# ----------------------------------------------------------------------------
# [ENTREPRISE] INTÉGRATION LDAP / ACTIVE DIRECTORY
# ----------------------------------------------------------------------------

"""
POURQUOI LDAP/AD ?

Sans LDAP :
[X] Gérer users manuellement dans Nexus
[X] Mot de passe séparé (Nexus ≠ Windows)
[X] Duplication d'users
[X] Pas de SSO

Avec LDAP :
[OK] Authentification centralisée
[OK] Même login que Windows/Email
[OK] Gestion users dans AD
[OK] Groupes AD -> Rôles Nexus


CONFIGURER LDAP
──────────────
"""

# Security -> LDAP -> Create connection

"""
Name : company-ldap
   Nom de cette connexion LDAP

LDAP server address : ldap://ldap.company.com
   ou : ldaps://ldap.company.com (SSL)
   
   [IDEE] LDAPS recommandé (port 636)
   LDAP simple : port 389

Search base : dc=company,dc=com
   Base DN pour recherche users
   
   Exemples :
   AD : dc=company,dc=com
   OpenLDAP : dc=example,dc=org

Authentication method : Simple Authentication
   - Anonymous : Pas d'auth (rare)
   - Simple : Username/password
   - DIGEST-MD5 : Hash MD5
   - CRAM-MD5 : Challenge-response

Username : cn=nexus-service,ou=ServiceAccounts,dc=company,dc=com
   Compte de service pour Nexus
   Doit avoir permissions lecture AD
   
   [IDEE] CRÉER COMPTE DÉDIÉ
   Ne pas utiliser compte admin AD !

Password : <password-compte-service>
```

# Test connection -> [OK] Success


"""
CONFIGURATION USER & GROUP
──────────────────────────

User relative DN : ou=Users
   Où chercher users
   Exemple : ou=Users,dc=company,dc=com

User object class : user
   Classe d'objet LDAP pour users
   AD : user
   OpenLDAP : inetOrgPerson

User ID attribute : sAMAccountName
   Attribut contenant username
   AD : sAMAccountName
   OpenLDAP : uid

User real name attribute : cn
   Nom complet
   AD : cn ou displayName

User email attribute : mail
   Email

User password attribute : (laisser vide)
   AD gère passwords

Group type : Dynamic Groups
   - Static : Membership dans attribut member
   - Dynamic : Membership via memberOf

Group member of attribute : memberOf
   AD : memberOf

Group base DN : ou=Groups,dc=company,dc=com
   Où chercher groupes

Group object class : group
   AD : group
   OpenLDAP : groupOfNames

Group ID attribute : cn
   Nom du groupe

Group member attribute : member
   Attribut contenant membres
"""

# Verify connection -> Test user lookup

"""
Test parameters:
Username : john.doe
Password : <password-john>

Test -> [OK] Success
User found:
  ID: john.doe
  Name: John Doe
  Email: john.doe@company.com
  Groups: Domain Users, Developers
"""

# Save


"""
MAPPER GROUPES AD -> RÔLES NEXUS
───────────────────────────────
"""

# Security -> Roles -> Create role -> External role mapping

"""
Mapped role : Developers
   Groupe AD à mapper
   
   [IDEE] Doit exister dans AD :
   CN=Developers,OU=Groups,DC=company,DC=com

Roles : (rôles Nexus à assigner)
   [x] developer-role
   [x] nx-deployment (si besoin)
```

# Create


"""
TESTER AUTHENTIFICATION LDAP
────────────────────────────
"""

# 1. Se déconnecter (Sign out)

# 2. Se reconnecter avec compte AD
# Username : john.doe (username AD)
# Password : <password-AD>

# 3. Vérifier rôles
# Si john.doe membre de groupe "Developers" dans AD
# -> Il aura rôle developer-role dans Nexus

# [OK] SSO fonctionnel !


"""
ACTIVER REALM LDAP
─────────────────

Par défaut, LDAP configuré mais pas actif
"""

# Security -> Realms

"""
Available Realms:
- LDAP Realm
- Local Authenticating Realm
- Local Authorizing Realm
- Docker Bearer Token Realm
- npm Bearer Token Realm

Active Realms: (ordre important!)
1. Local Authenticating Realm    (Nexus local d'abord)
2. LDAP Realm                     (LDAP en fallback)
3. Docker Bearer Token Realm
4. npm Bearer Token Realm

[IDEE] ORDRE REALMS

Nexus essaie dans l'ordre :
1. Cherche user dans base locale
2. Si pas trouvé -> Essaie LDAP
3. Si trouvé -> Authentifie

Avantage :
- Compte admin local toujours fonctionnel
- LDAP down -> admin peut se connecter
```

# Save


# ----------------------------------------------------------------------------
# [SECURISE] API TOKENS (USER TOKENS)
# ----------------------------------------------------------------------------

"""
API TOKEN = Alternative au password

Cas d'usage :
- Scripts automatisés
- Intégration CI/CD
- Clients programmatiques
- Éviter passwords en clair


GÉNÉRER TOKEN
────────────
"""

# User menu (en haut à droite) -> User Token

"""
Access user token:

Current status: No token created yet

Actions:
[Access user token]  [Reset user token]
"""

# Cliquer "Access user token"

"""
User token:

Name Code (base64):
  bmF0ZTpjNjM4MWYyMy0wYTU5LTQ3MDEtODdmMi0yNzZhMWIwZmJlZmI=

[IDEE] COPIER ET SAUVEGARDER !
Token affiché UNE SEULE FOIS
Impossible de le re-afficher
"""


"""
UTILISER TOKEN
─────────────

Remplace username:password
"""

# Avec curl
curl -H "Authorization: Bearer bmF0ZTpjNjM4MWYyMy0wYTU5LTQ3MDEtODdmMi0yNzZhMWIwZmJlZmI=" \
  http://nexus:8081/service/rest/v1/repositories

# Ou en base64 décodé (username:password)
# Le token est en fait : username:<token-value>

# Décoder
echo "bmF0ZTpjNjM4MWYyMy0wYTU5LTQ3MDEtODdmMi0yNzZhMWIwZmJlZmI=" | base64 -d
# Résultat : john.doe:c6381f23-0a59-4701-87f2-276a1b0fbefb

# Utiliser comme credentials
curl -u john.doe:c6381f23-0a59-4701-87f2-276a1b0fbefb \
  http://nexus:8081/service/rest/v1/repositories


"""
CONFIGURATION MAVEN AVEC TOKEN
──────────────────────────────
"""

# ~/.m2/settings.xml

<settings>
  <servers>
    <server>
      <id>nexus</id>
      <username>john.doe</username>
      <password>c6381f23-0a59-4701-87f2-276a1b0fbefb</password>
    </server>
  </servers>
</settings>


"""
RÉVOQUER TOKEN
─────────────

Si compromis ou plus utilisé :
"""

# User menu -> User Token -> Reset user token

"""
[ATTENTION] ATTENTION
Tous scripts/configs utilisant ce token cesseront de fonctionner !
"""

# Confirmer -> Nouveau token généré


"""
BONNES PRATIQUES TOKENS
───────────────────────

[OK] Un token par application/script
   Permet révocation ciblée

[OK] Rotation régulière (90-180 jours)
   Limiter fenêtre d'exposition

[OK] Stocker sécurisé
   Vault, secrets manager, env variables
   [X] Jamais dans Git !

[OK] Principe moindre privilège
   User avec token = permissions minimales

[OK] Monitoring utilisation
   Détecter usage anormal
"""


# ----------------------------------------------------------------------------
# [COURS] EXERCICE PRATIQUE 4 : SÉCURITÉ COMPLÈTE
# ----------------------------------------------------------------------------

"""
OBJECTIF : Configurer utilisateurs et permissions pour équipe


SCÉNARIO :

Équipe de 10 personnes :
- 1 admin (vous)
- 5 développeurs
- 2 déployeurs CI/CD
- 2 managers (lecture seule)


ÉTAPES :

1. Créer rôles personnalisés
   [ ] developer-role
     - Lire tous repos
     - Upload snapshots seulement
   
   [ ] deployer-role
     - Lire tous repos
     - Upload releases et snapshots
   
   [ ] manager-role
     - Lire tous repos
     - Pas d'upload

2. Créer utilisateurs
   [ ] 5 développeurs (dev1 à dev5)
     - Role : developer-role
   
   [ ] 2 déployeurs (jenkins, gitlab-ci)
     - Role : deployer-role
   
   [ ] 2 managers (manager1, manager2)
     - Role : manager-role

3. Générer tokens
   [ ] Token pour jenkins
   [ ] Token pour gitlab-ci

4. Tester permissions
   [ ] Se connecter comme dev1
   [ ] Essayer upload dans my-releases -> [X] Refusé
   [ ] Essayer upload dans my-snapshots -> [OK] OK
   
   [ ] Se connecter comme jenkins
   [ ] Essayer upload dans my-releases -> [OK] OK

5. Désactiver anonymous
   [ ] Security -> Anonymous Access -> Disable
   [ ] Tester : Déconnecté -> Pas d'accès


VALIDATION :

[OK] 3 rôles créés avec bonnes permissions
[OK] 9 utilisateurs créés (+ admin = 10)
[OK] Permissions fonctionnent comme attendu
[OK] Tokens générés et testés
[OK] Anonymous désactivé


TEMPS ESTIMÉ : 60 minutes
```


# ----------------------------------------------------------------------------
# [DOCS] RÉCAPITULATIF CHAPITRE 4
# ----------------------------------------------------------------------------

"""
CE QUE VOUS AVEZ APPRIS

[OK] Modèle RBAC Nexus (User -> Role -> Privilege)
[OK] Créer utilisateurs locaux
[OK] Comprendre rôles par défaut (admin, anonymous, deployment)
[OK] Créer rôles personnalisés
[OK] Configurer privileges granulaires
[OK] Intégrer LDAP/Active Directory
[OK] Générer et utiliser API tokens
[OK] Configurer realms


[CLE] POINTS CLÉS

1. User -> Role -> Privilege -> Action
   Hiérarchie claire

2. Principe du moindre privilège
   Donner seulement ce qui est nécessaire

3. Rôles personnalisés > Rôles défaut
   Adapter aux besoins réels

4. LDAP = SSO simplifié
   Même login que Windows/Email

5. Tokens pour automation
   Pas de passwords en scripts

6. Désactiver anonymous en prod
   Sécurité avant commodité


[OBJECTIF] BONNES PRATIQUES SÉCURITÉ

[OK] Changer password admin immédiatement
[OK] Créer users spécifiques (pas admin partagé)
[OK] Rôles par fonction (dev, deployer, manager)
[OK] LDAP si infrastructure AD existante
[OK] Tokens pour CI/CD
[OK] Désactiver anonymous en production
[OK] Audit régulier des permissions
[OK] Rotation passwords/tokens (90j)


[X] À ÉVITER

[X] Partager compte admin
[X] Donner nx-admin à tout le monde
[X] Laisser anonymous enabled en prod
[X] Passwords en clair dans configs
[X] Pas de révision des permissions


[BRAVO] FÉLICITATIONS ! PARTIE 1 TERMINÉE !
═══════════════════════════════════════

Vous maîtrisez maintenant :

CHAPITRE 0 : Introduction Nexus
[OK] Concepts fondamentaux
[OK] OSS vs Pro
[OK] Cas d'usage

CHAPITRE 1 : Installation
[OK] Docker et binaire
[OK] Configuration initiale
[OK] Interface Web

CHAPITRE 2 : Repositories
[OK] Hosted, Proxy, Group
[OK] Maven, npm, Docker
[OK] Blob stores

CHAPITRE 3 : Artifacts
[OK] Upload (UI, CLI)
[OK] Recherche et téléchargement
[OK] Cleanup policies

CHAPITRE 4 : Sécurité
[OK] Users, Roles, Privileges
[OK] LDAP/AD
[OK] API Tokens


-> PROCHAINE ÉTAPE : PARTIE 2 !
════════════════════════════════

La Partie 2 couvrira :

CHAPITRE 5 : Intégration Maven
- Configuration pom.xml
- settings.xml
- Deploy vers Nexus
- Résolution dépendances

CHAPITRE 6 : Intégration Gradle
- Configuration build.gradle
- Plugins publishing
- Multi-projets

CHAPITRE 7 : Proxy et Cache
- Optimisation cache
- Remote repositories
- Metadata caching

CHAPITRE 8 : Snapshots vs Releases
- Stratégies versioning
- Gestion snapshots
- Promotion releases


[GUIDE] FICHIER SUIVANT : nexus_partie2.txt

Prêt pour l'intégration avec vos outils de build ! [RAPIDE]


═══════════════════════════════════════════════════════════════
AIDE-MÉMOIRE RAPIDE - CHAPITRE 4
═══════════════════════════════════════════════════════════════

CRÉER USER :
Security -> Users -> Create local user

CRÉER ROLE :
Security -> Roles -> Create role

ASSIGNER ROLE :
Security -> Users -> <user> -> Edit -> Roles

CONFIGURER LDAP :
Security -> LDAP -> Create connection

ACTIVER REALM :
Security -> Realms -> Drag & drop

GÉNÉRER TOKEN :
User menu -> User Token -> Access user token

DÉSACTIVER ANONYMOUS :
Security -> Anonymous Access -> Disable

═══════════════════════════════════════════════════════════════
"""


# ============================================================================
# FIN DE NEXUS_PARTIE1.TXT
# ============================================================================
#
# [BRAVO] VOUS AVEZ COMPLÉTÉ LA PARTIE 1 : FONDAMENTAUX
#
# Temps de lecture : ~6-8 heures
# Exercices : 4 pratiques
# Concepts maîtrisés : 40+
#
# [DOCS] CONTINUEZ AVEC nexus_partie2.txt
#    Pour apprendre l'intégration Maven, Gradle et gestion avancée
#
# [FORCE] Excellente progression !
# ============================================================================
# ============================================================================
# [LIVRE] NEXUS REPOSITORY MANAGER - PARTIE 2
# INTÉGRATION MAVEN ET GRADLE
# ============================================================================
#
# [OBJECTIF] CETTE PARTIE COUVRE :
# - Chapitre 5 : Intégration Maven
# - Chapitre 6 : Intégration Gradle
# - Chapitre 7 : Proxy et Cache
# - Chapitre 8 : Snapshots vs Releases
#
# [TEMPS] TEMPS : ~6-8 heures
# [DOCS] PRÉREQUIS : Partie 1 complétée
# ============================================================================


# ============================================================================
# [GUIDE] CHAPITRE 5 : INTÉGRATION MAVEN
# ============================================================================

"""
[OBJECTIF] OBJECTIFS D'APPRENTISSAGE

À la fin de ce chapitre, vous saurez :
[OK] Configurer Maven pour utiliser Nexus
[OK] Télécharger dépendances depuis Nexus
[OK] Déployer artifacts vers Nexus
[OK] Gérer settings.xml
[OK] Configuration multi-projets
[OK] Résolution de dépendances
[OK] Troubleshooting Maven-Nexus
"""


# ----------------------------------------------------------------------------
# [REFLEXION] MAVEN SANS NEXUS (RAPPEL DU PROBLÈME)
# ----------------------------------------------------------------------------

"""
CONFIGURATION MAVEN STANDARD (Sans Nexus)
─────────────────────────────────────────
"""

# pom.xml
"""
<project>
  <dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
      <version>3.2.0</version>
    </dependency>
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-databind</artifactId>
      <version>2.15.3</version>
    </dependency>
    <!-- 20+ autres dépendances -->
  </dependencies>
</project>
"""

# mvn install

"""
[X] PROBLÈMES

Chaque build :
1. Maven cherche dans ~/.m2/repository (cache local)
2. Si pas trouvé -> Télécharge depuis Maven Central
3. Téléchargement 200+ MB
4. 5-10 minutes par build
5. Dépendance Internet
6. Maven Central down = builds bloqués

Équipe de 10 devs :
-> 2 GB téléchargés par jour
-> Bande passante saturée
-> Builds lents


[OK] SOLUTION AVEC NEXUS

Configuration Nexus comme proxy :
1. Maven cherche dans ~/.m2/repository
2. Si pas trouvé -> Demande à Nexus
3. Nexus vérifie son cache
4. Si en cache -> Retourne immédiatement (réseau local)
5. Si pas en cache -> Télécharge depuis Maven Central (1 fois)
6. Builds suivants ultra-rapides

Équipe de 10 devs :
-> 200 MB téléchargés (1 fois)
-> Reste servi depuis Nexus (LAN)
-> Builds 10x plus rapides
"""


# ----------------------------------------------------------------------------
# [CONFIG] CONFIGURER MAVEN POUR NEXUS
# ----------------------------------------------------------------------------

"""
DEUX FICHIERS À CONFIGURER

1. pom.xml (Projet)
   - Repositories pour télécharger dépendances
   - DistributionManagement pour déployer artifacts
   
2. settings.xml (Utilisateur)
   - Credentials (username/password)
   - Mirrors (redirection)
   - Proxies (si proxy d'entreprise)


EMPLACEMENT settings.xml
────────────────────────

Global : <MAVEN_HOME>/conf/settings.xml
   Tous les utilisateurs de la machine
   
User : ~/.m2/settings.xml
   Utilisateur courant seulement
   
[IDEE] RECOMMANDATION : User settings
~/.m2/settings.xml (Linux/Mac)
C:\Users\<username>\.m2\settings.xml (Windows)
"""


# ----------------------------------------------------------------------------
# [FICHIER] CONFIGURATION pom.xml
# ----------------------------------------------------------------------------

"""
AJOUTER REPOSITORIES NEXUS
──────────────────────────

Dans pom.xml du projet :
"""

<project>
  <modelVersion>4.0.0</modelVersion>
  
  <groupId>com.example</groupId>
  <artifactId>my-app</artifactId>
  <version>1.0.0</version>
  
  <!-- [IDEE] REPOSITORIES : Pour télécharger dépendances -->
  <repositories>
    <repository>
      <id>nexus-public</id>
      <name>Nexus Public Repository</name>
      <url>http://nexus.company.com:8081/repository/maven-public/</url>
      <releases>
        <enabled>true</enabled>
      </releases>
      <snapshots>
        <enabled>true</enabled>
      </snapshots>
    </repository>
  </repositories>
  
  <!-- [IDEE] PLUGIN REPOSITORIES : Pour plugins Maven -->
  <pluginRepositories>
    <pluginRepository>
      <id>nexus-public</id>
      <name>Nexus Public Repository</name>
      <url>http://nexus.company.com:8081/repository/maven-public/</url>
      <releases>
        <enabled>true</enabled>
      </releases>
      <snapshots>
        <enabled>true</enabled>
      </snapshots>
    </pluginRepository>
  </pluginRepositories>
  
  <dependencies>
    <!-- Vos dépendances -->
  </dependencies>
</project>

"""
[IDEE] EXPLICATION

<id>nexus-public</id>
   Identifiant unique du repository
   Doit correspondre à ID dans settings.xml (pour credentials)

<url>http://nexus.company.com:8081/repository/maven-public/</url>
   URL du GROUP repository Nexus
   [ATTENTION] Utiliser GROUP (maven-public) pas PROXY (maven-central)
   
   Pourquoi GROUP ?
   -> Agrège hosted + proxy
   -> Une seule URL pour tout

<releases><enabled>true</enabled></releases>
   Télécharger releases depuis ce repo

<snapshots><enabled>true</enabled></snapshots>
   Télécharger snapshots depuis ce repo
   
   [IDEE] Si seulement releases :
   <snapshots><enabled>false</enabled></snapshots>


TESTER CONFIGURATION
───────────────────
"""

# Supprimer cache local pour forcer téléchargement
rm -rf ~/.m2/repository

# Build
mvn clean install

"""
Output attendu :
[INFO] Downloading from nexus-public: http://nexus.company.com:8081/...
[INFO] Downloaded from nexus-public: http://nexus.company.com:8081/...
"""

# Vérifier dans Nexus UI
# Browse -> maven-public -> Voir artifacts cachés


"""
CONFIGURATION DEPLOYMENT
───────────────────────

Pour uploader vos artifacts vers Nexus :
"""

<project>
  <!-- ... -->
  
  <!-- [IDEE] DISTRIBUTION MANAGEMENT : Pour déployer -->
  <distributionManagement>
    <repository>
      <id>nexus-releases</id>
      <name>Nexus Release Repository</name>
      <url>http://nexus.company.com:8081/repository/maven-releases/</url>
    </repository>
    <snapshotRepository>
      <id>nexus-snapshots</id>
      <name>Nexus Snapshot Repository</name>
      <url>http://nexus.company.com:8081/repository/maven-snapshots/</url>
    </snapshotRepository>
  </distributionManagement>
</project>

"""
[IDEE] EXPLICATION

<repository> : Pour versions RELEASE (1.0.0, 2.1.3)
<snapshotRepository> : Pour versions SNAPSHOT (1.0.0-SNAPSHOT)

Maven choisit automatiquement selon <version> :
- <version>1.0.0</version> -> Upload vers nexus-releases
- <version>1.0.0-SNAPSHOT</version> -> Upload vers nexus-snapshots

<id>nexus-releases</id>
   Doit correspondre à <server><id> dans settings.xml
"""


# ----------------------------------------------------------------------------
# [SECURISE] CONFIGURATION settings.xml
# ----------------------------------------------------------------------------

"""
CREDENTIALS POUR NEXUS
─────────────────────

Créer/éditer ~/.m2/settings.xml :
"""

<?xml version="1.0" encoding="UTF-8"?>
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0
                              http://maven.apache.org/xsd/settings-1.0.0.xsd">
  
  <!-- [IDEE] SERVERS : Credentials pour Nexus -->
  <servers>
    <!-- Pour télécharger (si repository privé) -->
    <server>
      <id>nexus-public</id>
      <username>john.doe</username>
      <password>my-password</password>
    </server>
    
    <!-- Pour déployer releases -->
    <server>
      <id>nexus-releases</id>
      <username>john.doe</username>
      <password>my-password</password>
    </server>
    
    <!-- Pour déployer snapshots -->
    <server>
      <id>nexus-snapshots</id>
      <username>john.doe</username>
      <password>my-password</password>
    </server>
  </servers>
  
</settings>

"""
[IDEE] EXPLICATION

<id>nexus-public</id>
   DOIT correspondre à <repository><id> dans pom.xml
   C'est comme ça que Maven fait la liaison !

<username> et <password>
   Credentials Nexus de l'utilisateur
   
   [IDEE] SÉCURITÉ :
   [X] Jamais commiter settings.xml avec passwords !
   [OK] Utiliser variables d'environnement
   [OK] Ou Maven Password Encryption (voir plus bas)


UTILISER VARIABLES D'ENVIRONNEMENT
──────────────────────────────────
"""

# settings.xml
<server>
  <id>nexus-releases</id>
  <username>${env.NEXUS_USERNAME}</username>
  <password>${env.NEXUS_PASSWORD}</password>
</server>

# Définir variables
# Linux/Mac
export NEXUS_USERNAME=john.doe
export NEXUS_PASSWORD=my-password

# Windows
set NEXUS_USERNAME=john.doe
set NEXUS_PASSWORD=my-password

# Ou dans ~/.bashrc (permanent)
echo 'export NEXUS_USERNAME=john.doe' >> ~/.bashrc
echo 'export NEXUS_PASSWORD=my-password' >> ~/.bashrc
source ~/.bashrc


"""
UTILISER API TOKEN
─────────────────

Plus sécurisé que password :
"""

# 1. Générer token dans Nexus UI
# User menu -> User Token -> Access user token

# 2. Décoder token base64
echo "bmF0ZTpjNjM4MWYyMy0wYTU5LTQ3MDEtODdmMi0yNzZhMWIwZmJlZmI=" | base64 -d
# Résultat : john.doe:c6381f23-0a59-4701-87f2-276a1b0fbefb

# 3. settings.xml
<server>
  <id>nexus-releases</id>
  <username>john.doe</username>
  <password>c6381f23-0a59-4701-87f2-276a1b0fbefb</password>
</server>


"""
CHIFFRER PASSWORD MAVEN
───────────────────────

Maven peut chiffrer passwords
"""

# 1. Créer master password
mvn --encrypt-master-password my-master-password

# Output : {jSMOWnoPFgsHVpMvz5VrIt5kRbzGpI8u+9EF1iFQyJQ=}

# 2. Créer ~/.m2/settings-security.xml
<settingsSecurity>
  <master>{jSMOWnoPFgsHVpMvz5VrIt5kRbzGpI8u+9EF1iFQyJQ=}</master>
</settingsSecurity>

# 3. Chiffrer password Nexus
mvn --encrypt-password my-nexus-password

# Output : {COQLCE6DU6GtcS5P=}

# 4. Utiliser dans settings.xml
<server>
  <id>nexus-releases</id>
  <username>john.doe</username>
  <password>{COQLCE6DU6GtcS5P=}</password>
</server>

"""
[IDEE] SÉCURITÉ RELATIVE
Chiffrement Maven = obfuscation, pas vraie encryption
Protège contre lecture simple du fichier
Mais pas contre attaquant déterminé
-> Préférer API tokens ou secrets managers (Vault)
"""


# ----------------------------------------------------------------------------
# [SYNC] MIROIR MAVEN CENTRAL
# ----------------------------------------------------------------------------

"""
FORCER TOUT TRAFIC VERS NEXUS
─────────────────────────────

Actuellement, si pom.xml ne configure pas Nexus,
Maven télécharge depuis Maven Central directement.

Pour FORCER l'utilisation de Nexus :
-> Configurer MIRROR dans settings.xml
"""

<settings>
  <!-- ... servers ... -->
  
  <!-- [IDEE] MIRRORS : Rediriger tout vers Nexus -->
  <mirrors>
    <mirror>
      <id>nexus-mirror</id>
      <name>Nexus Mirror</name>
      <url>http://nexus.company.com:8081/repository/maven-public/</url>
      <mirrorOf>*</mirrorOf>
    </mirror>
  </mirrors>
</settings>

"""
[IDEE] EXPLICATION

<mirrorOf>*</mirrorOf>
   * = Tout
   Redirige TOUS les repositories vers Nexus
   
   Variantes :
   <mirrorOf>central</mirrorOf>          -> Seulement Maven Central
   <mirrorOf>*,!repo1</mirrorOf>         -> Tout sauf repo1
   <mirrorOf>external:*</mirrorOf>       -> Repositories externes seulement


AVANTAGES MIRROR
───────────────

[OK] Configuration centralisée
   Plus besoin de modifier chaque pom.xml
   
[OK] Force l'utilisation de Nexus
   Impossible de bypass
   
[OK] Simplifie onboarding
   Nouveaux devs : Juste settings.xml à configurer


CONFIGURATION COMPLÈTE settings.xml
───────────────────────────────────

Template complet :
"""

<?xml version="1.0" encoding="UTF-8"?>
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0
                              http://maven.apache.org/xsd/settings-1.0.0.xsd">
  
  <!-- Credentials Nexus -->
  <servers>
    <server>
      <id>nexus</id>
      <username>${env.NEXUS_USERNAME}</username>
      <password>${env.NEXUS_PASSWORD}</password>
    </server>
  </servers>
  
  <!-- Redirection vers Nexus -->
  <mirrors>
    <mirror>
      <id>nexus-mirror</id>
      <name>Nexus Repository Manager</name>
      <url>http://nexus.company.com:8081/repository/maven-public/</url>
      <mirrorOf>*</mirrorOf>
    </mirror>
  </mirrors>
  
  <!-- Profils (optionnel) -->
  <profiles>
    <profile>
      <id>nexus-profile</id>
      <repositories>
        <repository>
          <id>nexus</id>
          <url>http://nexus.company.com:8081/repository/maven-public/</url>
          <releases><enabled>true</enabled></releases>
          <snapshots><enabled>true</enabled></snapshots>
        </repository>
      </repositories>
      <pluginRepositories>
        <pluginRepository>
          <id>nexus</id>
          <url>http://nexus.company.com:8081/repository/maven-public/</url>
          <releases><enabled>true</enabled></releases>
          <snapshots><enabled>true</enabled></snapshots>
        </pluginRepository>
      </pluginRepositories>
    </profile>
  </profiles>
  
  <!-- Activer profil par défaut -->
  <activeProfiles>
    <activeProfile>nexus-profile</activeProfile>
  </activeProfiles>
  
</settings>


# ----------------------------------------------------------------------------
# [SORTIE] DÉPLOYER VERS NEXUS
# ----------------------------------------------------------------------------

"""
COMMANDE DEPLOY
──────────────
"""

# S'assurer que pom.xml a distributionManagement
# S'assurer que settings.xml a credentials

# Deploy
mvn clean deploy

"""
QUE FAIT mvn deploy ?
────────────────────

1. mvn clean        -> Nettoie target/
2. mvn compile      -> Compile sources
3. mvn test         -> Exécute tests
4. mvn package      -> Crée JAR/WAR
5. mvn install      -> Installe dans ~/.m2/repository
6. mvn deploy       -> Upload vers Nexus !

Output :
[INFO] --- maven-deploy-plugin:3.0.0:deploy (default-deploy) @ my-app ---
[INFO] Uploading to nexus-snapshots: http://nexus.company.com:8081/.../my-app-1.0.0-SNAPSHOT.jar
[INFO] Uploaded to nexus-snapshots: http://nexus.company.com:8081/.../my-app-1.0.0-SNAPSHOT.jar (5.2 MB at 12 MB/s)
[INFO] Uploading to nexus-snapshots: http://nexus.company.com:8081/.../my-app-1.0.0-SNAPSHOT.pom
[INFO] BUILD SUCCESS
"""


"""
VÉRIFIER DANS NEXUS
──────────────────
"""

# Browse -> maven-snapshots (ou maven-releases)
# Search -> my-app
# Voir artifact uploadé


"""
DEPLOY RELEASE vs SNAPSHOT
──────────────────────────

SNAPSHOT (1.0.0-SNAPSHOT) :
"""

<version>1.0.0-SNAPSHOT</version>

# mvn deploy
# -> Upload vers repository snapshots
# -> Peut être re-uploadé (écrase ancien)
# -> Maven vérifie updates régulièrement

"""
RELEASE (1.0.0) :
"""

<version>1.0.0</version>

# mvn deploy
# -> Upload vers repository releases
# -> Immuable (pas de re-upload)
# -> Maven télécharge 1 fois et cache
# -> Tentative re-upload -> ERREUR (si redeploy disabled)


"""
DEPLOY SOURCES ET JAVADOC
─────────────────────────

Inclure sources et javadoc dans deployment :
"""

<build>
  <plugins>
    <!-- Plugin pour générer sources JAR -->
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-source-plugin</artifactId>
      <version>3.3.0</version>
      <executions>
        <execution>
          <id>attach-sources</id>
          <goals>
            <goal>jar</goal>
          </goals>
        </execution>
      </executions>
    </plugin>
    
    <!-- Plugin pour générer javadoc JAR -->
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-javadoc-plugin</artifactId>
      <version>3.6.0</version>
      <executions>
        <execution>
          <id>attach-javadocs</id>
          <goals>
            <goal>jar</goal>
          </goals>
        </execution>
      </executions>
    </plugin>
  </plugins>
</build>

# mvn deploy
# -> Upload :
#   - my-app-1.0.0.jar
#   - my-app-1.0.0-sources.jar
#   - my-app-1.0.0-javadoc.jar
#   - my-app-1.0.0.pom


# ----------------------------------------------------------------------------
# [RECHERCHE] RÉSOLUTION DE DÉPENDANCES
# ----------------------------------------------------------------------------

"""
ORDRE DE RÉSOLUTION MAVEN
─────────────────────────

Quand Maven cherche une dépendance :

1. Cache local (~/.m2/repository)
   Si trouvé -> Utilise
   Si pas trouvé -> Continue

2. Repositories dans pom.xml
   Dans l'ordre déclaré
   
3. Mirror (si configuré dans settings.xml)
   Redirige vers Nexus

4. Maven Central (si pas de mirror)


EXEMPLE CONCRET
──────────────

Dépendance demandée : spring-boot-starter-web:3.2.0
"""

# 1. Cherche dans ~/.m2/repository
#    ~/.m2/repository/org/springframework/boot/spring-boot-starter-web/3.2.0/
#    Pas trouvé -> Continue

# 2. Cherche dans repositories pom.xml
#    <repository><id>nexus-public</id></repository>
#    GET http://nexus.company.com:8081/repository/maven-public/org/springframework/boot/spring-boot-starter-web/3.2.0/spring-boot-starter-web-3.2.0.jar

# 3. Nexus reçoit requête
#    Cherche dans maven-releases -> Pas trouvé
#    Cherche dans maven-snapshots -> Pas trouvé
#    Cherche dans maven-central-proxy -> Pas en cache

# 4. Nexus télécharge depuis Maven Central
#    https://repo1.maven.org/maven2/org/springframework/boot/spring-boot-starter-web/3.2.0/spring-boot-starter-web-3.2.0.jar
#    Stocke dans blob store

# 5. Nexus retourne à Maven

# 6. Maven stocke dans ~/.m2/repository

# 7. Build continue


"""
FORCER UPDATE
────────────

Maven cache metadata (versions disponibles)
Pour forcer re-téléchargement :
"""

# Update snapshots
mvn clean install -U

# Ou
mvn clean install --update-snapshots

"""
[IDEE] -U FLAG

Force Maven à :
- Re-vérifier versions SNAPSHOT
- Re-télécharger si plus récent
- Utile si snapshot mis à jour

Sans -U :
Maven vérifie snapshots toutes les 24h par défaut
"""


"""
DÉPENDANCES TRANSITIVES
──────────────────────

Maven télécharge automatiquement dépendances des dépendances
"""

# pom.xml
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-web</artifactId>
  <version>3.2.0</version>
</dependency>

# Maven télécharge aussi :
# - spring-core
# - spring-web
# - spring-webmvc
# - tomcat-embed-core
# - jackson-databind
# - ... 20+ autres dépendances

"""
Voir arbre de dépendances :
"""

mvn dependency:tree

# Output :
"""
[INFO] com.example:my-app:jar:1.0.0
[INFO] \- org.springframework.boot:spring-boot-starter-web:jar:3.2.0:compile
[INFO]    +- org.springframework.boot:spring-boot-starter:jar:3.2.0:compile
[INFO]    |  +- org.springframework.boot:spring-boot:jar:3.2.0:compile
[INFO]    |  +- org.springframework.boot:spring-boot-autoconfigure:jar:3.2.0:compile
[INFO]    |  \- jakarta.annotation:jakarta.annotation-api:jar:2.1.1:compile
[INFO]    +- org.springframework:spring-web:jar:6.1.1:compile
[INFO]    \- org.springframework:spring-webmvc:jar:6.1.1:compile
...
"""


# ----------------------------------------------------------------------------
# [COURS] EXERCICE PRATIQUE 5 : PROJET MAVEN COMPLET
# ----------------------------------------------------------------------------

"""
OBJECTIF : Créer projet Maven utilisant Nexus


CAHIER DES CHARGES :

Créer application Spring Boot qui :
1. Télécharge dépendances depuis Nexus
2. Build et test localement
3. Deploy vers Nexus snapshots
4. Deploy release vers Nexus releases


ÉTAPES :

1. Créer settings.xml
   [ ] ~/.m2/settings.xml
   [ ] Servers avec credentials
   [ ] Mirror vers Nexus

2. Créer projet Maven
   [ ] mvn archetype:generate
   [ ] Ou Spring Initializr (start.spring.io)

3. Configurer pom.xml
   [ ] <repositories> pointant vers Nexus
   [ ] <distributionManagement> pour deploy

4. Ajouter dépendances
   [ ] spring-boot-starter-web
   [ ] spring-boot-starter-test

5. Build et télécharger dépendances
   [ ] mvn clean install
   [ ] Vérifier téléchargement depuis Nexus (logs)
   [ ] Vérifier cache dans Browse Nexus

6. Deploy snapshot
   [ ] <version>1.0.0-SNAPSHOT</version>
   [ ] mvn clean deploy
   [ ] Vérifier dans Nexus maven-snapshots

7. Deploy release
   [ ] <version>1.0.0</version>
   [ ] mvn clean deploy
   [ ] Vérifier dans Nexus maven-releases

8. Utiliser artifact dans autre projet
   [ ] Créer second projet
   [ ] Ajouter votre artifact comme dépendance
   [ ] Build et vérifier résolution


VALIDATION :

[OK] settings.xml configuré correctement
[OK] Dépendances téléchargées depuis Nexus
[OK] Snapshot déployé et visible dans Nexus
[OK] Release déployée et visible dans Nexus
[OK] Artifact réutilisable dans autre projet


TEMPS ESTIMÉ : 60-90 minutes
"""


# ----------------------------------------------------------------------------
# [BUG] TROUBLESHOOTING
# ----------------------------------------------------------------------------

"""
PROBLÈME 1 : 401 UNAUTHORIZED
────────────────────────────

Erreur :
[ERROR] Failed to execute goal ... Return code is: 401, ReasonPhrase: Unauthorized.

Causes :
[X] Username/password incorrect
[X] ID server ≠ ID repository
[X] User n'a pas permissions

Solutions :
[OK] Vérifier credentials dans settings.xml
[OK] Vérifier <server><id> = <repository><id>
[OK] Vérifier rôle user (nx-deployment minimum)


PROBLÈME 2 : 404 NOT FOUND
──────────────────────────

Erreur :
[ERROR] Failed to execute goal ... Return code is: 404, ReasonPhrase: Not Found.

Causes :
[X] URL repository incorrecte
[X] Repository n'existe pas dans Nexus
[X] Repository offline

Solutions :
[OK] Vérifier URL (copier depuis Nexus UI)
[OK] Vérifier repository existe (Browse dans Nexus)
[OK] Vérifier repository status = Online


PROBLÈME 3 : DÉPENDANCE NON RÉSOLUE
───────────────────────────────────

Erreur :
[ERROR] Failed to execute goal ... Could not resolve dependencies ... artifact not found

Causes :
[X] Artifact n'existe pas dans repositories
[X] Repository proxy pas configuré
[X] Proxy remote unreachable

Solutions :
[OK] Vérifier artifact existe (search dans Nexus)
[OK] Vérifier proxy repository configuré (maven-central)
[OK] Test connection vers remote (Maven Central)
[OK] Force update : mvn clean install -U


PROBLÈME 4 : REDEPLOY FORBIDDEN
───────────────────────────────

Erreur :
[ERROR] Failed to execute goal ... Return code is: 400, ReasonPhrase: Repository does not allow updating assets

Cause :
[X] Repository releases a "Disable redeploy"
[X] Artifact déjà existe

Solutions :
[OK] Incrémenter version (1.0.0 -> 1.0.1)
[OK] Ou utiliser repository snapshots
[OK] Ou changer policy repository (Allow redeploy)


PROBLÈME 5 : SLOW BUILD
───────────────────────

Build très lent malgré Nexus

Causes :
[X] Snapshot update check trop fréquent
[X] Pas de mirror configuré
[X] Repository proxy cache mal configuré

Solutions :
[OK] Configurer mirror dans settings.xml
[OK] Ajuster updatePolicy
"""

<repository>
  <id>nexus</id>
  <url>...</url>
  <snapshots>
    <updatePolicy>daily</updatePolicy>  <!-- never, always, interval:X -->
  </snapshots>
</repository>

"""
[OK] Vérifier Nexus cache settings (voir Chapitre 7)


LOGS DEBUG
─────────

Pour plus d'informations :
"""

# Build avec debug
mvn clean install -X

# Ou
mvn clean install --debug

# Output très verbeux montrant :
# - Repositories interrogés
# - URLs demandées
# - Réponses reçues
# - Erreurs détaillées


# ----------------------------------------------------------------------------
# [DOCS] RÉCAPITULATIF CHAPITRE 5
# ----------------------------------------------------------------------------

"""
CE QUE VOUS AVEZ APPRIS

[OK] Configurer pom.xml (repositories, distributionManagement)
[OK] Configurer settings.xml (servers, mirrors)
[OK] Télécharger dépendances depuis Nexus
[OK] Déployer artifacts (mvn deploy)
[OK] Sécuriser credentials (variables env, encryption, tokens)
[OK] Résolution de dépendances Maven
[OK] Troubleshooting Maven-Nexus


[CLE] POINTS CLÉS

1. pom.xml + settings.xml
   pom = Quoi, settings = Qui/Comment

2. GROUP repository pour télécharger
   Pas proxy direct

3. Mirror force utilisation Nexus
   <mirrorOf>*</mirrorOf>

4. SNAPSHOT vs RELEASE
   Différents repositories, différentes policies

5. mvn deploy ≠ mvn install
   install = local, deploy = Nexus


[OBJECTIF] BONNES PRATIQUES

[OK] Mirror dans settings.xml (centralisation)
[OK] Variables env pour credentials (sécurité)
[OK] API tokens plutôt que passwords
[OK] Versioning sémantique (SemVer)
[OK] Sources et javadoc dans deploy
[OK] Snapshots pour dev, releases pour prod


-> PROCHAINE ÉTAPE : Chapitre 6 - Gradle !

Vous allez apprendre :
- Configuration build.gradle
- Plugins Gradle
- Publishing vers Nexus
- Groovy vs Kotlin DSL

Passons à Gradle ! [RAPIDE]
"""


# ============================================================================
# [GUIDE] CHAPITRE 6 : INTÉGRATION GRADLE
# ============================================================================

"""
[OBJECTIF] OBJECTIFS D'APPRENTISSAGE

À la fin de ce chapitre, vous saurez :
[OK] Configurer Gradle pour utiliser Nexus
[OK] Télécharger dépendances depuis Nexus
[OK] Publier artifacts vers Nexus
[OK] Gradle Groovy vs Kotlin DSL
[OK] Configuration multi-projets
[OK] Build cache avec Nexus
[OK] Troubleshooting Gradle-Nexus
"""


# ----------------------------------------------------------------------------
# [REFLEXION] GRADLE VS MAVEN (RAPPEL)
# ----------------------------------------------------------------------------

"""
GRADLE                          MAVEN
────────────────────────────────────────
build.gradle (Groovy/Kotlin)    pom.xml (XML)
Convention over config          Convention strict
Flexible, scriptable            Rigide, verbose
Incremental builds              Full rebuild
Parallélisme natif              Sequential
Build cache                     Local cache seulement
Moderne (depuis 2012)           Ancien (depuis 2004)


[IDEE] CE CHAPITRE

Focus sur Gradle avec Nexus
Configuration similaire à Maven mais syntaxe différente
"""


# ----------------------------------------------------------------------------
# [CONFIG] CONFIGURATION build.gradle (GROOVY DSL)
# ----------------------------------------------------------------------------

"""
CONFIGURER REPOSITORIES
──────────────────────

build.gradle :
"""

plugins {
    id 'java'
    id 'maven-publish'
}

group = 'com.example'
version = '1.0.0'

// [IDEE] REPOSITORIES : Pour télécharger dépendances
repositories {
    maven {
        name = 'Nexus'
        url = 'http://nexus.company.com:8081/repository/maven-public/'
        allowInsecureProtocol = true  // Si HTTP (développement)
        
        // Credentials (optionnel si repository public)
        credentials {
            username = project.findProperty('nexusUsername') ?: System.getenv('NEXUS_USERNAME')
            password = project.findProperty('nexusPassword') ?: System.getenv('NEXUS_PASSWORD')
        }
    }
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web:3.2.0'
    implementation 'com.fasterxml.jackson.core:jackson-databind:2.15.3'
    
    testImplementation 'org.junit.jupiter:junit-jupiter:5.10.0'
}

"""
[IDEE] EXPLICATION

repositories { maven { ... } }
   Définit où télécharger dépendances

url = '...'
   URL du repository Nexus (GROUP)

allowInsecureProtocol = true
   Nécessaire si HTTP (pas HTTPS)
   [ATTENTION] Production : Toujours HTTPS !

credentials { ... }
   Username/password pour Nexus
   
project.findProperty('nexusUsername')
   Cherche propriété dans gradle.properties
   
System.getenv('NEXUS_USERNAME')
   Fallback sur variable d'environnement


PROPRIÉTÉS (gradle.properties)
─────────────────────────────

Créer gradle.properties (à la racine du projet) :
"""

# gradle.properties
nexusUsername=john.doe
nexusPassword=my-password

# OU utiliser variables d'environnement
# nexusUsername=${NEXUS_USERNAME}
# nexusPassword=${NEXUS_PASSWORD}

"""
[ATTENTION] SÉCURITÉ

gradle.properties peut contenir secrets
-> Ajouter à .gitignore !
"""

# .gitignore
gradle.properties
!gradle.properties.template

# Créer template pour équipe
# gradle.properties.template
"""
nexusUsername=your-username
nexusPassword=your-password
"""


"""
CONFIGURATION PUBLISHING
───────────────────────

Pour publier vers Nexus :
"""

publishing {
    publications {
        mavenJava(MavenPublication) {
            from components.java
            
            groupId = 'com.example'
            artifactId = 'my-library'
            version = '1.0.0'
            
            // Ajouter sources (optionnel)
            artifact sourceJar
            
            // Ajouter javadoc (optionnel)
            artifact javadocJar
        }
    }
    
    repositories {
        maven {
            name = 'NexusReleases'
            url = version.endsWith('SNAPSHOT') 
                ? 'http://nexus.company.com:8081/repository/maven-snapshots/'
                : 'http://nexus.company.com:8081/repository/maven-releases/'
            allowInsecureProtocol = true
            
            credentials {
                username = project.findProperty('nexusUsername')
                password = project.findProperty('nexusPassword')
            }
        }
    }
}

// Tâches pour générer sources et javadoc JAR
tasks.register('sourceJar', Jar) {
    from sourceSets.main.allSource
    archiveClassifier = 'sources'
}

tasks.register('javadocJar', Jar) {
    from javadoc
    archiveClassifier = 'javadoc'
}

"""
[IDEE] EXPLICATION

publishing { publications { ... } }
   Définit QUOI publier

publishing { repositories { ... } }
   Définit OÙ publier

version.endsWith('SNAPSHOT')
   Choix automatique snapshots vs releases

from components.java
   Publie artifact Java standard (JAR)

artifact sourceJar
   Ajoute sources au publishing


PUBLIER VERS NEXUS
─────────────────
"""

# Build
./gradlew build

# Publish
./gradlew publish

# Ou publish vers repository spécifique
./gradlew publishMavenJavaPublicationToNexusReleasesRepository

"""
Output :
> Task :publishMavenJavaPublicationToNexusReleasesRepository
Uploading: com/example/my-library/1.0.0/my-library-1.0.0.jar to repository NexusReleases at http://nexus.company.com:8081/...
Uploaded: com/example/my-library/1.0.0/my-library-1.0.0.jar (5.2 MB)
Uploading: com/example/my-library/1.0.0/my-library-1.0.0.pom to repository NexusReleases at http://nexus.company.com:8081/...

BUILD SUCCESSFUL
"""


# ----------------------------------------------------------------------------
# [OUTIL] CONFIGURATION build.gradle.kts (KOTLIN DSL)
# ----------------------------------------------------------------------------

"""
GRADLE KOTLIN DSL

Alternative moderne à Groovy
Type-safe, meilleure autocomplétion IDE

build.gradle.kts :
"""

import org.gradle.api.publish.maven.MavenPublication

plugins {
    java
    `maven-publish`
}

group = "com.example"
version = "1.0.0"

repositories {
    maven {
        name = "Nexus"
        url = uri("http://nexus.company.com:8081/repository/maven-public/")
        isAllowInsecureProtocol = true
        
        credentials {
            username = project.findProperty("nexusUsername") as String? 
                ?: System.getenv("NEXUS_USERNAME")
            password = project.findProperty("nexusPassword") as String? 
                ?: System.getenv("NEXUS_PASSWORD")
        }
    }
}

dependencies {
    implementation("org.springframework.boot:spring-boot-starter-web:3.2.0")
    implementation("com.fasterxml.jackson.core:jackson-databind:2.15.3")
    
    testImplementation("org.junit.jupiter:junit-jupiter:5.10.0")
}

publishing {
    publications {
        create<MavenPublication>("mavenJava") {
            from(components["java"])
            
            groupId = "com.example"
            artifactId = "my-library"
            version = "1.0.0"
        }
    }
    
    repositories {
        maven {
            name = "NexusReleases"
            url = uri(
                if (version.toString().endsWith("SNAPSHOT"))
                    "http://nexus.company.com:8081/repository/maven-snapshots/"
                else
                    "http://nexus.company.com:8081/repository/maven-releases/"
            )
            isAllowInsecureProtocol = true
            
            credentials {
                username = project.findProperty("nexusUsername") as String?
                password = project.findProperty("nexusPassword") as String?
            }
        }
    }
}

"""
[IDEE] DIFFÉRENCES KOTLIN DSL vs GROOVY

uri(...)                vs  '...'
isAllowInsecureProtocol vs  allowInsecureProtocol
create<MavenPublication> vs  mavenJava(MavenPublication)
as String?              vs  (cast implicite)
"""


# ----------------------------------------------------------------------------
# [WEB] CONFIGURATION GLOBALE (init.gradle)
# ----------------------------------------------------------------------------

"""
CONFIGURATION POUR TOUS LES PROJETS

Plutôt que configurer chaque build.gradle
-> Configurer une fois globalement


CRÉER init.gradle
────────────────

~/.gradle/init.gradle (Linux/Mac)
C:\Users\<user>\.gradle\init.gradle (Windows)
"""

allprojects {
    repositories {
        // Retirer repositories par défaut
        all { 
            ArtifactRepository repository ->
            if (repository instanceof MavenArtifactRepository) {
                if (repository.url.toString().contains('repo.maven.org') ||
                    repository.url.toString().contains('jcenter')) {
                    remove(repository)
                }
            }
        }
        
        // Ajouter Nexus en premier
        maven {
            name = 'Nexus'
            url = 'http://nexus.company.com:8081/repository/maven-public/'
            allowInsecureProtocol = true
            credentials {
                username = System.getenv('NEXUS_USERNAME')
                password = System.getenv('NEXUS_PASSWORD')
            }
        }
    }
}

"""
[IDEE] EFFET

Tous les builds Gradle sur cette machine utiliseront Nexus
Plus besoin de modifier chaque build.gradle !


AVANTAGES :
[OK] Configuration centralisée
[OK] Onboarding simplifié
[OK] Force utilisation Nexus

INCONVÉNIENTS :
[X] Configuration cachée (pas dans projet)
[X] Peut surprendre nouveaux devs
"""


# ----------------------------------------------------------------------------
# [PACKAGE] MULTI-PROJETS
# ----------------------------------------------------------------------------

"""
PROJET AVEC SOUS-MODULES

Structure :
"""

my-project/
├── settings.gradle
├── build.gradle
├── module-a/
│   └── build.gradle
├── module-b/
│   └── build.gradle
└── module-c/
    └── build.gradle

"""
CONFIGURATION settings.gradle
────────────────────────────
"""

# settings.gradle
rootProject.name = 'my-project'

include 'module-a'
include 'module-b'
include 'module-c'

"""
CONFIGURATION build.gradle (ROOT)
────────────────────────────────
"""

# build.gradle
plugins {
    id 'java' apply false
    id 'maven-publish' apply false
}

subprojects {
    apply plugin: 'java'
    apply plugin: 'maven-publish'
    
    group = 'com.example'
    version = '1.0.0'
    
    // Repositories communs à tous modules
    repositories {
        maven {
            url = 'http://nexus.company.com:8081/repository/maven-public/'
            allowInsecureProtocol = true
            credentials {
                username = project.findProperty('nexusUsername')
                password = project.findProperty('nexusPassword')
            }
        }
    }
    
    // Publishing commun
    publishing {
        repositories {
            maven {
                url = version.endsWith('SNAPSHOT')
                    ? 'http://nexus.company.com:8081/repository/maven-snapshots/'
                    : 'http://nexus.company.com:8081/repository/maven-releases/'
                allowInsecureProtocol = true
                credentials {
                    username = project.findProperty('nexusUsername')
                    password = project.findProperty('nexusPassword')
                }
            }
        }
    }
}

"""
MODULE build.gradle
──────────────────
"""

# module-a/build.gradle
dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web:3.2.0'
    
    // Dépendance vers autre module
    implementation project(':module-b')
}

publishing {
    publications {
        mavenJava(MavenPublication) {
            from components.java
            artifactId = 'module-a'
        }
    }
}

"""
BUILD ET PUBLISH TOUT
────────────────────
"""

# Build tous les modules
./gradlew build

# Publish tous les modules
./gradlew publish


# ----------------------------------------------------------------------------
# [RAPIDE] BUILD CACHE
# ----------------------------------------------------------------------------

"""
GRADLE BUILD CACHE

Gradle peut cacher résultats de build
-> Évite recompilations inutiles
-> Partage cache entre devs via Nexus (PRO)


ACTIVER BUILD CACHE LOCAL
─────────────────────────
"""

# gradle.properties
org.gradle.caching=true

# Ou en ligne de commande
./gradlew build --build-cache

"""
[IDEE] CACHE LOCAL

Stocké dans ~/.gradle/caches/build-cache-1/
Partagé entre projets locaux
Pas partagé entre devs


BUILD CACHE REMOTE (NEXUS PRO)
──────────────────────────────

Nexus Pro peut héberger build cache Gradle
-> Partage entre toute l'équipe

[ATTENTION] Fonctionnalité PRO uniquement !
"""


# ----------------------------------------------------------------------------
# [COURS] EXERCICE PRATIQUE 6 : PROJET GRADLE COMPLET
# ----------------------------------------------------------------------------

"""
OBJECTIF : Créer projet Gradle multi-modules avec Nexus


CAHIER DES CHARGES :

Projet my-app avec 2 modules :
- common : Utilitaires partagés
- app : Application principale


ÉTAPES :

1. Créer structure projet
   my-app/
   ├── settings.gradle
   ├── build.gradle
   ├── gradle.properties
   ├── common/
   │   ├── build.gradle
   │   └── src/main/java/...
   └── app/
       ├── build.gradle
       └── src/main/java/...

2. Configurer gradle.properties
   [ ] nexusUsername=...
   [ ] nexusPassword=...

3. Configurer build.gradle (root)
   [ ] Repositories Nexus
   [ ] Publishing configuration

4. Configurer common/build.gradle
   [ ] Dépendances (Gson par ex)
   [ ] Publishing pour module common

5. Configurer app/build.gradle
   [ ] Dépendance vers :common
   [ ] Dépendances externes

6. Build et test
   [ ] ./gradlew build
   [ ] Vérifier compilation
   [ ] Vérifier dépendances téléchargées depuis Nexus

7. Publish
   [ ] ./gradlew publish
   [ ] Vérifier dans Nexus :
     - common-1.0.0.jar
     - app-1.0.0.jar

8. Utiliser dans autre projet
   [ ] Créer projet test
   [ ] Dépendance vers com.example:common:1.0.0
   [ ] Build et vérifier résolution


VALIDATION :

[OK] Multi-module fonctionne
[OK] Dépendances téléchargées depuis Nexus
[OK] Modules publiés dans Nexus
[OK] Artifacts réutilisables


TEMPS ESTIMÉ : 60-90 minutes
"""


# ----------------------------------------------------------------------------
# [DOCS] RÉCAPITULATIF CHAPITRE 6
# ----------------------------------------------------------------------------

"""
CE QUE VOUS AVEZ APPRIS

[OK] Configuration build.gradle (Groovy)
[OK] Configuration build.gradle.kts (Kotlin)
[OK] Repositories et credentials
[OK] Plugin maven-publish
[OK] Configuration multi-modules
[OK] Build cache
[OK] init.gradle (configuration globale)


[CLE] POINTS CLÉS

1. repositories { maven { ... } }
   Télécharger dépendances

2. publishing { ... }
   Publier vers Nexus

3. gradle.properties
   Externaliser credentials

4. Groovy vs Kotlin DSL
   Kotlin = Type-safe, moderne

5. init.gradle
   Configuration globale machine


[OBJECTIF] BONNES PRATIQUES

[OK] gradle.properties en .gitignore
[OK] Variables d'environnement pour CI/CD
[OK] Template gradle.properties.template
[OK] Build cache activé
[OK] Versioning sémantique


-> PROCHAINE ÉTAPE : Chapitre 7 - Proxy et Cache !

Performance et optimisation :
- Configuration cache Nexus
- Metadata caching
- Remote repositories
- Cleanup automatique

Optimisons Nexus ! [RAPIDE]
"""


# ============================================================================
# FIN DE NEXUS_PARTIE2.TXT - CHAPITRES 5 ET 6
# CONTINUEZ AVEC CHAPITRES 7 ET 8
# ============================================================================
# ============================================================================
# [LIVRE] NEXUS REPOSITORY MANAGER - PARTIE 2 (SUITE)
# CHAPITRES 6, 7 ET 8
# ============================================================================


# ============================================================================
# [GUIDE] CHAPITRE 6 : INTÉGRATION GRADLE
# ============================================================================

"""
[OBJECTIF] OBJECTIFS D'APPRENTISSAGE

À la fin de ce chapitre, vous saurez :
[OK] Configurer Gradle pour Nexus
[OK] Télécharger dépendances depuis Nexus
[OK] Publier artifacts avec maven-publish
[OK] Utiliser Kotlin DSL
[OK] Gérer multi-projets
[OK] Différences Maven vs Gradle
"""


# ----------------------------------------------------------------------------
# [REFLEXION] GRADLE vs MAVEN
# ----------------------------------------------------------------------------

"""
COMPARAISON RAPIDE
─────────────────

┌────────────────────┬─────────────┬──────────────┐
│ Aspect             │ Maven       │ Gradle       │
├────────────────────┼─────────────┼──────────────┤
│ Format config      │ XML (pom)   │ Groovy/Kotlin│
│ Verbosité          │ +++         │ +            │
│ Performance        │ Normal      │ Rapide       │
│ Incrémental build  │ Non         │ Oui          │
│ Cache build        │ Limité      │ Avancé       │
│ Flexibilité        │ Convention  │ Très flexible│
│ Courbe apprentiss. │ Facile      │ Moyenne      │
│ Écosystème         │ Mature      │ Moderne      │
└────────────────────┴─────────────┴──────────────┘


POURQUOI GRADLE ?
────────────────

[OK] Builds incrémentiels (plus rapide)
[OK] Cache distribué
[OK] DSL moderne (Groovy ou Kotlin)
[OK] Moins verbeux que Maven
[OK] Très flexible

[X] Plus complexe
[X] Moins de documentation
[X] Courbe d'apprentissage


GRADLE + NEXUS
──────────────

Configuration similaire à Maven :
1. Configurer repositories (téléchargement)
2. Configurer publishing (upload)
3. Gérer credentials
"""


# ----------------------------------------------------------------------------
# [LISTE] PRÉREQUIS GRADLE
# ----------------------------------------------------------------------------

"""
VÉRIFIER INSTALLATION
────────────────────
"""

gradle --version

# Sortie :
"""
Gradle 8.5
Kotlin: 1.9.20
Groovy: 3.0.19
JVM: 17.0.8 (Oracle Corporation 17.0.8+9-LTS-211)
OS: Linux 5.15.0 amd64
"""


"""
INSTALLER GRADLE
───────────────
"""

# Ubuntu/Debian
sudo apt update
sudo apt install gradle

# macOS
brew install gradle

# Windows
# https://gradle.org/install/
# Ou via SDKMAN
sdk install gradle


"""
GRADLE WRAPPER (Recommandé)
──────────────────────────

Gradle Wrapper = Gradle embarqué dans projet
Avantages :
[OK] Version fixe par projet
[OK] Pas besoin d'installer Gradle globalement
[OK] Build reproductible
"""

# Générer wrapper
gradle wrapper

# Fichiers créés :
"""
gradlew          # Script Linux/Mac
gradlew.bat      # Script Windows
gradle/
  wrapper/
    gradle-wrapper.jar
    gradle-wrapper.properties
"""

# Utiliser wrapper
./gradlew build  # Au lieu de gradle build


"""
STRUCTURE PROJET GRADLE
──────────────────────

my-project/
├── build.gradle          # Configuration (Groovy)
ou
├── build.gradle.kts      # Configuration (Kotlin DSL)
├── settings.gradle       # Settings multi-projets
├── gradle.properties     # Properties
├── gradlew               # Wrapper Unix
├── gradlew.bat           # Wrapper Windows
└── src/
    ├── main/
    │   ├── java/
    │   └── resources/
    └── test/
        ├── java/
        └── resources/
"""


# ----------------------------------------------------------------------------
# [CONFIG] CONFIGURATION GRADLE POUR NEXUS
# ----------------------------------------------------------------------------

"""
FICHIER build.gradle (Groovy DSL)
─────────────────────────────────

Configuration pour télécharger ET publier
"""

# build.gradle
plugins {
    id 'java'
    id 'maven-publish'  // Plugin publishing
}

group = 'com.company'
version = '1.0.0'

// ========================================
// REPOSITORIES : Où TÉLÉCHARGER dépendances
// ========================================
repositories {
    // Nexus maven-public (group)
    maven {
        url = uri('http://localhost:8081/repository/maven-public/')
        credentials {
            username = project.findProperty('nexusUsername') ?: 'admin'
            password = project.findProperty('nexusPassword') ?: 'admin123'
        }
        // Autoriser HTTP (dev uniquement!)
        allowInsecureProtocol = true
    }
    
    // Fallback Maven Central (si Nexus down)
    // mavenCentral()
}

// ========================================
// DÉPENDANCES
// ========================================
dependencies {
    // Dépendances externes (via Nexus proxy)
    implementation 'org.springframework.boot:spring-boot-starter-web:3.2.0'
    implementation 'org.apache.commons:commons-lang3:3.14.0'
    
    // Dépendance interne (depuis Nexus hosted)
    implementation 'com.company:common-utils:1.0.0'
    
    // Test
    testImplementation 'junit:junit:4.13.2'
}

// ========================================
// PUBLISHING : Où UPLOADER artifacts
// ========================================
publishing {
    publications {
        maven(MavenPublication) {
            // Informations artifact
            groupId = 'com.company'
            artifactId = 'my-library'
            version = '1.0.0'
            
            // Inclure JAR compilé
            from components.java
            
            // Inclure sources (optionnel)
            artifact sourceJar
            
            // Inclure javadoc (optionnel)
            artifact javadocJar
        }
    }
    
    repositories {
        maven {
            name = 'nexus'
            
            // Choisir repository selon version
            def releasesRepoUrl = 'http://localhost:8081/repository/maven-releases/'
            def snapshotsRepoUrl = 'http://localhost:8081/repository/maven-snapshots/'
            url = version.endsWith('SNAPSHOT') ? snapshotsRepoUrl : releasesRepoUrl
            
            credentials {
                username = project.findProperty('nexusUsername') ?: 'admin'
                password = project.findProperty('nexusPassword') ?: 'admin123'
            }
            allowInsecureProtocol = true
        }
    }
}

// ========================================
// TASKS : Générer sources et javadoc JARs
// ========================================
task sourceJar(type: Jar) {
    from sourceSets.main.allJava
    archiveClassifier = 'sources'
}

task javadocJar(type: Jar) {
    from javadoc
    archiveClassifier = 'javadoc'
}


"""
[IDEE] DÉCRYPTAGE build.gradle


1. PLUGINS
─────────

plugins {
    id 'java'          // Support Java
    id 'maven-publish' // Publishing vers Nexus
}


2. REPOSITORIES (Téléchargement)
────────────────────────────────

repositories {
    maven {
        url = uri('http://localhost:8081/repository/maven-public/')
        credentials { ... }
    }
}

[IDEE] maven-public = Group repository Nexus


3. DEPENDENCIES
──────────────

dependencies {
    implementation 'group:artifact:version'
    testImplementation 'group:artifact:version'
}

Scopes Gradle :
- implementation : Runtime + Compile
- api : Exposé aux consumers
- compileOnly : Compile seulement
- runtimeOnly : Runtime seulement
- testImplementation : Tests


4. PUBLISHING
────────────

publishing {
    publications { ... }  // Quoi publier
    repositories { ... }  // Où publier
}
"""


"""
FICHIER build.gradle.kts (Kotlin DSL)
────────────────────────────────────

Alternative moderne à Groovy
"""

# build.gradle.kts
plugins {
    java
    `maven-publish`
}

group = "com.company"
version = "1.0.0"

repositories {
    maven {
        url = uri("http://localhost:8081/repository/maven-public/")
        credentials {
            username = project.findProperty("nexusUsername") as String? ?: "admin"
            password = project.findProperty("nexusPassword") as String? ?: "admin123"
        }
        isAllowInsecureProtocol = true
    }
}

dependencies {
    implementation("org.springframework.boot:spring-boot-starter-web:3.2.0")
    implementation("com.company:common-utils:1.0.0")
    testImplementation("junit:junit:4.13.2")
}

publishing {
    publications {
        create<MavenPublication>("maven") {
            groupId = "com.company"
            artifactId = "my-library"
            version = "1.0.0"
            
            from(components["java"])
        }
    }
    
    repositories {
        maven {
            name = "nexus"
            url = uri(
                if (version.toString().endsWith("SNAPSHOT")) 
                    "http://localhost:8081/repository/maven-snapshots/"
                else 
                    "http://localhost:8081/repository/maven-releases/"
            )
            credentials {
                username = project.findProperty("nexusUsername") as String? ?: "admin"
                password = project.findProperty("nexusPassword") as String? ?: "admin123"
            }
            isAllowInsecureProtocol = true
        }
    }
}

"""
[IDEE] Kotlin DSL vs Groovy

Kotlin DSL :
[OK] Type-safe (autocomplétion IDE)
[OK] Moderne
[X] Plus verbeux

Groovy DSL :
[OK] Concis
[OK] Plus de documentation
[X] Pas type-safe
"""


"""
FICHIER gradle.properties
────────────────────────

Stocker credentials (ne pas commiter!)
"""

# gradle.properties
nexusUsername=admin
nexusPassword=admin123

# Ou utiliser tokens
nexusUsername=john.doe
nexusPassword=c6381f23-0a59-4701-87f2-276a1b0fbefb


"""
.gitignore
─────────

Ne jamais commiter credentials !
"""

# .gitignore
gradle.properties
local.properties


# ----------------------------------------------------------------------------
# [ENTREE] TÉLÉCHARGER DÉPENDANCES
# ----------------------------------------------------------------------------

"""
BUILD PROJET
───────────
"""

./gradlew build

"""
[IDEE] QUE SE PASSE-T-IL ?

1. Gradle lit build.gradle
2. Voit dépendances :
   - spring-boot-starter-web:3.2.0
   - common-utils:1.0.0
3. Consulte repositories configurés
4. Demande à Nexus maven-public
5. Nexus cherche dans hosted -> proxy
6. Retourne artifacts
7. Gradle cache dans ~/.gradle/caches
8. Build continue


LOGS GRADLE
──────────
"""

./gradlew build --info

# Sortie :
"""
> Task :compileJava
Resolving dependencies for configuration ':compileClasspath'
Downloading from nexus: http://localhost:8081/repository/maven-public/org/springframework/boot/spring-boot-starter-web/3.2.0/spring-boot-starter-web-3.2.0.pom
Downloaded from nexus: http://localhost:8081/repository/maven-public/org/springframework/boot/spring-boot-starter-web/3.2.0/spring-boot-starter-web-3.2.0.pom (2.1 kB at 180 kB/s)
"""


"""
VOIR DÉPENDANCES
───────────────
"""

./gradlew dependencies

# Arbre complet :
"""
compileClasspath - Compile classpath for source set 'main'.
+--- org.springframework.boot:spring-boot-starter-web:3.2.0
|    +--- org.springframework.boot:spring-boot-starter:3.2.0
|    |    +--- org.springframework.boot:spring-boot:3.2.0
|    |    |    \--- org.springframework:spring-core:6.1.0
|    |    \--- ...
\--- com.company:common-utils:1.0.0
"""


"""
CACHE GRADLE
───────────

Gradle cache dans :
~/.gradle/caches/modules-2/files-2.1/

Structure :
"""
~/.gradle/caches/modules-2/files-2.1/
└── org.springframework.boot/
    └── spring-boot-starter-web/
        └── 3.2.0/
            ├── abc123/spring-boot-starter-web-3.2.0.jar
            └── def456/spring-boot-starter-web-3.2.0.pom


# ----------------------------------------------------------------------------
# [SORTIE] PUBLIER ARTIFACTS
# ----------------------------------------------------------------------------

"""
PUBLIER VERS NEXUS
─────────────────
"""

./gradlew publish

"""
[IDEE] QUE FAIT publish ?

1. Compile sources
2. Crée JAR (build/libs/my-library-1.0.0.jar)
3. Génère POM
4. Upload vers Nexus
   - JAR
   - POM
   - Sources (si configuré)
   - Javadoc (si configuré)


LOGS PUBLISH
───────────
"""

# Sortie :
"""
> Task :generatePomFileForMavenPublication
> Task :publishMavenPublicationToNexusRepository
Uploading: com/company/my-library/1.0.0/my-library-1.0.0.jar to repository nexus at http://localhost:8081/repository/maven-releases/
Uploading: com/company/my-library/1.0.0/my-library-1.0.0.pom to repository nexus at http://localhost:8081/repository/maven-releases/
BUILD SUCCESSFUL
"""


"""
PUBLIER SNAPSHOT
───────────────
"""

# build.gradle
version = '1.0.0-SNAPSHOT'

./gradlew publish

# Upload vers maven-snapshots automatiquement !


"""
PUBLIER SEULEMENT VERS UN REPOSITORY
────────────────────────────────────
"""

./gradlew publishMavenPublicationToNexusRepository


"""
TASKS PUBLISHING
───────────────

Voir toutes tasks publish :
"""

./gradlew tasks --group publishing

# Sortie :
"""
Publishing tasks
----------------
publish - Publishes all publications to all repositories.
publishMavenPublicationToNexusRepository - Publishes Maven publication 'maven' to Maven repository 'nexus'.
publishToMavenLocal - Publishes all Maven publications to the local Maven repository.
"""


# ----------------------------------------------------------------------------
# [SECURISE] SÉCURISER CREDENTIALS
# ----------------------------------------------------------------------------

"""
MÉTHODE 1 : gradle.properties (Local)
─────────────────────────────────────

Ne pas commiter ce fichier !
"""

# gradle.properties (projet racine)
nexusUsername=admin
nexusPassword=admin123

# build.gradle utilise :
credentials {
    username = project.findProperty('nexusUsername')
    password = project.findProperty('nexusPassword')
}


"""
MÉTHODE 2 : Variables d'environnement
─────────────────────────────────────
"""

# Définir variables
export NEXUS_USERNAME=admin
export NEXUS_PASSWORD=admin123

# build.gradle
credentials {
    username = System.getenv('NEXUS_USERNAME')
    password = System.getenv('NEXUS_PASSWORD')
}


"""
MÉTHODE 3 : gradle.properties (Global)
──────────────────────────────────────

Pour tous projets
"""

# ~/.gradle/gradle.properties
nexusUsername=admin
nexusPassword=admin123

# Tous projets utilisent automatiquement


"""
MÉTHODE 4 : Gradle Credentials Plugin
─────────────────────────────────────
"""

# build.gradle
plugins {
    id 'nu.studer.credentials' version '3.0'
}

repositories {
    maven {
        url = uri('http://localhost:8081/repository/maven-public/')
        credentials(PasswordCredentials)
    }
}

# Stocker credentials
./gradlew addCredentials --key nexus --username admin --password admin123


# ----------------------------------------------------------------------------
# [CONSTRUCTION] MULTI-PROJETS GRADLE
# ----------------------------------------------------------------------------

"""
STRUCTURE MULTI-PROJETS
──────────────────────

Cas : Application avec modules
"""

my-application/
├── settings.gradle        # Définir sous-projets
├── build.gradle          # Config commune
├── common/               # Module 1
│   └── build.gradle
├── api/                  # Module 2
│   └── build.gradle
└── web/                  # Module 3
    └── build.gradle


"""
FICHIER settings.gradle
──────────────────────
"""

# settings.gradle
rootProject.name = 'my-application'

include 'common'
include 'api'
include 'web'


"""
FICHIER build.gradle (Racine)
─────────────────────────────

Configuration commune à tous modules
"""

# build.gradle
plugins {
    id 'java' apply false
    id 'maven-publish' apply false
}

// Configuration commune
subprojects {
    apply plugin: 'java'
    apply plugin: 'maven-publish'
    
    group = 'com.company'
    version = '1.0.0'
    
    repositories {
        maven {
            url = uri('http://localhost:8081/repository/maven-public/')
            credentials {
                username = project.findProperty('nexusUsername')
                password = project.findProperty('nexusPassword')
            }
            allowInsecureProtocol = true
        }
    }
    
    publishing {
        repositories {
            maven {
                name = 'nexus'
                url = uri('http://localhost:8081/repository/maven-releases/')
                credentials {
                    username = project.findProperty('nexusUsername')
                    password = project.findProperty('nexusPassword')
                }
                allowInsecureProtocol = true
            }
        }
    }
}


"""
FICHIER common/build.gradle
──────────────────────────

Configuration spécifique module
"""

# common/build.gradle
dependencies {
    implementation 'org.apache.commons:commons-lang3:3.14.0'
}

publishing {
    publications {
        maven(MavenPublication) {
            artifactId = 'common'
            from components.java
        }
    }
}


"""
FICHIER api/build.gradle
───────────────────────
"""

# api/build.gradle
dependencies {
    // Dépendance inter-module
    implementation project(':common')
    
    implementation 'org.springframework.boot:spring-boot-starter-web:3.2.0'
}

publishing {
    publications {
        maven(MavenPublication) {
            artifactId = 'api'
            from components.java
        }
    }
}


"""
BUILD MULTI-PROJETS
──────────────────
"""

# Build tout
./gradlew build

# Build module spécifique
./gradlew :api:build

# Publish tous modules
./gradlew publish

# Publish module spécifique
./gradlew :common:publish


# ----------------------------------------------------------------------------
# [COURS] EXERCICE PRATIQUE 6 : PROJET GRADLE COMPLET
# ----------------------------------------------------------------------------

"""
OBJECTIF : Créer bibliothèque Gradle et publier vers Nexus


ÉTAPES :

1. Créer projet Gradle
"""
mkdir my-gradle-lib
cd my-gradle-lib
gradle init

# Choisir :
# Type: library
# Language: Java
# Build script DSL: Groovy
# Test framework: JUnit 4

"""
2. Configurer build.gradle
"""
# build.gradle
plugins {
    id 'java-library'
    id 'maven-publish'
}

group = 'com.company'
version = '1.0.0'

repositories {
    maven {
        url = uri('http://localhost:8081/repository/maven-public/')
        credentials {
            username = 'admin'
            password = 'admin123'
        }
        allowInsecureProtocol = true
    }
}

dependencies {
    implementation 'org.apache.commons:commons-lang3:3.14.0'
    testImplementation 'junit:junit:4.13.2'
}

publishing {
    publications {
        maven(MavenPublication) {
            groupId = 'com.company'
            artifactId = 'my-gradle-lib'
            version = '1.0.0'
            from components.java
        }
    }
    repositories {
        maven {
            name = 'nexus'
            url = uri('http://localhost:8081/repository/maven-releases/')
            credentials {
                username = 'admin'
                password = 'admin123'
            }
            allowInsecureProtocol = true
        }
    }
}

"""
3. Créer classe
"""
# src/main/java/com/company/Calculator.java
package com.company;

public class Calculator {
    public int add(int a, int b) {
        return a + b;
    }
}

"""
4. Build et publish
"""
./gradlew build
./gradlew publish

"""
5. Vérifier dans Nexus
   Browse -> maven-releases -> com/company/my-gradle-lib
"""

"""
6. Utiliser dans autre projet
"""
# Nouveau projet build.gradle
dependencies {
    implementation 'com.company:my-gradle-lib:1.0.0'
}

"""
VALIDATION :

[OK] Artifact publié dans Nexus
[OK] Visible dans maven-releases
[OK] Utilisable depuis autre projet
[OK] Téléchargement depuis Nexus fonctionne


TEMPS ESTIMÉ : 45 minutes
"""


# ============================================================================
# [GUIDE] CHAPITRE 7 : PROXY ET CACHE AVANCÉ
# ============================================================================

"""
[OBJECTIF] OBJECTIFS D'APPRENTISSAGE

À la fin de ce chapitre, vous saurez :
[OK] Optimiser cache Nexus
[OK] Configurer metadata caching
[OK] Gérer remote repositories
[OK] Negative cache
[OK] Cleanup cache
[OK] Performance tuning
"""


# ----------------------------------------------------------------------------
# [SYNC] COMPRENDRE LE CACHE NEXUS
# ----------------------------------------------------------------------------

"""
FLUX DE CACHE
────────────

Client (Maven/Gradle)
       v Demande artifact
Nexus Proxy Repository
       v Artifact en cache ?
       ├─ OUI -> Retourne immédiatement
       └─ NON -> Télécharge depuis remote -> Cache -> Retourne


NIVEAUX DE CACHE
───────────────

1. CLIENT LOCAL
   Maven : ~/.m2/repository
   Gradle : ~/.gradle/caches
   
2. NEXUS CACHE
   Blob store : /nexus-data/blobs/
   
3. REMOTE REPOSITORY
   Maven Central, npmjs.org, etc.


BÉNÉFICES CACHE
──────────────

Sans cache Nexus :
- Développeur 1 -> Maven Central (2 min)
- Développeur 2 -> Maven Central (2 min)
- CI Build -> Maven Central (2 min)
Total : 6 minutes

Avec cache Nexus :
- Développeur 1 -> Maven Central -> Nexus cache (2 min)
- Développeur 2 -> Nexus cache (10 sec)
- CI Build -> Nexus cache (10 sec)
Total : 2 min 20 sec (65% économie)
"""


# ----------------------------------------------------------------------------
# [CONFIG] CONFIGURATION PROXY REPOSITORY
# ----------------------------------------------------------------------------

"""
CRÉER/ÉDITER PROXY REPOSITORY
─────────────────────────────
"""

# Repositories -> maven-central (proxy) -> Edit

"""
Remote storage : https://repo1.maven.org/maven2/
   URL du repository distant

Auto blocking enabled : [OK]
   Si remote indisponible -> Bloquer temporairement
   Évite timeouts répétés
   
   [IDEE] Auto-débloque après période

Auto blocking : 
   - Condition: Remote Unavailable
   - Block time: 1440 minutes (24h)


Maximum component age : 1440 minutes (24h)
   Durée cache des components
   
   [IDEE] RECOMMANDATIONS :
   Releases : -1 (illimité) ou 43200 (30 jours)
   Snapshots : 1440 (24h)
   
   -1 = Ne jamais re-vérifier (performance max)
   1440 = Re-vérifier après 1 jour


Maximum metadata age : -1 (illimité)
   Durée cache des metadata (maven-metadata.xml, pom)
   
   [IDEE] RECOMMANDATIONS :
   Releases : -1 (metadata stable)
   Snapshots : 5-60 minutes (metadata change)


Negative cache enabled : [OK]
   Cache les "404 Not Found"
   
   [IDEE] POURQUOI ?
   Si artifact n'existe pas -> Éviter re-demander
   
Negative cache TTL : 1440 minutes
   Combien de temps garder 404 en cache


Content max age : 1440
   Cache navigateur (pour downloads via browser)
   
Metadata max age : 1440
   Cache navigateur metadata
"""


"""
[IDEE] STRATÉGIES SELON TYPE
───────────────────────

PROXY MAVEN CENTRAL (Releases)
------------------------------
Maximum component age : -1 (illimité)
Maximum metadata age : -1
Negative cache : [OK] (1440)

Raison : Releases ne changent jamais


PROXY MAVEN SNAPSHOTS
---------------------
Maximum component age : 60 (1 heure)
Maximum metadata age : 5 (5 minutes)
Negative cache : [OK] (60)

Raison : Snapshots changent fréquemment


PROXY NPM
--------
Maximum component age : 1440 (24h)
Maximum metadata age : 60 (1h)
Negative cache : [OK] (1440)

Raison : npm packages stables mais updates possibles
"""


# ----------------------------------------------------------------------------
# [NETTOYAGE] CLEANUP ET MAINTENANCE CACHE
# ----------------------------------------------------------------------------

"""
TÂCHES AUTOMATIQUES NEXUS
─────────────────────────

Par défaut, Nexus exécute tasks nocturnes :
"""

# System -> Tasks

"""
Tasks par défaut :

1. Compact blob store
   Récupère espace disque des artifacts supprimés
   Fréquence : Quotidienne (3h00)

2. Delete unused manifests and images
   Docker cleanup
   Fréquence : Quotidienne (3h00)

3. Repository - Rebuild Maven repository metadata
   Régénère maven-metadata.xml
   Fréquence : Sur demande

4. Repository - Purge unused snapshots
   Supprime vieux snapshots (selon policy)
   Fréquence : Quotidienne
"""


"""
CRÉER TASK CLEANUP CUSTOM
────────────────────────
"""

# System -> Tasks -> Create task

"""
Type : Repository - Delete unused components and assets

Repository : maven-central (proxy)

Cleanup Criteria:
  Last Downloaded : 90 days
  -> Supprimer artifacts pas téléchargés depuis 90j
  
Schedule : Cron
  Cron expression : 0 0 2 * * ?
  -> Tous les jours à 2h00

Save
"""


"""
EXPRESSIONS CRON
───────────────

Format : sec min hour day month dayOfWeek [year]

Exemples :
0 0 2 * * ?          -> Tous les jours 2h00
0 0 2 * * SUN        -> Tous les dimanches 2h00
0 0 2 1 * ?          -> 1er de chaque mois 2h00
0 */30 * * * ?       -> Toutes les 30 minutes
0 0 2 ? * MON-FRI    -> Lun-Ven 2h00
"""


"""
FORCER TASK MANUELLEMENT
───────────────────────
"""

# System -> Tasks -> <task> -> Run

# Utile pour :
# - Tester configuration
# - Cleanup urgent
# - Maintenance planifiée


"""
PURGER CACHE PROXY MANUELLEMENT
───────────────────────────────
"""

# Méthode 1 : Via UI
# Browse -> maven-central -> Select all -> Delete

# Méthode 2 : Via API
curl -u admin:admin123 -X DELETE \
  "http://localhost:8081/service/rest/v1/components/<component-id>"

# Méthode 3 : Via Script Groovy (Admin)
# System -> Scripts -> Create script
"""
import org.sonatype.nexus.repository.storage.StorageFacet
import org.sonatype.nexus.repository.storage.Query

repository.repositoryManager.get('maven-central').with {
  def storageFacet = facet(StorageFacet)
  def tx = storageFacet.txSupplier().get()
  
  try {
    tx.begin()
    storageFacet.deleteComponent(tx, component)
    tx.commit()
  } finally {
    tx.close()
  }
}
"""


# ----------------------------------------------------------------------------
# [RAPIDE] OPTIMISATION PERFORMANCE
# ----------------------------------------------------------------------------

"""
TUNING NEXUS JVM
───────────────

Augmenter mémoire si beaucoup de repos/traffic
"""

# Docker
docker run -d \
  -e INSTALL4J_ADD_VM_PARAMS="-Xms4096m -Xmx4096m -XX:MaxDirectMemorySize=4096m" \
  sonatype/nexus3

# Binaire (nexus.vmoptions)
-Xms4096m
-Xmx4096m
-XX:MaxDirectMemorySize=4096m


"""
UTILISER BLOB STORES MULTIPLES
──────────────────────────────

Séparer par type pour I/O parallèle
"""

# Créer blob stores
blob-maven     -> SSD rapide
blob-docker    -> HDD grande capacité
blob-npm       -> SSD rapide

# Assigner aux repositories
maven-central -> blob-maven
docker-proxy -> blob-docker
npm-proxy -> blob-npm


"""
CLOUD BLOB STORAGE
─────────────────

Pour très grande capacité
"""

# AWS S3
# System -> Blob Stores -> Create blob store
"""
Type : S3
Bucket : nexus-artifacts
Region : us-east-1
Access Key ID : AKIAIOSFODNN7EXAMPLE
Secret Access Key : wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
"""

# Azure Blob
"""
Type : Azure Blob Storage
Account Name : nexusstorage
Account Key : <key>
Container Name : artifacts
"""


"""
COMPRESSION ET DEDUPLICATION
───────────────────────────

Nexus supporte :
[OK] Compression artifacts (automatic)
[OK] Deduplication (même artifact = stocké 1 fois)

[IDEE] Transparent, pas de config nécessaire
"""


# ----------------------------------------------------------------------------
# [GRAPHIQUE] MONITORING CACHE
# ----------------------------------------------------------------------------

"""
STATISTIQUES CACHE
─────────────────

Voir utilisation cache
"""

# Browse -> <repository> -> Cliquer dessus

"""
Affiche :
- Total Components : 1,234
- Total Assets : 5,678
- Total Size : 15.3 GB
- Blob Store : default
"""


"""
VOIR DERNIERS TÉLÉCHARGEMENTS
─────────────────────────────
"""

# Browse -> Components
# Trier par : Last Downloaded

"""
Identifier artifacts populaires vs jamais utilisés
"""


"""
API STATS
────────
"""

# Récupérer stats via API
curl -u admin:admin123 \
  "http://localhost:8081/service/rest/v1/repositories/maven-central"

# JSON response contient :
"""
{
  "name": "maven-central",
  "format": "maven2",
  "type": "proxy",
  "online": true,
  "storage": {
    "blobStoreName": "default",
    "strictContentTypeValidation": true
  },
  "proxy": {
    "remoteUrl": "https://repo1.maven.org/maven2/",
    ...
  },
  ...
}
"""


# ============================================================================
# [GUIDE] CHAPITRE 8 : SNAPSHOTS VS RELEASES - STRATÉGIES
# ============================================================================

"""
[OBJECTIF] OBJECTIFS D'APPRENTISSAGE

À la fin de ce chapitre, vous saurez :
[OK] Différences Snapshot vs Release
[OK] Stratégies de versioning
[OK] Gestion du cycle de vie
[OK] Promotion Release
[OK] Bonnes pratiques versioning
"""


# ----------------------------------------------------------------------------
# [MELANGE] SNAPSHOT VS RELEASE
# ----------------------------------------------------------------------------

"""
SNAPSHOT (Version de développement)
───────────────────────────────────

Caractéristiques :
- Version se termine par -SNAPSHOT
- Peut être re-déployée (mutable)
- Metadata timestamp
- Maven/Gradle vérifie updates
- Utilisée pendant développement


Exemple :
  1.0.0-SNAPSHOT
  2.1.0-SNAPSHOT
  3.0.0-RC1-SNAPSHOT


Stockage Nexus :
"""
maven-snapshots/
  com/company/my-lib/
    1.0.0-SNAPSHOT/
      my-lib-1.0.0-20241218.103045-1.jar
      my-lib-1.0.0-20241218.143022-2.jar  <- Timestamp unique
      my-lib-1.0.0-20241218.160011-3.jar
      maven-metadata.xml  <- Pointe vers latest

"""
Comportement Maven/Gradle :
- Vérifie nouvelle snapshot régulièrement
- -U force update
- updatePolicy contrôle fréquence


RELEASE (Version stable)
───────────────────────

Caractéristiques :
- Version SANS -SNAPSHOT
- Immutable (ne peut être re-déployée)
- Permanent
- Maven/Gradle cache localement
- Utilisée en production


Exemple :
  1.0.0
  2.1.3
  3.0.0-RC1
  3.0.0


Stockage Nexus :
"""
maven-releases/
  com/company/my-lib/
    1.0.0/
      my-lib-1.0.0.jar     <- Unique, permanent
      my-lib-1.0.0.pom

"""
Comportement Maven/Gradle :
- Télécharge 1 fois
- Cache permanent
- Jamais re-vérifié (sauf purge cache)
"""


# ----------------------------------------------------------------------------
# [LISTE] CYCLE DE VIE ARTIFACT
# ----------------------------------------------------------------------------

"""
WORKFLOW STANDARD
────────────────

1. DÉVELOPPEMENT (Snapshot)
   Version : 1.0.0-SNAPSHOT
   Repository : maven-snapshots
   Fréquence deploy : Continue (CI/CD)
   
   v Tests, QA
   
2. RELEASE CANDIDATE (RC)
   Version : 1.0.0-RC1
   Repository : maven-releases
   Fréquence : Avant release
   
   v Tests finaux
   
3. RELEASE STABLE
   Version : 1.0.0
   Repository : maven-releases
   Fréquence : Release officielle
   
   v Production
   
4. MAINTENANCE
   Version : 1.0.1, 1.0.2 (patches)
   Repository : maven-releases
   
   v Nouvelle feature
   
5. NEXT VERSION
   Version : 1.1.0-SNAPSHOT
   Cycle recommence


EXAMPLE TIMELINE
───────────────

Semaine 1-4 :
  1.0.0-SNAPSHOT (develop)
  Daily deploys vers maven-snapshots

Semaine 5 :
  1.0.0-RC1 -> maven-releases
  Tests QA
  
  Bugs trouvés
  
  1.0.0-RC2 -> maven-releases
  Re-tests
  
Semaine 6 :
  1.0.0 -> maven-releases (RELEASE!)
  Deploy production
  
Semaine 7 :
  Bug critique trouvé
  
  1.0.1-SNAPSHOT -> maven-snapshots
  Fix + tests
  1.0.1 -> maven-releases (PATCH)
  
Semaine 8 :
  Développement nouvelle feature
  1.1.0-SNAPSHOT -> maven-snapshots
"""


# ----------------------------------------------------------------------------
# [NOMBRE] SEMANTIC VERSIONING (SemVer)
# ----------------------------------------------------------------------------

"""
FORMAT : MAJOR.MINOR.PATCH[-QUALIFIER]

Exemples :
  1.0.0
  2.3.1
  3.0.0-RC1
  1.2.3-SNAPSHOT


RÈGLES SEMVER
────────────

MAJOR (X.0.0)
  Breaking changes
  Incompatibilité API
  
  Exemple :
  1.0.0 -> 2.0.0
  - Méthode renommée
  - Paramètre supprimé
  - Comportement changé

MINOR (0.X.0)
  Nouvelles features
  Backward compatible
  
  Exemple :
  1.0.0 -> 1.1.0
  - Nouvelle méthode ajoutée
  - Nouvelle classe ajoutée
  - Feature optionnelle

PATCH (0.0.X)
  Bugfixes
  Backward compatible
  
  Exemple :
  1.0.0 -> 1.0.1
  - Correction bug
  - Fix sécurité
  - Optimisation


QUALIFIERS (optionnel)
  -SNAPSHOT : Développement
  -alpha : Alpha release
  -beta : Beta release
  -RC1 : Release Candidate 1
  -M1 : Milestone 1


EXEMPLES RÉELS
─────────────

Spring Boot :
  3.0.0 -> 3.1.0 -> 3.1.1 -> 3.2.0

Progression :
  1.0.0-SNAPSHOT  (dev)
  1.0.0-alpha1    (early access)
  1.0.0-beta1     (feature complete, testing)
  1.0.0-RC1       (release candidate)
  1.0.0           (stable release)
  1.0.1           (patch)
  1.1.0-SNAPSHOT  (next version dev)
"""


# ----------------------------------------------------------------------------
# [OBJECTIF] BONNES PRATIQUES
# ----------------------------------------------------------------------------

"""
RÈGLES D'OR
──────────

1. SNAPSHOTS POUR DÉVELOPPEMENT SEULEMENT
   [X] Jamais en production
   [OK] Seulement dev/staging/test

2. RELEASES IMMUABLES
   [X] Jamais re-déployer même version release
   [OK] Incrémenter version si changement

3. VERSIONING COHÉRENT
   [OK] Suivre SemVer
   [X] Sauter versions aléatoirement

4. CLEANUP SNAPSHOTS
   [OK] Supprimer vieux snapshots (>30j)
   [X] Garder infiniment

5. DOCUMENTATION CHANGELOG
   [OK] Documenter changements par version
   [X] Releases sans notes


ANTI-PATTERNS À ÉVITER
─────────────────────

[X] Snapshot en production
   Risque : Version change sans préavis

[X] Re-déployer release
   Risque : Même version = contenus différents

[X] Version "latest"
   Risque : Non déterministe

[X] Snapshots jamais nettoyés
   Risque : Gaspillage espace disque

[X] Sauter major versions
   Exemple : 1.0.0 -> 3.0.0 (où est 2.x.x?)
"""


"""
EXEMPLE CONFIGURATION NEXUS
───────────────────────────

Repository Snapshots :
- Deployment policy : Allow redeploy [OK]
- Cleanup policy : Delete >30 days
- Maximum metadata age : 5 minutes

Repository Releases :
- Deployment policy : Disable redeploy [OK]
- Cleanup policy : None (garder tout)
- Maximum metadata age : -1 (illimité)
"""


# ----------------------------------------------------------------------------
# [DOCS] RÉCAPITULATIF PARTIE 2 COMPLÈTE
# ----------------------------------------------------------------------------

"""
[BRAVO] FÉLICITATIONS ! PARTIE 2 TERMINÉE !

VOUS MAÎTRISEZ MAINTENANT :

Chapitre 5 : Intégration Maven
[OK] Configuration settings.xml et pom.xml
[OK] Téléchargement dépendances via Nexus
[OK] Déploiement artifacts (mvn deploy)
[OK] Gestion snapshots et releases
[OK] Sécurisation credentials
[OK] Résolution dépendances
[OK] Troubleshooting Maven

Chapitre 6 : Intégration Gradle
[OK] Configuration build.gradle (Groovy et Kotlin)
[OK] Plugin maven-publish
[OK] Téléchargement et publishing
[OK] Multi-projets Gradle
[OK] Sécurisation credentials Gradle

Chapitre 7 : Proxy et Cache
[OK] Configuration proxy repositories
[OK] Optimisation cache (metadata, components)
[OK] Cleanup et maintenance
[OK] Performance tuning
[OK] Monitoring cache

Chapitre 8 : Snapshots vs Releases
[OK] Différences snapshot/release
[OK] Semantic Versioning (SemVer)
[OK] Cycle de vie artifacts
[OK] Bonnes pratiques versioning


[CLE] POINTS CLÉS GLOBAUX

1. Nexus = Cache centralisé
   1 téléchargement -> ∞ réutilisations

2. Group repository = Point unique
   Simplifie config clients

3. Snapshots = Dev, Releases = Prod
   Never the twain shall meet!

4. Credentials sécurisés
   Encryption, tokens, env vars

5. Cache optimisé = Builds rapides
   Metadata, negative cache, cleanup


-> PROCHAINE ÉTAPE : PARTIE 3 !

La Partie 3 couvrira :

CHAPITRE 9 : Docker Registry
- Configurer registry Docker
- Push/Pull images
- Tags et versions

CHAPITRE 10 : npm Registry
- Packages npm privés
- Scopes et organizations

CHAPITRE 11 : PyPI Repository
- Packages Python
- Twine publishing

CHAPITRE 12 : Raw Repository
- Fichiers génériques
- Scripts, assets, configs

CHAPITRE 13 : Autres Formats
- NuGet (.NET)
- Helm (Kubernetes)
- APT/YUM (Linux packages)


[GUIDE] FICHIER SUIVANT : nexus_partie3.txt

Formats spécialisés et use cases avancés ! [RAPIDE]
"""


# ============================================================================
# FIN DE NEXUS_PARTIE2.TXT
# ============================================================================
# ============================================================================
# [LIVRE] NEXUS REPOSITORY MANAGER - PARTIE 3
# FORMATS AVANCÉS ET CAS D'USAGE SPÉCIALISÉS
# ============================================================================
#
# [OBJECTIF] CETTE PARTIE COUVRE :
# - Chapitre 9 : Docker Registry Nexus
# - Chapitre 10 : npm Registry Privé
# - Chapitre 11 : PyPI Repository Python
# - Chapitre 12 : Raw Repository et Fichiers Génériques
# - Chapitre 13 : Autres Formats (NuGet, Helm, APT/YUM)
#
# [TEMPS] TEMPS : ~8-10 heures
# [DOCS] PRÉREQUIS : Parties 1 et 2 complétées
# ============================================================================


# ============================================================================
# [GUIDE] CHAPITRE 9 : DOCKER REGISTRY NEXUS
# ============================================================================

"""
[OBJECTIF] OBJECTIFS D'APPRENTISSAGE

À la fin de ce chapitre, vous saurez :
[OK] Configurer Docker registry dans Nexus
[OK] Push/Pull images Docker via Nexus
[OK] Gérer tags et versions
[OK] Configurer SSL/TLS pour Docker
[OK] Nettoyer images Docker
[OK] Utiliser Docker Compose avec Nexus
[OK] Intégration CI/CD
"""


# ----------------------------------------------------------------------------
# [DOCKER] POURQUOI DOCKER REGISTRY PRIVÉ ?
# ----------------------------------------------------------------------------

"""
SANS NEXUS (Docker Hub direct)
──────────────────────────────

$ docker pull nginx:latest
"""

"""
[X] PROBLÈMES

1. RATE LIMITS Docker Hub
   100 pulls / 6h (anonymous)
   200 pulls / 6h (free account)
   -> CI/CD bloqué !

2. BANDE PASSANTE
   Image 500MB × 10 builds/jour = 5GB
   
3. IMAGES PRIVÉES
   Docker Hub privé = Payant
   
4. DÉPENDANCE EXTERNE
   Docker Hub down -> Builds impossibles
   
5. SÉCURITÉ
   Pas de scan vulnérabilités (gratuit)


AVEC NEXUS DOCKER REGISTRY
──────────────────────────

[OK] Cache local Docker Hub
[OK] Pas de rate limits internes
[OK] Images privées gratuites
[OK] Builds offline possibles
[OK] Scan vulnérabilités (Nexus Pro)
[OK] Contrôle versions centralisé
"""


# ----------------------------------------------------------------------------
# [CONFIG] CONFIGURER DOCKER REGISTRY
# ----------------------------------------------------------------------------

"""
ARCHITECTURE DOCKER NEXUS
────────────────────────

Docker nécessite PORTS DÉDIÉS :

┌─────────────────────────────────────┐
│  Nexus Server                       │
│                                     │
│  Port 8081 : Web UI                 │
│  Port 8082 : Docker Hosted          │
│  Port 8083 : Docker Proxy (Hub)     │
│  Port 8084 : Docker Group           │
└─────────────────────────────────────┘


[IDEE] POURQUOI PORTS SÉPARÉS ?

Docker Registry API ≠ Nexus Web UI
Docker client communique sur port dédié
"""


"""
ÉTAPE 1 : CRÉER DOCKER HOSTED REPOSITORY
────────────────────────────────────────

Pour stocker VOS images
"""

# Nexus UI -> Repositories -> Create repository -> docker (hosted)

"""
Name : docker-hosted

HTTP : 8082
   Port dédié pour ce registry
   
   [ATTENTION] SI NEXUS DANS DOCKER :
   Mapper ce port !
   docker run -p 8082:8082 sonatype/nexus3

HTTPS : (laisser vide pour l'instant)

Enable Docker V1 API : [X]
   V1 obsolète, toujours V2

Allow anonymous docker pull : [OK]
   Permet pull sans authentification
   Pratique pour CI/CD
   
   [IDEE] PRODUCTION : Désactiver si images sensibles

Deployment policy : Allow redeploy
   Permet push même tag (ex: latest)
   
   Options :
   - Allow redeploy : Peut écraser tags
   - Disable redeploy : Tags immuables
   - Read-only : Aucun push

Blob store : default

Storage -> Maximum component age : -1
Storage -> Maximum metadata age : -1
"""

# Create

# [OK] Registry Docker hosted créé !


"""
ÉTAPE 2 : CRÉER DOCKER PROXY REPOSITORY
───────────────────────────────────────

Pour cacher Docker Hub
"""

# Create repository -> docker (proxy)

"""
Name : dockerhub-proxy

HTTP : 8083

Remote storage : https://registry-1.docker.io
   Docker Hub registry URL
   
   [IDEE] AUTRES REGISTRIES :
   Google Container Registry : https://gcr.io
   AWS ECR : https://<account>.dkr.ecr.<region>.amazonaws.com
   GitHub : https://ghcr.io

Docker Index : Use Docker Hub
   Pour recherche d'images
   
   Options :
   - Use Docker Hub
   - Use proxy registry
   - Custom index URL

Allow anonymous docker pull : [OK]

Maximum component age : -1 (releases)
Maximum metadata age : -1
Negative cache : [OK] (1440)
"""

# Create


"""
ÉTAPE 3 : CRÉER DOCKER GROUP REPOSITORY
───────────────────────────────────────

Point d'accès unique
"""

# Create repository -> docker (group)

"""
Name : docker-all

HTTP : 8084

Member repositories : (ORDRE IMPORTANT!)
   [x] docker-hosted         (1er - vos images)
   [x] dockerhub-proxy       (2ème - Docker Hub cache)

[IDEE] ORDRE RÉSOLUTION

docker pull myapp:latest
1. Cherche dans docker-hosted -> Trouvé ? OK
2. Sinon cherche dans dockerhub-proxy
3. dockerhub-proxy télécharge depuis Docker Hub

Blob store : default
"""

# Create

# [OK] Infrastructure Docker complète !


"""
VÉRIFIER CONFIGURATION
─────────────────────
"""

# Browse -> Voir 3 repositories Docker :
"""
docker-hosted    (hosted) - Port 8082
dockerhub-proxy  (proxy)  - Port 8083
docker-all       (group)  - Port 8084
"""


# ----------------------------------------------------------------------------
# [SECURISE] CONFIGURER DOCKER CLIENT
# ----------------------------------------------------------------------------

"""
DOCKER AVEC HTTP (Développement)
────────────────────────────────

Par défaut, Docker nécessite HTTPS
Pour dev/test avec HTTP :
"""

# Linux : /etc/docker/daemon.json
# Mac : Docker Desktop -> Settings -> Docker Engine
# Windows : Docker Desktop -> Settings -> Docker Engine

{
  "insecure-registries": [
    "localhost:8082",
    "localhost:8083",
    "localhost:8084",
    "nexus.company.com:8082",
    "nexus.company.com:8083",
    "nexus.company.com:8084"
  ]
}

# Redémarrer Docker
sudo systemctl restart docker          # Linux
# Ou redémarrer Docker Desktop (Mac/Windows)


"""
TESTER CONNEXION
────────────────
"""

# Test sans auth (si anonymous pull activé)
docker pull localhost:8082/hello-world

# Si erreur "Get https://localhost:8082/v2/: http: server gave HTTP response to HTTPS client"
# -> Vérifier insecure-registries configuré


"""
LOGIN DOCKER
───────────
"""

# Login vers hosted registry
docker login localhost:8082

# Prompt :
"""
Username: admin
Password: <votre-password>

Login Succeeded
"""

# Login vers group registry
docker login localhost:8084
# Username: admin
# Password: <votre-password>


"""
[IDEE] CREDENTIALS STOCKÉES

Linux : ~/.docker/config.json
Mac/Windows : ~/.docker/config.json

{
  "auths": {
    "localhost:8082": {
      "auth": "YWRtaW46YWRtaW4xMjM="
    },
    "localhost:8084": {
      "auth": "YWRtaW46YWRtaW4xMjM="
    }
  }
}

auth = base64(username:password)
"""


# ----------------------------------------------------------------------------
# [SORTIE] PUSH IMAGES VERS NEXUS
# ----------------------------------------------------------------------------

"""
WORKFLOW COMPLET
───────────────

1. Build image localement
2. Tag avec nom registry Nexus
3. Push vers Nexus
4. Vérifier dans Nexus UI
"""


"""
EXEMPLE : APPLICATION SIMPLE
───────────────────────────
"""

# 1. Créer Dockerfile
cat > Dockerfile << 'EOF'
FROM nginx:alpine
COPY index.html /usr/share/nginx/html/
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
EOF

# 2. Créer fichier HTML
cat > index.html << 'EOF'
<!DOCTYPE html>
<html>
<head><title>My App</title></head>
<body>
  <h1>Hello from Nexus Docker Registry!</h1>
</body>
</html>
EOF

# 3. Build image
docker build -t my-app:1.0.0 .

"""
Output :
[+] Building 2.1s (8/8) FINISHED
 => [1/2] FROM docker.io/library/nginx:alpine
 => [2/2] COPY index.html /usr/share/nginx/html/
 => exporting to image
 => => naming to docker.io/library/my-app:1.0.0
"""

# 4. Vérifier image locale
docker images

"""
REPOSITORY   TAG       IMAGE ID       CREATED          SIZE
my-app       1.0.0     abc123def456   10 seconds ago   23.5MB
"""

# 5. Tag pour Nexus (IMPORTANT!)
docker tag my-app:1.0.0 localhost:8082/my-app:1.0.0

"""
[IDEE] FORMAT TAG NEXUS

<nexus-host>:<port>/<image-name>:<version>

Exemples :
localhost:8082/my-app:1.0.0
nexus.company.com:8082/frontend:2.3.1
registry.example.com:8082/namespace/app:latest
"""

# 6. Push vers Nexus
docker push localhost:8082/my-app:1.0.0

"""
Output :
The push refers to repository [localhost:8082/my-app]
a1b2c3d4e5f6: Pushed
7f8e9d0a1b2c: Pushed
1.0.0: digest: sha256:abc123... size: 1234
"""

# 7. Vérifier dans Nexus UI
# Browse -> docker-hosted -> Components
# Voir : my-app / 1.0.0


"""
PUSH MULTIPLE TAGS
─────────────────

Même image, plusieurs tags :
"""

docker tag my-app:1.0.0 localhost:8082/my-app:latest
docker tag my-app:1.0.0 localhost:8082/my-app:stable

docker push localhost:8082/my-app:latest
docker push localhost:8082/my-app:stable

# Dans Nexus : my-app avec 3 tags (1.0.0, latest, stable)


# ----------------------------------------------------------------------------
# [ENTREE] PULL IMAGES DEPUIS NEXUS
# ----------------------------------------------------------------------------

"""
PULL IMAGE HOSTED (Vos images)
──────────────────────────────
"""

# Supprimer image locale (pour tester pull)
docker rmi localhost:8082/my-app:1.0.0

# Pull depuis Nexus
docker pull localhost:8082/my-app:1.0.0

"""
1.0.0: Pulling from my-app
a1b2c3d4e5f6: Pull complete
Digest: sha256:abc123...
Status: Downloaded newer image for localhost:8082/my-app:1.0.0
"""

# Run container
docker run -d -p 8080:80 localhost:8082/my-app:1.0.0

# Test
curl http://localhost:8080
# Output : Hello from Nexus Docker Registry!


"""
PULL IMAGE PROXY (Docker Hub via Nexus)
───────────────────────────────────────

Via group registry (port 8084) :
"""

# Pull nginx depuis Docker Hub (via cache Nexus)
docker pull localhost:8084/nginx:alpine

"""
[IDEE] QUE SE PASSE-T-IL ?

1. Docker demande nginx:alpine à localhost:8084 (group)
2. Nexus cherche dans docker-hosted -> Pas trouvé
3. Nexus cherche dans dockerhub-proxy
   a. En cache ? -> Retourne
   b. Pas en cache ? -> Télécharge depuis Docker Hub -> Cache -> Retourne
4. Docker reçoit image
5. Prochains pulls ultra-rapides (cache Nexus)


VÉRIFIER CACHE
─────────────
"""

# Browse -> dockerhub-proxy -> Components
# Voir : nginx / alpine
# [OK] Image mise en cache !


"""
PULL IMAGES PUBLIQUES POPULAIRES
────────────────────────────────

Toujours via group registry (8084) :
"""

docker pull localhost:8084/postgres:15
docker pull localhost:8084/redis:7-alpine
docker pull localhost:8084/node:20-alpine

# Toutes cachées dans dockerhub-proxy


# ----------------------------------------------------------------------------
# [LABEL] GESTION TAGS ET VERSIONS
# ----------------------------------------------------------------------------

"""
STRATÉGIE TAGGING
────────────────

Bonnes pratiques Docker :
"""

# Version sémantique
docker tag my-app:1.0.0 localhost:8082/my-app:1.0.0
docker tag my-app:1.0.0 localhost:8082/my-app:1.0     # Minor
docker tag my-app:1.0.0 localhost:8082/my-app:1       # Major

# Tags spéciaux
docker tag my-app:1.0.0 localhost:8082/my-app:latest
docker tag my-app:1.0.0 localhost:8082/my-app:stable

# Tags environnement
docker tag my-app:1.0.0 localhost:8082/my-app:dev
docker tag my-app:1.0.0 localhost:8082/my-app:staging
docker tag my-app:1.0.0 localhost:8082/my-app:prod

# Tags build
docker tag my-app:1.0.0 localhost:8082/my-app:build-123
docker tag my-app:1.0.0 localhost:8082/my-app:commit-abc123


"""
VOIR TOUS LES TAGS
─────────────────
"""

# Via API Nexus
curl -u admin:admin123 \
  "http://localhost:8081/service/rest/v1/search?repository=docker-hosted&name=my-app"

# Via Nexus UI
# Browse -> docker-hosted -> my-app -> Voir tous tags


"""
SUPPRIMER TAG
────────────

[ATTENTION] Nexus ne supprime pas vraiment le layer
Seulement la référence (tag)
"""

# Via UI
# Browse -> docker-hosted -> my-app -> Select tag -> Delete

# Via API (plus complexe, nécessite component ID)


# ----------------------------------------------------------------------------
# [VERROUILLE] DOCKER AVEC SSL/TLS (PRODUCTION)
# ----------------------------------------------------------------------------

"""
POUR PRODUCTION : HTTPS OBLIGATOIRE
───────────────────────────────────

Docker refuse HTTP en production (sécurité)
-> Configurer HTTPS via :
1. Certificat SSL dans Nexus (complexe)
2. Reverse proxy (RECOMMANDÉ)


OPTION 1 : REVERSE PROXY NGINX
──────────────────────────────

Architecture :
"""

Client Docker
    v HTTPS
Nginx (reverse proxy)
    v HTTP (interne)
Nexus Docker Registry

"""
Configuration Nginx :
"""

# /etc/nginx/sites-available/docker-registry

upstream nexus_docker {
    server localhost:8082;
}

server {
    listen 443 ssl;
    server_name docker.company.com;
    
    ssl_certificate /etc/ssl/certs/docker.company.com.crt;
    ssl_certificate_key /etc/ssl/private/docker.company.com.key;
    
    client_max_body_size 0;  # Pas de limite upload
    chunked_transfer_encoding on;
    
    location / {
        proxy_pass http://nexus_docker;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto "https";
    }
}

# Activer config
sudo ln -s /etc/nginx/sites-available/docker-registry /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx

"""
Utilisation :
"""

docker login docker.company.com
docker push docker.company.com/my-app:1.0.0
docker pull docker.company.com/my-app:1.0.0


"""
OPTION 2 : TRAEFIK (Kubernetes)
───────────────────────────────

Si Nexus dans Kubernetes :
"""

# traefik-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: nexus-docker
  annotations:
    cert-manager.io/cluster-issuer: "letsencrypt-prod"
spec:
  tls:
  - hosts:
    - docker.company.com
    secretName: nexus-docker-tls
  rules:
  - host: docker.company.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: nexus
            port:
              number: 8082


# ----------------------------------------------------------------------------
# [NETTOYAGE] CLEANUP IMAGES DOCKER
# ----------------------------------------------------------------------------

"""
DOCKER LAYERS PRENNENT BEAUCOUP D'ESPACE
────────────────────────────────────────

Image 500MB × 100 versions = 50GB rapidement !


STRATÉGIE 1 : CLEANUP POLICY
────────────────────────────
"""

# System -> Cleanup Policies -> Create

"""
Name : docker-old-images

Format : docker

Cleanup Criteria:

Component Age : 90 days
   Supprimer images > 90 jours
   
Last Downloaded : 30 days
   Supprimer si pas utilisé depuis 30j
   
Asset Name Matcher : .*
   Tous assets
"""

# Appliquer à repository
# Repositories -> docker-hosted -> Cleanup policies
# [x] docker-old-images


"""
STRATÉGIE 2 : SUPPRIMER PAR TAG
───────────────────────────────

Garder seulement N derniers tags :
"""

# Via API (script Python/Shell)
# Exemple : Garder 10 derniers tags de my-app

"""
1. Lister tous tags
2. Trier par date
3. Supprimer tous sauf 10 derniers
"""


"""
STRATÉGIE 3 : SUPPRIMER UNTAGGED
────────────────────────────────

Images sans tag (après suppression tag)
"""

# Task Nexus : "Docker - Delete unused manifests and images"
# System -> Tasks -> Run cette task


"""
COMPACTER BLOB STORE
───────────────────

Après suppression, libérer espace réel :
"""

# System -> Tasks -> "Admin - Compact blob store"
# Run


# ----------------------------------------------------------------------------
# [COURS] EXERCICE PRATIQUE 9 : DOCKER WORKFLOW COMPLET
# ----------------------------------------------------------------------------

"""
OBJECTIF : Pipeline Docker complet avec Nexus


SCÉNARIO :

Application Node.js à containeriser et déployer


ÉTAPES :

1. Créer application Node.js simple
   [ ] package.json
   [ ] server.js (Express server)

2. Créer Dockerfile
   [ ] FROM node:20-alpine
   [ ] COPY et install
   [ ] EXPOSE 3000

3. Build image
   [ ] docker build -t mynode:1.0.0 .

4. Tag pour Nexus
   [ ] docker tag mynode:1.0.0 localhost:8082/mynode:1.0.0
   [ ] docker tag mynode:1.0.0 localhost:8082/mynode:latest

5. Push vers Nexus
   [ ] docker push localhost:8082/mynode:1.0.0
   [ ] docker push localhost:8082/mynode:latest

6. Vérifier dans Nexus
   [ ] Browse -> docker-hosted
   [ ] Voir mynode avec 2 tags

7. Pull et run depuis Nexus
   [ ] docker rmi (supprimer local)
   [ ] docker pull localhost:8082/mynode:1.0.0
   [ ] docker run -p 3000:3000 localhost:8082/mynode:1.0.0
   [ ] curl http://localhost:3000

8. Pull image Docker Hub via cache
   [ ] docker pull localhost:8084/redis:alpine
   [ ] Vérifier dans dockerhub-proxy

9. Docker Compose avec Nexus
   [ ] Créer docker-compose.yml
   [ ] Services : mynode + redis (depuis Nexus)
   [ ] docker-compose up


CODE FOURNI :

# server.js
const express = require('express');
const app = express();

app.get('/', (req, res) => {
  res.json({ message: 'Hello from Nexus Docker!' });
});

app.listen(3000, () => {
  console.log('Server running on port 3000');
});

# package.json
{
  "name": "mynode",
  "version": "1.0.0",
  "dependencies": {
    "express": "^4.18.0"
  },
  "scripts": {
    "start": "node server.js"
  }
}

# Dockerfile
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install --production
COPY server.js ./
EXPOSE 3000
CMD ["npm", "start"]

# docker-compose.yml
version: '3.8'
services:
  app:
    image: localhost:8082/mynode:1.0.0
    ports:
      - "3000:3000"
    depends_on:
      - redis
  
  redis:
    image: localhost:8084/redis:alpine
    ports:
      - "6379:6379"


VALIDATION :

[OK] Image buildée et taguée
[OK] Push vers Nexus réussi
[OK] Pull depuis Nexus fonctionne
[OK] Container run avec succès
[OK] Docker Compose avec images Nexus


TEMPS ESTIMÉ : 60-90 minutes
"""


# ----------------------------------------------------------------------------
# [DOCS] RÉCAPITULATIF CHAPITRE 9
# ----------------------------------------------------------------------------

"""
CE QUE VOUS AVEZ APPRIS

[OK] Créer repositories Docker (hosted, proxy, group)
[OK] Configurer ports dédiés (8082, 8083, 8084)
[OK] Insecure registries (dev) et HTTPS (prod)
[OK] Push/Pull images Docker
[OK] Stratégies de tagging
[OK] Cache Docker Hub via proxy
[OK] Cleanup images et layers
[OK] Reverse proxy SSL (Nginx)


[CLE] POINTS CLÉS

1. Docker = Ports dédiés
   Pas de /repository/ comme Maven

2. Tag format : <host>:<port>/<name>:<version>
   localhost:8082/myapp:1.0.0

3. Group registry pour pull
   Hosted + Proxy aggregés

4. Insecure registries (dev only!)
   Production = HTTPS obligatoire

5. Cleanup nécessaire
   Layers prennent beaucoup d'espace


[OBJECTIF] BONNES PRATIQUES

[OK] Group registry pour clients (8084)
[OK] Versioning sémantique images
[OK] Tags multiples (latest, stable, version)
[OK] Cleanup policy automatique
[OK] HTTPS en production (reverse proxy)
[OK] Anonymous pull désactivé (prod)


-> PROCHAINE ÉTAPE : Chapitre 10 - npm !

Packages JavaScript/Node.js :
- npm registry privé
- Scopes organisations
- Publishing packages
- Authentification npm

Passons à npm ! [PACKAGE]
"""


# ============================================================================
# [GUIDE] CHAPITRE 10 : NPM REGISTRY PRIVÉ
# ============================================================================

"""
[OBJECTIF] OBJECTIFS D'APPRENTISSAGE

À la fin de ce chapitre, vous saurez :
[OK] Configurer npm registry Nexus
[OK] Publier packages npm privés
[OK] Utiliser scopes (@company/package)
[OK] Configurer .npmrc
[OK] Intégration CI/CD npm
[OK] Gérer dépendances npm
[OK] Cleanup packages npm
"""


# ----------------------------------------------------------------------------
# [PACKAGE] POURQUOI NPM REGISTRY PRIVÉ ?
# ----------------------------------------------------------------------------

"""
SANS NEXUS (npmjs.org direct)
─────────────────────────────

$ npm install express
"""

"""
[X] PROBLÈMES

1. PACKAGES PRIVÉS npmjs.org
   Payant ($7/mois par utilisateur)
   
2. BANDE PASSANTE
   node_modules 500MB × 20 builds = 10GB/jour
   
3. RATE LIMITS
   Download limits npmjs.org
   
4. DÉPENDANCE EXTERNE
   npmjs down -> Builds bloqués
   
5. SÉCURITÉ
   Packages publics -> Risque supply chain
   

AVEC NEXUS NPM REGISTRY
───────────────────────

[OK] Packages privés gratuits
[OK] Cache npmjs.org local
[OK] Pas de rate limits internes
[OK] Builds offline possibles
[OK] Contrôle centralisé
[OK] Scopes organisations (@company/*)
"""


# ----------------------------------------------------------------------------
# [CONFIG] CONFIGURER NPM REPOSITORIES
# ----------------------------------------------------------------------------

"""
NEXUS CRÉE PAR DÉFAUT :
──────────────────────

- npm-hosted : Vos packages privés
- npmjs-proxy : Cache npmjs.org
- npm-group : Agrégation (hosted + proxy)


VÉRIFIER/CRÉER REPOSITORIES
──────────────────────────
"""

# Browse -> Repositories

"""
npm-hosted (hosted)
  URL : http://localhost:8081/repository/npm-hosted/

npmjs-proxy (proxy)
  URL : http://localhost:8081/repository/npmjs-proxy/
  Remote : https://registry.npmjs.org

npm-group (group)
  URL : http://localhost:8081/repository/npm-group/
  Members : npm-hosted, npmjs-proxy
"""


"""
SI ABSENTS, CRÉER :
──────────────────
"""

# HOSTED
# Create repository -> npm (hosted)

"""
Name : npm-hosted
Deployment policy : Allow redeploy
Blob store : default
"""

# PROXY
# Create repository -> npm (proxy)

"""
Name : npmjs-proxy
Remote storage : https://registry.npmjs.org
"""

# GROUP
# Create repository -> npm (group)

"""
Name : npm-group
Members :
  [x] npm-hosted
  [x] npmjs-proxy
"""


# ----------------------------------------------------------------------------
# [OUTIL] CONFIGURER NPM CLIENT
# ----------------------------------------------------------------------------

"""
CONFIGURATION .npmrc
───────────────────

3 niveaux possibles :
"""

# 1. Projet (.npmrc à la racine projet)
# 2. User (~/.npmrc)
# 3. Global (/etc/npmrc)

"""
[IDEE] RECOMMANDATION : User-level

Créer/éditer ~/.npmrc :
"""

# ~/.npmrc
registry=http://localhost:8081/repository/npm-group/
always-auth=true

# Credentials (méthode 1 : base64)
_auth=YWRtaW46YWRtaW4xMjM=

# Ou (méthode 2 : email + auth token)
email=admin@company.com
//localhost:8081/repository/npm-group/:_authToken=NpmToken.abc123def456

"""
[IDEE] GÉNÉRER _auth BASE64

echo -n 'admin:admin123' | base64
# Output : YWRtaW46YWRtaW4xMjM=
"""


"""
CONFIGURATION PAR SCOPE
──────────────────────

Pour packages @company/* :
"""

# ~/.npmrc
@company:registry=http://localhost:8081/repository/npm-hosted/
//localhost:8081/repository/npm-hosted/:_authToken=NpmToken.abc123


"""
CONFIGURATION PROJET (.npmrc)
────────────────────────────

À la racine du projet npm :
"""

# project/.npmrc
registry=http://localhost:8081/repository/npm-group/
always-auth=true

# [ATTENTION] Ne jamais commiter credentials !
# .gitignore
"""
.npmrc
"""

# Créer template
# .npmrc.template
"""
registry=http://localhost:8081/repository/npm-group/
always-auth=true
_auth=YOUR_BASE64_CREDENTIALS_HERE
"""


"""
LOGIN NPM
────────

Alternative à _auth : npm login
"""

npm login --registry=http://localhost:8081/repository/npm-group/

# Prompt :
"""
Username: admin
Password: <password>
Email: admin@company.com

Logged in as admin on http://localhost:8081/repository/npm-group/.
"""

# Credentials stockées dans ~/.npmrc automatiquement


"""
VÉRIFIER CONFIGURATION
─────────────────────
"""

# Voir registry configuré
npm config get registry

# Output : http://localhost:8081/repository/npm-group/

# Tester connexion
npm ping --registry=http://localhost:8081/repository/npm-group/

# Output :
"""
Ping success: {}
"""


# ----------------------------------------------------------------------------
# [ENTREE] INSTALLER PACKAGES DEPUIS NEXUS
# ----------------------------------------------------------------------------

"""
INSTALLATION STANDARD
────────────────────

Avec .npmrc configuré :
"""

# Créer projet
mkdir my-project
cd my-project
npm init -y

# Installer dépendances
npm install express
npm install lodash axios

"""
[IDEE] QUE SE PASSE-T-IL ?

1. npm consulte ~/.npmrc
2. Voit registry = Nexus npm-group
3. Demande express à Nexus
4. Nexus cherche dans npm-hosted -> Pas trouvé
5. Nexus cherche dans npmjs-proxy
   a. En cache ? -> Retourne
   b. Pas en cache ? -> Télécharge npmjs.org -> Cache -> Retourne
6. npm installe dans node_modules


VÉRIFIER CACHE NEXUS
───────────────────
"""

# Browse -> npmjs-proxy -> Components
# Voir packages cachés :
"""
express / 4.18.2
lodash / 4.17.21
axios / 1.6.0
+ toutes dépendances transitives
"""


"""
INSTALLATION AVEC SCOPE
──────────────────────

Pour packages @company :
"""

npm install @company/my-package

# Nexus cherche dans npm-hosted (scope @company)


"""
LOGS DÉTAILLÉS
─────────────
"""

npm install express --loglevel silly

# Voir toutes requêtes vers Nexus


# ----------------------------------------------------------------------------
# [SORTIE] PUBLIER PACKAGE NPM
# ----------------------------------------------------------------------------

"""
CRÉER PACKAGE NPM
────────────────
"""

# 1. Créer dossier
mkdir my-awesome-lib
cd my-awesome-lib

# 2. Init package
npm init

# Prompt (exemple) :
"""
package name: @company/awesome-lib
version: 1.0.0
description: Awesome utility library
entry point: index.js
test command: npm test
git repository: https://github.com/company/awesome-lib
keywords: utils, awesome
author: John Doe <john@company.com>
license: MIT
"""

# 3. Créer code
cat > index.js << 'EOF'
/**
 * Awesome utility functions
 */

function hello(name) {
  return `Hello, ${name}!`;
}

function add(a, b) {
  return a + b;
}

module.exports = { hello, add };
EOF

# 4. Créer README
cat > README.md << 'EOF'
# @company/awesome-lib

Awesome utility library for amazing things!

## Installation

```bash
npm install @company/awesome-lib
```

## Usage

```javascript
const { hello, add } = require('@company/awesome-lib');

console.log(hello('World'));  // Hello, World!
console.log(add(2, 3));       // 5
```
EOF

# 5. Vérifier package.json
cat package.json

"""
{
  "name": "@company/awesome-lib",
  "version": "1.0.0",
  "description": "Awesome utility library",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": ["utils", "awesome"],
  "author": "John Doe <john@company.com>",
  "license": "MIT"
}
"""


"""
CONFIGURER PUBLISHING
────────────────────

Ajouter publishConfig dans package.json :
"""

{
  "name": "@company/awesome-lib",
  "version": "1.0.0",
  "...": "...",
  "publishConfig": {
    "registry": "http://localhost:8081/repository/npm-hosted/"
  }
}

"""
[IDEE] publishConfig

Spécifie où publier (npm-hosted)
Différent de registry global (npm-group)
"""


"""
PUBLIER PACKAGE
──────────────
"""

# Login (si pas déjà fait)
npm login --registry=http://localhost:8081/repository/npm-hosted/

# Publish
npm publish

"""
Output :
npm notice 
npm notice [PACKAGE]  @company/awesome-lib@1.0.0
npm notice === Tarball Contents === 
npm notice 156B index.js     
npm notice 387B package.json 
npm notice 245B README.md    
npm notice === Tarball Details === 
npm notice name:          @company/awesome-lib                 
npm notice version:       1.0.0                                
npm notice package size:  788 B                                
npm notice unpacked size: 788 B                                
npm notice shasum:        abc123def456                         
npm notice total files:   3                                    
npm notice 
+ @company/awesome-lib@1.0.0
"""


"""
VÉRIFIER DANS NEXUS
──────────────────
"""

# Browse -> npm-hosted -> Components
# Voir : @company/awesome-lib / 1.0.0

# Ou via API
curl -u admin:admin123 \
  "http://localhost:8081/service/rest/v1/search?repository=npm-hosted&name=@company/awesome-lib"


# ----------------------------------------------------------------------------
# [SYNC] UTILISER PACKAGE PUBLIÉ
# ----------------------------------------------------------------------------

"""
DANS AUTRE PROJET
────────────────
"""

# Nouveau projet
mkdir my-app
cd my-app
npm init -y

# Installer package privé
npm install @company/awesome-lib

"""
Output :
+ @company/awesome-lib@1.0.0
added 1 package
"""

# Utiliser dans code
cat > app.js << 'EOF'
const { hello, add } = require('@company/awesome-lib');

console.log(hello('Nexus'));
console.log(add(10, 20));
EOF

# Run
node app.js

"""
Output :
Hello, Nexus!
30
"""


# ----------------------------------------------------------------------------
# [NOMBRE] VERSIONING ET UPDATES
# ----------------------------------------------------------------------------

"""
PUBLIER NOUVELLE VERSION
───────────────────────
"""

# Modifier code
# index.js - ajouter nouvelle fonction
function multiply(a, b) {
  return a * b;
}

module.exports = { hello, add, multiply };

# Incrémenter version
npm version patch  # 1.0.0 -> 1.0.1
# ou
npm version minor  # 1.0.1 -> 1.1.0
# ou
npm version major  # 1.1.0 -> 2.0.0

# Publish
npm publish

"""
[IDEE] SEMVER NPM

patch : Bugfix (1.0.0 -> 1.0.1)
minor : Feature (1.0.1 -> 1.1.0)
major : Breaking (1.1.0 -> 2.0.0)
"""


"""
UPDATE DANS PROJET CONSOMMATEUR
───────────────────────────────
"""

# Voir versions disponibles
npm view @company/awesome-lib versions

# Output : [ '1.0.0', '1.0.1', '1.1.0', '2.0.0' ]

# Update
npm update @company/awesome-lib

# Ou version spécifique
npm install @company/awesome-lib@2.0.0


"""
UNPUBLISH (DANGEREUX!)
─────────────────────

[ATTENTION] Supprimer package publié
"""

npm unpublish @company/awesome-lib@1.0.0 --force

# Ou toutes versions
npm unpublish @company/awesome-lib --force

"""
[ATTENTION] ATTENTION

npm unpublish = Dangereux !
Si package utilisé -> Casse builds
-> Préférer deprecate
"""

npm deprecate @company/awesome-lib@1.0.0 "Please upgrade to 2.0.0"


# ----------------------------------------------------------------------------
# [ENTREPRISE] SCOPES ORGANISATIONS
# ----------------------------------------------------------------------------

"""
SCOPE = NAMESPACE

Format : @scope/package

Exemples :
@angular/core
@babel/cli
@company/my-package


AVANTAGES SCOPES
───────────────

[OK] Évite conflits noms
   @company/utils vs @other/utils
   
[OK] Groupement logique
   Tous packages @company/* appartiennent à company
   
[OK] Accès contrôlé
   Scope privé = Packages privés
   
[OK] Publishing simplifié
   Scope -> Registry dédié


CONFIGURER SCOPE
───────────────
"""

# ~/.npmrc
@company:registry=http://localhost:8081/repository/npm-hosted/
//localhost:8081/repository/npm-hosted/:_authToken=NpmToken.abc123

"""
[IDEE] COMPORTEMENT

Packages @company/* -> npm-hosted (privé)
Autres packages -> npm-group (public cache)
"""


"""
CRÉER PACKAGE AVEC SCOPE
───────────────────────
"""

npm init --scope=@company

# Ou manuellement dans package.json
{
  "name": "@company/my-package",
  ...
}


"""
PUBLIER SCOPED PACKAGE
─────────────────────
"""

npm publish --access public   # Public (npmjs.org)
npm publish --access restricted  # Privé (défaut)

# Pour Nexus, access n'a pas d'importance (toujours privé)


# ----------------------------------------------------------------------------
# [COURS] EXERCICE PRATIQUE 10 : NPM WORKFLOW COMPLET
# ----------------------------------------------------------------------------

"""
OBJECTIF : Créer et publier library npm


SCÉNARIO :

Library @company/string-utils avec fonctions utilitaires


ÉTAPES :

1. Configurer npm avec Nexus
   [ ] ~/.npmrc avec registry npm-group
   [ ] Credentials (login ou _auth)

2. Créer package @company/string-utils
   [ ] npm init --scope=@company
   [ ] name: @company/string-utils
   [ ] version: 1.0.0

3. Implémenter fonctions
   [ ] capitalize(str)
   [ ] reverse(str)
   [ ] truncate(str, length)
   [ ] slugify(str)

4. Ajouter tests (optionnel mais recommandé)
   [ ] npm install --save-dev jest
   [ ] Créer tests

5. Publier v1.0.0
   [ ] npm publish

6. Vérifier dans Nexus
   [ ] Browse -> npm-hosted
   [ ] Voir @company/string-utils

7. Utiliser dans projet
   [ ] Créer nouveau projet
   [ ] npm install @company/string-utils
   [ ] Utiliser fonctions

8. Update et republier
   [ ] Ajouter fonction camelCase(str)
   [ ] npm version minor (-> 1.1.0)
   [ ] npm publish

9. Update dans projet consommateur
   [ ] npm update @company/string-utils
   [ ] Utiliser nouvelle fonction


CODE FOURNI :

# index.js
function capitalize(str) {
  return str.charAt(0).toUpperCase() + str.slice(1);
}

function reverse(str) {
  return str.split('').reverse().join('');
}

function truncate(str, length) {
  return str.length > length 
    ? str.substring(0, length) + '...' 
    : str;
}

function slugify(str) {
  return str
    .toLowerCase()
    .replace(/[^\w\s-]/g, '')
    .replace(/\s+/g, '-');
}

module.exports = {
  capitalize,
  reverse,
  truncate,
  slugify
};


VALIDATION :

[OK] Package publié dans npm-hosted
[OK] Visible dans Nexus UI
[OK] Installable via npm install
[OK] Fonctions utilisables
[OK] Versioning fonctionne


TEMPS ESTIMÉ : 45-60 minutes
"""


# ----------------------------------------------------------------------------
# [DOCS] RÉCAPITULATIF CHAPITRE 10
# ----------------------------------------------------------------------------

"""
CE QUE VOUS AVEZ APPRIS

[OK] Repositories npm (hosted, proxy, group)
[OK] Configuration .npmrc
[OK] Installation packages via Nexus
[OK] Publishing packages npm
[OK] Scopes organisations (@company/*)
[OK] Versioning et updates
[OK] npm login et authentification


[CLE] POINTS CLÉS

1. npm-group pour consommer
   Cache npmjs.org + packages privés

2. npm-hosted pour publier
   Vos packages privés

3. .npmrc = Configuration npm
   Registry, credentials

4. Scopes = Namespaces
   @company/* isolés

5. publishConfig dans package.json
   Spécifie où publier


[OBJECTIF] BONNES PRATIQUES

[OK] Scopes pour packages privés (@company/*)
[OK] publishConfig dans package.json
[OK] .npmrc en .gitignore
[OK] Template .npmrc pour équipe
[OK] Semantic versioning (1.0.0)
[OK] README et documentation
[OK] Tests avant publish


-> PROCHAINE ÉTAPE : Chapitre 11 - PyPI !

Packages Python :
- Repository PyPI
- Publishing avec Twine
- requirements.txt
- Virtual environments

Passons à Python ! [PYTHON]
"""


# ============================================================================
# FIN PARTIE 3 - CHAPITRES 9 ET 10
# CONTINUEZ AVEC CHAPITRES 11, 12 ET 13...
# ============================================================================
# ============================================================================
# [LIVRE] NEXUS REPOSITORY MANAGER - PARTIE 3 (SUITE)
# CHAPITRES 11, 12 ET 13
# ============================================================================


# ============================================================================
# [GUIDE] CHAPITRE 11 : PyPI REPOSITORY PYTHON
# ============================================================================

"""
[OBJECTIF] OBJECTIFS D'APPRENTISSAGE

À la fin de ce chapitre, vous saurez :
[OK] Configurer PyPI repository Nexus
[OK] Installer packages Python depuis Nexus
[OK] Publier packages avec Twine
[OK] Configurer pip et pip.conf
[OK] Virtual environments avec Nexus
[OK] requirements.txt
[OK] Intégration CI/CD Python
"""


# ----------------------------------------------------------------------------
# [PYTHON] POURQUOI PyPI PRIVÉ ?
# ----------------------------------------------------------------------------

"""
SANS NEXUS (PyPI.org direct)
────────────────────────────

$ pip install requests
"""

"""
[X] PROBLÈMES

1. PACKAGES PRIVÉS PyPI.org
   Payant ou impossible
   
2. BANDE PASSANTE
   Packages ML (TensorFlow, PyTorch) = plusieurs GB
   
3. RATE LIMITS
   PyPI.org peut throttle downloads
   
4. DÉPENDANCE EXTERNE
   PyPI down -> pip install échoue
   
5. VERSIONS MULTIPLES
   Projet A (Python 3.9) vs Projet B (Python 3.11)
   

AVEC NEXUS PyPI REPOSITORY
──────────────────────────

[OK] Packages privés gratuits
[OK] Cache PyPI.org local
[OK] Pas de rate limits
[OK] Builds offline
[OK] Versions Python multiples
[OK] Contrôle dépendances
"""


# ----------------------------------------------------------------------------
# [CONFIG] CONFIGURER PyPI REPOSITORIES
# ----------------------------------------------------------------------------

"""
CRÉER REPOSITORIES PyPI
──────────────────────
"""

# HOSTED (packages privés)
# Create repository -> pypi (hosted)

"""
Name : pypi-hosted

Deployment policy : Allow redeploy
   (ou Disable pour releases)

Blob store : default
"""

# PROXY (cache PyPI.org)
# Create repository -> pypi (proxy)

"""
Name : pypi-proxy

Remote storage : https://pypi.org
   PyPI officiel
   
   Alternatives :
   https://pypi.python.org/simple
   https://test.pypi.org (test)

Maximum component age : -1
Maximum metadata age : 1440
Negative cache : [OK]
"""

# GROUP (agrégation)
# Create repository -> pypi (group)

"""
Name : pypi-all

Members : (ordre important!)
   [x] pypi-hosted
   [x] pypi-proxy
"""


# ----------------------------------------------------------------------------
# [OUTIL] CONFIGURER PIP CLIENT
# ----------------------------------------------------------------------------

"""
CONFIGURATION pip.conf
─────────────────────

3 niveaux :
- Global : /etc/pip.conf (Linux) ou C:\ProgramData\pip\pip.ini (Windows)
- User : ~/.pip/pip.conf (Linux) ou %APPDATA%\pip\pip.ini (Windows)
- Project : ./pip.conf


[IDEE] RECOMMANDATION : User-level

Linux/Mac : ~/.pip/pip.conf
"""

[global]
index-url = http://localhost:8081/repository/pypi-all/simple
trusted-host = localhost

"""
Windows : %APPDATA%\pip\pip.ini
"""

[global]
index-url = http://localhost:8081/repository/pypi-all/simple
trusted-host = localhost

"""
[IDEE] EXPLICATION

index-url : Repository pip principal
  Pointe vers pypi-all (group)

trusted-host : Autoriser HTTP (dev)
  Production : Utiliser HTTPS


AVEC AUTHENTIFICATION
────────────────────

Si repository privé :
"""

[global]
index-url = http://admin:admin123@localhost:8081/repository/pypi-all/simple
trusted-host = localhost

"""
[ATTENTION] SÉCURITÉ

Credentials en clair dans config !
-> Alternatives :
1. Variables d'environnement
2. Keyring
3. Token Nexus
"""


"""
CONFIGURATION VIA ENVIRONNEMENT
───────────────────────────────
"""

# Linux/Mac
export PIP_INDEX_URL=http://localhost:8081/repository/pypi-all/simple
export PIP_TRUSTED_HOST=localhost

# Windows
set PIP_INDEX_URL=http://localhost:8081/repository/pypi-all/simple
set PIP_TRUSTED_HOST=localhost

# Permanent (.bashrc, .zshrc)
echo 'export PIP_INDEX_URL=http://localhost:8081/repository/pypi-all/simple' >> ~/.bashrc
echo 'export PIP_TRUSTED_HOST=localhost' >> ~/.bashrc


"""
VÉRIFIER CONFIGURATION
─────────────────────
"""

# Voir config pip
pip config list

# Output :
"""
global.index-url='http://localhost:8081/repository/pypi-all/simple'
global.trusted-host='localhost'
"""

# Test connexion
pip search requests --index http://localhost:8081/repository/pypi-all/simple


# ----------------------------------------------------------------------------
# [ENTREE] INSTALLER PACKAGES DEPUIS NEXUS
# ----------------------------------------------------------------------------

"""
INSTALLATION STANDARD
────────────────────

Avec pip.conf configuré :
"""

# Installer package
pip install requests

"""
[IDEE] QUE SE PASSE-T-IL ?

1. pip lit pip.conf
2. Voit index-url = Nexus pypi-all
3. Demande requests à Nexus
4. Nexus cherche dans pypi-hosted -> Pas trouvé
5. Nexus cherche dans pypi-proxy
   a. En cache ? -> Retourne
   b. Pas en cache ? -> Télécharge PyPI.org -> Cache -> Retourne
6. pip installe package


VÉRIFIER CACHE NEXUS
───────────────────
"""

# Browse -> pypi-proxy -> Components
# Voir packages cachés :
"""
requests / 2.31.0
certifi / 2023.11.17
charset-normalizer / 3.3.2
...
"""


"""
INSTALLATION AVEC requirements.txt
──────────────────────────────────
"""

# requirements.txt
requests==2.31.0
flask==3.0.0
pandas==2.1.4
numpy==1.26.2

# Install all
pip install -r requirements.txt

"""
[IDEE] VERSIONS

Sans version : Dernière disponible
  requests

Exact : Version spécifique
  requests==2.31.0

Compatible : Version compatible
  requests>=2.30.0,<3.0.0

Flexible : Minimum version
  requests>=2.30.0
"""


"""
VIRTUAL ENVIRONMENT
──────────────────

Bonnes pratiques Python :
"""

# Créer venv
python -m venv venv

# Activer
# Linux/Mac
source venv/bin/activate

# Windows
venv\Scripts\activate

# Installer depuis Nexus
pip install -r requirements.txt

# Désactiver
deactivate


# ----------------------------------------------------------------------------
# [SORTIE] PUBLIER PACKAGE PYTHON
# ----------------------------------------------------------------------------

"""
CRÉER PACKAGE PYTHON
───────────────────
"""

# Structure projet
"""
my-awesome-lib/
├── setup.py
├── setup.cfg (optionnel)
├── pyproject.toml (moderne)
├── README.md
├── LICENSE
└── my_awesome_lib/
    ├── __init__.py
    └── utils.py
"""

# 1. Créer structure
mkdir -p my-awesome-lib/my_awesome_lib
cd my-awesome-lib

# 2. Code principal
cat > my_awesome_lib/__init__.py << 'EOF'
"""
My Awesome Library
~~~~~~~~~~~~~~~~~~

A library that does awesome things!
"""

__version__ = "1.0.0"
__author__ = "John Doe"

from .utils import hello, add

__all__ = ['hello', 'add']
EOF

cat > my_awesome_lib/utils.py << 'EOF'
def hello(name):
    """Say hello to someone."""
    return f"Hello, {name}!"

def add(a, b):
    """Add two numbers."""
    return a + b
EOF

# 3. setup.py (classique)
cat > setup.py << 'EOF'
from setuptools import setup, find_packages

with open("README.md", "r", encoding="utf-8") as fh:
    long_description = fh.read()

setup(
    name="my-awesome-lib",
    version="1.0.0",
    author="John Doe",
    author_email="john@company.com",
    description="A library that does awesome things",
    long_description=long_description,
    long_description_content_type="text/markdown",
    url="https://github.com/company/my-awesome-lib",
    packages=find_packages(),
    classifiers=[
        "Programming Language :: Python :: 3",
        "Programming Language :: Python :: 3.9",
        "Programming Language :: Python :: 3.10",
        "Programming Language :: Python :: 3.11",
        "License :: OSI Approved :: MIT License",
        "Operating System :: OS Independent",
    ],
    python_requires=">=3.9",
    install_requires=[
        "requests>=2.28.0",
    ],
)
EOF

# 4. pyproject.toml (moderne, recommandé)
cat > pyproject.toml << 'EOF'
[build-system]
requires = ["setuptools>=61.0", "wheel"]
build-backend = "setuptools.build_meta"

[project]
name = "my-awesome-lib"
version = "1.0.0"
description = "A library that does awesome things"
readme = "README.md"
authors = [
    {name = "John Doe", email = "john@company.com"}
]
license = {text = "MIT"}
classifiers = [
    "Programming Language :: Python :: 3",
    "License :: OSI Approved :: MIT License",
]
requires-python = ">=3.9"
dependencies = [
    "requests>=2.28.0",
]

[project.urls]
Homepage = "https://github.com/company/my-awesome-lib"
EOF

# 5. README.md
cat > README.md << 'EOF'
# My Awesome Lib

A library that does awesome things!

## Installation

```bash
pip install my-awesome-lib
```

## Usage

```python
from my_awesome_lib import hello, add

print(hello("World"))  # Hello, World!
print(add(2, 3))       # 5
```
EOF


"""
BUILDER PACKAGE
──────────────
"""

# Installer build tools
pip install build twine

# Build package
python -m build

"""
Output :
Successfully built my_awesome_lib-1.0.0.tar.gz and my_awesome_lib-1.0.0-py3-none-any.whl

Fichiers créés dans dist/ :
- my_awesome_lib-1.0.0.tar.gz (source distribution)
- my_awesome_lib-1.0.0-py3-none-any.whl (wheel)
"""


"""
CONFIGURER .pypirc POUR NEXUS
────────────────────────────

~/.pypirc (Linux/Mac)
"""

[distutils]
index-servers =
    nexus

[nexus]
repository = http://localhost:8081/repository/pypi-hosted/
username = admin
password = admin123

"""
Windows : %USERPROFILE%\.pypirc
"""


"""
PUBLIER AVEC TWINE
─────────────────
"""

# Upload vers Nexus
twine upload -r nexus dist/*

"""
Output :
Uploading distributions to http://localhost:8081/repository/pypi-hosted/
Uploading my_awesome_lib-1.0.0-py3-none-any.whl
100% ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 5.2/5.2 kB • 00:00 • ?
Uploading my_awesome_lib-1.0.0.tar.gz
100% ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 4.8/4.8 kB • 00:00 • ?
"""

"""
[IDEE] TWINE OPTIONS

-r nexus : Repository name (depuis .pypirc)
--skip-existing : Skip si version existe
--verbose : Logs détaillés
"""


"""
VÉRIFIER DANS NEXUS
──────────────────
"""

# Browse -> pypi-hosted -> Components
# Voir : my-awesome-lib / 1.0.0


# ----------------------------------------------------------------------------
# [SYNC] UTILISER PACKAGE PUBLIÉ
# ----------------------------------------------------------------------------

"""
INSTALLER DEPUIS NEXUS
─────────────────────
"""

# Nouveau projet
mkdir my-app
cd my-app
python -m venv venv
source venv/bin/activate

# Installer package privé
pip install my-awesome-lib

"""
Output :
Collecting my-awesome-lib
  Downloading http://localhost:8081/repository/pypi-all/packages/.../my_awesome_lib-1.0.0-py3-none-any.whl
Collecting requests>=2.28.0
  Using cached ...
Installing collected packages: requests, my-awesome-lib
Successfully installed my-awesome-lib-1.0.0 requests-2.31.0
"""

# Utiliser dans code
cat > app.py << 'EOF'
from my_awesome_lib import hello, add

print(hello("Nexus"))
print(add(10, 20))
EOF

# Run
python app.py

"""
Output :
Hello, Nexus!
30
"""


# ----------------------------------------------------------------------------
# [NOMBRE] VERSIONING
# ----------------------------------------------------------------------------

"""
PUBLIER NOUVELLE VERSION
───────────────────────
"""

# 1. Modifier code (ajouter fonction)
# my_awesome_lib/utils.py
def multiply(a, b):
    """Multiply two numbers."""
    return a * b

# 2. Update version
# setup.py : version="1.1.0"
# pyproject.toml : version = "1.1.0"
# __init__.py : __version__ = "1.1.0"

# 3. Rebuild
python -m build

# 4. Upload
twine upload -r nexus dist/*


"""
UPDATE DANS PROJET
─────────────────
"""

# Voir versions disponibles
pip index versions my-awesome-lib

# Update
pip install --upgrade my-awesome-lib

# Ou version spécifique
pip install my-awesome-lib==1.1.0


# ----------------------------------------------------------------------------
# [COURS] EXERCICE PRATIQUE 11 : PYTHON PACKAGE COMPLET
# ----------------------------------------------------------------------------

"""
OBJECTIF : Créer et publier package Python


SCÉNARIO :

Package math-helpers avec fonctions mathématiques


ÉTAPES :

1. Configurer pip
   [ ] pip.conf avec Nexus pypi-all
   [ ] .pypirc avec credentials

2. Créer package math-helpers
   [ ] Structure projet
   [ ] pyproject.toml ou setup.py
   [ ] Version 1.0.0

3. Implémenter fonctions
   [ ] factorial(n)
   [ ] fibonacci(n)
   [ ] is_prime(n)
   [ ] gcd(a, b)

4. Build package
   [ ] python -m build
   [ ] Vérifier dist/

5. Publier v1.0.0
   [ ] twine upload -r nexus dist/*

6. Vérifier dans Nexus
   [ ] Browse -> pypi-hosted
   [ ] Voir math-helpers

7. Installer dans projet
   [ ] Nouveau venv
   [ ] pip install math-helpers
   [ ] Utiliser fonctions

8. Update et republier
   [ ] Ajouter lcm(a, b)
   [ ] Version 1.1.0
   [ ] Rebuild et upload

9. Test upgrade
   [ ] pip install --upgrade math-helpers


CODE FOURNI :

# math_helpers/utils.py
def factorial(n):
    if n <= 1:
        return 1
    return n * factorial(n - 1)

def fibonacci(n):
    if n <= 1:
        return n
    a, b = 0, 1
    for _ in range(n - 1):
        a, b = b, a + b
    return b

def is_prime(n):
    if n < 2:
        return False
    for i in range(2, int(n ** 0.5) + 1):
        if n % i == 0:
            return False
    return True

def gcd(a, b):
    while b:
        a, b = b, a % b
    return a


VALIDATION :

[OK] Package construit (tar.gz + whl)
[OK] Publié dans pypi-hosted
[OK] Installable via pip
[OK] Fonctions utilisables
[OK] Versioning fonctionne


TEMPS ESTIMÉ : 60 minutes
"""


# ----------------------------------------------------------------------------
# [DOCS] RÉCAPITULATIF CHAPITRE 11
# ----------------------------------------------------------------------------

"""
CE QUE VOUS AVEZ APPRIS

[OK] Repositories PyPI (hosted, proxy, group)
[OK] Configuration pip.conf
[OK] Installation packages via Nexus
[OK] Build packages (setup.py, pyproject.toml)
[OK] Publishing avec Twine
[OK] .pypirc configuration
[OK] Virtual environments


[CLE] POINTS CLÉS

1. pip.conf = Configuration pip
   index-url vers pypi-all

2. pyproject.toml = Moderne
   Remplace setup.py

3. Twine = Publishing tool
   Upload vers Nexus

4. .pypirc = Credentials
   Repository configurations

5. venv = Isolation
   Par projet


[OBJECTIF] BONNES PRATIQUES

[OK] pyproject.toml (moderne)
[OK] Virtual environments toujours
[OK] requirements.txt versionné
[OK] .pypirc en .gitignore
[OK] Semantic versioning
[OK] README et LICENSE
[OK] Tests avec pytest


-> PROCHAINE ÉTAPE : Chapitre 12 - Raw !

Fichiers génériques :
- Scripts shell/Python
- Configs
- Assets (images, fonts)
- Backups

Polyvalence totale ! [DOSSIER]
"""


# ============================================================================
# [GUIDE] CHAPITRE 12 : RAW REPOSITORY - FICHIERS GÉNÉRIQUES
# ============================================================================

"""
[OBJECTIF] OBJECTIFS D'APPRENTISSAGE

À la fin de ce chapitre, vous saurez :
[OK] Créer Raw repository
[OK] Upload fichiers via UI et curl
[OK] Organiser fichiers (chemins)
[OK] Télécharger via wget/curl
[OK] Cas d'usage pratiques
[OK] Permissions et sécurité
[OK] Automatisation scripts
"""


# ----------------------------------------------------------------------------
# [DOSSIER] QU'EST-CE QUE RAW REPOSITORY ?
# ----------------------------------------------------------------------------

"""
RAW REPOSITORY = STOCKAGE GÉNÉRIQUE
───────────────────────────────────

Peut stocker N'IMPORTE QUOI :
- Scripts (shell, python, powershell)
- Configurations (nginx, apache, yaml)
- Assets (images, fonts, css)
- Binaires (executables, archives)
- Documentation (PDF, markdown)
- Backups (dumps SQL, exports)
- Certificates (SSL, keys)


[IDEE] DIFFÉRENCE vs AUTRES FORMATS

Maven : Seulement JARs, POMs (structure imposée)
npm : Seulement packages npm
Docker : Seulement images Docker
Raw : Tout ! Aucune structure imposée


CAS D'USAGE TYPIQUES
────────────────────

1. SCRIPTS BOOTSTRAP
   install.sh, setup.ps1
   curl https://nexus/scripts/install.sh | bash

2. CONFIGURATIONS
   nginx.conf, prometheus.yml
   Templates pour déploiements

3. ASSETS WEB
   Images, fonts, CSS/JS
   CDN interne

4. BINARIES
   Outils compilés
   CLI tools internes

5. DOCUMENTATION
   PDFs, manuels
   Centralisés

6. BACKUPS/EXPORTS
   Database dumps
   Configuration backups
"""


# ----------------------------------------------------------------------------
# [CONFIG] CRÉER RAW REPOSITORY
# ----------------------------------------------------------------------------

"""
HOSTED REPOSITORY
────────────────
"""

# Create repository -> raw (hosted)

"""
Name : raw-hosted

Content Disposition : Inline
   - Inline : Affiche dans navigateur (HTML, images, PDF)
   - Attachment : Force téléchargement
   
   [IDEE] RECOMMANDATION : Inline
   Permet visualisation directe

Deployment policy : Allow redeploy

Blob store : default
"""

# Create

# [OK] Repository créé !
# URL : http://localhost:8081/repository/raw-hosted/


"""
PROXY REPOSITORY (Rare)
──────────────────────

Peut proxifier site web/CDN
"""

# Create repository -> raw (proxy)

"""
Name : raw-proxy-cdn

Remote storage : https://cdn.example.com/

Use cases :
- Mirror CDN interne
- Cache assets externes
"""


"""
GROUP REPOSITORY
───────────────
"""

# Create repository -> raw (group)

"""
Name : raw-all

Members :
  [x] raw-hosted
  [x] raw-proxy-cdn (si existe)
"""


# ----------------------------------------------------------------------------
# [SORTIE] UPLOAD FICHIERS
# ----------------------------------------------------------------------------

"""
MÉTHODE 1 : VIA UI
─────────────────
"""

# Browse -> raw-hosted -> Upload component

"""
Directory : /scripts/
   Chemin virtuel dans repository
   Exemple : /scripts/, /configs/, /assets/images/

Filename : install.sh
   Nom fichier

Asset :
   Sélectionner fichier local
"""

# Upload

# [OK] Accessible à :
# http://localhost:8081/repository/raw-hosted/scripts/install.sh


"""
MÉTHODE 2 : CURL
───────────────

Plus pratique pour automation
"""

# Upload fichier
curl -v -u admin:admin123 \
  --upload-file install.sh \
  http://localhost:8081/repository/raw-hosted/scripts/install.sh

"""
[IDEE] FORMAT URL

http://localhost:8081/repository/<repo-name>/<path>/<filename>

Exemples :
/repository/raw-hosted/scripts/deploy.sh
/repository/raw-hosted/configs/nginx/default.conf
/repository/raw-hosted/assets/images/logo.png
/repository/raw-hosted/backups/db-2024-01-15.sql
"""


"""
UPLOAD MULTIPLE FICHIERS
────────────────────────
"""

# Script bash
for file in scripts/*.sh; do
  filename=$(basename "$file")
  curl -u admin:admin123 \
    --upload-file "$file" \
    "http://localhost:8081/repository/raw-hosted/scripts/$filename"
done

# Ou upload directory entier (Python)
cat > upload_dir.py << 'EOF'
#!/usr/bin/env python3
import os
import requests
from pathlib import Path

NEXUS_URL = "http://localhost:8081/repository/raw-hosted"
USERNAME = "admin"
PASSWORD = "admin123"

def upload_directory(local_dir, remote_path=""):
    for root, dirs, files in os.walk(local_dir):
        for file in files:
            local_file = os.path.join(root, file)
            relative_path = os.path.relpath(local_file, local_dir)
            remote_file = f"{NEXUS_URL}/{remote_path}/{relative_path}".replace("\\", "/")
            
            print(f"Uploading {local_file} to {remote_file}")
            
            with open(local_file, 'rb') as f:
                response = requests.put(
                    remote_file,
                    data=f,
                    auth=(USERNAME, PASSWORD)
                )
                
            if response.status_code == 201:
                print(f"[OK] Success")
            else:
                print(f"[X] Failed: {response.status_code}")

if __name__ == "__main__":
    upload_directory("./my-scripts", "scripts")
EOF

chmod +x upload_dir.py
./upload_dir.py


"""
UPLOAD AVEC METADATA
───────────────────

Headers custom :
"""

curl -u admin:admin123 \
  -H "Content-Type: application/x-sh" \
  --upload-file install.sh \
  http://localhost:8081/repository/raw-hosted/scripts/install.sh


# ----------------------------------------------------------------------------
# [ENTREE] TÉLÉCHARGER FICHIERS
# ----------------------------------------------------------------------------

"""
WGET
────
"""

# Télécharger fichier
wget http://localhost:8081/repository/raw-hosted/scripts/install.sh

# Avec authentification
wget --user=admin --password=admin123 \
  http://localhost:8081/repository/raw-hosted/scripts/install.sh


"""
CURL
────
"""

# Télécharger et sauvegarder
curl -o install.sh \
  http://localhost:8081/repository/raw-hosted/scripts/install.sh

# Avec auth
curl -u admin:admin123 -o install.sh \
  http://localhost:8081/repository/raw-hosted/scripts/install.sh

# Pipe directement (scripts)
curl http://localhost:8081/repository/raw-hosted/scripts/install.sh | bash


"""
NAVIGATEUR
─────────

Directement dans browser :
"""

http://localhost:8081/repository/raw-hosted/assets/images/logo.png
http://localhost:8081/repository/raw-hosted/docs/manual.pdf

"""
[IDEE] CONTENT DISPOSITION

Inline : Affiche dans browser
Attachment : Force download
"""


"""
API REST
───────
"""

# Lister fichiers dans path
curl -u admin:admin123 \
  "http://localhost:8081/service/rest/v1/search?repository=raw-hosted"

# Search par chemin
curl -u admin:admin123 \
  "http://localhost:8081/service/rest/v1/search/assets?repository=raw-hosted&name=scripts/*"


# ----------------------------------------------------------------------------
# [DOSSIER] ORGANISATION FICHIERS
# ----------------------------------------------------------------------------

"""
STRUCTURE RECOMMANDÉE
────────────────────

raw-hosted/
├── scripts/
│   ├── bootstrap/
│   │   ├── install.sh
│   │   ├── setup.py
│   │   └── configure.ps1
│   ├── maintenance/
│   │   ├── backup.sh
│   │   └── cleanup.sh
│   └── deployment/
│       ├── deploy-prod.sh
│       └── rollback.sh
│
├── configs/
│   ├── nginx/
│   │   ├── default.conf
│   │   └── ssl.conf
│   ├── prometheus/
│   │   └── prometheus.yml
│   └── docker/
│       └── docker-compose.yml
│
├── assets/
│   ├── images/
│   │   ├── logo.png
│   │   └── banner.jpg
│   ├── fonts/
│   │   └── roboto.woff2
│   └── css/
│       └── main.css
│
├── binaries/
│   ├── linux/
│   │   └── mytool-v1.0.0-linux-amd64
│   ├── windows/
│   │   └── mytool-v1.0.0-win64.exe
│   └── macos/
│       └── mytool-v1.0.0-darwin-amd64
│
├── docs/
│   ├── manuals/
│   │   └── user-guide.pdf
│   └── diagrams/
│       └── architecture.png
│
└── backups/
    ├── 2024-01-15/
    │   └── database.sql
    └── 2024-01-16/
        └── database.sql


[IDEE] CONVENTIONS

1. Slash final pour directories (dans URL)
2. Lowercase pour paths
3. Hyphens pour espaces
4. Versioning dans nom fichier si pertinent
"""


# ----------------------------------------------------------------------------
# [IDEE] CAS D'USAGE PRATIQUES
# ----------------------------------------------------------------------------

"""
1. BOOTSTRAP SCRIPTS
───────────────────

Installation automatisée serveurs
"""

# upload bootstrap script
cat > bootstrap.sh << 'EOF'
#!/bin/bash
set -e

echo "[RAPIDE] Bootstrapping server..."

# Install Docker
curl -fsSL https://get.docker.com | sh

# Install Nexus CLI
curl -o /usr/local/bin/nexus-cli \
  http://nexus.company.com:8081/repository/raw-hosted/binaries/nexus-cli
chmod +x /usr/local/bin/nexus-cli

# Download configs
mkdir -p /etc/myapp
curl -o /etc/myapp/config.yml \
  http://nexus.company.com:8081/repository/raw-hosted/configs/myapp/production.yml

echo "[OK] Bootstrap complete!"
EOF

curl -u admin:admin123 \
  --upload-file bootstrap.sh \
  http://localhost:8081/repository/raw-hosted/scripts/bootstrap.sh

# Sur nouveau serveur
curl http://nexus.company.com:8081/repository/raw-hosted/scripts/bootstrap.sh | sudo bash


"""
2. CONFIGURATION MANAGEMENT
──────────────────────────

Configurations centralisées
"""

# Upload configs
curl -u admin:admin123 \
  --upload-file nginx.conf \
  http://localhost:8081/repository/raw-hosted/configs/nginx/production.conf

curl -u admin:admin123 \
  --upload-file prometheus.yml \
  http://localhost:8081/repository/raw-hosted/configs/prometheus/prod.yml

# Sur serveurs
curl -o /etc/nginx/nginx.conf \
  http://nexus.company.com:8081/repository/raw-hosted/configs/nginx/production.conf

curl -o /etc/prometheus/prometheus.yml \
  http://nexus.company.com:8081/repository/raw-hosted/configs/prometheus/prod.yml

sudo systemctl reload nginx
sudo systemctl reload prometheus


"""
3. CDN INTERNE
─────────────

Assets web centralisés
"""

# Upload assets
curl -u admin:admin123 \
  --upload-file logo.png \
  http://localhost:8081/repository/raw-hosted/assets/images/logo.png

curl -u admin:admin123 \
  --upload-file main.css \
  http://localhost:8081/repository/raw-hosted/assets/css/main.css

# Dans HTML
"""
<img src="http://nexus.company.com:8081/repository/raw-hosted/assets/images/logo.png">
<link rel="stylesheet" href="http://nexus.company.com:8081/repository/raw-hosted/assets/css/main.css">
"""


"""
4. BINARIES DISTRIBUTION
───────────────────────

CLI tools internes
"""

# Build et upload
go build -o mycli-linux-amd64 main.go

curl -u admin:admin123 \
  --upload-file mycli-linux-amd64 \
  http://localhost:8081/repository/raw-hosted/binaries/mycli/v1.0.0/linux-amd64

# Installation users
curl -L -o /usr/local/bin/mycli \
  http://nexus.company.com:8081/repository/raw-hosted/binaries/mycli/v1.0.0/linux-amd64
chmod +x /usr/local/bin/mycli


"""
5. BACKUP STORAGE
────────────────

Backups centralisés
"""

# Backup script
cat > backup.sh << 'EOF'
#!/bin/bash
DATE=$(date +%Y-%m-%d)
BACKUP_FILE="database-$DATE.sql"

# Dump database
pg_dump mydb > "$BACKUP_FILE"

# Upload to Nexus
curl -u admin:admin123 \
  --upload-file "$BACKUP_FILE" \
  "http://localhost:8081/repository/raw-hosted/backups/$DATE/$BACKUP_FILE"

# Cleanup local
rm "$BACKUP_FILE"

echo "[OK] Backup uploaded: $DATE/$BACKUP_FILE"
EOF

# Cron daily
# 0 2 * * * /path/to/backup.sh


# ----------------------------------------------------------------------------
# [COURS] EXERCICE PRATIQUE 12 : RAW REPOSITORY COMPLET
# ----------------------------------------------------------------------------

"""
OBJECTIF : Système de déploiement avec scripts


SCÉNARIO :

Scripts bootstrap et configuration pour serveurs


ÉTAPES :

1. Créer structure fichiers localement
   deploy/
   ├── bootstrap.sh
   ├── configs/
   │   ├── nginx.conf
   │   └── app.env
   └── scripts/
       ├── deploy.sh
       └── rollback.sh

2. Bootstrap script
   [ ] Install Docker
   [ ] Install Docker Compose
   [ ] Download configs from Nexus

3. Deploy script
   [ ] Pull Docker images
   [ ] Start containers
   [ ] Health check

4. Upload vers Nexus
   [ ] curl upload bootstrap
   [ ] curl upload configs
   [ ] curl upload scripts

5. Test sur serveur vierge
   [ ] curl bootstrap | bash
   [ ] Vérifier installation

6. Versionner
   [ ] v1.0.0 directory
   [ ] latest symlink (si possible)


VALIDATION :

[OK] Tous fichiers uploadés
[OK] Bootstrap téléchargeable
[OK] Installation fonctionne
[OK] Configs appliquées
[OK] Versioning en place


TEMPS ESTIMÉ : 45-60 minutes
"""


# ----------------------------------------------------------------------------
# [DOCS] RÉCAPITULATIF CHAPITRE 12
# ----------------------------------------------------------------------------

"""
CE QUE VOUS AVEZ APPRIS

[OK] Raw repository (hosted, proxy, group)
[OK] Upload via UI et curl
[OK] Organisation fichiers (paths)
[OK] Téléchargement (wget, curl, browser)
[OK] Cas d'usage pratiques
[OK] Automation avec scripts


[CLE] POINTS CLÉS

1. Raw = Polyvalent
   N'importe quel type fichier

2. URL = repo + path + filename
   /repository/raw-hosted/scripts/file.sh

3. Content Disposition
   Inline vs Attachment

4. curl --upload-file
   Upload simple et efficace

5. Organisation importante
   Structure logique par type/usage


[OBJECTIF] BONNES PRATIQUES

[OK] Structure paths cohérente
[OK] Versioning dans chemins (binaries)
[OK] README dans chaque directory
[OK] Cleanup automatique vieux backups
[OK] Permissions appropriées
[OK] HTTPS en production


-> PROCHAINE ÉTAPE : Chapitre 13 - Autres Formats !

Formats spécialisés :
- NuGet (.NET)
- Helm (Kubernetes)
- APT/YUM (Linux packages)
- Et plus !

Découvrons la diversité ! [RAINBOW]
"""


# ============================================================================
# [GUIDE] CHAPITRE 13 : AUTRES FORMATS (NuGet, Helm, APT, YUM)
# ============================================================================

"""
[OBJECTIF] OBJECTIFS D'APPRENTISSAGE

À la fin de ce chapitre, vous saurez :
[OK] Configurer repository NuGet (.NET)
[OK] Configurer repository Helm (Kubernetes)
[OK] Configurer repositories APT/YUM (Linux)
[OK] Autres formats supportés
[OK] Choisir bon format
"""


# ----------------------------------------------------------------------------
# [PACKAGE] NUGET (.NET PACKAGES)
# ----------------------------------------------------------------------------

"""
CRÉER REPOSITORY NUGET
─────────────────────
"""

# HOSTED
# Create repository -> nuget (hosted)

"""
Name : nuget-hosted
Deployment policy : Allow redeploy
"""

# PROXY (nuget.org)
# Create repository -> nuget (proxy)

"""
Name : nuget-proxy
Remote storage : https://api.nuget.org/v3/index.json
"""

# GROUP
# Create repository -> nuget (group)

"""
Name : nuget-all
Members :
  [x] nuget-hosted
  [x] nuget-proxy
"""


"""
CONFIGURER NUGET CLIENT
──────────────────────
"""

# Ajouter source
nuget sources Add \
  -Name "Nexus" \
  -Source "http://localhost:8081/repository/nuget-all/" \
  -Username admin \
  -Password admin123

# Ou éditer NuGet.Config
# Windows : %AppData%\NuGet\NuGet.Config
# Linux/Mac : ~/.config/NuGet/NuGet.Config

"""
<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <packageSources>
    <add key="Nexus" value="http://localhost:8081/repository/nuget-all/" />
  </packageSources>
  <packageSourceCredentials>
    <Nexus>
      <add key="Username" value="admin" />
      <add key="ClearTextPassword" value="admin123" />
    </Nexus>
  </packageSourceCredentials>
</configuration>
"""


"""
INSTALLER PACKAGES
─────────────────
"""

# .NET CLI
dotnet add package Newtonsoft.Json

# NuGet CLI
nuget install Newtonsoft.Json


"""
PUBLIER PACKAGE
──────────────
"""

# Build
dotnet pack -c Release

# Publish
dotnet nuget push MyPackage.1.0.0.nupkg \
  --source http://localhost:8081/repository/nuget-hosted/ \
  --api-key admin:admin123


# ----------------------------------------------------------------------------
# [SAILBOAT] HELM (KUBERNETES CHARTS)
# ----------------------------------------------------------------------------

"""
CRÉER REPOSITORY HELM
────────────────────
"""

# HOSTED
# Create repository -> helm (hosted)

"""
Name : helm-hosted
Deployment policy : Allow redeploy
"""

# PROXY (Artifact Hub)
# Create repository -> helm (proxy)

"""
Name : helm-proxy
Remote storage : https://charts.helm.sh/stable
"""


"""
CONFIGURER HELM CLIENT
─────────────────────
"""

# Ajouter repository
helm repo add nexus-hosted http://localhost:8081/repository/helm-hosted/ \
  --username admin \
  --password admin123

helm repo add nexus-proxy http://localhost:8081/repository/helm-proxy/

# Update
helm repo update


"""
INSTALLER CHART
──────────────
"""

# Depuis proxy (public charts)
helm install my-release nexus-proxy/nginx

# Depuis hosted (private charts)
helm install my-app nexus-hosted/my-chart


"""
PUBLIER CHART
────────────
"""

# Créer chart
helm create my-chart

# Package
helm package my-chart

# Upload
curl -u admin:admin123 \
  --upload-file my-chart-0.1.0.tgz \
  http://localhost:8081/repository/helm-hosted/

# Ou avec plugin
helm plugin install https://github.com/chartmuseum/helm-push
helm push my-chart-0.1.0.tgz nexus-hosted


# ----------------------------------------------------------------------------
# [LINUX] APT (DEBIAN/UBUNTU PACKAGES)
# ----------------------------------------------------------------------------

"""
CRÉER REPOSITORY APT
───────────────────
"""

# HOSTED
# Create repository -> apt (hosted)

"""
Name : apt-hosted

Distribution : focal
   Ubuntu version (focal, jammy, etc.)

Signing :
  Keypair : (générer ou importer)
  Passphrase : <passphrase>
"""


"""
CONFIGURER APT CLIENT
────────────────────
"""

# Ajouter repository
echo "deb http://localhost:8081/repository/apt-hosted/ focal main" | \
  sudo tee /etc/apt/sources.list.d/nexus.list

# Ajouter GPG key
wget -qO - http://localhost:8081/repository/apt-hosted/gpg.key | \
  sudo apt-key add -

# Update
sudo apt update


"""
INSTALLER PACKAGE
────────────────
"""

sudo apt install my-package


"""
PUBLIER PACKAGE
──────────────

(Plus complexe, nécessite build .deb)
"""

# Build .deb package
dpkg-deb --build my-package

# Upload
curl -u admin:admin123 \
  -H "Content-Type: multipart/form-data" \
  --data-binary "@./my-package.deb" \
  http://localhost:8081/repository/apt-hosted/


# ----------------------------------------------------------------------------
# [TOP_HAT] YUM (RHEL/CENTOS PACKAGES)
# ----------------------------------------------------------------------------

"""
CRÉER REPOSITORY YUM
───────────────────
"""

# HOSTED
# Create repository -> yum (hosted)

"""
Name : yum-hosted

Deployment policy : Allow redeploy

Depth : 0
   Directory depth (0 = flat)
"""


"""
CONFIGURER YUM CLIENT
────────────────────
"""

# Créer repo file
# /etc/yum.repos.d/nexus.repo

"""
[nexus]
name=Nexus Repository
baseurl=http://localhost:8081/repository/yum-hosted/
enabled=1
gpgcheck=0
username=admin
password=admin123
"""

# Update cache
sudo yum makecache


"""
INSTALLER PACKAGE
────────────────
"""

sudo yum install my-package


"""
PUBLIER PACKAGE
──────────────

(Nécessite .rpm package)
"""

# Upload RPM
curl -u admin:admin123 \
  --upload-file my-package.rpm \
  http://localhost:8081/repository/yum-hosted/my-package.rpm


# ----------------------------------------------------------------------------
# [WEB] AUTRES FORMATS SUPPORTÉS
# ----------------------------------------------------------------------------

"""
FORMATS ADDITIONNELS
───────────────────

Nexus supporte 20+ formats :


WEB/FRONTEND
───────────
- npm : JavaScript packages
- bower : Frontend packages (obsolète)
- rubygems : Ruby gems


LANGAGES COMPILATION
───────────────────
- maven2 : Java/JVM artifacts
- gradle : Build system
- nuget : .NET packages
- go : Go modules
- pypi : Python packages


CONTAINERS
─────────
- docker : Docker images
- helm : Kubernetes charts


SYSTÈME
──────
- apt : Debian/Ubuntu packages
- yum : RedHat/CentOS packages
- raw : Generic files


MOBILE
─────
- cocoapods : iOS dependencies
- conan : C/C++ packages


AUTRES
─────
- gitlfs : Git Large File Storage
- r : R packages
- p2 : Eclipse plugins
- conda : Anaconda packages


[IDEE] VOIR TOUS FORMATS

Create repository -> Voir liste complète
"""


# ----------------------------------------------------------------------------
# [OBJECTIF] CHOISIR BON FORMAT
# ----------------------------------------------------------------------------

"""
DECISION TREE
────────────

Java/Kotlin/Scala project ?
  -> maven2

JavaScript/TypeScript/Node ?
  -> npm

Python project ?
  -> pypi

.NET/C# project ?
  -> nuget

Docker containers ?
  -> docker

Kubernetes charts ?
  -> helm

Linux system packages ?
  -> apt (Debian/Ubuntu)
  -> yum (RHEL/CentOS)

Scripts/configs/assets ?
  -> raw

Autre ?
  -> Vérifier formats supportés
  -> Si absent : raw (fallback)


COMBINAISONS COURANTES
─────────────────────

Full-stack Web App :
- npm (frontend)
- maven2/pypi (backend)
- docker (containers)
- helm (kubernetes)

Microservices :
- maven2 (Java services)
- docker (images)
- helm (déploiements)
- raw (scripts)

DevOps Infrastructure :
- raw (scripts Terraform, Ansible)
- docker (tools images)
- helm (charts)
- apt/yum (agent installation)
"""


# ----------------------------------------------------------------------------
# [DOCS] RÉCAPITULATIF CHAPITRE 13 ET PARTIE 3
# ----------------------------------------------------------------------------

"""
CE QUE VOUS AVEZ APPRIS - CHAPITRE 13

[OK] Repository NuGet (.NET)
[OK] Repository Helm (Kubernetes)
[OK] Repositories APT/YUM (Linux)
[OK] 20+ formats supportés
[OK] Choix format approprié


[BRAVO] FÉLICITATIONS ! PARTIE 3 TERMINÉE !
═════════════════════════════════════

Vous maîtrisez maintenant :

Chapitre 9 : Docker Registry
[OK] Repositories Docker (hosted, proxy, group)
[OK] Push/Pull images
[OK] Tags et versioning
[OK] SSL/TLS configuration
[OK] Cleanup images

Chapitre 10 : npm Registry
[OK] Packages npm privés
[OK] Scopes organisations (@company/*)
[OK] Configuration .npmrc
[OK] Publishing et versioning

Chapitre 11 : PyPI Repository
[OK] Packages Python
[OK] Configuration pip.conf
[OK] Build et Twine publishing
[OK] Virtual environments

Chapitre 12 : Raw Repository
[OK] Fichiers génériques
[OK] Scripts, configs, assets
[OK] Upload/download automation
[OK] Cas d'usage pratiques

Chapitre 13 : Autres Formats
[OK] NuGet (.NET)
[OK] Helm (Kubernetes)
[OK] APT/YUM (Linux)
[OK] 20+ formats supportés


[CLE] POINTS CLÉS GLOBAUX PARTIE 3

1. Format par technologie
   Chaque stack -> Format dédié

2. Proxy = Cache externe
   Économie bande passante

3. Hosted = Privé
   Vos artifacts internes

4. Group = Agrégation
   Point d'accès unique

5. Raw = Fallback universel
   Tout ce qui n'a pas format dédié


[OBJECTIF] BONNES PRATIQUES GLOBALES

[OK] Group repositories pour clients
[OK] Versioning sémantique partout
[OK] Cleanup policies automatiques
[OK] HTTPS en production
[OK] Credentials sécurisés (tokens)
[OK] Documentation README
[OK] CI/CD intégration


-> PROCHAINE ÉTAPE : PARTIE 4 !

Administration et Production :

Chapitre 14 : Backup et Restauration
Chapitre 15 : Monitoring et Logs
Chapitre 16 : Sécurité Avancée
Chapitre 17 : Performance Tuning
Chapitre 18 : CI/CD Intégration
Chapitre 19 : High Availability
Chapitre 20 : Best Practices Production


[GUIDE] FICHIER SUIVANT : nexus_partie4.txt

Administration professionnelle ! [RAPIDE]


═════════════════════════════════════════════════════════════
RÉSUMÉ COMPLET GUIDE NEXUS - PARTIES 1, 2 ET 3
═════════════════════════════════════════════════════════════

[LIVRE] PARTIE 1 : FONDAMENTAUX (4 chapitres, 6-8h)
  Chapitre 0 : Introduction
  Chapitre 1 : Installation
  Chapitre 2 : Repositories
  Chapitre 3 : Artifacts
  Chapitre 4 : Sécurité

[LIVRE] PARTIE 2 : INTÉGRATION BUILD TOOLS (4 chapitres, 6-8h)
  Chapitre 5 : Maven
  Chapitre 6 : Gradle
  Chapitre 7 : Proxy et Cache
  Chapitre 8 : Snapshots vs Releases

[LIVRE] PARTIE 3 : FORMATS AVANCÉS (5 chapitres, 8-10h)
  Chapitre 9 : Docker Registry
  Chapitre 10 : npm Registry
  Chapitre 11 : PyPI Python
  Chapitre 12 : Raw Repository
  Chapitre 13 : Autres Formats

TOTAL : 13 chapitres, 20-26 heures de lecture ! [DOCS]

-> CONTINUEZ AVEC PARTIE 4 pour maîtriser la production ! [FORCE]
═════════════════════════════════════════════════════════════
"""


# ============================================================================
# FIN DE NEXUS_PARTIE3.TXT
# ============================================================================
# ============================================================================
# [LIVRE] NEXUS REPOSITORY MANAGER - PARTIE 4
# ADMINISTRATION ET PRODUCTION
# ============================================================================
#
# [OBJECTIF] CETTE PARTIE COUVRE :
# - Chapitre 14 : Backup et Restauration
# - Chapitre 15 : Monitoring et Logs
# - Chapitre 16 : Sécurité Avancée
# - Chapitre 17 : Performance Tuning et Optimisation
#
# [TEMPS] TEMPS : ~8-10 heures
# [DOCS] PRÉREQUIS : Parties 1, 2 et 3 complétées
# [OBJECTIF] PUBLIC : DevOps, SysAdmins, Architectes
# ============================================================================


# ============================================================================
# [GUIDE] CHAPITRE 14 : BACKUP ET RESTAURATION
# ============================================================================

"""
[OBJECTIF] OBJECTIFS D'APPRENTISSAGE

À la fin de ce chapitre, vous saurez :
[OK] Identifier données critiques Nexus
[OK] Stratégies de backup (complètes et incrémentielles)
[OK] Backup automatisé (scripts, cron)
[OK] Backup base de données
[OK] Backup blob stores
[OK] Backup configuration
[OK] Restauration complète
[OK] Disaster Recovery Plan
[OK] Tests de restauration
"""


# ----------------------------------------------------------------------------
# [SAUVEGARDE] DONNÉES À SAUVEGARDER
# ----------------------------------------------------------------------------

"""
STRUCTURE DONNÉES NEXUS
──────────────────────

Nexus stocke données dans plusieurs locations :

/opt/sonatype-work/nexus3/  (ou /nexus-data/ si Docker)
├── db/                      # Base de données OrientDB
│   ├── component/           # Métadonnées components
│   ├── config/              # Configuration Nexus
│   ├── security/            # Users, roles, tokens
│   └── ...
├── blobs/                   # Blob stores (artifacts)
│   ├── default/             # Blob store par défaut
│   │   ├── content/         # Artifacts réels
│   │   └── metadata/
│   ├── docker/              # Blob store Docker
│   └── ...
├── elasticsearch/           # Index recherche
├── log/                     # Logs Nexus
├── tmp/                     # Temporaire
└── etc/                     # Configurations additionnelles


[IDEE] PRIORITÉS BACKUP

CRITIQUE (obligatoire) :
[OK] db/ - Base de données (métadonnées)
[OK] blobs/ - Artifacts (plusieurs GB/TB)
[OK] etc/nexus.properties - Configuration système

IMPORTANT :
[OK] keystores/ - Certificats SSL
[OK] backup/ - Backups internes Nexus

OPTIONNEL :
[ATTENTION] elasticsearch/ - Régénérable (rebuild index)
[ATTENTION] log/ - Historique (si conservation requise)
[X] tmp/ - Pas nécessaire
"""


# ----------------------------------------------------------------------------
# [OBJECTIF] STRATÉGIES DE BACKUP
# ----------------------------------------------------------------------------

"""
STRATÉGIE 1 : FULL BACKUP (Complet)
───────────────────────────────────

Copie complète de /nexus-data/

Avantages :
[OK] Simple à implémenter
[OK] Restauration rapide
[OK] Tout inclus

Inconvénients :
[X] Très volumineux (TB)
[X] Temps long
[X] Bande passante élevée


STRATÉGIE 2 : INCREMENTAL BACKUP
────────────────────────────────

Backup uniquement changements depuis dernier backup

Avantages :
[OK] Rapide
[OK] Moins d'espace
[OK] Plusieurs sauvegardes possibles

Inconvénients :
[X] Restauration plus complexe
[X] Nécessite tous backups chaîne


STRATÉGIE 3 : DIFFERENTIAL BACKUP
─────────────────────────────────

Backup changements depuis dernier FULL

Avantages :
[OK] Plus rapide que full
[OK] Restauration simple (full + dernier diff)

Inconvénients :
[X] Croît avec le temps


STRATÉGIE RECOMMANDÉE : HYBRIDE
───────────────────────────────

Full backup : Hebdomadaire (dimanche)
Incremental : Quotidien (lundi-samedi)

Exemple planning :
"""
Dimanche   : FULL backup        (100 GB)
Lundi      : Incremental        (5 GB)
Mardi      : Incremental        (7 GB)
Mercredi   : Incremental        (6 GB)
Jeudi      : Incremental        (8 GB)
Vendredi   : Incremental        (10 GB)
Samedi     : Incremental        (9 GB)
─────────────────────────────────────────
Total semaine : 145 GB vs 700 GB (7× full)


# ----------------------------------------------------------------------------
# [OUTILS] BACKUP MANUEL
# ----------------------------------------------------------------------------

"""
MÉTHODE 1 : BACKUP COMPLET BASIQUE
──────────────────────────────────

Arrêter Nexus -> Copier données -> Redémarrer
"""

# 1. Arrêter Nexus
# Docker
docker stop nexus

# Systemd
sudo systemctl stop nexus

# 2. Backup données
DATE=$(date +%Y-%m-%d)
BACKUP_DIR="/backup/nexus-$DATE"

mkdir -p "$BACKUP_DIR"

# Copier tout /nexus-data
cp -r /opt/sonatype-work/nexus3/* "$BACKUP_DIR/"

# Ou avec rsync (plus efficace)
rsync -av --progress /opt/sonatype-work/nexus3/ "$BACKUP_DIR/"

# 3. Compresser (optionnel mais recommandé)
cd /backup
tar -czf nexus-$DATE.tar.gz nexus-$DATE/

# 4. Redémarrer Nexus
# Docker
docker start nexus

# Systemd
sudo systemctl start nexus

"""
[ATTENTION] INCONVÉNIENTS MÉTHODE

[X] Downtime Nexus pendant backup
[X] Pas acceptable en production
"""


"""
MÉTHODE 2 : BACKUP À CHAUD (Avec Nexus running)
───────────────────────────────────────────────

Utiliser task Nexus "Export databases for backup"
"""

# 1. Créer task backup dans Nexus UI
# System -> Tasks -> Create task

"""
Type : Admin - Export databases for backup

Location : /nexus-data/backup
   Dossier où exporter DB

Schedule : Cron
   0 2 * * *  (tous les jours 2h du matin)
"""

# 2. Cette task exporte :
# - db/ -> /nexus-data/backup/db-backup-TIMESTAMP/

# 3. Script backup externe (copier export)

#!/bin/bash
# backup-nexus.sh

set -e

DATE=$(date +%Y-%m-%d-%H%M%S)
NEXUS_DATA="/opt/sonatype-work/nexus3"
BACKUP_BASE="/backup/nexus"
BACKUP_DIR="$BACKUP_BASE/$DATE"

echo "[RAPIDE] Starting Nexus backup: $DATE"

mkdir -p "$BACKUP_DIR"

# 1. Copier dernière export DB
echo "[PACKAGE] Backing up database..."
LATEST_DB_BACKUP=$(ls -td $NEXUS_DATA/backup/db-backup-* | head -1)
if [ -z "$LATEST_DB_BACKUP" ]; then
    echo "[X] No database backup found!"
    exit 1
fi
cp -r "$LATEST_DB_BACKUP" "$BACKUP_DIR/db"

# 2. Copier blobs (artifacts)
echo "[PACKAGE] Backing up blobs..."
rsync -av --progress "$NEXUS_DATA/blobs/" "$BACKUP_DIR/blobs/"

# 3. Copier configuration
echo "[PACKAGE] Backing up configuration..."
cp -r "$NEXUS_DATA/etc" "$BACKUP_DIR/"

# 4. Compresser
echo "[COMPRESSION]  Compressing backup..."
cd "$BACKUP_BASE"
tar -czf "nexus-backup-$DATE.tar.gz" "$DATE/"

# 5. Supprimer dossier non compressé
rm -rf "$DATE"

# 6. Cleanup vieux backups (garder 7 jours)
echo "[NETTOYAGE] Cleaning old backups..."
find "$BACKUP_BASE" -name "nexus-backup-*.tar.gz" -mtime +7 -delete

echo "[OK] Backup completed: nexus-backup-$DATE.tar.gz"

# Taille backup
du -h "$BACKUP_BASE/nexus-backup-$DATE.tar.gz"

# 7. Optionnel : Upload vers S3/Cloud
# aws s3 cp "$BACKUP_BASE/nexus-backup-$DATE.tar.gz" s3://my-backups/nexus/


"""
RENDRE SCRIPT EXÉCUTABLE
───────────────────────
"""

chmod +x backup-nexus.sh


"""
AUTOMATISER AVEC CRON
────────────────────
"""

# Éditer crontab
crontab -e

# Ajouter ligne (tous les jours 3h)
0 3 * * * /opt/scripts/backup-nexus.sh >> /var/log/nexus-backup.log 2>&1


# ----------------------------------------------------------------------------
# [CLOUD] BACKUP CLOUD (S3, Azure, GCS)
# ----------------------------------------------------------------------------

"""
BACKUP VERS AWS S3
─────────────────

Ajouter à script backup :
"""

# Installation AWS CLI
pip install awscli --break-system-packages

# Configuration
aws configure
# AWS Access Key ID: AKIAIOSFODNN7EXAMPLE
# AWS Secret Access Key: wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
# Default region name: us-east-1

# Dans script backup
aws s3 cp "$BACKUP_BASE/nexus-backup-$DATE.tar.gz" \
    s3://my-company-backups/nexus/ \
    --storage-class STANDARD_IA

# Lifecycle policy S3 (optionnel)
# Transition vers Glacier après 30 jours
# Delete après 90 jours


"""
BACKUP VERS AZURE BLOB
─────────────────────
"""

# Installation Azure CLI
pip install azure-cli --break-system-packages

# Login
az login

# Upload
az storage blob upload \
    --account-name mycompanystorage \
    --container-name nexus-backups \
    --name "nexus-backup-$DATE.tar.gz" \
    --file "$BACKUP_BASE/nexus-backup-$DATE.tar.gz"


"""
BACKUP VERS GOOGLE CLOUD STORAGE
────────────────────────────────
"""

# Installation gsutil
pip install gsutil --break-system-packages

# Configuration
gcloud init

# Upload
gsutil cp "$BACKUP_BASE/nexus-backup-$DATE.tar.gz" \
    gs://my-company-backups/nexus/


# ----------------------------------------------------------------------------
# [SYNC] RESTAURATION
# ----------------------------------------------------------------------------

"""
RESTAURATION COMPLÈTE
────────────────────

Scénario : Serveur Nexus perdu, restaurer depuis backup
"""

#!/bin/bash
# restore-nexus.sh

set -e

BACKUP_FILE="$1"
NEXUS_DATA="/opt/sonatype-work/nexus3"

if [ -z "$BACKUP_FILE" ]; then
    echo "Usage: $0 <backup-file.tar.gz>"
    exit 1
fi

echo "[SYNC] Starting Nexus restoration from: $BACKUP_FILE"

# 1. Arrêter Nexus
echo "[STOP] Stopping Nexus..."
sudo systemctl stop nexus

# 2. Backup données actuelles (sécurité)
if [ -d "$NEXUS_DATA" ]; then
    echo "[SAUVEGARDE] Backing up current data..."
    mv "$NEXUS_DATA" "${NEXUS_DATA}.old-$(date +%Y%m%d-%H%M%S)"
fi

# 3. Extraire backup
echo "[PACKAGE] Extracting backup..."
mkdir -p "$NEXUS_DATA"
tar -xzf "$BACKUP_FILE" -C /tmp/
BACKUP_DIR=$(tar -tzf "$BACKUP_FILE" | head -1 | cut -f1 -d"/")
rsync -av "/tmp/$BACKUP_DIR/" "$NEXUS_DATA/"

# 4. Permissions
echo "[SECURISE] Setting permissions..."
sudo chown -R nexus:nexus "$NEXUS_DATA"

# 5. Redémarrer Nexus
echo "[RAPIDE] Starting Nexus..."
sudo systemctl start nexus

# 6. Attendre démarrage
echo "[HOURGLASS_WITH_FLOWING_SAND] Waiting for Nexus to start..."
sleep 30

# 7. Vérifier
if curl -s http://localhost:8081 > /dev/null; then
    echo "[OK] Nexus restoration completed successfully!"
else
    echo "[X] Nexus failed to start!"
    exit 1
fi

# 8. Cleanup
rm -rf "/tmp/$BACKUP_DIR"

echo "[NOTE] Check logs: tail -f /opt/sonatype-work/nexus3/log/nexus.log"


"""
RESTAURATION DEPUIS CLOUD
─────────────────────────
"""

# Depuis S3
aws s3 cp s3://my-company-backups/nexus/nexus-backup-2024-01-15.tar.gz /tmp/

# Puis restaurer
./restore-nexus.sh /tmp/nexus-backup-2024-01-15.tar.gz


"""
RESTAURATION PARTIELLE (Base de données seulement)
──────────────────────────────────────────────────

Si seulement DB corrompue (blobs OK)
"""

# 1. Arrêter Nexus
sudo systemctl stop nexus

# 2. Sauvegarder DB actuelle
mv /opt/sonatype-work/nexus3/db /opt/sonatype-work/nexus3/db.corrupted

# 3. Extraire DB depuis backup
tar -xzf nexus-backup-2024-01-15.tar.gz
cp -r nexus-backup-2024-01-15/db /opt/sonatype-work/nexus3/

# 4. Permissions
sudo chown -R nexus:nexus /opt/sonatype-work/nexus3/db

# 5. Redémarrer
sudo systemctl start nexus


# ----------------------------------------------------------------------------
# [TEST] TESTER RESTAURATION
# ----------------------------------------------------------------------------

"""
TEST RESTAURATION (Critique!)
─────────────────────────────

[ATTENTION] Ne jamais supposer que backup fonctionne !
Tester régulièrement (mensuel ou trimestriel)
"""

#!/bin/bash
# test-restore.sh

# Plan de test :

# 1. Créer serveur test (VM, container)
docker run -d --name nexus-test \
    -p 8082:8081 \
    -v /tmp/nexus-test-data:/nexus-data \
    sonatype/nexus3

# 2. Attendre démarrage
sleep 60

# 3. Arrêter
docker stop nexus-test

# 4. Restaurer backup dans container test
tar -xzf /backup/nexus-backup-latest.tar.gz -C /tmp/
cp -r /tmp/nexus-backup-*/db /tmp/nexus-test-data/
cp -r /tmp/nexus-backup-*/blobs /tmp/nexus-test-data/

# 5. Redémarrer
docker start nexus-test

# 6. Tests fonctionnels
sleep 60

# Test 1 : UI accessible
if curl -s http://localhost:8082 | grep -q "Nexus"; then
    echo "[OK] Test 1: UI accessible"
else
    echo "[X] Test 1: UI failed"
fi

# Test 2 : Login
curl -u admin:admin123 http://localhost:8082/service/rest/v1/status
if [ $? -eq 0 ]; then
    echo "[OK] Test 2: Login works"
else
    echo "[X] Test 2: Login failed"
fi

# Test 3 : Repositories présents
REPOS=$(curl -s -u admin:admin123 http://localhost:8082/service/rest/v1/repositories | jq '. | length')
if [ "$REPOS" -gt 0 ]; then
    echo "[OK] Test 3: $REPOS repositories found"
else
    echo "[X] Test 3: No repositories"
fi

# Test 4 : Artifacts accessibles
# (Adapter selon vos artifacts)
curl -s http://localhost:8082/repository/maven-public/org/springframework/spring-core/5.3.0/spring-core-5.3.0.pom
if [ $? -eq 0 ]; then
    echo "[OK] Test 4: Artifacts accessible"
else
    echo "[X] Test 4: Artifacts not found"
fi

# Cleanup
docker stop nexus-test
docker rm nexus-test
rm -rf /tmp/nexus-test-data


"""
DOCUMENTATION TEST
─────────────────

Créer rapport test :
"""

# test-restore-report-2024-01-15.md
"""
# Nexus Restore Test Report

**Date**: 2024-01-15
**Backup tested**: nexus-backup-2024-01-14.tar.gz
**Backup size**: 45 GB
**Test environment**: Docker container (8 CPU, 16GB RAM)

## Test Results

| Test | Description | Status | Duration |
|------|-------------|--------|----------|
| 1 | Extract backup | [OK] Pass | 3m 24s |
| 2 | Start Nexus | [OK] Pass | 1m 45s |
| 3 | UI accessible | [OK] Pass | 5s |
| 4 | Authentication | [OK] Pass | 2s |
| 5 | Repositories count | [OK] Pass (12 repos) | 3s |
| 6 | Maven artifacts | [OK] Pass | 8s |
| 7 | Docker images | [OK] Pass | 12s |
| 8 | npm packages | [OK] Pass | 6s |

## RTO Actual: 5m 24s
**Target RTO**: 15 minutes [OK]

## Observations
- Restore completed faster than expected
- All repositories intact
- Artifacts accessible
- No errors in logs

## Recommendations
- Current backup strategy adequate
- Next test: 2024-04-15

**Tested by**: John Doe (DevOps)
**Approved by**: Jane Smith (CTO)
"""


# ----------------------------------------------------------------------------
# [LISTE] DISASTER RECOVERY PLAN (DRP)
# ----------------------------------------------------------------------------

"""
PLAN DE REPRISE D'ACTIVITÉ
─────────────────────────

Document formel définissant procédures
"""

# drp-nexus.md
"""
# Disaster Recovery Plan - Nexus Repository Manager

**Version**: 1.2
**Last Updated**: 2024-01-15
**Owner**: DevOps Team

## Objectifs

- **RTO** (Recovery Time Objective): 30 minutes
- **RPO** (Recovery Point Objective): 24 hours

RTO = Temps max downtime acceptable
RPO = Perte de données max acceptable


## Scénarios Disaster

### Scénario 1: Serveur Nexus inaccessible
**Probabilité**: Moyenne
**Impact**: Critique

**Actions**:
1. Vérifier infrastructure (réseau, VM, Docker)
2. Vérifier logs Nexus
3. Redémarrer service
4. Si échec -> Scénario 2

**Responsable**: On-call Engineer
**ETA**: 15 minutes


### Scénario 2: Corruption base de données
**Probabilité**: Faible
**Impact**: Critique

**Actions**:
1. Arrêter Nexus
2. Sauvegarder DB corrompue (/db -> /db.corrupted)
3. Restaurer DB depuis backup
4. Redémarrer Nexus
5. Vérifier intégrité

**Responsable**: Senior DevOps
**ETA**: 45 minutes
**Script**: /opt/scripts/restore-db.sh


### Scénario 3: Perte complète serveur
**Probabilité**: Très faible
**Impact**: Catastrophique

**Actions**:
1. Provisionner nouveau serveur
2. Installer Nexus
3. Restaurer backup complet
4. Mettre à jour DNS
5. Tester fonctionnalités
6. Communiquer aux équipes

**Responsable**: DevOps Lead + Infra Team
**ETA**: 2-4 hours
**Script**: /opt/scripts/restore-nexus.sh


### Scénario 4: Blob store corrompu
**Probabilité**: Faible
**Impact**: Élevé (certains artifacts perdus)

**Actions**:
1. Identifier blobs corrompus (checksums)
2. Restaurer blobs depuis backup
3. Rebuild metadata
4. Re-index artifacts

**Responsable**: Senior DevOps
**ETA**: 3-6 hours


## Procédures Détaillées

### Procédure: Restauration Complète

**Prérequis**:
- Backup récent disponible
- Serveur de remplacement prêt
- Accès root/admin

**Étapes**:

1. **Préparation** (5 min)
   - Télécharger dernier backup depuis S3
   - Vérifier checksum backup
   - Préparer serveur cible

2. **Installation Nexus** (10 min)
   ```bash
   # Docker
   docker run -d --name nexus \
     -p 8081:8081 \
     -v /opt/nexus-data:/nexus-data \
     sonatype/nexus3
   ```

3. **Restauration** (15 min)
   ```bash
   docker stop nexus
   tar -xzf nexus-backup.tar.gz
   rsync -av backup-data/ /opt/nexus-data/
   chown -R 200:200 /opt/nexus-data
   docker start nexus
   ```

4. **Validation** (10 min)
   - UI accessible
   - Login fonctionne
   - Repositories présents
   - Artifacts accessibles
   - Tests CI/CD

5. **Communication** (5 min)
   - Notifier équipes (Slack, email)
   - Mettre à jour status page

**Total ETA**: 45 minutes


## Contacts Urgence

| Rôle | Nom | Téléphone | Email |
|------|-----|-----------|-------|
| DevOps Lead | John Doe | +221 77 123 4567 | john@company.com |
| On-call Engineer | Rotating | +221 77 000 0000 | oncall@company.com |
| Infrastructure | Jane Smith | +221 76 999 8888 | jane@company.com |
| Management | Bob Johnson | +221 70 555 5555 | bob@company.com |


## Escalation

1. **Level 1** (0-15 min): On-call Engineer
   - Diagnostic initial
   - Actions correctives simples

2. **Level 2** (15-30 min): DevOps Lead
   - Si Level 1 échoue
   - Restauration DB/services

3. **Level 3** (30+ min): DevOps Lead + Infra Team
   - Restauration complète
   - Infrastructure provisioning

4. **Level 4** (Critical): + Management
   - Communication externe
   - Décisions business


## Backups

**Localisation**:
- **Local**: /backup/nexus/ (7 jours retention)
- **S3**: s3://company-backups/nexus/ (90 jours)
- **Glacier**: Archive après 30 jours

**Fréquence**:
- **Full backup**: Dimanche 2h00
- **Incremental**: Lundi-Samedi 2h00
- **DB export**: Quotidien 1h00 (task Nexus)

**Taille moyenne**: 45 GB (compressé)

**Vérification**:
- **Restore test**: Mensuel (15 de chaque mois)
- **Integrity check**: Hebdomadaire


## Post-Mortem

Après chaque incident :
1. Rédiger rapport incident
2. Identifier root cause
3. Actions préventives
4. Mise à jour DRP
5. Partage équipe (lessons learned)

Template: /docs/incident-report-template.md
"""


# ----------------------------------------------------------------------------
# [COURS] EXERCICE PRATIQUE 14 : BACKUP/RESTORE COMPLET
# ----------------------------------------------------------------------------

"""
OBJECTIF : Implémenter stratégie backup et tester restauration


SCÉNARIO :

Production Nexus avec backups automatisés


ÉTAPES :

1. Analyser données Nexus
   [ ] Identifier taille db/
   [ ] Identifier taille blobs/
   [ ] Estimer taille backup

2. Créer script backup
   [ ] backup-nexus.sh
   [ ] Export DB via task
   [ ] Copier db + blobs + etc
   [ ] Compression tar.gz
   [ ] Cleanup vieux backups

3. Tester script
   [ ] Exécuter manuellement
   [ ] Vérifier backup créé
   [ ] Vérifier taille

4. Automatiser avec cron
   [ ] Backup quotidien 2h
   [ ] Logs dans /var/log/

5. Backup cloud (optionnel)
   [ ] Configurer AWS CLI / Azure CLI
   [ ] Upload vers S3 / Blob Storage
   [ ] Vérifier dans console cloud

6. Script restauration
   [ ] restore-nexus.sh
   [ ] Prend backup en paramètre
   [ ] Extrait et copie données
   [ ] Redémarre Nexus

7. Test restauration
   [ ] Créer environnement test (VM/container)
   [ ] Restaurer backup
   [ ] Valider fonctionnalités
   [ ] Documenter résultats

8. Documentation DRP
   [ ] Créer drp-nexus.md
   [ ] Définir RTO/RPO
   [ ] Procédures scénarios
   [ ] Contacts urgence


VALIDATION :

[OK] Script backup fonctionne
[OK] Backup automatisé quotidien
[OK] Backup cloud (si configuré)
[OK] Restauration testée et validée
[OK] DRP documenté
[OK] Équipe formée aux procédures


TEMPS ESTIMÉ : 2-3 heures
"""


# ----------------------------------------------------------------------------
# [DOCS] RÉCAPITULATIF CHAPITRE 14
# ----------------------------------------------------------------------------

"""
CE QUE VOUS AVEZ APPRIS

[OK] Identifier données critiques Nexus
[OK] Stratégies backup (full, incremental, differential)
[OK] Backup manuel et automatisé
[OK] Script backup complet
[OK] Backup vers cloud (S3, Azure, GCS)
[OK] Restauration complète et partielle
[OK] Tests restauration réguliers
[OK] Disaster Recovery Plan (DRP)


[CLE] POINTS CLÉS

1. Backups = Assurance vie
   Tester régulièrement !

2. Stratégie hybride
   Full hebdo + incremental quotidien

3. 3-2-1 Rule
   3 copies, 2 médias, 1 offsite

4. RTO et RPO
   Définir objectifs réalistes

5. DRP documenté
   Procédures claires et testées


[OBJECTIF] BONNES PRATIQUES

[OK] Backup automatisé quotidien
[OK] Stockage offsite (cloud)
[OK] Tests restauration mensuels
[OK] Documentation procédures
[OK] Monitoring backups
[OK] Alertes si backup échoue
[OK] Retention policy (7j local, 90j cloud)
[OK] Encryption backups sensibles


-> PROCHAINE ÉTAPE : Chapitre 15 - Monitoring !

Vous allez apprendre :
- Metrics et KPIs
- Logs analysis
- Alerting
- Dashboards
- Health checks

Surveillons Nexus ! [GRAPHIQUE]
"""


# ============================================================================
# [GUIDE] CHAPITRE 15 : MONITORING ET LOGS
# ============================================================================

"""
[OBJECTIF] OBJECTIFS D'APPRENTISSAGE

À la fin de ce chapitre, vous saurez :
[OK] Identifier métriques critiques
[OK] Analyser logs Nexus
[OK] Configurer monitoring (Prometheus)
[OK] Créer dashboards (Grafana)
[OK] Configurer alertes
[OK] Health checks
[OK] Performance monitoring
[OK] Troubleshooting via logs
"""


# ----------------------------------------------------------------------------
# [GRAPHIQUE] MÉTRIQUES CRITIQUES
# ----------------------------------------------------------------------------

"""
KPIs NEXUS À MONITORER
─────────────────────

DISPONIBILITÉ
- Uptime (%) : 99.9% target
- Temps réponse UI (ms) : <200ms
- Temps réponse API (ms) : <100ms

PERFORMANCE
- Throughput downloads (MB/s)
- Throughput uploads (MB/s)
- Nombre requêtes/seconde
- Latence moyenne (ms)

RESOURCES
- CPU usage (%)
- Memory usage (MB / %)
- Disk usage (GB / %)
- Disk I/O (read/write MB/s)
- Network I/O (MB/s)

APPLICATIONS
- Nombre repositories
- Nombre components
- Nombre assets
- Taille blob stores (GB)
- Cache hit ratio (%)

ERRORS
- HTTP errors 4xx (rate)
- HTTP errors 5xx (rate)
- Failed uploads
- Failed downloads
- Database errors


[IDEE] THRESHOLDS ALERTES

[ATTENTION] WARNING :
- CPU > 70%
- Memory > 80%
- Disk > 85%
- Response time > 500ms

[ALERTE] CRITICAL :
- CPU > 90%
- Memory > 95%
- Disk > 95%
- Response time > 2000ms
- Uptime < 99%
"""


# ----------------------------------------------------------------------------
# [NOTE] LOGS NEXUS
# ----------------------------------------------------------------------------

"""
FICHIERS LOGS
────────────

/opt/sonatype-work/nexus3/log/
├── nexus.log              # Log principal
├── request.log            # Requêtes HTTP
├── audit.log              # Actions audit
├── tasks/                 # Logs tasks
│   ├── cleanup.log
│   └── ...
└── karaf.log              # OSGi container


NEXUS.LOG - LOG PRINCIPAL
────────────────────────
"""

# tail -f /opt/sonatype-work/nexus3/log/nexus.log

"""
2024-01-16 10:23:45,123+0000 INFO  [qtp123456789-123] *SYSTEM org.sonatype.nexus.repository - Repository 'maven-central' started
2024-01-16 10:23:46,234+0000 WARN  [qtp123456789-124] admin org.example - Slow request: GET /repository/maven-public/... (1234ms)
2024-01-16 10:23:47,345+0000 ERROR [qtp123456789-125] *SYSTEM org.sonatype.nexus.blobstore - Failed to access blob: IOException

Format :
[timestamp] [level] [thread] [user] [logger] - [message]
"""


"""
REQUEST.LOG - REQUÊTES HTTP
──────────────────────────
"""

# tail -f /opt/sonatype-work/nexus3/log/request.log

"""
192.168.1.100 - admin [16/Jan/2024:10:23:45 +0000] "GET /repository/maven-public/org/springframework/spring-core/5.3.0/spring-core-5.3.0.jar HTTP/1.1" 200 1234567 1234
192.168.1.101 - - [16/Jan/2024:10:23:46 +0000] "GET /repository/docker-hosted/v2/my-app/manifests/latest HTTP/1.1" 200 5678 89

Format Apache combined :
IP - user [timestamp] "method URI protocol" status bytes duration_ms
"""


"""
AUDIT.LOG - ACTIONS AUDIT
────────────────────────
"""

# tail -f /opt/sonatype-work/nexus3/log/audit.log

"""
2024-01-16T10:23:45.123+00:00 admin 192.168.1.100 CREATE repository.repository [maven-releases]
2024-01-16T10:24:10.456+00:00 admin 192.168.1.100 UPDATE security.user [john.doe]
2024-01-16T10:25:30.789+00:00 jenkins 192.168.1.200 CREATE component [com.example:my-app:1.0.0]

Actions tracées :
- CREATE/UPDATE/DELETE repositories
- CREATE/UPDATE/DELETE users
- CREATE/DELETE components
- Configuration changes
"""


"""
LOG LEVELS
─────────

TRACE : Très détaillé (debug profond)
DEBUG : Informations debug
INFO  : Informations normales
WARN  : Avertissements (attention requise)
ERROR : Erreurs (nécessite action)
"""


"""
CONFIGURER LOG LEVEL
───────────────────
"""

# Nexus UI -> System -> Logging

"""
Logger : org.sonatype.nexus
Level : INFO (par défaut)

Pour debug :
Logger : org.sonatype.nexus.repository
Level : DEBUG

[ATTENTION] DEBUG très verbeux !
Utiliser temporairement seulement
"""

# Ou éditer logback.xml
# /opt/sonatype-work/nexus3/etc/logback/logback.xml

<logger name="org.sonatype.nexus.repository" level="DEBUG"/>


"""
ANALYSER LOGS
────────────

Grep patterns utiles :
"""

# Erreurs
grep ERROR nexus.log

# Erreurs dernière heure
grep ERROR nexus.log | grep "$(date +%Y-%m-%d\ %H)"

# Slow requests (>1000ms)
awk '$NF>1000' request.log

# Top 10 IPs
awk '{print $1}' request.log | sort | uniq -c | sort -rn | head -10

# Top 10 URIs
awk '{print $7}' request.log | sort | uniq -c | sort -rn | head -10

# Errors 5xx
grep " 5[0-9][0-9] " request.log

# User actions
grep "admin" audit.log


"""
ROTATION LOGS
────────────

Configuration logback.xml :
"""

<appender name="logfile" class="ch.qos.logback.core.rolling.RollingFileAppender">
  <file>${karaf.data}/log/nexus.log</file>
  <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
    <fileNamePattern>${karaf.data}/log/nexus-%d{yyyy-MM-dd}.log.gz</fileNamePattern>
    <maxHistory>90</maxHistory>
  </rollingPolicy>
</appender>

"""
[IDEE] ROTATION

- Daily rotation
- Compression automatique (.gz)
- Rétention 90 jours
"""


# ----------------------------------------------------------------------------
# [HAUSSE] MONITORING AVEC PROMETHEUS
# ----------------------------------------------------------------------------

"""
ARCHITECTURE MONITORING
──────────────────────

┌─────────────┐
│    Nexus    │
│  (metrics)  │
└──────┬──────┘
       │ scrape (:8081/metrics)
       v
┌─────────────┐
│ Prometheus  │
│  (storage)  │
└──────┬──────┘
       │ query
       v
┌─────────────┐
│   Grafana   │
│ (dashboard) │
└─────────────┘


ACTIVER METRICS NEXUS (Nexus Pro)
─────────────────────────────────

[ATTENTION] Metrics endpoint = Nexus Pro uniquement !

Nexus OSS : Pas de /metrics natif
Solutions :
1. JMX exporter
2. Logs parsing
3. API monitoring
"""


"""
SOLUTION OSS : JMX EXPORTER
──────────────────────────
"""

# 1. Télécharger JMX Exporter
wget https://repo1.maven.org/maven2/io/prometheus/jmx/jmx_prometheus_javaagent/0.19.0/jmx_prometheus_javaagent-0.19.0.jar -O /opt/jmx_exporter.jar

# 2. Configuration JMX
cat > /opt/jmx_exporter_config.yml << 'EOF'
---
lowercaseOutputName: true
lowercaseOutputLabelNames: true
whitelistObjectNames:
  - "java.lang:*"
  - "java.nio:*"
  - "org.eclipse.jetty.server:*"
rules:
  - pattern: 'java.lang<type=(\w+)><>(\w+):'
    name: java_lang_$1_$2
  - pattern: 'java.nio<type=BufferPool, name=(\w+)><>(\w+)'
    name: java_nio_bufferpool_$2
    labels:
      pool: "$1"
EOF

# 3. Ajouter à Nexus startup
# Éditer nexus.vmoptions ou INSTALL4J_ADD_VM_PARAMS

-javaagent:/opt/jmx_exporter.jar=9090:/opt/jmx_exporter_config.yml

# Docker
docker run -d \
  -e INSTALL4J_ADD_VM_PARAMS="-javaagent:/opt/jmx_exporter.jar=9090:/opt/jmx_exporter_config.yml" \
  -v /opt/jmx_exporter.jar:/opt/jmx_exporter.jar \
  -v /opt/jmx_exporter_config.yml:/opt/jmx_exporter_config.yml \
  -p 9090:9090 \
  sonatype/nexus3

# 4. Vérifier metrics
curl http://localhost:9090/metrics

"""
Output exemple :
# HELP jvm_memory_bytes_used Used bytes of a given JVM memory area.
# TYPE jvm_memory_bytes_used gauge
jvm_memory_bytes_used{area="heap",} 1.234567e+09
jvm_memory_bytes_used{area="nonheap",} 2.345678e+08
"""


"""
INSTALLER PROMETHEUS
───────────────────
"""

# Docker Compose
cat > docker-compose-monitoring.yml << 'EOF'
version: '3.8'

services:
  prometheus:
    image: prom/prometheus:latest
    container_name: prometheus
    ports:
      - "9091:9090"
    volumes:
      - ./prometheus.yml:/etc/prometheus/prometheus.yml
      - prometheus-data:/prometheus
    command:
      - '--config.file=/etc/prometheus/prometheus.yml'
      - '--storage.tsdb.path=/prometheus'
      - '--storage.tsdb.retention.time=90d'

  grafana:
    image: grafana/grafana:latest
    container_name: grafana
    ports:
      - "3000:3000"
    volumes:
      - grafana-data:/var/lib/grafana
    environment:
      - GF_SECURITY_ADMIN_PASSWORD=admin123
      - GF_USERS_ALLOW_SIGN_UP=false

volumes:
  prometheus-data:
  grafana-data:
EOF

# Configuration Prometheus
cat > prometheus.yml << 'EOF'
global:
  scrape_interval: 15s
  evaluation_interval: 15s

scrape_configs:
  - job_name: 'nexus-jmx'
    static_configs:
      - targets: ['nexus-host:9090']
        labels:
          instance: 'nexus-prod'

  - job_name: 'nexus-api'
    metrics_path: '/service/rest/v1/status'
    static_configs:
      - targets: ['nexus-host:8081']
    basic_auth:
      username: 'monitoring-user'
      password: 'monitoring-password'
EOF

# Démarrer
docker-compose -f docker-compose-monitoring.yml up -d


"""
QUERIES PROMETHEUS UTILES
────────────────────────
"""

# CPU usage
rate(process_cpu_seconds_total[5m]) * 100

# Memory heap used
jvm_memory_bytes_used{area="heap"} / jvm_memory_bytes_max{area="heap"} * 100

# GC time
rate(jvm_gc_collection_seconds_sum[5m])

# Thread count
jvm_threads_current

# HTTP request rate
rate(jetty_requests_total[5m])

# HTTP request duration 95th percentile
histogram_quantile(0.95, rate(jetty_request_time_seconds_bucket[5m]))


# ----------------------------------------------------------------------------
# [GRAPHIQUE] DASHBOARDS GRAFANA
# ----------------------------------------------------------------------------

"""
CONFIGURER GRAFANA
─────────────────
"""

# 1. Accéder Grafana
# http://localhost:3000
# Login : admin / admin123

# 2. Ajouter data source
# Configuration -> Data Sources -> Add data source
# Type : Prometheus
# URL : http://prometheus:9090
# Save & Test

# 3. Importer dashboard
# Dashboards -> Import
# Dashboard ID: 1860 (Node Exporter Full)
# Ou créer custom


"""
DASHBOARD NEXUS CUSTOM
─────────────────────

Panels recommandés :
"""

# Panel 1 : Uptime
up{job="nexus-jmx"}

# Panel 2 : CPU Usage
rate(process_cpu_seconds_total{job="nexus-jmx"}[5m]) * 100

# Panel 3 : Memory Heap
jvm_memory_bytes_used{area="heap"} / 1024 / 1024 / 1024

# Panel 4 : GC Activity
rate(jvm_gc_collection_seconds_sum[5m])

# Panel 5 : Thread Count
jvm_threads_current

# Panel 6 : HTTP Requests Rate
rate(jetty_requests_total[5m])

# Panel 7 : HTTP Response Time (p95)
histogram_quantile(0.95, rate(jetty_request_time_seconds_bucket[5m]))

# Panel 8 : Disk Usage
# (nécessite node_exporter sur host Nexus)
(node_filesystem_size_bytes - node_filesystem_free_bytes) / node_filesystem_size_bytes * 100


"""
EXPORT/IMPORT DASHBOARD
──────────────────────
"""

# Export JSON
# Dashboard -> Settings -> JSON Model -> Copy

# Import
# Dashboards -> Import -> Paste JSON


# ----------------------------------------------------------------------------
# [ALERTE] ALERTING
# ----------------------------------------------------------------------------

"""
ALERTES PROMETHEUS
─────────────────
"""

# prometheus-alerts.yml
cat > prometheus-alerts.yml << 'EOF'
groups:
  - name: nexus
    interval: 30s
    rules:
      # High CPU
      - alert: NexusHighCPU
        expr: rate(process_cpu_seconds_total{job="nexus-jmx"}[5m]) * 100 > 80
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "Nexus high CPU usage"
          description: "Nexus CPU > 80% for 5 minutes (current: {{ $value }}%)"

      # High Memory
      - alert: NexusHighMemory
        expr: jvm_memory_bytes_used{area="heap"} / jvm_memory_bytes_max{area="heap"} * 100 > 90
        for: 5m
        labels:
          severity: critical
        annotations:
          summary: "Nexus high memory usage"
          description: "Nexus memory > 90% (current: {{ $value }}%)"

      # Down
      - alert: NexusDown
        expr: up{job="nexus-jmx"} == 0
        for: 1m
        labels:
          severity: critical
        annotations:
          summary: "Nexus is down"
          description: "Nexus has been down for 1 minute"

      # Slow Response
      - alert: NexusSlowResponse
        expr: histogram_quantile(0.95, rate(jetty_request_time_seconds_bucket[5m])) > 2
        for: 10m
        labels:
          severity: warning
        annotations:
          summary: "Nexus slow response time"
          description: "95th percentile response time > 2s (current: {{ $value }}s)"
EOF

# Ajouter à prometheus.yml
"""
rule_files:
  - "prometheus-alerts.yml"

alerting:
  alertmanagers:
    - static_configs:
        - targets: ['alertmanager:9093']
"""


"""
ALERTMANAGER (Notifications)
───────────────────────────
"""

# docker-compose-monitoring.yml (ajouter)
"""
  alertmanager:
    image: prom/alertmanager:latest
    container_name: alertmanager
    ports:
      - "9093:9093"
    volumes:
      - ./alertmanager.yml:/etc/alertmanager/alertmanager.yml
"""

# alertmanager.yml
cat > alertmanager.yml << 'EOF'
global:
  resolve_timeout: 5m

route:
  group_by: ['alertname', 'cluster']
  group_wait: 10s
  group_interval: 10s
  repeat_interval: 12h
  receiver: 'team-devops'

receivers:
  - name: 'team-devops'
    email_configs:
      - to: 'devops@company.com'
        from: 'alerts@company.com'
        smarthost: 'smtp.company.com:587'
        auth_username: 'alerts@company.com'
        auth_password: 'password'
    slack_configs:
      - api_url: 'https://hooks.slack.com/services/YOUR/WEBHOOK/URL'
        channel: '#alerts-nexus'
        title: '{{ .GroupLabels.alertname }}'
        text: '{{ range .Alerts }}{{ .Annotations.description }}{{ end }}'
EOF


"""
ALERTES GRAFANA
──────────────

Alternative/Complément Prometheus
"""

# Dans dashboard Grafana
# Panel -> Alert tab -> Create Alert

"""
Conditions:
WHEN avg() OF query(A, 5m, now) IS ABOVE 80

Notifications:
Send to: team-devops-email
Message: CPU usage above 80%
"""


# ----------------------------------------------------------------------------
# [COURS] EXERCICE PRATIQUE 15 : MONITORING COMPLET
# ----------------------------------------------------------------------------

"""
OBJECTIF : Stack monitoring complète Nexus


ÉTAPES :

1. Logs analysis
   [ ] Analyser nexus.log (grep ERROR)
   [ ] Top 10 IPs (request.log)
   [ ] Slow requests (>1000ms)

2. JMX Exporter
   [ ] Télécharger jmx_exporter.jar
   [ ] Configuration jmx_exporter_config.yml
   [ ] Ajouter à Nexus JVM
   [ ] Vérifier /metrics

3. Prometheus
   [ ] docker-compose Prometheus
   [ ] Configuration scrape Nexus
   [ ] Tester queries

4. Grafana
   [ ] Ajouter data source Prometheus
   [ ] Créer dashboard custom (8 panels)
   [ ] Test visualisations

5. Alerting
   [ ] Configurer règles alertes
   [ ] Alertmanager setup
   [ ] Test notification (Slack/Email)

6. Charger Nexus (test)
   [ ] Script téléchargement massif
   [ ] Observer métriques
   [ ] Vérifier alertes


VALIDATION :

[OK] Logs analysables
[OK] Metrics exposées (/metrics)
[OK] Prometheus scrape fonctionne
[OK] Dashboard Grafana opérationnel
[OK] Alertes configurées
[OK] Notifications reçues


TEMPS ESTIMÉ : 2-3 heures
"""


# ----------------------------------------------------------------------------
# [DOCS] RÉCAPITULATIF CHAPITRE 15
# ----------------------------------------------------------------------------

"""
CE QUE VOUS AVEZ APPRIS

[OK] Métriques critiques Nexus
[OK] Analyse logs (nexus, request, audit)
[OK] Log levels et rotation
[OK] JMX Exporter (Nexus OSS)
[OK] Prometheus configuration
[OK] Dashboards Grafana
[OK] Alerting (Prometheus + Alertmanager)
[OK] Notifications (Email, Slack)


[CLE] POINTS CLÉS

1. Logs = Première source debug
   nexus.log, request.log, audit.log

2. Metrics = Proactif
   Détecter problèmes avant users

3. Dashboards = Visibilité
   État système temps réel

4. Alertes = Réactivité
   Notifier équipe immédiatement

5. Retention = Balance
   90 jours logs, 90 jours metrics


[OBJECTIF] BONNES PRATIQUES

[OK] Monitoring 24/7
[OK] Dashboards accessibles équipe
[OK] Alertes graduées (warn/critical)
[OK] On-call rotation
[OK] Logs centralisés (ELK, Splunk)
[OK] Retention policy définie
[OK] Review métriques régulier


-> PROCHAINE ÉTAPE : Chapitre 16 - Sécurité Avancée !

Vous allez apprendre :
- Hardening Nexus
- SSL/TLS
- Firewall rules
- Audit et compliance
- Vulnerability scanning

Sécurisons Nexus ! [VERROUILLE]
"""


# ============================================================================
# FIN PARTIE 4 - CHAPITRES 14 ET 15
# CONTINUEZ AVEC CHAPITRES 16 ET 17...
# ============================================================================
# ============================================================================
# [LIVRE] NEXUS REPOSITORY MANAGER - PARTIE 4 (SUITE)
# CHAPITRES 16 ET 17
# ============================================================================


# ============================================================================
# [GUIDE] CHAPITRE 16 : SÉCURITÉ AVANCÉE
# ============================================================================

"""
[OBJECTIF] OBJECTIFS D'APPRENTISSAGE

À la fin de ce chapitre, vous saurez :
[OK] Hardening Nexus (durcissement sécurité)
[OK] Configurer SSL/TLS
[OK] Firewall rules
[OK] Network segmentation
[OK] Audit et compliance
[OK] Vulnerability scanning
[OK] Security headers
[OK] Intrusion detection
"""


# ----------------------------------------------------------------------------
# [VERROUILLE] HARDENING NEXUS (DURCISSEMENT)
# ----------------------------------------------------------------------------

"""
CHECKLIST SÉCURITÉ NEXUS
───────────────────────

[ ] 1. CHANGER PASSWORD PAR DÉFAUT
[ ] 2. DÉSACTIVER ANONYMOUS ACCESS (production)
[ ] 3. CONFIGURER HTTPS (SSL/TLS)
[ ] 4. FIREWALL RULES (ports minimaux)
[ ] 5. LDAP/SSO (authentification centralisée)
[ ] 6. ROLE-BASED ACCESS CONTROL (RBAC strict)
[ ] 7. API TOKENS (pas de passwords en clair)
[ ] 8. AUDIT LOGGING (activé)
[ ] 9. REGULAR UPDATES (patches sécurité)
[ ] 10. VULNERABILITY SCANNING
[ ] 11. BACKUP ENCRYPTÉ
[ ] 12. NETWORK SEGMENTATION
[ ] 13. SECURITY HEADERS
[ ] 14. RATE LIMITING
[ ] 15. INTRUSION DETECTION


ÉTAPE 1 : PASSWORD POLICY
─────────────────────────

[ATTENTION] Nexus OSS : Pas de password policy natif
Solutions :
1. LDAP/AD (policy externe)
2. Éducation utilisateurs
3. Password manager (recommandé)


Bonnes pratiques :
[OK] Minimum 12 caractères
[OK] Complexité (maj + min + chiffres + spéciaux)
[OK] Rotation 90 jours
[OK] Pas de réutilisation (5 derniers)
[OK] MFA si possible (via SAML/LDAP)


ÉTAPE 2 : DÉSACTIVER ANONYMOUS ACCESS
─────────────────────────────────────

Production = Toujours désactivé !
"""

# Nexus UI -> Security -> Anonymous Access
# [ ] Allow anonymous users to access the server

"""
Vérifier :
"""

# Test sans auth
curl http://nexus:8081/repository/maven-public/
# Expected : 401 Unauthorized

# Test avec auth
curl -u admin:admin123 http://nexus:8081/repository/maven-public/
# Expected : 200 OK


"""
ÉTAPE 3 : MINIMAL PRIVILEGES
────────────────────────────

Principe du moindre privilège appliqué
"""

# Exemple rôle Developer (read-only releases, write snapshots)
"""
Privileges :
[x] nx-search-read
[x] nx-repository-view-*-*-browse
[x] nx-repository-view-*-*-read
[x] nx-repository-view-maven2-*-snapshots-add
[x] nx-repository-view-maven2-*-snapshots-edit

[X] PAS nx-repository-admin (trop permissif)
[X] PAS nx-users (pas de gestion users)
"""


"""
ÉTAPE 4 : ROTATION API TOKENS
─────────────────────────────

Tokens = Expiration recommandée
"""

# Script auto-rotation tokens (exemple conceptuel)
"""
1. Générer nouveau token
2. Mettre à jour CI/CD avec nouveau token
3. Attendre période transition (24h)
4. Révoquer ancien token
"""


"""
ÉTAPE 5 : AUDIT LOGGING
──────────────────────

Activer audit complet :
"""

# System -> Capabilities -> Audit

"""
[x] Enable audit logging
[x] Log authentication events
[x] Log authorization events
[x] Log configuration changes
[x] Log component changes

Retention : 365 jours (compliance)
"""

# Vérifier audit.log
tail -f /opt/sonatype-work/nexus3/log/audit.log


"""
ÉTAPE 6 : REGULAR UPDATES
────────────────────────

[ATTENTION] Vulnérabilités corrigées dans nouvelles versions
"""

# Vérifier version actuelle
# Help -> About -> Version

# Comparer avec latest
# https://help.sonatype.com/repomanager3/product-information/release-notes

# Update process :
"""
1. Lire release notes (breaking changes ?)
2. Tester upgrade en staging
3. Backup production
4. Maintenance window
5. Upgrade production
6. Validation tests
7. Rollback plan ready
"""


# ----------------------------------------------------------------------------
# [SECURISE] SSL/TLS CONFIGURATION
# ----------------------------------------------------------------------------

"""
POURQUOI HTTPS ?
───────────────

[X] HTTP :
- Credentials en clair sur réseau
- MITM attacks possibles
- Données sensibles exposées

[OK] HTTPS :
- Encryption transport
- Authentification serveur
- Intégrité données


MÉTHODE 1 : REVERSE PROXY (RECOMMANDÉE)
───────────────────────────────────────

Nexus HTTP -> Nginx HTTPS -> Clients

Avantages :
[OK] Nexus simple (HTTP)
[OK] Nginx gère SSL (mature, optimisé)
[OK] Load balancing possible
[OK] Web Application Firewall (WAF) possible
"""

# Installation Nginx
sudo apt install nginx

# Configuration Nginx
# /etc/nginx/sites-available/nexus

upstream nexus {
    server localhost:8081;
}

server {
    listen 80;
    server_name nexus.company.com;
    
    # Redirect HTTP -> HTTPS
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl http2;
    server_name nexus.company.com;
    
    # SSL Certificates
    ssl_certificate /etc/ssl/certs/nexus.company.com.crt;
    ssl_certificate_key /etc/ssl/private/nexus.company.com.key;
    
    # SSL Configuration (Mozilla Intermediate)
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256...';
    ssl_prefer_server_ciphers on;
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 10m;
    
    # Security Headers
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-Frame-Options "DENY" always;
    add_header X-XSS-Protection "1; mode=block" always;
    
    # Large uploads (Docker layers, JARs)
    client_max_body_size 0;
    chunked_transfer_encoding on;
    
    # Timeouts
    proxy_read_timeout 300;
    proxy_connect_timeout 300;
    proxy_send_timeout 300;
    send_timeout 300;
    
    # Proxy to Nexus
    location / {
        proxy_pass http://nexus;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto "https";
        proxy_set_header X-Forwarded-Host $host;
    }
}

# Activer site
sudo ln -s /etc/nginx/sites-available/nexus /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx


"""
OBTENIR CERTIFICAT SSL
─────────────────────

Option 1 : Let's Encrypt (gratuit)
"""

# Installation Certbot
sudo apt install certbot python3-certbot-nginx

# Obtenir certificat
sudo certbot --nginx -d nexus.company.com

# Auto-renewal (cron automatique)
sudo certbot renew --dry-run

"""
Option 2 : Certificat commercial
Acheter chez CA (DigiCert, GlobalSign, etc.)

Option 3 : Certificat auto-signé (DEV ONLY!)
"""

# Générer certificat auto-signé
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
  -keyout /etc/ssl/private/nexus.key \
  -out /etc/ssl/certs/nexus.crt \
  -subj "/C=US/ST=State/L=City/O=Company/CN=nexus.company.com"


"""
MÉTHODE 2 : SSL DIRECT DANS NEXUS
─────────────────────────────────

Plus complexe, moins flexible
"""

# Créer keystore
keytool -genkeypair -keyalg RSA -alias jetty \
  -keystore /opt/sonatype-work/nexus3/etc/ssl/keystore.jks \
  -storepass changeit -keypass changeit \
  -validity 365 -keysize 2048 \
  -dname "CN=nexus.company.com, OU=IT, O=Company, L=City, ST=State, C=US"

# Éditer nexus.properties
# /opt/sonatype-work/nexus3/etc/nexus.properties

"""
application-port-ssl=8443
nexus-args=${jetty.etc}/jetty.xml,${jetty.etc}/jetty-https.xml
ssl.etc=${karaf.data}/etc/ssl
"""

# Éditer jetty-https.xml (configuration jetty)
# /opt/sonatype-work/nexus3/etc/jetty/jetty-https.xml

# Redémarrer Nexus
sudo systemctl restart nexus

# Accès : https://nexus.company.com:8443


# ----------------------------------------------------------------------------
# [HOT] FIREWALL CONFIGURATION
# ----------------------------------------------------------------------------

"""
PRINCIPE : FERMER TOUT, OUVRIR MINIMUM
─────────────────────────────────────

Ports nécessaires :
- 22 (SSH) : Administration seulement
- 443 (HTTPS) : Accès Nexus
- 8081 (HTTP) : Seulement depuis reverse proxy (localhost)
- 8082-8084 (Docker) : Si Docker registry

Ports à BLOQUER :
- 8081 direct depuis Internet (si reverse proxy)
- Tous autres ports
"""


"""
UFW (Ubuntu Firewall)
────────────────────
"""

# Installer
sudo apt install ufw

# Politique par défaut (DENY ALL)
sudo ufw default deny incoming
sudo ufw default allow outgoing

# SSH (administration)
sudo ufw allow from 192.168.1.0/24 to any port 22
# Ou spécifique : sudo ufw allow from 192.168.1.100 to any port 22

# HTTPS (public)
sudo ufw allow 443/tcp

# HTTP Nexus (localhost only - depuis Nginx)
sudo ufw allow from 127.0.0.1 to any port 8081

# Docker registries (si utilisés)
sudo ufw allow from 10.0.0.0/8 to any port 8082
sudo ufw allow from 10.0.0.0/8 to any port 8083
sudo ufw allow from 10.0.0.0/8 to any port 8084

# Activer
sudo ufw enable

# Vérifier
sudo ufw status verbose


"""
FIREWALLD (CentOS/RHEL)
──────────────────────
"""

# Services
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --permanent --add-service=ssh

# Ports custom
sudo firewall-cmd --permanent --add-port=8082/tcp
sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="10.0.0.0/8" port protocol="tcp" port="8082" accept'

# Reload
sudo firewall-cmd --reload

# Vérifier
sudo firewall-cmd --list-all


"""
IPTABLES (Avancé)
────────────────
"""

#!/bin/bash
# iptables-nexus.sh

# Flush existing rules
iptables -F
iptables -X
iptables -t nat -F
iptables -t nat -X
iptables -t mangle -F
iptables -t mangle -X

# Default policy : DROP
iptables -P INPUT DROP
iptables -P FORWARD DROP
iptables -P OUTPUT ACCEPT

# Loopback
iptables -A INPUT -i lo -j ACCEPT

# Established connections
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT

# SSH (admin network only)
iptables -A INPUT -p tcp -s 192.168.1.0/24 --dport 22 -j ACCEPT

# HTTPS (public)
iptables -A INPUT -p tcp --dport 443 -j ACCEPT

# HTTP Nexus (localhost only)
iptables -A INPUT -p tcp -s 127.0.0.1 --dport 8081 -j ACCEPT

# Docker registries (internal network)
iptables -A INPUT -p tcp -s 10.0.0.0/8 --dport 8082:8084 -j ACCEPT

# Drop invalid packets
iptables -A INPUT -m state --state INVALID -j DROP

# Rate limiting (DDoS protection)
iptables -A INPUT -p tcp --dport 443 -m state --state NEW -m recent --set
iptables -A INPUT -p tcp --dport 443 -m state --state NEW -m recent --update --seconds 60 --hitcount 20 -j DROP

# Log dropped packets
iptables -A INPUT -j LOG --log-prefix "IPTables-Dropped: "

# Save rules
iptables-save > /etc/iptables/rules.v4


# ----------------------------------------------------------------------------
# [WEB] NETWORK SEGMENTATION
# ----------------------------------------------------------------------------

"""
SEGMENTATION RÉSEAU NEXUS
────────────────────────

Zones réseau recommandées :
"""

┌─────────────────────────────────────────────────┐
│             DMZ / PUBLIC ZONE                   │
│  - Reverse Proxy (Nginx)                        │
│  - WAF                                           │
│  - Load Balancer                                │
└────────────┬────────────────────────────────────┘
             │ Firewall
             v
┌─────────────────────────────────────────────────┐
│         APPLICATION ZONE (Internal)             │
│  - Nexus Server (10.1.1.10)                     │
│  - Database (si externe)                        │
└────────────┬────────────────────────────────────┘
             │ Firewall
             v
┌─────────────────────────────────────────────────┐
│         MANAGEMENT ZONE (Restricted)            │
│  - Monitoring (Prometheus, Grafana)             │
│  - Log aggregation                              │
│  - Backup server                                │
└─────────────────────────────────────────────────┘

"""
RÈGLES FIREWALLS INTER-ZONES

Public -> DMZ :
  443 (HTTPS) : Allowed

DMZ -> Application :
  8081 (HTTP Nexus) : Allowed from Nginx IP only

Application -> Management :
  9090 (Prometheus metrics) : Allowed
  Syslog (514) : Allowed

Management -> Application :
  8081 (monitoring) : Allowed
  22 (SSH admin) : Allowed

Internet -> Management :
  DENY ALL
"""


# ----------------------------------------------------------------------------
# [LISTE] COMPLIANCE ET AUDIT
# ----------------------------------------------------------------------------

"""
CONFORMITÉ RÉGLEMENTAIRE
───────────────────────

Selon industrie, réglementations applicables :


SOC 2 (Services cloud)
─────────────────────
[x] Access controls (RBAC)
[x] Encryption in transit (HTTPS)
[x] Encryption at rest (backups)
[x] Audit logging
[x] Vulnerability management
[x] Incident response


HIPAA (Santé)
────────────
[x] PHI data encryption
[x] Access audit trails
[x] Authentication (MFA)
[x] Backup and disaster recovery
[x] Physical security


PCI-DSS (Paiements)
──────────────────
[x] Firewall configuration
[x] Strong passwords
[x] Data encryption
[x] Regular security testing
[x] Access monitoring


RGPD (Données personnelles EU)
──────────────────────────────
[x] Data encryption
[x] Access controls
[x] Audit trails (who accessed what)
[x] Data retention policies
[x] Right to be forgotten (delete users)


CONFIGURATION COMPLIANCE NEXUS
──────────────────────────────
"""

# 1. Activer audit logging complet
# System -> Capabilities -> Audit

# 2. Retention logs (365 jours minimum)
# logback.xml -> maxHistory = 365

# 3. Backup encrypté
# Script backup avec encryption
tar -czf - /nexus-data | openssl enc -aes-256-cbc -salt -out backup-encrypted.tar.gz.enc -k "encryption-password"

# 4. Access reviews réguliers
# Exporter liste users/roles mensuel
curl -u admin:admin123 http://localhost:8081/service/rest/v1/security/users > users-$(date +%Y-%m).json

# 5. Vulnerability scanning (voir section suivante)


"""
AUDIT REPORT AUTOMATISÉ
──────────────────────
"""

#!/bin/bash
# nexus-audit-report.sh

DATE=$(date +%Y-%m-%d)
REPORT_FILE="nexus-audit-report-$DATE.txt"

echo "=== NEXUS AUDIT REPORT ===" > $REPORT_FILE
echo "Date: $DATE" >> $REPORT_FILE
echo "" >> $REPORT_FILE

# 1. Users count
echo "=== USERS ===" >> $REPORT_FILE
curl -s -u admin:admin123 http://localhost:8081/service/rest/v1/security/users | jq '. | length' >> $REPORT_FILE

# 2. Active repositories
echo "=== REPOSITORIES ===" >> $REPORT_FILE
curl -s -u admin:admin123 http://localhost:8081/service/rest/v1/repositories | jq '. | length' >> $REPORT_FILE

# 3. Components count
echo "=== COMPONENTS ===" >> $REPORT_FILE
curl -s -u admin:admin123 "http://localhost:8081/service/rest/v1/search?repository=*" | jq '.items | length' >> $REPORT_FILE

# 4. Recent failed logins (audit.log)
echo "=== FAILED LOGINS (24h) ===" >> $REPORT_FILE
grep "AUTHENTICATION_FAILED" /opt/sonatype-work/nexus3/log/audit.log | grep $(date +%Y-%m-%d) | wc -l >> $REPORT_FILE

# 5. Configuration changes (7 days)
echo "=== CONFIG CHANGES (7d) ===" >> $REPORT_FILE
grep "UPDATE\|CREATE\|DELETE" /opt/sonatype-work/nexus3/log/audit.log | grep -E "$(date +%Y-%m-%d)|$(date -d '1 day ago' +%Y-%m-%d)" | wc -l >> $REPORT_FILE

# 6. SSL certificate expiry
echo "=== SSL CERTIFICATE ===" >> $REPORT_FILE
echo | openssl s_client -connect nexus.company.com:443 2>/dev/null | openssl x509 -noout -enddate >> $REPORT_FILE

echo "" >> $REPORT_FILE
echo "Report generated: $(date)" >> $REPORT_FILE

# Email report
mail -s "Nexus Audit Report $DATE" security@company.com < $REPORT_FILE


# ----------------------------------------------------------------------------
# [RECHERCHE] VULNERABILITY SCANNING
# ----------------------------------------------------------------------------

"""
SCANNER NEXUS LUI-MÊME
─────────────────────

Vérifier vulnérabilités Nexus application
"""

# Option 1 : Sonatype Lifecycle (PRO)
# Intégré dans Nexus Pro
# Scanne automatiquement components uploadés

# Option 2 : CVE Database check
# Vérifier version Nexus contre CVE database

# Check version
NEXUS_VERSION=$(curl -s http://localhost:8081/service/rest/v1/status | jq -r '.version')

# Search CVE
# https://nvd.nist.gov/vuln/search/results?query=nexus+repository+manager


"""
SCANNER DEPENDENCIES
───────────────────

Vérifier vulnérabilités dans artifacts stockés
"""

# Option 1 : OWASP Dependency Check
# https://github.com/jeremylong/DependencyCheck

# Download
wget https://github.com/jeremylong/DependencyCheck/releases/download/v9.0.0/dependency-check-9.0.0-release.zip
unzip dependency-check-9.0.0-release.zip

# Scan Maven repository
./dependency-check/bin/dependency-check.sh \
  --scan /opt/sonatype-work/nexus3/blobs/default/content \
  --format HTML \
  --out ./dependency-check-report

# Ouvrir rapport
firefox ./dependency-check-report/dependency-check-report.html


# Option 2 : Trivy (Docker images)
# https://github.com/aquasecurity/trivy

# Install
sudo apt install trivy

# Scan Docker image depuis Nexus
trivy image nexus.company.com:8082/my-app:latest

# Output :
"""
CRITICAL: 5
HIGH: 12
MEDIUM: 23
LOW: 45
"""


# Option 3 : Nexus IQ Server (PRO)
# Commercial product by Sonatype
# Deep component intelligence
# Policy enforcement
# License compliance


"""
AUTOMATED VULNERABILITY SCANNING
────────────────────────────────

Intégration CI/CD
"""

# .gitlab-ci.yml
vulnerability_scan:
  stage: security
  script:
    - trivy image --exit-code 1 --severity CRITICAL,HIGH $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
  allow_failure: false

# Jenkins pipeline
"""
stage('Security Scan') {
    steps {
        sh 'trivy image --exit-code 1 --severity CRITICAL ${DOCKER_IMAGE}'
    }
}
"""


# ----------------------------------------------------------------------------
# [COURS] EXERCICE PRATIQUE 16 : HARDENING COMPLET
# ----------------------------------------------------------------------------

"""
OBJECTIF : Sécuriser Nexus production-ready


ÉTAPES :

1. Audit sécurité initial
   [ ] Checklist 15 points
   [ ] Identifier faiblesses

2. Passwords et tokens
   [ ] Changer admin password
   [ ] Désactiver anonymous
   [ ] Créer users avec least privilege

3. SSL/TLS
   [ ] Configurer Nginx reverse proxy
   [ ] Obtenir certificat Let's Encrypt
   [ ] Tester HTTPS (ssllabs.com)

4. Firewall
   [ ] UFW configuration
   [ ] Fermer ports non nécessaires
   [ ] Rate limiting

5. Network segmentation
   [ ] Isoler Nexus (subnet dédié)
   [ ] Firewall rules inter-zones

6. Audit logging
   [ ] Activer audit complet
   [ ] Retention 365 jours
   [ ] Export logs centralisé

7. Vulnerability scanning
   [ ] Scan Nexus version (CVE)
   [ ] Scan sample artifact (OWASP)
   [ ] Scan Docker image (Trivy)

8. Compliance report
   [ ] Générer audit report
   [ ] Documenter configuration
   [ ] Review avec équipe sécurité


VALIDATION :

[OK] SSL/TLS A+ rating (ssllabs.com)
[OK] Firewall actif et configuré
[OK] Anonymous désactivé
[OK] Audit logs activés
[OK] Vulnerability scan clean
[OK] Compliance checklist complète


TEMPS ESTIMÉ : 3-4 heures
"""


# ----------------------------------------------------------------------------
# [DOCS] RÉCAPITULATIF CHAPITRE 16
# ----------------------------------------------------------------------------

"""
CE QUE VOUS AVEZ APPRIS

[OK] Hardening checklist (15 points)
[OK] SSL/TLS configuration (Nginx, Let's Encrypt)
[OK] Firewall rules (UFW, firewalld, iptables)
[OK] Network segmentation (zones DMZ/App/Mgmt)
[OK] Compliance (SOC2, HIPAA, PCI-DSS, RGPD)
[OK] Audit logging et reporting
[OK] Vulnerability scanning (OWASP, Trivy)


[CLE] POINTS CLÉS

1. Sécurité = Layers
   Multiple couches défense

2. HTTPS = Obligatoire production
   Reverse proxy recommandé

3. Firewall = Deny by default
   Ouvrir seulement nécessaire

4. Audit = Compliance
   Logs détaillés 365 jours

5. Vulnerability = Proactif
   Scan régulier (hebdomadaire)


[OBJECTIF] BONNES PRATIQUES

[OK] SSL/TLS avec certificat valide
[OK] Firewall restrictif
[OK] RBAC strict (least privilege)
[OK] Audit complet activé
[OK] Vulnerability scanning CI/CD
[OK] Regular security reviews
[OK] Incident response plan
[OK] Security training équipe


-> PROCHAINE ÉTAPE : Chapitre 17 - Performance !

Vous allez apprendre :
- Tuning JVM
- Database optimization
- Caching strategies
- Scaling horizontal
- Load testing

Optimisons Nexus ! [RAPIDE]
"""


# ============================================================================
# [GUIDE] CHAPITRE 17 : PERFORMANCE TUNING ET OPTIMISATION
# ============================================================================

"""
[OBJECTIF] OBJECTIFS D'APPRENTISSAGE

À la fin de ce chapitre, vous saurez :
[OK] Identifier goulots d'étranglement
[OK] Tuning JVM (heap, GC)
[OK] Optimiser base de données
[OK] Stratégies de caching
[OK] Optimiser blob stores
[OK] Load balancing
[OK] Scaling horizontal
[OK] Load testing
"""


# ----------------------------------------------------------------------------
# [RECHERCHE] IDENTIFIER PERFORMANCE BOTTLENECKS
# ----------------------------------------------------------------------------

"""
MÉTRIQUES PERFORMANCE CLÉS
─────────────────────────

Temps de réponse :
- UI : <200ms (bon), <500ms (acceptable), >1s (problème)
- API : <100ms (bon), <300ms (acceptable), >500ms (problème)
- Download : Limité par réseau (devrait saturer bande passante)

Throughput :
- Requêtes/seconde : Dépend hardware (100-1000 req/s typique)
- Downloads MB/s : Dépend réseau
- Concurrent users : 100+ simultané OK


OUTILS DIAGNOSTIC
────────────────

1. Nexus UI -> System -> Support -> System Information
   - Heap usage
   - Thread count
   - Request statistics

2. JMX Metrics (via JConsole, VisualVM)
   - GC activity
   - Memory pools
   - Thread dumps

3. Logs analysis
   - Slow requests (request.log)
   - Errors (nexus.log)

4. External monitoring
   - Prometheus + Grafana
   - APM tools (New Relic, Datadog)


SYMPTÔMES COURANTS
─────────────────

Symptôme : UI lente
Causes possibles :
- Heap memory insuffisante
- GC pauses longues
- Database slow queries
- Disk I/O saturé

Symptôme : Timeouts downloads
Causes possibles :
- Network congestion
- Blob store slow (HDD vs SSD)
- Proxy repository remote down

Symptôme : High CPU
Causes possibles :
- GC thrashing
- Indexing en cours
- Cleanup tasks running
- Trop de concurrent requests
"""


# ----------------------------------------------------------------------------
# [HOT_BEVERAGE] JVM TUNING
# ----------------------------------------------------------------------------

"""
CONFIGURATION JVM PAR DÉFAUT
───────────────────────────

Nexus par défaut :
-Xms2703m  (heap min)
-Xmx2703m  (heap max)
-XX:MaxDirectMemorySize=2703m  (direct memory)


RECOMMANDATIONS SELON CHARGE
────────────────────────────

Small deployment (10-50 users) :
-Xms4g
-Xmx4g
-XX:MaxDirectMemorySize=4g

Medium deployment (50-200 users) :
-Xms8g
-Xmx8g
-XX:MaxDirectMemorySize=8g

Large deployment (200-1000 users) :
-Xms16g
-Xmx16g
-XX:MaxDirectMemorySize=16g

Enterprise (1000+ users) :
-Xms32g
-Xmx32g
-XX:MaxDirectMemorySize=32g


[IDEE] RÈGLE : 50-75% RAM DISPONIBLE

Serveur 16GB RAM -> Nexus 8-12GB
Serveur 32GB RAM -> Nexus 16-24GB
Serveur 64GB RAM -> Nexus 32-48GB


APPLIQUER CONFIGURATION JVM
──────────────────────────
"""

# Docker
docker run -d \
  -e INSTALL4J_ADD_VM_PARAMS="-Xms8g -Xmx8g -XX:MaxDirectMemorySize=8g" \
  -p 8081:8081 \
  sonatype/nexus3

# Binaire Linux
# Éditer nexus.vmoptions
# /opt/sonatype-work/nexus3/bin/nexus.vmoptions

-Xms8g
-Xmx8g
-XX:MaxDirectMemorySize=8g

# Redémarrer
sudo systemctl restart nexus


"""
GARBAGE COLLECTOR (GC) TUNING
─────────────────────────────

GC par défaut : G1GC (Java 11+)

Options GC supplémentaires :
"""

# G1GC optimisé
-XX:+UseG1GC
-XX:MaxGCPauseMillis=200
-XX:G1ReservePercent=10
-XX:G1HeapRegionSize=32m

# Logs GC (diagnostic)
-Xlog:gc*:file=/opt/sonatype-work/nexus3/log/gc.log:time,uptime:filecount=5,filesize=10M


"""
MONITORER GC
───────────
"""

# Analyser gc.log
# Utiliser GCViewer ou GCEasy.io

# Métriques importantes :
# - GC pause time : <200ms idéal
# - GC frequency : Pas trop fréquent
# - Full GC : Éviter (signe heap insuffisant)


"""
HEAP DUMP EN CAS DE OOM
──────────────────────

Capturer heap dump si OutOfMemoryError :
"""

-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/opt/sonatype-work/nexus3/log

# Analyser heap dump avec Eclipse MAT ou VisualVM


# ----------------------------------------------------------------------------
# [SAUVEGARDE] DATABASE OPTIMIZATION
# ----------------------------------------------------------------------------

"""
NEXUS UTILISE OrientDB
─────────────────────

Base de données embedded (pas de tuning externe)

Optimisations possibles :


1. COMPACTION RÉGULIÈRE
──────────────────────

Compacter DB régulièrement :
"""

# Task Nexus : "Admin - Compact blobstore"
# System -> Tasks -> Create task
"""
Type : Admin - Compact blobstore
Blob store : default
Schedule : Weekly (Sunday 2:00 AM)
"""

"""
Effet :
- Récupère espace blobs supprimés
- Améliore performance read
"""


"""
2. REBUILD INDEX
───────────────

Si recherche lente :
"""

# Task : "Admin - Rebuild repository search"
# System -> Tasks -> Create task
"""
Type : Admin - Rebuild repository search
Repository : All repositories (or specific)
Schedule : Manual (or Monthly)
"""


"""
3. DATABASE BACKUP/RESTORE
─────────────────────────

Si DB corrompue ou lente :
1. Export DB (task "Export databases for backup")
2. Arrêter Nexus
3. Supprimer db/
4. Restaurer depuis export
5. Redémarrer Nexus
"""


# ----------------------------------------------------------------------------
# [RAPIDE] BLOB STORE OPTIMIZATION
# ----------------------------------------------------------------------------

"""
BLOB STORES = STOCKAGE ARTIFACTS

Performance dépend :
- Type disque (SSD >> HDD)
- I/O throughput
- Latence réseau (si cloud storage)


STRATÉGIE 1 : SSD POUR HOT DATA
───────────────────────────────

Séparer blobs par fréquence accès :
"""

# Blob store "hot" (SSD)
# Pour repositories fréquemment utilisés
# - maven-central (proxy)
# - npm-proxy
# - docker-hosted

# Blob store "cold" (HDD)
# Pour archives, vieux snapshots
# - maven-snapshots-archive
# - old-releases


"""
STRATÉGIE 2 : CLOUD STORAGE
───────────────────────────

Pour très gros volumes (TB) :
"""

# AWS S3 blob store
# System -> Blob Stores -> Create blob store
"""
Type : S3
Bucket : nexus-artifacts
Region : us-east-1
Access Key : AKIAIOSFODNN7EXAMPLE
Secret Key : wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY

[IDEE] S3 Tiers :
- Standard : Accès fréquent
- IA (Infrequent Access) : Accès rare, moins cher
- Glacier : Archive long terme, très peu cher
"""

# Lifecycle policy S3
"""
Transition après 30 jours : Standard -> IA
Transition après 90 jours : IA -> Glacier
"""


"""
STRATÉGIE 3 : MULTIPLE BLOB STORES
──────────────────────────────────

Répartir I/O sur plusieurs disques :
"""

# Blob store 1 : /data1/nexus-blobs (disk 1)
# Blob store 2 : /data2/nexus-blobs (disk 2)
# Blob store 3 : /data3/nexus-blobs (disk 3)

# Assigner repositories :
# maven-releases -> blob1
# maven-snapshots -> blob2
# docker-hosted -> blob3

"""
Effet : I/O parallèle, évite saturation 1 disque
"""


# ----------------------------------------------------------------------------
# [SYNC] CACHING STRATEGIES
# ----------------------------------------------------------------------------

"""
NIVEAUX DE CACHE NEXUS
─────────────────────

1. Client cache (Maven ~/.m2, Gradle ~/.gradle)
2. Nexus internal cache (memory)
3. Nexus proxy cache (blob store)


OPTIMISER PROXY CACHE
────────────────────

Configuration repository proxy :
"""

# Maximum component age : -1 (unlimited)
# Pour releases stables -> Cache permanent

# Maximum metadata age : -1
# Pour Maven metadata -> Cache permanent

# Negative cache : Enabled
# TTL : 1440 minutes (24h)
# Cache 404 responses -> Évite requêtes inutiles


"""
OPTIMISER METADATA
─────────────────

Maven metadata (maven-metadata.xml) :
- Releases : Cache permanent (-1)
- Snapshots : Cache court (5-60 minutes)

Raison : Releases immuables, snapshots changent
"""


"""
WARM CACHE (PRELOADING)
──────────────────────

Pré-charger artifacts populaires :
"""

#!/bin/bash
# warm-cache.sh

# Liste top 100 artifacts populaires
ARTIFACTS=(
    "org/springframework/spring-core/5.3.0/spring-core-5.3.0.jar"
    "com/google/guava/guava/31.0-jre/guava-31.0-jre.jar"
    # ... etc
)

for artifact in "${ARTIFACTS[@]}"; do
    curl -o /dev/null http://nexus:8081/repository/maven-public/$artifact
    echo "Cached: $artifact"
done


# ----------------------------------------------------------------------------
# [SCALES] LOAD BALANCING
# ----------------------------------------------------------------------------

"""
POURQUOI LOAD BALANCER ?
───────────────────────

Avantages :
[OK] Haute disponibilité (si 1 Nexus down)
[OK] Meilleure performance (répartition charge)
[OK] Maintenance sans downtime


ARCHITECTURE LOAD BALANCED
─────────────────────────
"""

Clients (Maven, Docker, npm)
       v
┌──────────────────┐
│  Load Balancer   │
│  (Nginx/HAProxy) │
└──────────────────┘
       v
    ┌─┴─┐
    │   │
    v   v
┌────────┐  ┌────────┐
│ Nexus1 │  │ Nexus2 │
└────────┘  └────────┘
    │           │
    └─────┬─────┘
          v
   ┌──────────────┐
   │ Shared       │
   │ Blob Storage │
   │ (NFS/S3)     │
   └──────────────┘


"""
NGINX LOAD BALANCER
──────────────────
"""

# /etc/nginx/nginx.conf

upstream nexus_backend {
    least_conn;  # Algoritme : least connections
    
    server nexus1.internal:8081 max_fails=3 fail_timeout=30s;
    server nexus2.internal:8081 max_fails=3 fail_timeout=30s;
    
    # Health check (Nginx Plus)
    # check interval=5s rise=2 fall=3;
}

server {
    listen 80;
    server_name nexus.company.com;
    
    location / {
        proxy_pass http://nexus_backend;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        
        # Sticky sessions (même client -> même backend)
        # Nécessaire pour uploads multi-part
        ip_hash;
    }
}


"""
HAPROXY LOAD BALANCER
────────────────────
"""

# /etc/haproxy/haproxy.cfg

frontend nexus_front
    bind *:80
    default_backend nexus_back

backend nexus_back
    balance leastconn
    option httpchk GET /service/rest/v1/status
    
    server nexus1 nexus1.internal:8081 check inter 5s rise 2 fall 3
    server nexus2 nexus2.internal:8081 check inter 5s rise 2 fall 3


# ----------------------------------------------------------------------------
# [HAUSSE] LOAD TESTING
# ----------------------------------------------------------------------------

"""
POURQUOI LOAD TEST ?
───────────────────

- Identifier limites système
- Valider configuration
- Planifier scaling
- Avant mise en production


OUTILS LOAD TESTING
──────────────────

1. Apache JMeter
2. Gatling
3. Locust
4. k6
"""


"""
EXEMPLE : JMETER TEST PLAN
─────────────────────────

Scenario :
- 100 users concurrents
- Téléchargement artifacts Maven
- 10 minutes duration
"""

# JMeter test plan XML (extrait)
"""
<ThreadGroup>
  <stringProp name="ThreadGroup.num_threads">100</stringProp>
  <stringProp name="ThreadGroup.ramp_time">60</stringProp>
  <stringProp name="ThreadGroup.duration">600</stringProp>
  
  <HTTPSamplerProxy>
    <stringProp name="HTTPSampler.domain">nexus.company.com</stringProp>
    <stringProp name="HTTPSampler.port">8081</stringProp>
    <stringProp name="HTTPSampler.path">/repository/maven-public/org/springframework/spring-core/5.3.0/spring-core-5.3.0.jar</stringProp>
    <stringProp name="HTTPSampler.method">GET</stringProp>
  </HTTPSamplerProxy>
</ThreadGroup>
"""

# Run JMeter
jmeter -n -t nexus-load-test.jmx -l results.jtl -e -o ./report/


"""
EXEMPLE : K6 SCRIPT
──────────────────
"""

# load-test.js
import http from 'k6/http';
import { check, sleep } from 'k6';

export let options = {
  stages: [
    { duration: '2m', target: 50 },  // Ramp up to 50 users
    { duration: '5m', target: 50 },  // Stay at 50
    { duration: '2m', target: 100 }, // Ramp to 100
    { duration: '5m', target: 100 }, // Stay at 100
    { duration: '2m', target: 0 },   // Ramp down
  ],
  thresholds: {
    http_req_duration: ['p(95)<500'], // 95% requests < 500ms
  },
};

export default function () {
  let response = http.get('http://nexus.company.com/repository/maven-public/org/springframework/spring-core/5.3.0/spring-core-5.3.0.jar');
  
  check(response, {
    'status is 200': (r) => r.status === 200,
    'response time < 500ms': (r) => r.timings.duration < 500,
  });
  
  sleep(1);
}

# Run k6
k6 run load-test.js


"""
ANALYSER RÉSULTATS
─────────────────

Métriques clés :
- Response time (p50, p95, p99)
- Error rate (%)
- Throughput (req/s)
- Concurrent users max

Identifier :
- Breaking point (quand ça casse)
- Degradation gracieuse
- Bottlenecks (CPU, memory, I/O, network)
"""


# ----------------------------------------------------------------------------
# [COURS] EXERCICE PRATIQUE 17 : PERFORMANCE OPTIMIZATION
# ----------------------------------------------------------------------------

"""
OBJECTIF : Optimiser Nexus pour production


ÉTAPES :

1. Baseline performance
   [ ] Mesurer temps réponse actuel
   [ ] Identifier métriques clés

2. JVM tuning
   [ ] Augmenter heap (8GB ou 16GB)
   [ ] Configurer GC logging
   [ ] Monitorer GC pauses

3. Blob store optimization
   [ ] Vérifier type disque (SSD recommandé)
   [ ] Créer blob stores multiples si possible
   [ ] Cleanup old blobs

4. Database maintenance
   [ ] Task compact blobstore
   [ ] Task rebuild search index

5. Caching optimization
   [ ] Proxy repos : component age = -1
   [ ] Metadata age approprié
   [ ] Negative cache enabled

6. Load testing
   [ ] JMeter ou k6 test plan
   [ ] 100 concurrent users
   [ ] Analyser résultats
   [ ] Identifier bottlenecks

7. Monitoring
   [ ] Prometheus + Grafana
   [ ] Dashboard performance
   [ ] Alertes si dégradation

8. Documentation
   [ ] Baseline vs optimized metrics
   [ ] Configuration appliquée
   [ ] Recommendations


VALIDATION :

[OK] Response time amélioré (>30%)
[OK] GC pauses < 200ms
[OK] Load test 100 users OK
[OK] Monitoring actif
[OK] Documentation complète


TEMPS ESTIMÉ : 3-4 heures
"""


# ----------------------------------------------------------------------------
# [DOCS] RÉCAPITULATIF CHAPITRE 17
# ----------------------------------------------------------------------------

"""
CE QUE VOUS AVEZ APPRIS

[OK] Identifier bottlenecks performance
[OK] JVM tuning (heap, GC)
[OK] Database optimization (compact, rebuild)
[OK] Blob store strategies (SSD, cloud, multiple)
[OK] Caching optimization
[OK] Load balancing (Nginx, HAProxy)
[OK] Load testing (JMeter, k6)


[CLE] POINTS CLÉS

1. Mesurer avant optimiser
   Baseline metrics essentiels

2. JVM = Premier levier
   50-75% RAM disponible

3. SSD >> HDD
   I/O critique pour performance

4. Cache = Performance
   Configuration appropriée par type

5. Load testing = Validation
   Avant production toujours


[OBJECTIF] BONNES PRATIQUES

[OK] JVM heap sizing approprié
[OK] G1GC avec tuning
[OK] SSD pour blob stores
[OK] Multiple blob stores (I/O parallèle)
[OK] Cache optimization par format
[OK] Load balancing si HA requis
[OK] Load testing régulier
[OK] Monitoring continu


═══════════════════════════════════════════════════════════
[BRAVO] FÉLICITATIONS ! PARTIE 4 COMPLÈTE (Chapitres 14-17) !
═══════════════════════════════════════════════════════════

VOUS MAÎTRISEZ MAINTENANT :

Chapitre 14 : Backup et Restauration
[OK] Stratégies backup (full, incremental)
[OK] Scripts automatisés
[OK] Backup cloud (S3, Azure)
[OK] Restauration et tests
[OK] Disaster Recovery Plan

Chapitre 15 : Monitoring et Logs
[OK] Métriques critiques
[OK] Logs analysis
[OK] Prometheus + Grafana
[OK] Alerting
[OK] Dashboards

Chapitre 16 : Sécurité Avancée
[OK] Hardening checklist
[OK] SSL/TLS configuration
[OK] Firewall et network segmentation
[OK] Compliance (SOC2, HIPAA, RGPD)
[OK] Vulnerability scanning

Chapitre 17 : Performance Tuning
[OK] JVM optimization
[OK] Database tuning
[OK] Blob store strategies
[OK] Load balancing
[OK] Load testing


-> PROCHAINE ÉTAPE : PARTIE 5 (Optionnelle)
═══════════════════════════════════════════

Partie 5 couvrirait :
- Chapitre 18 : Intégration CI/CD approfondie
- Chapitre 19 : High Availability (HA)
- Chapitre 20 : Best Practices Production (récapitulatif global)


VOUS ÊTES MAINTENANT EXPERT NEXUS ! [COURS][RAPIDE]
"""


# ============================================================================
# FIN PARTIE 4 - ADMINISTRATION ET PRODUCTION
# ============================================================================



═══════════════════════════════════════════════════════════════════════════════
[LIVRE] NEXUS REPOSITORY MANAGER - CAS PRATIQUE RÉEL
    ÉQUIPE DE 3 DÉVELOPPEURS : APPLICATION PYTHON + JAVA
═══════════════════════════════════════════════════════════════════════════════

[OBJECTIF] Section bonus ultra-détaillée : Simulation grandeur nature
[UTILISATEURS] Équipe : Alice (Backend Java), Bob (Backend Python), Charlie (DevOps/Intégration)
[CONSTRUCTION] Projet : API de gestion de produits (microservice Python + bibliothèque Java)
[CALENDRIER] Version : Janvier 2026

═══════════════════════════════════════════════════════════════════════════════
[DOCS] TABLE DES MATIÈRES
═══════════════════════════════════════════════════════════════════════════════

  SECTION A : CONTEXTE ET ARCHITECTURE DU PROJET
  SECTION B : MISE EN PLACE NEXUS PAR CHARLIE (DevOps)
  SECTION C : TRAVAIL D'ALICE (Développeuse Java)
  SECTION D : TRAVAIL DE BOB (Développeur Python)
  SECTION E : COLLABORATION ET INTÉGRATION ENTRE LES TROIS
  SECTION F : SCÉNARIOS DU QUOTIDIEN (Situations réelles)
  SECTION G : TROUBLESHOOTING VÉCU PAR L'ÉQUIPE
  SECTION H : BILAN ET BONNES PRATIQUES APPRISES

═══════════════════════════════════════════════════════════════════════════════
SECTION A : CONTEXTE ET ARCHITECTURE DU PROJET
═══════════════════════════════════════════════════════════════════════════════

────────────────────────────────────────────────────────────────────────────
A.1 - PRÉSENTATION DE L'ÉQUIPE
────────────────────────────────────────────────────────────────────────────

┌─────────────────────────────────────────────────────────────────────────┐
│  ALICE  │ Développeuse Senior Java                                      │
│         │ Responsable : Bibliothèque de validation partagée             │
│         │ Stack : Java 17, Spring Boot 3, Maven                        │
│         │ PC : MacOS, IntelliJ IDEA                                     │
├─────────────────────────────────────────────────────────────────────────┤
│  BOB    │ Développeur Python                                            │
│         │ Responsable : API REST FastAPI                                │
│         │ Stack : Python 3.11, FastAPI, pip/pyproject.toml             │
│         │ PC : Ubuntu 22.04, VS Code                                   │
├─────────────────────────────────────────────────────────────────────────┤
│  CHARLIE│ DevOps / Développeur Intégration                             │
│         │ Responsable : Nexus, CI/CD, Infrastructure                   │
│         │ Stack : Docker, GitHub Actions, Bash                         │
│         │ PC : Ubuntu 22.04, Terminal                                  │
└─────────────────────────────────────────────────────────────────────────┘


────────────────────────────────────────────────────────────────────────
A.2 - ARCHITECTURE DU PROJET "PRODUCTMANAGER"
────────────────────────────────────────────────────────────────────────

Le projet "ProductManager" est une application de gestion de produits
composée de deux parties distinctes qui se partagent du code via Nexus :

┌────────────────────────────────────────────────────────────────────────┐
│                      PRODUCTMANAGER SYSTEM                             │
│                                                                        │
│  ┌─────────────────────┐        ┌─────────────────────────────────┐   │
│  │  product-validator  │        │      product-api (Python)        │   │
│  │  (Java Library)     │        │                                  │   │
│  │  par Alice          │[BLACK_LEFT-POINTING_POINTER]───────│  Utilise product-validator       │   │
│  │                     │        │  via Nexus PyPI (wrapper Python) │   │
│  │  - Validation SKU   │        │  par Bob                        │   │
│  │  - Validation prix  │        │                                  │   │
│  │  - Règles métier    │        │  Endpoints REST :               │   │
│  │  Publié sur Nexus   │        │  POST /products                 │   │
│  │  (maven + pypi)     │        │  GET  /products/{id}            │   │
│  └─────────────────────┘        │  PUT  /products/{id}            │   │
│                                 └─────────────────────────────────┘   │
│                                                                        │
│  ┌─────────────────────────────────────────────────────────────────┐   │
│  │                    NEXUS REPOSITORY (Charlie)                   │   │
│  │  maven-releases     │  pypi-hosted  │  raw-scripts             │   │
│  │  maven-snapshots    │  pypi-proxy   │  docker-hosted           │   │
│  └─────────────────────────────────────────────────────────────────┘   │
└────────────────────────────────────────────────────────────────────────┘


────────────────────────────────────────────────────────────────────────
A.3 - POURQUOI NEXUS DANS CE CONTEXTE ?
────────────────────────────────────────────────────────────────────────

PROBLÈME SANS NEXUS :
─────────────────────

Alice développe product-validator.jar
Bob a besoin de ce code en Python
-> Solution actuelle (mauvaise) :
  1. Alice envoie le JAR par Slack à Bob
  2. Bob ne peut pas utiliser un JAR dans Python
  3. Bob copie le code de validation à la main
  4. Deux codebases désynchronisées
  5. Quand Alice corrige un bug, Bob ne sait pas

SOLUTION AVEC NEXUS :
─────────────────────

1. Alice publie product-validator:1.0.0 sur Nexus (maven-releases)
2. Alice publie aussi un wrapper Python sur Nexus (pypi-hosted)
   -> product-validator-py:1.0.0
3. Bob installe via pip install product-validator-py
4. Charlie configure CI/CD pour automatiser tout ça
5. Tout le monde utilise la même version, tracée, versionnée


════════════════════════════════════════════════════════════════════════
SECTION B : MISE EN PLACE NEXUS PAR CHARLIE (DevOps)
════════════════════════════════════════════════════════════════════════

────────────────────────────────────────────────────────────────────────
B.1 - INSTALLATION NEXUS PAR CHARLIE
────────────────────────────────────────────────────────────────────────

Charlie installe Nexus sur le serveur de l'équipe (IP: 192.168.1.50).

# Sur le serveur Ubuntu 22.04 de l'équipe

# Étape 1 : Installer Docker
sudo apt update
sudo apt install docker.io docker-compose -y
sudo usermod -aG docker charlie
newgrp docker

# Étape 2 : Créer dossier de travail
mkdir -p /opt/nexus/{data,backup}
sudo chown -R 200:200 /opt/nexus/data

# Étape 3 : Créer docker-compose.yml
cat > /opt/nexus/docker-compose.yml << 'EOF'
version: '3.8'

services:
  nexus:
    image: sonatype/nexus3:latest
    container_name: nexus
    restart: unless-stopped
    ports:
      - "8081:8081"    # Web UI + Maven + npm + PyPI
      - "8082:8082"    # Docker Hosted Registry
      - "8083:8083"    # Docker Proxy (Docker Hub cache)
    volumes:
      - /opt/nexus/data:/nexus-data
    environment:
      - INSTALL4J_ADD_VM_PARAMS=-Xms2g -Xmx2g -XX:MaxDirectMemorySize=2g
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8081/service/rest/v1/status"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 120s
EOF

# Étape 4 : Démarrer Nexus
cd /opt/nexus
docker-compose up -d

# Étape 5 : Attendre démarrage (2-3 minutes)
echo "[HOURGLASS_WITH_FLOWING_SAND] Attente du démarrage de Nexus..."
sleep 120

# Étape 6 : Récupérer mot de passe admin initial
docker exec nexus cat /nexus-data/admin.password
# -> Ex: 3a4b5c6d-7e8f-9a0b-c1d2-e3f4a5b6c7d8

# Étape 7 : Accéder à http://192.168.1.50:8081
# Login : admin
# Password : (celui récupéré ci-dessus)
# -> Changer password : Admin@ProductManager2024!


────────────────────────────────────────────────────────────────────────
B.2 - CRÉATION DES REPOSITORIES PAR CHARLIE
────────────────────────────────────────────────────────────────────────

Charlie crée tous les repositories nécessaires pour l'équipe.

════════════════════════
B.2.1 - REPOSITORIES MAVEN (Pour Alice)
════════════════════════

Charlie crée via l'interface Nexus :

── REPOSITORY 1 : maven-releases (hosted) ──
  Name               : maven-releases
  Version policy     : Release
  Deployment policy  : Disable redeploy     <- Immuabilité des releases
  Blob store         : default
  -> URL : http://192.168.1.50:8081/repository/maven-releases/

── REPOSITORY 2 : maven-snapshots (hosted) ──
  Name               : maven-snapshots
  Version policy     : Snapshot
  Deployment policy  : Allow redeploy       <- Dev en cours
  Cleanup policy     : delete-old-snapshots (>30 jours)
  Blob store         : default
  -> URL : http://192.168.1.50:8081/repository/maven-snapshots/

── REPOSITORY 3 : maven-central-proxy (proxy) ──
  Name               : maven-central-proxy
  Remote storage     : https://repo1.maven.org/maven2/
  Maximum component age : -1 (illimité pour les releases)
  Maximum metadata age  : 1440 (24h)
  Negative cache enabled: true
  -> URL : http://192.168.1.50:8081/repository/maven-central-proxy/

── REPOSITORY 4 : maven-public (group) ──
  Name               : maven-public
  Member repositories:
    1. maven-releases      (vos artifacts d'abord)
    2. maven-snapshots
    3. maven-central-proxy (fallback internet)
  -> URL : http://192.168.1.50:8081/repository/maven-public/


════════════════════════
B.2.2 - REPOSITORIES PyPI (Pour Bob)
════════════════════════

── REPOSITORY 5 : pypi-hosted (hosted) ──
  Name               : pypi-hosted
  Deployment policy  : Allow redeploy
  -> URL : http://192.168.1.50:8081/repository/pypi-hosted/

── REPOSITORY 6 : pypi-proxy (proxy) ──
  Name               : pypi-proxy
  Remote storage     : https://pypi.org
  Maximum component age : -1
  Maximum metadata age  : 1440
  -> URL : http://192.168.1.50:8081/repository/pypi-proxy/

── REPOSITORY 7 : pypi-all (group) ──
  Name               : pypi-all
  Members            :
    1. pypi-hosted   (packages internes en priorité)
    2. pypi-proxy    (PyPI.org en fallback)
  -> URL : http://192.168.1.50:8081/repository/pypi-all/


════════════════════════
B.2.3 - REPOSITORIES DOCKER (Pour l'équipe)
════════════════════════

── REPOSITORY 8 : docker-hosted (hosted) ──
  Name               : docker-hosted
  HTTP Port          : 8082
  Allow anonymous pull: true (réseau interne seulement)
  -> URL : http://192.168.1.50:8082/

── REPOSITORY 9 : docker-proxy (proxy) ──
  Name               : docker-proxy
  HTTP Port          : 8083
  Remote storage     : https://registry-1.docker.io
  Docker Index       : Use Docker Hub
  -> URL : http://192.168.1.50:8083/

── REPOSITORY 10 : raw-scripts (hosted) ──
  Name               : raw-scripts
  Content Disposition: Attachment
  -> URL : http://192.168.1.50:8081/repository/raw-scripts/


────────────────────────────────────────────────────────────────────────
B.3 - CRÉATION DES UTILISATEURS PAR CHARLIE
────────────────────────────────────────────────────────────────────────

Charlie crée des comptes dédiés pour chaque développeur.
Principe : Moindre privilège.

════════════════════════
B.3.1 - CRÉER LES RÔLES
════════════════════════

── RÔLE : java-developer-role ──
  ID               : java-developer-role
  Description      : Développeur Java - Alice
  Privileges :
    [x] nx-search-read
    [x] nx-repository-view-maven2-*-read
    [x] nx-repository-view-maven2-*-browse
    [x] nx-repository-view-maven2-maven-snapshots-add
    [x] nx-repository-view-maven2-maven-snapshots-edit
    [x] nx-repository-view-maven2-maven-snapshots-delete
    [x] nx-repository-view-maven2-maven-releases-add
    [x] nx-repository-view-pypi-pypi-hosted-add   <- Pour publier wrapper Python
    [x] nx-repository-view-pypi-*-read
    [x] nx-repository-view-pypi-*-browse

── RÔLE : python-developer-role ──
  ID               : python-developer-role
  Description      : Développeur Python - Bob
  Privileges :
    [x] nx-search-read
    [x] nx-repository-view-pypi-*-read
    [x] nx-repository-view-pypi-*-browse
    [x] nx-repository-view-pypi-pypi-hosted-add
    [x] nx-repository-view-pypi-pypi-hosted-edit
    [x] nx-repository-view-maven2-*-read     <- Pour consulter les libs Java
    [x] nx-repository-view-maven2-*-browse
    [x] nx-repository-view-docker-*-read
    [x] nx-repository-view-docker-*-browse
    [x] nx-repository-view-docker-docker-hosted-add

════════════════════════
B.3.2 - CRÉER LES UTILISATEURS
════════════════════════

── UTILISATEUR : alice ──
  ID           : alice
  First Name   : Alice
  Last Name    : Martin
  Email        : alice@productmanager.local
  Status       : Active
  Roles        : java-developer-role
  Password     : AliceSecure@2024!

── UTILISATEUR : bob ──
  ID           : bob
  First Name   : Bob
  Last Name    : Dupont
  Email        : bob@productmanager.local
  Status       : Active
  Roles        : python-developer-role
  Password     : BobSecure@2024!

── UTILISATEUR : ci-runner ──
  ID           : ci-runner
  First Name   : CI
  Last Name    : Runner
  Email        : ci@productmanager.local
  Status       : Active
  Roles        : java-developer-role + python-developer-role
  Password     : (généré aléatoirement)
  -> Token API : Générer via User Token pour GitHub Actions


────────────────────────────────────────────────────────────────────────
B.4 - CONFIGURATION INSECURE REGISTRY DOCKER (Charlie)
────────────────────────────────────────────────────────────────────────

Sur chaque machine de l'équipe (alice, bob, charlie) :

# /etc/docker/daemon.json
{
  "insecure-registries": [
    "192.168.1.50:8082",
    "192.168.1.50:8083"
  ],
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "10m",
    "max-file": "3"
  }
}

# Redémarrer Docker sur chaque poste
sudo systemctl daemon-reload
sudo systemctl restart docker

# Vérifier
docker info | grep -A5 "Insecure Registries"


────────────────────────────────────────────────────────────────────────
B.5 - DÉSACTIVATION ANONYMOUS ACCESS (Charlie)
────────────────────────────────────────────────────────────────────────

En production interne, Charlie désactive l'accès anonyme :

# Nexus UI -> Security -> Anonymous Access
# -> Décocher "Allow anonymous users to access the server"
# -> Save

# Vérifier que l'accès est bloqué sans credentials :
curl http://192.168.1.50:8081/repository/maven-public/
# Expected: 401 Unauthorized

# Vérifier que l'accès fonctionne avec credentials :
curl -u alice:AliceSecure@2024! http://192.168.1.50:8081/repository/maven-public/
# Expected: 200 OK


════════════════════════════════════════════════════════════════════════
SECTION C : TRAVAIL D'ALICE (Développeuse Java)
════════════════════════════════════════════════════════════════════════

Alice développe la bibliothèque "product-validator" qui contient
les règles métier de validation des produits. Cette bibliothèque
est utilisée par Bob via un wrapper Python.

────────────────────────────────────────────────────────────────────────
C.1 - CONFIGURATION MAVEN D'ALICE
────────────────────────────────────────────────────────────────────────

════════════════════════
C.1.1 - settings.xml (Fichier global Maven d'Alice)
════════════════════════

# Sur le poste d'Alice : ~/.m2/settings.xml

<?xml version="1.0" encoding="UTF-8"?>
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0
            http://maven.apache.org/xsd/settings-1.0.0.xsd">

  <!-- ====================================================== -->
  <!--  SERVEURS : Credentials pour chaque repository Nexus  -->
  <!-- ====================================================== -->
  <servers>

    <!-- Pour TÉLÉCHARGER depuis maven-public (group) -->
    <server>
      <id>nexus-public</id>
      <username>alice</username>
      <password>AliceSecure@2024!</password>
      <!--
        [IDEE] NOTE : En CI/CD, utiliser variables d'environnement :
        <username>${env.NEXUS_USERNAME}</username>
        <password>${env.NEXUS_PASSWORD}</password>
      -->
    </server>

    <!-- Pour DEPLOYER vers maven-releases -->
    <server>
      <id>nexus-releases</id>
      <username>alice</username>
      <password>AliceSecure@2024!</password>
    </server>

    <!-- Pour DEPLOYER vers maven-snapshots -->
    <server>
      <id>nexus-snapshots</id>
      <username>alice</username>
      <password>AliceSecure@2024!</password>
    </server>

  </servers>

  <!-- ====================================================== -->
  <!--  MIRROR : Tout le trafic Maven -> Nexus                -->
  <!-- ====================================================== -->
  <mirrors>
    <mirror>
      <id>nexus-mirror</id>
      <name>Nexus Repository Manager - Team Mirror</name>
      <url>http://192.168.1.50:8081/repository/maven-public/</url>
      <!--
        <mirrorOf>*</mirrorOf>
        Cela signifie : "Pour TOUS les repositories, utiliser Nexus"
        Maven Central, Spring, Google -> Tout passe par notre Nexus
        Nexus proxifie et met en cache pour nous.
      -->
      <mirrorOf>*</mirrorOf>
    </mirror>
  </mirrors>

  <!-- ====================================================== -->
  <!--  PROFILS : Configuration des repositories             -->
  <!-- ====================================================== -->
  <profiles>
    <profile>
      <id>nexus</id>
      <repositories>
        <repository>
          <id>nexus-public</id>
          <name>Nexus Repository Manager</name>
          <url>http://192.168.1.50:8081/repository/maven-public/</url>
          <releases>
            <enabled>true</enabled>
            <updatePolicy>never</updatePolicy>
            <!--
              [IDEE] updatePolicy=never pour les releases :
              Une release ne change jamais -> Pas besoin de revérifier
              Améliore performances Maven
            -->
          </releases>
          <snapshots>
            <enabled>true</enabled>
            <updatePolicy>always</updatePolicy>
            <!--
              [IDEE] updatePolicy=always pour les snapshots :
              Les snapshots changent souvent -> Toujours vérifier
              -SNAPSHOT peut être rebuild par quelqu'un d'autre
            -->
          </snapshots>
        </repository>
      </repositories>
      <pluginRepositories>
        <pluginRepository>
          <id>nexus-public</id>
          <url>http://192.168.1.50:8081/repository/maven-public/</url>
          <releases><enabled>true</enabled></releases>
          <snapshots><enabled>true</enabled></snapshots>
        </pluginRepository>
      </pluginRepositories>
    </profile>
  </profiles>

  <!-- Activer le profil par défaut -->
  <activeProfiles>
    <activeProfile>nexus</activeProfile>
  </activeProfiles>

</settings>


════════════════════════
C.1.2 - VÉRIFICATION DE LA CONFIG ALICE
════════════════════════

# Alice vérifie que Maven utilise bien Nexus :
mvn help:effective-settings

# Elle crée un projet test et vérifie les downloads :
mvn archetype:generate \
    -DgroupId=com.productmanager \
    -DartifactId=test-nexus \
    -DarchetypeArtifactId=maven-archetype-quickstart \
    -DinteractiveMode=false

cd test-nexus
mvn clean install -X 2>&1 | grep "Downloading from nexus"

# Sortie attendue :
# [DEBUG] Downloading from nexus-mirror: http://192.168.1.50:8081/repository/maven-public/...
# [OK] Maven télécharge bien depuis Nexus !


────────────────────────────────────────────────────────────────────────
C.2 - STRUCTURE DU PROJET JAVA D'ALICE
────────────────────────────────────────────────────────────────────────

product-validator/
├── pom.xml                              <- Configuration Maven principale
├── src/
│   ├── main/
│   │   └── java/
│   │       └── com/
│   │           └── productmanager/
│   │               └── validator/
│   │                   ├── ProductValidator.java    <- Classe principale
│   │                   ├── ValidationResult.java    <- Objet résultat
│   │                   ├── ValidationRule.java      <- Interface règle
│   │                   └── rules/
│   │                       ├── SkuValidationRule.java
│   │                       ├── PriceValidationRule.java
│   │                       └── StockValidationRule.java
│   └── test/
│       └── java/
│           └── com/
│               └── productmanager/
│                   └── validator/
│                       └── ProductValidatorTest.java
└── README.md


────────────────────────────────────────────────────────────────────────
C.3 - LE pom.xml D'ALICE (Complet et commenté)
────────────────────────────────────────────────────────────────────────

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
           http://maven.apache.org/xsd/maven-4.0.0.xsd">

  <modelVersion>4.0.0</modelVersion>

  <!-- ====================================================== -->
  <!--  IDENTITÉ DE L'ARTIFACT                               -->
  <!-- ====================================================== -->
  <groupId>com.productmanager</groupId>
  <artifactId>product-validator</artifactId>
  <version>1.0.0-SNAPSHOT</version>
  <!--
    [IDEE] Pendant le développement : 1.0.0-SNAPSHOT
    Avant la release : Alice changera en 1.0.0
    La version détermine vers quel repository Maven déploie :
    - *-SNAPSHOT -> maven-snapshots
    - Sans SNAPSHOT -> maven-releases
  -->
  <packaging>jar</packaging>

  <name>Product Validator Library</name>
  <description>
    Bibliothèque de validation des produits pour ProductManager.
    Utilisée par le service Java ET le service Python (via wrapper).
  </description>

  <!-- ====================================================== -->
  <!--  CONFIGURATION DE L'ENCODAGE ET DES PROPRIÉTÉS       -->
  <!-- ====================================================== -->
  <properties>
    <java.version>17</java.version>
    <maven.compiler.source>17</maven.compiler.source>
    <maven.compiler.target>17</maven.compiler.target>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <junit.version>5.10.0</junit.version>
    <jackson.version>2.15.3</jackson.version>
  </properties>

  <!-- ====================================================== -->
  <!--  DÉPENDANCES                                          -->
  <!-- ====================================================== -->
  <dependencies>

    <!-- Jackson : Pour sérialisation JSON des résultats de validation -->
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-databind</artifactId>
      <version>${jackson.version}</version>
      <!--
        [IDEE] Maven télécharge jackson-databind depuis Nexus :
        1. Cherche dans maven-releases (hosted) -> Pas trouvé
        2. Cherche dans maven-snapshots (hosted) -> Pas trouvé
        3. Cherche dans maven-central-proxy -> Pas en cache
        4. Nexus télécharge depuis Maven Central et met en cache
        5. Renvoie à Maven d'Alice
        -> Prochaine fois : Servi depuis le cache Nexus (rapide!)
      -->
    </dependency>

    <!-- Commons Lang : Utilitaires String -->
    <dependency>
      <groupId>org.apache.commons</groupId>
      <artifactId>commons-lang3</artifactId>
      <version>3.14.0</version>
    </dependency>

    <!-- ──────────────────────────────────────────── -->
    <!-- DÉPENDANCES DE TEST (scope=test uniquement)  -->
    <!-- ──────────────────────────────────────────── -->

    <!-- JUnit 5 -->
    <dependency>
      <groupId>org.junit.jupiter</groupId>
      <artifactId>junit-jupiter</artifactId>
      <version>${junit.version}</version>
      <scope>test</scope>
    </dependency>

    <!-- AssertJ : Assertions lisibles -->
    <dependency>
      <groupId>org.assertj</groupId>
      <artifactId>assertj-core</artifactId>
      <version>3.24.2</version>
      <scope>test</scope>
    </dependency>

  </dependencies>

  <!-- ====================================================== -->
  <!--  DISTRIBUTION MANAGEMENT : Où publier dans Nexus      -->
  <!-- ====================================================== -->
  <distributionManagement>
    <!--
      [IDEE] Maven sélectionne automatiquement selon la version :
      - <version>x.y.z-SNAPSHOT</version> -> snapshotRepository
      - <version>x.y.z</version>           -> repository
    -->
    <repository>
      <id>nexus-releases</id>
      <!--
        Cet <id> DOIT correspondre au <id> dans settings.xml <servers>
        C'est ainsi que Maven trouve les credentials
      -->
      <name>Nexus Releases Repository</name>
      <url>http://192.168.1.50:8081/repository/maven-releases/</url>
    </repository>
    <snapshotRepository>
      <id>nexus-snapshots</id>
      <name>Nexus Snapshots Repository</name>
      <url>http://192.168.1.50:8081/repository/maven-snapshots/</url>
    </snapshotRepository>
  </distributionManagement>

  <!-- ====================================================== -->
  <!--  BUILD : Plugins Maven                                -->
  <!-- ====================================================== -->
  <build>
    <plugins>

      <!-- Compilateur Java 17 -->
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.11.0</version>
        <configuration>
          <release>17</release>
        </configuration>
      </plugin>

      <!-- Tests : JUnit 5 support -->
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-surefire-plugin</artifactId>
        <version>3.2.2</version>
      </plugin>

      <!-- Plugin Sources : Génère product-validator-1.0.0-sources.jar -->
      <!--
        [IDEE] Important ! Permet à Bob de voir le code source
        dans son IDE même en utilisant le JAR compilé.
        Nexus stockera 3 fichiers :
        - product-validator-1.0.0.jar          (compilé)
        - product-validator-1.0.0-sources.jar  (sources)
        - product-validator-1.0.0-javadoc.jar  (docs)
      -->
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-source-plugin</artifactId>
        <version>3.3.0</version>
        <executions>
          <execution>
            <id>attach-sources</id>
            <goals>
              <goal>jar-no-fork</goal>
            </goals>
          </execution>
        </executions>
      </plugin>

      <!-- Plugin Javadoc -->
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-javadoc-plugin</artifactId>
        <version>3.6.3</version>
        <executions>
          <execution>
            <id>attach-javadocs</id>
            <goals>
              <goal>jar</goal>
            </goals>
          </execution>
        </executions>
      </plugin>

    </plugins>
  </build>

</project>


────────────────────────────────────────────────────────────────────────
C.4 - LE CODE JAVA D'ALICE
────────────────────────────────────────────────────────────────────────

════════════════════════
C.4.1 - ProductValidator.java (Classe principale)
════════════════════════

# src/main/java/com/productmanager/validator/ProductValidator.java

package com.productmanager.validator;

import com.productmanager.validator.rules.PriceValidationRule;
import com.productmanager.validator.rules.SkuValidationRule;
import com.productmanager.validator.rules.StockValidationRule;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * Point d'entrée principal de la bibliothèque de validation.
 *
 * <p>Exemple d'utilisation :</p>
 * <pre>
 *   ProductValidator validator = new ProductValidator();
 *   ValidationResult result = validator.validate(productData);
 *   if (result.isValid()) {
 *     // Produit valide
 *   }
 * </pre>
 *
 * @version 1.0.0
 * @author Alice Martin
 */
public class ProductValidator {

    private final List<ValidationRule> rules;

    public ProductValidator() {
        this.rules = new ArrayList<>();
        // Règles par défaut
        this.rules.add(new SkuValidationRule());
        this.rules.add(new PriceValidationRule());
        this.rules.add(new StockValidationRule());
    }

    /**
     * Valide un produit selon toutes les règles configurées.
     *
     * @param productData Map contenant les données du produit :
     *                    - "sku"   : String (obligatoire, format PROD-XXXX)
     *                    - "price" : Double (obligatoire, >0)
     *                    - "stock" : Integer (obligatoire, >=0)
     *                    - "name"  : String (obligatoire, non vide)
     * @return ValidationResult avec isValid() et liste d'erreurs
     */
    public ValidationResult validate(Map<String, Object> productData) {
        List<String> errors = new ArrayList<>();

        for (ValidationRule rule : rules) {
            List<String> ruleErrors = rule.validate(productData);
            errors.addAll(ruleErrors);
        }

        return new ValidationResult(errors.isEmpty(), errors);
    }

    /**
     * Valide uniquement le SKU d'un produit.
     *
     * @param sku Le SKU à valider
     * @return true si le SKU est valide
     */
    public boolean isValidSku(String sku) {
        return sku != null && sku.matches("^PROD-[A-Z0-9]{4,8}$");
    }

    /**
     * Valide uniquement le prix d'un produit.
     *
     * @param price Le prix à valider
     * @return true si le prix est valide
     */
    public boolean isValidPrice(double price) {
        return price > 0 && price <= 999999.99;
    }
}


════════════════════════
C.4.2 - ValidationResult.java
════════════════════════

# src/main/java/com/productmanager/validator/ValidationResult.java

package com.productmanager.validator;

import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.Collections;
import java.util.List;

/**
 * Résultat d'une validation de produit.
 * Sérialisable en JSON pour communication inter-services.
 */
public class ValidationResult {

    @JsonProperty("valid")
    private final boolean valid;

    @JsonProperty("errors")
    private final List<String> errors;

    @JsonProperty("error_count")
    private final int errorCount;

    public ValidationResult(boolean valid, List<String> errors) {
        this.valid = valid;
        this.errors = Collections.unmodifiableList(errors);
        this.errorCount = errors.size();
    }

    public boolean isValid() { return valid; }

    public List<String> getErrors() { return errors; }

    public int getErrorCount() { return errorCount; }

    public String getFirstError() {
        return errors.isEmpty() ? null : errors.get(0);
    }

    @Override
    public String toString() {
        if (valid) {
            return "ValidationResult{VALID}";
        }
        return "ValidationResult{INVALID, errors=" + errors + "}";
    }
}


════════════════════════
C.4.3 - SkuValidationRule.java
════════════════════════

# src/main/java/com/productmanager/validator/rules/SkuValidationRule.java

package com.productmanager.validator.rules;

import com.productmanager.validator.ValidationRule;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * Règle de validation du SKU (Stock Keeping Unit).
 * Format attendu : PROD-[A-Z0-9]{4,8}
 * Exemples valides : PROD-ABC1, PROD-XY12Z, PROD-ABCD1234
 * Exemples invalides : ABC-123, PROD-ab, prod-ABC1
 */
public class SkuValidationRule implements ValidationRule {

    private static final String SKU_PATTERN = "^PROD-[A-Z0-9]{4,8}$";
    private static final String FIELD_NAME = "sku";

    @Override
    public List<String> validate(Map<String, Object> productData) {
        List<String> errors = new ArrayList<>();

        Object skuObj = productData.get(FIELD_NAME);

        if (skuObj == null) {
            errors.add("Le champ 'sku' est obligatoire");
            return errors;
        }

        String sku = skuObj.toString().trim();

        if (sku.isEmpty()) {
            errors.add("Le SKU ne peut pas être vide");
        } else if (!sku.matches(SKU_PATTERN)) {
            errors.add(
                String.format(
                    "SKU invalide : '%s'. Format attendu : PROD-[A-Z0-9]{4,8} " +
                    "(ex: PROD-ABC1, PROD-XY12Z)",
                    sku
                )
            );
        }

        return errors;
    }
}


════════════════════════
C.4.4 - PriceValidationRule.java
════════════════════════

# src/main/java/com/productmanager/validator/rules/PriceValidationRule.java

package com.productmanager.validator.rules;

import com.productmanager.validator.ValidationRule;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * Règle de validation du prix.
 * Le prix doit être positif et ne pas dépasser 999 999.99
 */
public class PriceValidationRule implements ValidationRule {

    private static final double MAX_PRICE = 999_999.99;
    private static final String FIELD_NAME = "price";

    @Override
    public List<String> validate(Map<String, Object> productData) {
        List<String> errors = new ArrayList<>();

        Object priceObj = productData.get(FIELD_NAME);

        if (priceObj == null) {
            errors.add("Le champ 'price' est obligatoire");
            return errors;
        }

        try {
            double price = Double.parseDouble(priceObj.toString());

            if (price <= 0) {
                errors.add(
                    String.format("Le prix doit être positif. Valeur reçue : %.2f", price)
                );
            } else if (price > MAX_PRICE) {
                errors.add(
                    String.format(
                        "Le prix dépasse le maximum autorisé (%.2f). Valeur reçue : %.2f",
                        MAX_PRICE, price
                    )
                );
            }

            // Vérification de la précision (maximum 2 décimales)
            String priceStr = priceObj.toString();
            if (priceStr.contains(".") && priceStr.split("\\.")[1].length() > 2) {
                errors.add(
                    "Le prix ne peut pas avoir plus de 2 décimales. Valeur reçue : " + priceStr
                );
            }

        } catch (NumberFormatException e) {
            errors.add("Le prix doit être un nombre. Valeur reçue : " + priceObj);
        }

        return errors;
    }
}


════════════════════════
C.4.5 - Tests JUnit d'Alice
════════════════════════

# src/test/java/com/productmanager/validator/ProductValidatorTest.java

package com.productmanager.validator;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import java.util.HashMap;
import java.util.Map;
import static org.assertj.core.api.Assertions.*;

@DisplayName("Tests de ProductValidator")
class ProductValidatorTest {

    private ProductValidator validator;
    private Map<String, Object> validProduct;

    @BeforeEach
    void setUp() {
        validator = new ProductValidator();
        validProduct = new HashMap<>();
        validProduct.put("sku", "PROD-ABC1");
        validProduct.put("price", 29.99);
        validProduct.put("stock", 100);
        validProduct.put("name", "Produit Test");
    }

    @Test
    @DisplayName("Un produit valide doit passer la validation")
    void testValidProduct() {
        ValidationResult result = validator.validate(validProduct);

        assertThat(result.isValid()).isTrue();
        assertThat(result.getErrors()).isEmpty();
        assertThat(result.getErrorCount()).isZero();
    }

    @ParameterizedTest
    @DisplayName("SKUs valides")
    @ValueSource(strings = {"PROD-ABC1", "PROD-XY12", "PROD-ABCD1234", "PROD-A0B1"})
    void testValidSkus(String sku) {
        assertThat(validator.isValidSku(sku)).isTrue();
    }

    @ParameterizedTest
    @DisplayName("SKUs invalides")
    @ValueSource(strings = {"abc-123", "PROD-ab", "PROD-", "prod-ABC1", "PRODABC1"})
    void testInvalidSkus(String sku) {
        assertThat(validator.isValidSku(sku)).isFalse();
    }

    @Test
    @DisplayName("Prix négatif -> Erreur de validation")
    void testNegativePrice() {
        validProduct.put("price", -5.00);
        ValidationResult result = validator.validate(validProduct);

        assertThat(result.isValid()).isFalse();
        assertThat(result.getErrors()).anyMatch(e -> e.contains("prix doit être positif"));
    }

    @Test
    @DisplayName("SKU manquant -> Erreur de validation")
    void testMissingSku() {
        validProduct.remove("sku");
        ValidationResult result = validator.validate(validProduct);

        assertThat(result.isValid()).isFalse();
        assertThat(result.getFirstError()).contains("'sku' est obligatoire");
    }
}


────────────────────────────────────────────────────────────────────────
C.5 - CYCLE DE DÉVELOPPEMENT D'ALICE
────────────────────────────────────────────────────────────────────────

════════════════════════
C.5.1 - Phase Développement (SNAPSHOT)
════════════════════════

# Alice travaille sur la version SNAPSHOT
# pom.xml : <version>1.0.0-SNAPSHOT</version>

# 1. Coder et tester localement
mvn clean test

# Sortie :
# [INFO] -------------------------------------------------------
# [INFO]  T E S T S
# [INFO] -------------------------------------------------------
# [INFO] Running com.productmanager.validator.ProductValidatorTest
# [INFO] Tests run: 8, Failures: 0, Errors: 0, Skipped: 0
# [INFO] BUILD SUCCESS

# 2. Déployer le SNAPSHOT vers Nexus
mvn clean deploy

# Sortie détaillée :
# [INFO] --- maven-deploy-plugin:3.1.1:deploy (default-deploy) @ product-validator ---
# [INFO] Uploading to nexus-snapshots: http://192.168.1.50:8081/repository/maven-snapshots/com/productmanager/product-validator/1.0.0-SNAPSHOT/product-validator-1.0.0-20240115.103045-1.jar
# [INFO] Uploaded to nexus-snapshots: http://192.168.1.50:8081/repository/maven-snapshots/com/productmanager/product-validator/1.0.0-SNAPSHOT/product-validator-1.0.0-20240115.103045-1.jar (15 kB at 1.2 MB/s)
# [INFO] Uploading to nexus-snapshots: http://192.168.1.50:8081/repository/maven-snapshots/com/productmanager/product-validator/1.0.0-SNAPSHOT/product-validator-1.0.0-20240115.103045-1-sources.jar
# [INFO] Uploaded: ... (sources)
# [INFO] BUILD SUCCESS

# 3. Vérifier dans Nexus UI :
# Browse -> maven-snapshots -> com/productmanager/product-validator/1.0.0-SNAPSHOT/
# -> Files présents :
#   product-validator-1.0.0-20240115.103045-1.jar
#   product-validator-1.0.0-20240115.103045-1-sources.jar
#   product-validator-1.0.0-20240115.103045-1-javadoc.jar
#   product-validator-1.0.0-20240115.103045-1.pom
#   maven-metadata.xml (pointe vers la dernière version)

# 4. Bob peut maintenant utiliser le SNAPSHOT :
# (Voir Section D - Côté Bob)


════════════════════════
C.5.2 - Préparer la Release 1.0.0
════════════════════════

Quand Alice est prête pour la release officielle :

# Méthode 1 : Manuellement (simple)

# a. Changer la version dans pom.xml
# De : <version>1.0.0-SNAPSHOT</version>
# À  : <version>1.0.0</version>

# b. Commiter le changement
git add pom.xml
git commit -m "release: prepare version 1.0.0"
git tag -a v1.0.0 -m "Release version 1.0.0"

# c. Déployer la release
mvn clean deploy

# Sortie :
# [INFO] Uploading to nexus-releases: http://192.168.1.50:8081/repository/maven-releases/com/productmanager/product-validator/1.0.0/product-validator-1.0.0.jar
# [INFO] Uploaded to nexus-releases: ... (15 kB at 1.5 MB/s)
# [INFO] BUILD SUCCESS

# d. Vérifier dans Nexus UI :
# Browse -> maven-releases -> com/productmanager/product-validator/1.0.0/
# -> Files :
#   product-validator-1.0.0.jar      <- RELEASE OFFICIELLE
#   product-validator-1.0.0-sources.jar
#   product-validator-1.0.0-javadoc.jar
#   product-validator-1.0.0.pom

# e. Passer en version SNAPSHOT suivante pour la suite du développement
# <version>1.1.0-SNAPSHOT</version>
git add pom.xml
git commit -m "dev: start working on version 1.1.0-SNAPSHOT"

# Méthode 2 : Via Maven Release Plugin (automatique)
mvn release:prepare -Dtag=v1.0.0 -DreleaseVersion=1.0.0 -DdevelopmentVersion=1.1.0-SNAPSHOT
mvn release:perform


════════════════════════
C.5.3 - Ce qu'Alice voit dans Nexus après
════════════════════════

Dans Browse -> maven-releases :
  com/productmanager/product-validator/
  └── 1.0.0/
      ├── product-validator-1.0.0.jar          (15.2 KB)
      ├── product-validator-1.0.0.jar.md5      (hash MD5)
      ├── product-validator-1.0.0.jar.sha1     (hash SHA1)
      ├── product-validator-1.0.0-sources.jar  (8.4 KB)
      ├── product-validator-1.0.0-javadoc.jar  (22.1 KB)
      └── product-validator-1.0.0.pom          (3.8 KB)

Dans Browse -> maven-snapshots :
  com/productmanager/product-validator/
  └── 1.0.0-SNAPSHOT/
      ├── product-validator-1.0.0-20240115.103045-1.jar   (ancien)
      ├── product-validator-1.0.0-20240115.143022-2.jar   (moins ancien)
      ├── product-validator-1.0.0-20240116.090011-3.jar   (plus récent)
      └── maven-metadata.xml (contient la référence au -3 = latest)


════════════════════════════════════════════════════════════════════════
SECTION D : TRAVAIL DE BOB (Développeur Python)
════════════════════════════════════════════════════════════════════════

Bob développe l'API REST Python qui expose les fonctionnalités de validation.
Il utilise la bibliothèque d'Alice via un wrapper Python qu'Alice a aussi publié.

────────────────────────────────────────────────────────────────────────
D.1 - CONFIGURATION PIP DE BOB
────────────────────────────────────────────────────────────────────────

════════════════════════
D.1.1 - Fichier pip.conf global de Bob
════════════════════════

# Sur le poste de Bob : ~/.pip/pip.conf
# (Windows : %APPDATA%\pip\pip.ini)

[global]
# Rediriger tous les téléchargements vers Nexus pypi-all
index-url = http://192.168.1.50:8081/repository/pypi-all/simple
# Nexus pypi-all = Group qui agrège pypi-hosted + pypi-proxy
# pip ira chercher dans nos packages internes EN PREMIER
# puis dans PyPI.org via le proxy si non trouvé localement

# Autoriser le domaine en HTTP (environnement de développement interne)
# En production : Utiliser HTTPS et retirer trusted-host
trusted-host = 192.168.1.50

# Timeout étendu pour les gros packages (numpy, tensorflow, etc.)
timeout = 120

# Afficher la progression du téléchargement
progress-bar = on

[install]
# Reporter les erreurs lors de l'installation
# (utile pour le debug)
report = /tmp/pip-report.json


════════════════════════
D.1.2 - Vérification de la config Bob
════════════════════════

# Bob vérifie que pip utilise bien Nexus :
pip config list

# Sortie attendue :
# global.index-url='http://192.168.1.50:8081/repository/pypi-all/simple'
# global.trusted-host='192.168.1.50'

# Test en installant un package courant
pip install requests

# Les logs montrent :
# Looking in indexes: http://192.168.1.50:8081/repository/pypi-all/simple
# Collecting requests
#   Downloading http://192.168.1.50:8081/repository/pypi-all/.../requests-2.31.0-py3-none-any.whl
# [OK] Téléchargement depuis Nexus !

# Vérifier dans Nexus UI que le package est mis en cache :
# Browse -> pypi-proxy -> Components -> requests


────────────────────────────────────────────────────────────────────────
D.2 - STRUCTURE DU PROJET PYTHON DE BOB
────────────────────────────────────────────────────────────────────────

product-api/
├── pyproject.toml                    <- Configuration moderne Python
├── README.md
├── .python-version                   <- Version Python requise (3.11)
├── requirements.txt                  <- Dépendances figées (pour déploiement)
├── requirements-dev.txt              <- Dépendances dev/test
├── src/
│   └── product_api/
│       ├── __init__.py
│       ├── main.py                   <- Point d'entrée FastAPI
│       ├── models.py                 <- Pydantic models
│       ├── routes/
│       │   ├── __init__.py
│       │   └── products.py           <- Routes /products
│       └── services/
│           ├── __init__.py
│           └── validation_service.py  <- Utilise product-validator-py
└── tests/
    ├── __init__.py
    ├── conftest.py
    └── test_products.py


────────────────────────────────────────────────────────────────────────
D.3 - LE pyproject.toml DE BOB (Complet et commenté)
────────────────────────────────────────────────────────────────────────

# product-api/pyproject.toml

[build-system]
requires = ["setuptools>=68.0", "wheel"]
build-backend = "setuptools.build_meta"

[project]
name = "product-api"
version = "1.0.0"
description = "API REST de gestion des produits - ProductManager"
readme = "README.md"
license = {text = "Proprietary"}
authors = [
    {name = "Bob Dupont", email = "bob@productmanager.local"}
]
requires-python = ">=3.11"

# ====================================================
#  DÉPENDANCES PRINCIPALES
# ====================================================
dependencies = [
    # Framework API
    "fastapi>=0.104.0,<0.200.0",
    "uvicorn[standard]>=0.24.0",

    # Validation et sérialisation
    "pydantic>=2.0.0",
    "pydantic-settings>=2.0.0",

    # Bibliothèque de validation d'Alice
    # Installée depuis Nexus pypi-hosted
    # Version exacte pour reproductibilité
    "product-validator-py==1.0.0",
    #
    # [IDEE] Comment cette dépendance est résolue par pip :
    # 1. pip interroge Nexus pypi-all (group)
    # 2. Nexus cherche dans pypi-hosted -> TROUVÉ! (publié par Alice)
    # 3. pip télécharge product_validator_py-1.0.0-py3-none-any.whl
    # 4. Installation locale
    #
    # Nexus pypi-hosted prend la priorité sur pypi-proxy
    # Donc nos packages internes sont toujours préférés

    # HTTP client pour tests internes
    "httpx>=0.25.0",

    # Base de données (SQLite pour le dev)
    "sqlalchemy>=2.0.0",
    "aiosqlite>=0.19.0",
]

[project.optional-dependencies]
dev = [
    "pytest>=7.4.0",
    "pytest-asyncio>=0.21.0",
    "pytest-cov>=4.1.0",
    "httpx>=0.25.0",
    "black>=23.0.0",
    "ruff>=0.1.0",
    "mypy>=1.5.0",
]

[project.scripts]
product-api = "product_api.main:app"

[tool.setuptools.packages.find]
where = ["src"]

[tool.pytest.ini_options]
testpaths = ["tests"]
asyncio_mode = "auto"

[tool.black]
line-length = 88
target-version = ["py311"]

[tool.ruff]
line-length = 88
target-version = "py311"


────────────────────────────────────────────────────────────────────────
D.4 - LE CODE PYTHON DE BOB
────────────────────────────────────────────────────────────────────────

════════════════════════
D.4.1 - models.py (Modèles Pydantic)
════════════════════════

# src/product_api/models.py

from pydantic import BaseModel, Field, field_validator
from typing import Optional
from decimal import Decimal
import re

class ProductCreate(BaseModel):
    """
    Modèle pour la création d'un produit.
    Validation Pydantic côté entrée HTTP.
    La validation métier est déléguée à product-validator-py.
    """
    sku: str = Field(
        ...,
        description="SKU du produit au format PROD-XXXX",
        example="PROD-ABC1"
    )
    name: str = Field(
        ...,
        min_length=1,
        max_length=200,
        description="Nom du produit",
        example="Widget Super Deluxe"
    )
    price: Decimal = Field(
        ...,
        gt=0,
        le=999999.99,
        decimal_places=2,
        description="Prix en euros (2 décimales max)",
        example=29.99
    )
    stock: int = Field(
        ...,
        ge=0,
        description="Quantité en stock",
        example=100
    )
    category: Optional[str] = Field(
        None,
        description="Catégorie du produit",
        example="Electronics"
    )

    @field_validator("sku")
    @classmethod
    def validate_sku_format(cls, v: str) -> str:
        """Validation Pydantic du format SKU (validation préliminaire)."""
        if not re.match(r'^PROD-[A-Z0-9]{4,8}$', v):
            raise ValueError(
                f"Format SKU invalide: '{v}'. "
                "Format attendu: PROD-[A-Z0-9]{{4,8}}"
            )
        return v


class ProductResponse(BaseModel):
    """Modèle de réponse pour un produit."""
    id: int
    sku: str
    name: str
    price: Decimal
    stock: int
    category: Optional[str]
    is_validated: bool = True


class ValidationError(BaseModel):
    """Détail d'une erreur de validation."""
    field: Optional[str] = None
    message: str


class ValidationResponse(BaseModel):
    """Réponse de validation d'un produit."""
    valid: bool
    errors: list[ValidationError] = []
    error_count: int = 0


════════════════════════
D.4.2 - validation_service.py (Intégration avec la lib d'Alice)
════════════════════════

# src/product_api/services/validation_service.py

"""
Service de validation qui utilise product-validator-py.

product-validator-py est un wrapper Python autour de la bibliothèque
Java développée par Alice (product-validator).

Ce wrapper est publié par Alice sur Nexus pypi-hosted et installé
par Bob via : pip install product-validator-py
"""

from product_validator_py import ProductValidator, ValidationResult
from product_api.models import ProductCreate, ValidationResponse, ValidationError
import logging

logger = logging.getLogger(__name__)


class ValidationService:
    """
    Service encapsulant la logique de validation.
    Utilise la bibliothèque partagée product-validator-py.
    """

    def __init__(self):
        # Instanciation du validateur partagé
        # product-validator-py est installé depuis Nexus pypi-hosted
        self._validator = ProductValidator()
        logger.info(
            f"ValidationService initialisé avec product-validator-py "
            f"v{self._validator.version}"
        )

    def validate_product(self, product: ProductCreate) -> ValidationResponse:
        """
        Valide un produit en utilisant les règles métier partagées.

        Les règles sont définies UNE SEULE FOIS par Alice en Java,
        exposées via le wrapper Python.
        Cela garantit la cohérence entre le service Java et l'API Python.
        """
        # Construire le dictionnaire attendu par le validateur
        product_data = {
            "sku": product.sku,
            "price": float(product.price),
            "stock": product.stock,
            "name": product.name,
        }

        # Appel de la validation (même logique que côté Java)
        result: ValidationResult = self._validator.validate(product_data)

        if result.is_valid:
            logger.debug(f"Produit {product.sku} validé avec succès")
            return ValidationResponse(valid=True)
        else:
            logger.warning(
                f"Produit {product.sku} invalide : {result.errors}"
            )
            validation_errors = [
                ValidationError(message=error)
                for error in result.errors
            ]
            return ValidationResponse(
                valid=False,
                errors=validation_errors,
                error_count=len(validation_errors)
            )

    def is_valid_sku(self, sku: str) -> bool:
        """Délègue la validation SKU à la bibliothèque partagée."""
        return self._validator.is_valid_sku(sku)


════════════════════════
D.4.3 - routes/products.py (Routes FastAPI)
════════════════════════

# src/product_api/routes/products.py

from fastapi import APIRouter, HTTPException, Depends, status
from fastapi.responses import JSONResponse
from product_api.models import (
    ProductCreate,
    ProductResponse,
    ValidationResponse
)
from product_api.services.validation_service import ValidationService
import logging

logger = logging.getLogger(__name__)
router = APIRouter(prefix="/products", tags=["products"])


def get_validation_service() -> ValidationService:
    """Dependency injection du service de validation."""
    return ValidationService()


@router.post(
    "/",
    response_model=ProductResponse,
    status_code=status.HTTP_201_CREATED,
    summary="Créer un nouveau produit",
    description="Crée un produit après validation complète via product-validator-py"
)
async def create_product(
    product: ProductCreate,
    validation_svc: ValidationService = Depends(get_validation_service)
):
    """
    Endpoint de création de produit.

    Flux de traitement :
    1. Pydantic valide le format HTTP (types, formats)
    2. ValidationService valide les règles métier (via product-validator-py)
    3. Si valide -> Sauvegarde en base
    4. Si invalide -> 422 avec détails des erreurs
    """
    # Validation métier via la bibliothèque partagée d'Alice
    validation = validation_svc.validate_product(product)

    if not validation.valid:
        logger.warning(
            f"Tentative de création d'un produit invalide : {product.sku}"
        )
        raise HTTPException(
            status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
            detail={
                "message": "Données produit invalides",
                "errors": [e.dict() for e in validation.errors]
            }
        )

    # TODO: Sauvegarder en base de données
    # Pour l'exemple, on renvoie un objet fictif
    logger.info(f"Produit créé avec succès : {product.sku}")

    return ProductResponse(
        id=1,
        sku=product.sku,
        name=product.name,
        price=product.price,
        stock=product.stock,
        category=product.category,
        is_validated=True
    )


@router.post(
    "/validate",
    response_model=ValidationResponse,
    summary="Valider un produit sans le créer"
)
async def validate_product(
    product: ProductCreate,
    validation_svc: ValidationService = Depends(get_validation_service)
):
    """
    Endpoint de validation seule.
    Utile pour les clients qui veulent valider avant d'envoyer.
    """
    return validation_svc.validate_product(product)


════════════════════════
D.4.4 - main.py (Application FastAPI)
════════════════════════

# src/product_api/main.py

from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from product_api.routes.products import router as products_router
import logging

# Configuration du logging
logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s - %(name)s - %(levelname)s - %(message)s"
)

app = FastAPI(
    title="Product Manager API",
    description="""
    ## API de gestion des produits

    Cette API utilise **product-validator-py** (publié sur Nexus) pour
    la validation des règles métier. La bibliothèque de validation est
    développée en Java par Alice et wrappée en Python.

    Cela garantit que les règles métier sont identiques quel que soit
    le service qui les utilise.
    """,
    version="1.0.0",
    docs_url="/docs",
    redoc_url="/redoc"
)

app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_methods=["*"],
    allow_headers=["*"],
)

app.include_router(products_router)

@app.get("/health", tags=["system"])
async def health_check():
    """Health check endpoint."""
    return {
        "status": "healthy",
        "service": "product-api",
        "version": "1.0.0"
    }

if __name__ == "__main__":
    import uvicorn
    uvicorn.run(app, host="0.0.0.0", port=8000, reload=True)


────────────────────────────────────────────────────────────────────────
D.5 - SETUP ENVIRONNEMENT PYTHON DE BOB
────────────────────────────────────────────────────────────────────────

════════════════════════
D.5.1 - Création de l'environnement virtuel
════════════════════════

# Bob crée son environnement de développement

# 1. Créer le projet
mkdir product-api && cd product-api

# 2. Créer environnement virtuel (isolé du système)
python3.11 -m venv venv

# 3. Activer l'environnement
source venv/bin/activate      # Linux/Mac
# venv\Scripts\activate       # Windows

# 4. Vérifier que pip utilise bien la bonne version
which pip
# -> /home/bob/product-api/venv/bin/pip

# 5. Installer les dépendances (depuis Nexus via pip.conf)
pip install -e ".[dev]"

# Sortie attendue :
# Looking in indexes: http://192.168.1.50:8081/repository/pypi-all/simple
# Obtaining file:///home/bob/product-api
# Collecting fastapi>=0.104.0
#   Downloading http://192.168.1.50:8081/.../fastapi-0.104.1-py3-none-any.whl (92 kB)
# Collecting product-validator-py==1.0.0
#   Downloading http://192.168.1.50:8081/.../product_validator_py-1.0.0-py3-none-any.whl (12 kB)
#   <- Installé depuis pypi-HOSTED (le package d'Alice !)
# ...
# Successfully installed fastapi-0.104.1 product-validator-py-1.0.0 ...

# 6. Générer requirements.txt figé (pour le déploiement)
pip freeze > requirements.txt

# requirements.txt contient les versions exactes de tout :
# fastapi==0.104.1
# product-validator-py==1.0.0
# uvicorn==0.24.0
# pydantic==2.5.0
# ...


════════════════════════
D.5.2 - Lancer l'API en développement
════════════════════════

# Activer le venv si pas déjà fait
source venv/bin/activate

# Lancer le serveur de développement
uvicorn product_api.main:app --reload --host 0.0.0.0 --port 8000

# Sortie :
# INFO:     Will watch for changes in these directories: ['/home/bob/product-api']
# INFO:     Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit)
# INFO:     ValidationService initialisé avec product-validator-py v1.0.0

# Tester l'API
curl -X POST http://localhost:8000/products/ \
  -H "Content-Type: application/json" \
  -d '{
    "sku": "PROD-ABC1",
    "name": "Widget Super Deluxe",
    "price": 29.99,
    "stock": 100,
    "category": "Electronics"
  }'

# Réponse :
# {
#   "id": 1,
#   "sku": "PROD-ABC1",
#   "name": "Widget Super Deluxe",
#   "price": 29.99,
#   "stock": 100,
#   "category": "Electronics",
#   "is_validated": true
# }

# Tester avec un SKU invalide
curl -X POST http://localhost:8000/products/ \
  -H "Content-Type: application/json" \
  -d '{
    "sku": "invalid-sku",
    "name": "Produit test",
    "price": 10.00,
    "stock": 5
  }'

# Réponse :
# HTTP 422 Unprocessable Entity
# {
#   "detail": {
#     "message": "Données produit invalides",
#     "errors": [
#       {"message": "SKU invalide : 'invalid-sku'. Format attendu : PROD-[A-Z0-9]{4,8}"}
#     ]
#   }
# }

# Documentation interactive disponible :
# http://localhost:8000/docs    (Swagger UI)
# http://localhost:8000/redoc  (ReDoc)


════════════════════════
D.5.3 - Publication d'un package Python par Bob
════════════════════════

Bob développe aussi des utilitaires Python réutilisables.
Il les publie sur Nexus pour que l'équipe puisse les utiliser.

# Exemple : Publier product-api-client sur Nexus
# (Client Python pour l'API, réutilisable dans d'autres services)

# Structure du package :
product-api-client/
├── pyproject.toml
└── src/
    └── product_api_client/
        ├── __init__.py
        └── client.py

# pyproject.toml du client :
[project]
name = "product-api-client"
version = "1.0.0"
description = "Client Python pour l'API ProductManager"
dependencies = ["httpx>=0.25.0", "pydantic>=2.0.0"]

# Configuration de publication vers Nexus
[tool.twine]
repository-url = "http://192.168.1.50:8081/repository/pypi-hosted/"
skip-existing = true

# ~/.pypirc de Bob
[distutils]
index-servers =
    nexus

[nexus]
repository = http://192.168.1.50:8081/repository/pypi-hosted/
username = bob
password = BobSecure@2024!

# Builder le package
pip install build
python -m build

# Sortie :
# Successfully built product_api_client-1.0.0.tar.gz
# Successfully built product_api_client-1.0.0-py3-none-any.whl

# Publier vers Nexus
pip install twine
twine upload -r nexus dist/*

# Sortie :
# Uploading distributions to http://192.168.1.50:8081/repository/pypi-hosted/
# Uploading product_api_client-1.0.0-py3-none-any.whl
# 100% ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 8.2/8.2 kB • 00:00
# Uploading product_api_client-1.0.0.tar.gz
# 100% ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 6.1/6.1 kB • 00:00

# [OK] Vérifier dans Nexus :
# Browse -> pypi-hosted -> product-api-client / 1.0.0


════════════════════════════════════════════════════════════════════════
SECTION E : COLLABORATION ET INTÉGRATION ENTRE LES TROIS
════════════════════════════════════════════════════════════════════════

────────────────────────────────────────────────────────────────────────
E.1 - LE WRAPPER PYTHON D'ALICE (product-validator-py)
────────────────────────────────────────────────────────────────────────

Alice crée un wrapper Python de sa bibliothèque Java.
Ce wrapper est publié sur Nexus et utilisé par Bob.

Structure :
product-validator-py/
├── pyproject.toml
├── README.md
└── src/
    └── product_validator_py/
        ├── __init__.py
        └── validator.py       <- Wrapper Python pur (réimplémentation)


════════════════════════
E.1.1 - validator.py (Implémentation Python des règles Java)
════════════════════════

# src/product_validator_py/validator.py

"""
Wrapper Python de la bibliothèque Java product-validator.

Cette classe réimplémente en Python PUR les règles de validation
définies dans la bibliothèque Java d'Alice.

IMPORTANT : Les règles doivent rester synchronisées !
Quand Alice modifie une règle en Java -> Alice met à jour ce fichier.
-> Publier une nouvelle version sur Nexus (ex: 1.1.0)
-> Bob mettra à jour sa dépendance

Versions :
- product-validator (Java) : 1.0.0
- product-validator-py (Python) : 1.0.0  <- Correspondance
"""

import re
from dataclasses import dataclass, field
from typing import Any

__version__ = "1.0.0"


@dataclass
class ValidationResult:
    """
    Résultat de validation, équivalent à
    com.productmanager.validator.ValidationResult en Java.
    """
    is_valid: bool
    errors: list[str] = field(default_factory=list)
    error_count: int = 0

    def __post_init__(self):
        self.error_count = len(self.errors)

    @property
    def first_error(self) -> str | None:
        """Premier message d'erreur ou None."""
        return self.errors[0] if self.errors else None

    def __str__(self) -> str:
        if self.is_valid:
            return "ValidationResult{VALID}"
        return f"ValidationResult{{INVALID, errors={self.errors}}}"


class ProductValidator:
    """
    Validateur de produits - équivalent Python de
    com.productmanager.validator.ProductValidator.

    Exemple d'usage :
        validator = ProductValidator()
        result = validator.validate({
            "sku": "PROD-ABC1",
            "price": 29.99,
            "stock": 100,
            "name": "Widget"
        })
        if result.is_valid:
            print("Produit valide!")
    """

    # Format SKU : PROD- suivi de 4 à 8 caractères alphanumériques majuscules
    _SKU_PATTERN = re.compile(r'^PROD-[A-Z0-9]{4,8}$')

    # Prix maximum
    _MAX_PRICE = 999_999.99

    @property
    def version(self) -> str:
        """Version du validateur (correspond à la version Java)."""
        return __version__

    def validate(self, product_data: dict[str, Any]) -> ValidationResult:
        """
        Valide un produit selon toutes les règles métier.

        Args:
            product_data: Dictionnaire avec :
                - "sku"   : str  (obligatoire)
                - "price" : float (obligatoire)
                - "stock" : int   (obligatoire)
                - "name"  : str   (obligatoire)

        Returns:
            ValidationResult avec is_valid et liste d'erreurs
        """
        errors = []

        # Règle 1 : Validation SKU
        errors.extend(self._validate_sku(product_data))

        # Règle 2 : Validation prix
        errors.extend(self._validate_price(product_data))

        # Règle 3 : Validation stock
        errors.extend(self._validate_stock(product_data))

        # Règle 4 : Validation nom
        errors.extend(self._validate_name(product_data))

        return ValidationResult(is_valid=len(errors) == 0, errors=errors)

    def is_valid_sku(self, sku: str) -> bool:
        """
        Valide uniquement le format d'un SKU.
        Équivalent à ProductValidator.isValidSku() en Java.
        """
        return sku is not None and bool(self._SKU_PATTERN.match(sku))

    def is_valid_price(self, price: float) -> bool:
        """
        Valide uniquement un prix.
        Équivalent à ProductValidator.isValidPrice() en Java.
        """
        return 0 < price <= self._MAX_PRICE

    def _validate_sku(self, data: dict) -> list[str]:
        """Règle de validation SKU (doit rester synchronisée avec Java)."""
        errors = []
        sku = data.get("sku")

        if sku is None:
            errors.append("Le champ 'sku' est obligatoire")
        elif not str(sku).strip():
            errors.append("Le SKU ne peut pas être vide")
        elif not self._SKU_PATTERN.match(str(sku).strip()):
            errors.append(
                f"SKU invalide : '{sku}'. "
                "Format attendu : PROD-[A-Z0-9]{4,8} "
                "(ex: PROD-ABC1, PROD-XY12Z)"
            )

        return errors

    def _validate_price(self, data: dict) -> list[str]:
        """Règle de validation prix (doit rester synchronisée avec Java)."""
        errors = []
        price = data.get("price")

        if price is None:
            errors.append("Le champ 'price' est obligatoire")
            return errors

        try:
            price_float = float(price)
            if price_float <= 0:
                errors.append(
                    f"Le prix doit être positif. Valeur reçue : {price_float:.2f}"
                )
            elif price_float > self._MAX_PRICE:
                errors.append(
                    f"Le prix dépasse le maximum autorisé ({self._MAX_PRICE:.2f}). "
                    f"Valeur reçue : {price_float:.2f}"
                )

            # Vérification décimales
            price_str = str(price)
            if '.' in price_str and len(price_str.split('.')[1]) > 2:
                errors.append(
                    f"Le prix ne peut pas avoir plus de 2 décimales. "
                    f"Valeur reçue : {price}"
                )
        except (ValueError, TypeError):
            errors.append(f"Le prix doit être un nombre. Valeur reçue : {price}")

        return errors

    def _validate_stock(self, data: dict) -> list[str]:
        """Règle de validation stock."""
        errors = []
        stock = data.get("stock")

        if stock is None:
            errors.append("Le champ 'stock' est obligatoire")
        else:
            try:
                stock_int = int(stock)
                if stock_int < 0:
                    errors.append(
                        f"Le stock ne peut pas être négatif. Valeur reçue : {stock_int}"
                    )
            except (ValueError, TypeError):
                errors.append(f"Le stock doit être un entier. Valeur reçue : {stock}")

        return errors

    def _validate_name(self, data: dict) -> list[str]:
        """Règle de validation du nom."""
        errors = []
        name = data.get("name")

        if name is None:
            errors.append("Le champ 'name' est obligatoire")
        elif not str(name).strip():
            errors.append("Le nom du produit ne peut pas être vide")
        elif len(str(name)) > 200:
            errors.append(
                f"Le nom dépasse 200 caractères. Longueur actuelle : {len(str(name))}"
            )

        return errors


════════════════════════
E.1.2 - Publication du wrapper par Alice
════════════════════════

# pyproject.toml de product-validator-py
[build-system]
requires = ["setuptools>=68.0", "wheel"]
build-backend = "setuptools.build_meta"

[project]
name = "product-validator-py"
version = "1.0.0"
description = "Wrapper Python de product-validator (bibliothèque Java)"
requires-python = ">=3.11"
dependencies = []   # Aucune dépendance externe !

# ~/.pypirc d'Alice (pour publier sur Nexus)
[distutils]
index-servers = nexus
[nexus]
repository = http://192.168.1.50:8081/repository/pypi-hosted/
username = alice
password = AliceSecure@2024!

# Build et publication par Alice
cd product-validator-py
python -m build
twine upload -r nexus dist/*

# [OK] Disponible pour Bob via :
# pip install product-validator-py==1.0.0


────────────────────────────────────────────────────────────────────────
E.2 - CI/CD PAR CHARLIE (GitHub Actions)
────────────────────────────────────────────────────────────────────────

Charlie configure les pipelines CI/CD pour automatiser :
- Les tests à chaque push
- Les déploiements vers Nexus à chaque merge

════════════════════════
E.2.1 - Pipeline Java (product-validator)
════════════════════════

# .github/workflows/java-ci.yml

name: Java CI - product-validator

on:
  push:
    branches: [ main, develop ]
    paths:
      - 'product-validator/**'
  pull_request:
    branches: [ main ]
    paths:
      - 'product-validator/**'

jobs:
  test:
    name: Tests Java
    runs-on: ubuntu-latest

    steps:
      - name: Checkout du code
        uses: actions/checkout@v4

      - name: Installation Java 17
        uses: actions/setup-java@v4
        with:
          java-version: '17'
          distribution: 'temurin'
          cache: 'maven'

      # Configurer Maven pour utiliser Nexus
      # Les credentials sont dans les GitHub Secrets du repo
      - name: Configuration Maven -> Nexus
        run: |
          mkdir -p ~/.m2
          cat > ~/.m2/settings.xml << EOF
          <settings>
            <servers>
              <server>
                <id>nexus-releases</id>
                <username>${{ secrets.NEXUS_USERNAME }}</username>
                <password>${{ secrets.NEXUS_PASSWORD }}</password>
              </server>
              <server>
                <id>nexus-snapshots</id>
                <username>${{ secrets.NEXUS_USERNAME }}</username>
                <password>${{ secrets.NEXUS_PASSWORD }}</password>
              </server>
              <server>
                <id>nexus-public</id>
                <username>${{ secrets.NEXUS_USERNAME }}</username>
                <password>${{ secrets.NEXUS_PASSWORD }}</password>
              </server>
            </servers>
            <mirrors>
              <mirror>
                <id>nexus-mirror</id>
                <url>http://192.168.1.50:8081/repository/maven-public/</url>
                <mirrorOf>*</mirrorOf>
              </mirror>
            </mirrors>
          </settings>
          EOF

      - name: Tests Maven
        working-directory: product-validator
        run: mvn clean test

      - name: Rapport de tests
        uses: dorny/test-reporter@v1
        if: always()
        with:
          name: Tests JUnit
          path: product-validator/target/surefire-reports/*.xml
          reporter: java-junit

  deploy-snapshot:
    name: Déploiement SNAPSHOT -> Nexus
    runs-on: ubuntu-latest
    needs: test
    # Seulement sur la branche develop (pas main)
    if: github.ref == 'refs/heads/develop'

    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-java@v4
        with:
          java-version: '17'
          distribution: 'temurin'
          cache: 'maven'

      - name: Configuration Maven
        run: |
          # (même config que ci-dessus)
          mkdir -p ~/.m2
          # ...

      - name: Deploy SNAPSHOT vers Nexus
        working-directory: product-validator
        run: |
          # S'assurer que c'est bien une version SNAPSHOT
          VERSION=$(mvn help:evaluate -Dexpression=project.version -q -DforceStdout)
          if [[ "$VERSION" != *"-SNAPSHOT" ]]; then
            echo "Erreur: version $VERSION n'est pas un SNAPSHOT"
            exit 1
          fi
          mvn clean deploy -DskipTests

      - name: Notification Slack
        uses: rtCamp/action-slack-notify@v2
        env:
          SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}
          SLACK_MESSAGE: |
            [OK] product-validator SNAPSHOT déployé sur Nexus
            Version: ${{ steps.version.outputs.version }}
            Par: ${{ github.actor }}

  deploy-release:
    name: Déploiement RELEASE -> Nexus
    runs-on: ubuntu-latest
    needs: test
    # Seulement sur la branche main ET si c'est un tag
    if: github.ref == 'refs/heads/main' && startsWith(github.ref, 'refs/tags/v')

    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-java@v4
        with:
          java-version: '17'
          distribution: 'temurin'

      - name: Deploy RELEASE vers Nexus
        working-directory: product-validator
        run: |
          VERSION=$(mvn help:evaluate -Dexpression=project.version -q -DforceStdout)
          if [[ "$VERSION" == *"-SNAPSHOT" ]]; then
            echo "Erreur: Impossible de release une version SNAPSHOT"
            exit 1
          fi
          mvn clean deploy -DskipTests
          echo "[OK] Release $VERSION déployée sur Nexus maven-releases"

      - name: Créer GitHub Release
        uses: actions/create-release@v1
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        with:
          tag_name: ${{ github.ref }}
          release_name: Release ${{ github.ref }}
          body: "Déployé sur Nexus maven-releases"


════════════════════════
E.2.2 - Pipeline Python (product-api)
════════════════════════

# .github/workflows/python-ci.yml

name: Python CI - product-api

on:
  push:
    branches: [ main, develop ]
    paths:
      - 'product-api/**'
  pull_request:
    branches: [ main ]
    paths:
      - 'product-api/**'

jobs:
  test:
    name: Tests Python
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - name: Installation Python 3.11
        uses: actions/setup-python@v4
        with:
          python-version: '3.11'
          cache: 'pip'

      # Configurer pip pour utiliser Nexus
      - name: Configuration pip -> Nexus
        run: |
          mkdir -p ~/.pip
          cat > ~/.pip/pip.conf << EOF
          [global]
          index-url = http://${{ secrets.NEXUS_USERNAME }}:${{ secrets.NEXUS_PASSWORD }}@192.168.1.50:8081/repository/pypi-all/simple
          trusted-host = 192.168.1.50
          timeout = 120
          EOF
          #
          # IMPORTANT :
          # index-url avec credentials embarqués dans l'URL
          # Alternative sécurisée : Utiliser keyring ou .netrc

      - name: Installation des dépendances
        working-directory: product-api
        run: |
          pip install -e ".[dev]"
          # pip télécharge depuis Nexus pypi-all (group)
          # - product-validator-py depuis pypi-hosted (package interne)
          # - fastapi, pydantic, etc. depuis pypi-proxy (cache PyPI.org)

      - name: Linting avec Ruff
        working-directory: product-api
        run: ruff check src/ tests/

      - name: Formatage avec Black
        working-directory: product-api
        run: black --check src/ tests/

      - name: Typage avec mypy
        working-directory: product-api
        run: mypy src/

      - name: Tests avec pytest
        working-directory: product-api
        run: |
          pytest tests/ \
            --cov=product_api \
            --cov-report=xml \
            --cov-report=term-missing \
            -v

      - name: Upload couverture
        uses: codecov/codecov-action@v3

  build-docker:
    name: Build image Docker
    runs-on: ubuntu-latest
    needs: test

    steps:
      - uses: actions/checkout@v4

      # Autoriser le registry HTTP interne
      - name: Configurer Docker insecure registry
        run: |
          cat > /etc/docker/daemon.json << EOF
          {"insecure-registries": ["192.168.1.50:8082"]}
          EOF
          sudo systemctl daemon-reload
          sudo systemctl restart docker

      - name: Login Docker Nexus
        run: |
          docker login 192.168.1.50:8082 \
            -u ${{ secrets.NEXUS_USERNAME }} \
            -p ${{ secrets.NEXUS_PASSWORD }}

      - name: Build image Docker
        working-directory: product-api
        run: |
          VERSION=$(cat pyproject.toml | grep '^version' | cut -d'"' -f2)
          docker build \
            --build-arg NEXUS_URL=http://192.168.1.50:8081/repository/pypi-all/simple \
            --build-arg NEXUS_USER=${{ secrets.NEXUS_USERNAME }} \
            --build-arg NEXUS_PASS=${{ secrets.NEXUS_PASSWORD }} \
            -t 192.168.1.50:8082/product-api:${VERSION} \
            -t 192.168.1.50:8082/product-api:latest \
            .

      - name: Push vers Nexus Docker Registry
        run: |
          VERSION=$(cat product-api/pyproject.toml | grep '^version' | cut -d'"' -f2)
          docker push 192.168.1.50:8082/product-api:${VERSION}
          docker push 192.168.1.50:8082/product-api:latest
          echo "[OK] Image Docker poussée vers Nexus"


════════════════════════
E.2.3 - Dockerfile de l'API Python (avec Nexus)
════════════════════════

# product-api/Dockerfile

# Stage 1 : Build
# Utilise l'image Python depuis le cache Docker Nexus
FROM 192.168.1.50:8083/python:3.11-slim AS builder
# ^ 192.168.1.50:8083 = docker-proxy (cache Docker Hub)
# python:3.11-slim sera téléchargé depuis Docker Hub UNE SEULE FOIS
# et ensuite servi depuis le cache Nexus

WORKDIR /build

# Arguments pour Nexus (passés au build)
ARG NEXUS_URL
ARG NEXUS_USER
ARG NEXUS_PASS

# Configurer pip pour utiliser Nexus pendant le build Docker
# IMPORTANT : Pas de credentials dans l'image finale !
RUN pip config set global.index-url http://${NEXUS_USER}:${NEXUS_PASS}@192.168.1.50:8081/repository/pypi-all/simple && \
    pip config set global.trusted-host 192.168.1.50

# Copier et installer dépendances
COPY pyproject.toml .
RUN pip install --no-cache-dir build

COPY src/ src/
RUN pip install --no-cache-dir ".[dev]"

# Stage 2 : Image finale (légère, sans credentials Nexus)
FROM 192.168.1.50:8083/python:3.11-slim AS runtime

WORKDIR /app

# Copier les packages installés depuis le stage builder
COPY --from=builder /usr/local/lib/python3.11/site-packages /usr/local/lib/python3.11/site-packages
COPY --from=builder /usr/local/bin /usr/local/bin
COPY src/ src/

# Variables d'environnement de l'application
ENV PYTHONPATH=/app/src
ENV PORT=8000
ENV LOG_LEVEL=info

# Exposition du port
EXPOSE 8000

# Health check
HEALTHCHECK --interval=30s --timeout=10s --start-period=30s --retries=3 \
    CMD curl -f http://localhost:8000/health || exit 1

# Commande de démarrage
CMD ["uvicorn", "product_api.main:app", \
     "--host", "0.0.0.0", \
     "--port", "8000", \
     "--workers", "4"]


════════════════════════════════════════════════════════════════════════
SECTION F : SCÉNARIOS DU QUOTIDIEN
════════════════════════════════════════════════════════════════════════

Voici des situations réelles vécues par l'équipe et comment Nexus
intervient dans chaque cas.

────────────────────────────────────────────────────────────────────────
F.1 - SCÉNARIO 1 : Alice publie une correction de bug
────────────────────────────────────────────────────────────────────────

SITUATION :
Bob remonte un bug : la validation du prix accepte les prix avec
3 décimales (ex: 10.999) alors qu'elle ne devrait pas.
Alice doit corriger et republier.

WORKFLOW :
──────────

# Alice corrige le bug dans PriceValidationRule.java
# Le bug : La vérification des décimales ne fonctionnait pas pour les Doubles

# 1. Alice code la correction dans PriceValidationRule.java

# 2. Alice met à jour la version SNAPSHOT (développement de 1.0.1)
# pom.xml : <version>1.0.1-SNAPSHOT</version>

# 3. Alice run les tests (y compris le test qui reproduit le bug)
mvn clean test

# 4. Alice déploie le SNAPSHOT
mvn clean deploy

# Le SNAPSHOT 1.0.1-SNAPSHOT est dans maven-snapshots
# Bob peut déjà tester si besoin

# 5. Alice corrige AUSSI le wrapper Python product-validator-py
# validator.py : méthode _validate_price()
# pyproject.toml : version = "1.0.1"

# 6. Alice build et publie le wrapper Python
python -m build
twine upload -r nexus dist/*

# -> product-validator-py-1.0.1 disponible sur Nexus pypi-hosted

# 7. Alice prévient Bob dans Slack :
# "J'ai corrigé le bug du prix. product-validator-py 1.0.1 est dispo sur Nexus.
#  Mets à jour ta dépendance : product-validator-py==1.0.1"

# 8. Bob met à jour dans pyproject.toml :
# "product-validator-py==1.0.1"
# pip install -e .
# -> Nexus télécharge la nouvelle version depuis pypi-hosted

# 9. Bob vérifie que le bug est corrigé dans son API
uvicorn product_api.main:app --reload
curl -X POST http://localhost:8000/products/ \
  -H "Content-Type: application/json" \
  -d '{"sku": "PROD-ABC1", "name": "Test", "price": 10.999, "stock": 5}'
# Expected : 422 avec message sur les décimales [OK]

# 10. Alice fait la release officielle
# pom.xml : <version>1.0.1</version>
mvn clean deploy

# La release 1.0.1 est dans maven-releases
# Immuable, versionnée, traçable dans Nexus


────────────────────────────────────────────────────────────────────────
F.2 - SCÉNARIO 2 : Bob installe des dépendances lourdes (numpy, pandas)
────────────────────────────────────────────────────────────────────────

SITUATION :
Bob doit ajouter des statistiques à l'API. Il a besoin de numpy et pandas.
Sans Nexus : 300+ MB à télécharger à chaque build CI.
Avec Nexus : Téléchargé UNE FOIS, mis en cache, servi en local ensuite.

WORKFLOW :
──────────

# 1. Bob ajoute les dépendances dans pyproject.toml
[project]
dependencies = [
    ...
    "numpy>=1.26.0",
    "pandas>=2.1.0",
]

# 2. Première installation (sur le poste de Bob)
pip install -e .
# pip télécharge depuis PyPI.org via le proxy Nexus
# Le téléchargement prend 2-3 minutes (300+ MB depuis internet)

# 3. Nexus met en cache numpy et pandas dans pypi-proxy

# 4. Deuxième installation (sur le poste de Charlie ou en CI)
pip install -e .
# <- pip télécharge depuis Nexus pypi-proxy (cache local)
# -> 10x plus rapide (réseau interne vs internet)

# Vérifier dans Nexus UI :
# Browse -> pypi-proxy -> Components
# numpy / 1.26.2   (en cache)
# pandas / 2.1.4   (en cache)

# En CI, économies :
# Avant Nexus : 5 minutes à chaque build (téléchargement internet)
# Après Nexus : 30 secondes (téléchargement réseau interne)


────────────────────────────────────────────────────────────────────────
F.3 - SCÉNARIO 3 : Charlie configure le cleanup automatique
────────────────────────────────────────────────────────────────────────

SITUATION :
Après 3 mois, maven-snapshots est plein de vieilles versions SNAPSHOT.
Charlie configure le cleanup automatique.

WORKFLOW :
──────────

# 1. Charlie crée une Cleanup Policy dans Nexus UI

# System -> Cleanup Policies -> Create Cleanup Policy
# Name               : cleanup-old-snapshots
# Format             : maven2
# Cleanup Criteria   :
#   Published Before : 30 days
#   Asset Name Matcher: .*-SNAPSHOT.*

# 2. Charlie applique la policy au repository
# Repositories -> maven-snapshots -> Edit
# Cleanup Policies : [x] cleanup-old-snapshots
# Save

# 3. Charlie crée une Cleanup Policy pour PyPI
# Name               : cleanup-old-pypi-snapshots
# Format             : pypi
# Published Before   : 30 days

# Applique à pypi-hosted
# Repositories -> pypi-hosted -> Edit
# Cleanup Policies : [x] cleanup-old-pypi-snapshots

# 4. Charlie vérifie les tasks automatiques
# System -> Tasks -> Voir "Repository - Cleanup repositories using their associated policies"
# -> Exécution automatique quotidienne à 1h du matin

# 5. Charlie exécute manuellement pour voir l'effet
# System -> Tasks -> "Repository - Delete orphaned snapshot artifacts"
# -> Run

# 6. Compacter le blob store pour libérer l'espace réel
# System -> Tasks -> "Admin - Compact blob store"
# Blob store : default -> Run

# Résultat :
# Avant : 45 GB utilisés
# Après : 28 GB utilisés
# -> 17 GB libérés en nettoyant les snapshots obsolètes !


────────────────────────────────────────────────────────────────────────
F.4 - SCÉNARIO 4 : L'internet tombe pendant un sprint
────────────────────────────────────────────────────────────────────────

SITUATION :
Un vendredi après-midi, la connexion internet de l'équipe est coupée.
Sprint critique, les builds doivent continuer.

AVEC NEXUS :
──────────

# Alice continue à builder normalement
mvn clean install
# Maven cherche les dépendances dans Nexus
# Nexus a tout en cache (maven-central-proxy)
# [OK] Build réussi même sans internet !

# Bob continue aussi
pip install -r requirements.txt
# pip télécharge depuis pypi-proxy (tout en cache)
# [OK] Installation réussie même sans internet !

# Les packages internes (product-validator-py, product-api-client)
# sont dans pypi-hosted -> Pas besoin d'internet du tout

# Docker pulls depuis docker-proxy (cache Docker Hub)
docker pull 192.168.1.50:8083/python:3.11-slim
# [OK] L'image est en cache dans Nexus

SANS NEXUS (simulation) :
────────────────────────
# mvn clean install -> BLOQUÉ sur téléchargement Maven Central [X]
# pip install requests -> timeout sur PyPI.org [X]
# Toute l'équipe est bloquée [X]


────────────────────────────────────────────────────────────────────────
F.5 - SCÉNARIO 5 : Onboarding d'un 4ème développeur (Diana)
────────────────────────────────────────────────────────────────────────

SITUATION :
Diana rejoint l'équipe. Charlie lui crée un compte Nexus et lui envoie
un guide d'onboarding. En 30 minutes, Diana est opérationnelle.

WORKFLOW ONBOARDING :
─────────────────────

# 1. Charlie crée le compte Diana dans Nexus
# Security -> Users -> Create local user
# ID       : diana
# Role     : java-developer-role + python-developer-role
# Password : TemporaryPass@2024! (Diana devra changer)

# 2. Charlie envoie un email d'onboarding avec :
# - URL Nexus : http://192.168.1.50:8081
# - Credentials temporaires
# - Instructions ci-dessous

# ──────────────────────────────────────────────
# GUIDE ONBOARDING NEXUS - Diana
# ──────────────────────────────────────────────

# ÉTAPE 1 : Configurer Maven (si Java)
mkdir -p ~/.m2
cat > ~/.m2/settings.xml << 'EOF'
<settings>
  <servers>
    <server><id>nexus-releases</id><username>diana</username><password>TemporaryPass@2024!</password></server>
    <server><id>nexus-snapshots</id><username>diana</username><password>TemporaryPass@2024!</password></server>
    <server><id>nexus-public</id><username>diana</username><password>TemporaryPass@2024!</password></server>
  </servers>
  <mirrors>
    <mirror>
      <id>nexus</id>
      <url>http://192.168.1.50:8081/repository/maven-public/</url>
      <mirrorOf>*</mirrorOf>
    </mirror>
  </mirrors>
</settings>
EOF

# ÉTAPE 2 : Configurer pip (si Python)
mkdir -p ~/.pip
cat > ~/.pip/pip.conf << 'EOF'
[global]
index-url = http://192.168.1.50:8081/repository/pypi-all/simple
trusted-host = 192.168.1.50
EOF

# ÉTAPE 3 : Configurer npm (si JavaScript)
cat >> ~/.npmrc << 'EOF'
registry=http://192.168.1.50:8081/repository/npm-group/
always-auth=true
_auth=GENERER_AVEC_echo -n "diana:TemporaryPass@2024!" | base64
EOF

# ÉTAPE 4 : Configurer Docker
sudo tee /etc/docker/daemon.json << 'EOF'
{"insecure-registries": ["192.168.1.50:8082", "192.168.1.50:8083"]}
EOF
sudo systemctl restart docker

# ÉTAPE 5 : Tester que tout fonctionne
mvn archetype:generate -DgroupId=com.test -DartifactId=test -DinteractiveMode=false
cd test && mvn clean install
# [OK] Si ça marche -> Maven est configuré

pip install requests
python -c "import requests; print('pip OK')"
# [OK] Si ça marche -> pip est configuré

docker pull 192.168.1.50:8083/nginx:alpine
# [OK] Si ça marche -> Docker est configuré

# 3. Diana peut cloner les projets et commencer à travailler immédiatement !


════════════════════════════════════════════════════════════════════════
SECTION G : TROUBLESHOOTING VÉCU PAR L'ÉQUIPE
════════════════════════════════════════════════════════════════════════

────────────────────────────────────────────────────────────────────────
G.1 - PROBLÈME : Alice ne peut pas déployer (401 Unauthorized)
────────────────────────────────────────────────────────────────────────

SYMPTÔME :
mvn clean deploy
# [ERROR] Failed to execute goal org.apache.maven.plugins:maven-deploy-plugin...
# [ERROR] Return code is: 401, ReasonPhrase: Unauthorized.

DIAGNOSTIC :
──────────

# 1. Vérifier que l'<id> dans pom.xml correspond à settings.xml
# pom.xml
<distributionManagement>
  <repository>
    <id>nexus-releases</id>   <- CET ID
    ...
  </repository>
</distributionManagement>

# settings.xml
<server>
  <id>nexus-releases</id>    <- DOIT ÊTRE IDENTIQUE
  <username>alice</username>
  <password>AliceSecure@2024!</password>
</server>

# 2. Vérifier le mot de passe
curl -u alice:AliceSecure@2024! http://192.168.1.50:8081/service/rest/v1/status
# Si 200 -> Credentials OK
# Si 401 -> Mauvais mot de passe

# 3. Vérifier les permissions
# Nexus UI -> Security -> Users -> alice -> Voir rôles
# Le rôle java-developer-role doit inclure :
# nx-repository-view-maven2-maven-releases-add

SOLUTION :
─────────
# Charlie vérifie les permissions dans Nexus UI
# et corrige le rôle si nécessaire.
# Ou Alice s'est trompée de mot de passe -> Réinitialiser.


────────────────────────────────────────────────────────────────────────
G.2 - PROBLÈME : Bob ne trouve pas product-validator-py (404)
────────────────────────────────────────────────────────────────────────

SYMPTÔME :
pip install product-validator-py==1.0.0
# ERROR: Could not find a version that satisfies the requirement product-validator-py==1.0.0
# ERROR: No matching distribution found for product-validator-py==1.0.0

DIAGNOSTIC :
──────────

# 1. Vérifier que pip utilise bien Nexus
pip config list
# Doit montrer index-url = http://192.168.1.50:8081/...

# 2. Vérifier que le package existe dans Nexus
# Nexus UI -> Browse -> pypi-hosted -> Components
# -> product-validator-py présent ?

# 3. Vérifier via l'API Nexus
curl -u bob:BobSecure@2024! \
  "http://192.168.1.50:8081/service/rest/v1/search?repository=pypi-hosted&name=product-validator-py"
# Si résultat vide -> Alice n'a pas encore publié !

# 4. Tester l'accès à l'index pip
curl http://192.168.1.50:8081/repository/pypi-all/simple/product-validator-py/
# Doit retourner la liste des versions disponibles

SOLUTION :
─────────
# Alice n'avait pas encore publié le wrapper Python.
# Alice publie : twine upload -r nexus dist/*
# Bob réessaie : pip install product-validator-py==1.0.0


────────────────────────────────────────────────────────────────────────
G.3 - PROBLÈME : Docker pull échoue (SSL error)
────────────────────────────────────────────────────────────────────────

SYMPTÔME :
docker pull 192.168.1.50:8083/nginx:alpine
# Error response from daemon: Get "https://192.168.1.50:8083/v2/":
# http: server gave HTTP response to HTTPS client

DIAGNOSTIC :
──────────
# Docker tente HTTPS par défaut même pour 192.168.1.50:8083
# Solution : Configurer les "insecure-registries"

SOLUTION :
─────────

# Sur le poste concerné :
sudo nano /etc/docker/daemon.json

# Ajouter :
{
  "insecure-registries": [
    "192.168.1.50:8082",
    "192.168.1.50:8083"
  ]
}

# Redémarrer Docker
sudo systemctl daemon-reload
sudo systemctl restart docker

# Retester
docker pull 192.168.1.50:8083/nginx:alpine
# [OK] Fonctionne maintenant


────────────────────────────────────────────────────────────────────────
G.4 - PROBLÈME : Maven utilise encore Maven Central (pas Nexus)
────────────────────────────────────────────────────────────────────────

SYMPTÔME :
mvn clean install
# [INFO] Downloading from central: https://repo.maven.apache.org/maven2/...
# (téléchargement depuis internet au lieu de Nexus)

DIAGNOSTIC :
──────────
# Le mirror n'est pas correctement configuré
# OU un pom.xml parent override le mirror

# Vérifier la config effective Maven
mvn help:effective-settings | grep -A5 "mirror"

SOLUTION :
─────────

# S'assurer que settings.xml contient :
<mirrors>
  <mirror>
    <id>nexus-mirror</id>
    <url>http://192.168.1.50:8081/repository/maven-public/</url>
    <mirrorOf>*</mirrorOf>   <- L'astérisque est crucial
  </mirror>
</mirrors>

# Si un pom.xml parent définit un repository qui bypasse le mirror :
# Ajouter dans le pom.xml du projet :
<repositories>
  <!-- Forcer l'utilisation du mirror en supprimant les autres repos -->
</repositories>

# Ou forcer avec -s :
mvn clean install -s ~/.m2/settings.xml


────────────────────────────────────────────────────────────────────────
G.5 - PROBLÈME : La version SNAPSHOT de Bob est obsolète
────────────────────────────────────────────────────────────────────────

SITUATION :
Alice a publié une nouvelle version de product-validator-py SNAPSHOT.
Bob continue à utiliser l'ancienne version.

SYMPTÔME :
# Bob utilise encore product-validator-py-1.1.0.dev20240115
# Alice a publié product-validator-py-1.1.0.dev20240116 (avec le fix)

SOLUTION :
─────────

# Bob force la mise à jour du package
pip install --upgrade product-validator-py==1.1.0.dev0

# Ou pour les SNAPSHOTs Python (dev releases) :
pip install --pre product-validator-py

# Vérifier la version installée
pip show product-validator-py
# Name: product-validator-py
# Version: 1.1.0.dev20240116
# [OK] Bonne version !

# Pour les SNAPSHOTs Maven (côté Alice/Charlie) :
mvn clean install -U   # -U force la mise à jour des snapshots


════════════════════════════════════════════════════════════════════════
SECTION H : BILAN ET BONNES PRATIQUES APPRISES
════════════════════════════════════════════════════════════════════════

────────────────────────────────────────────────────────────────────────
H.1 - CE QUE NEXUS A APPORTÉ À L'ÉQUIPE
────────────────────────────────────────────────────────────────────────

════════════════════════
AVANT NEXUS (problèmes)
════════════════════════

[X] Alice envoyait le JAR par Slack à Bob -> Bob l'intégrait manuellement
[X] Deux versions différentes de la logique de validation coexistaient
[X] 5-10 minutes de téléchargement à chaque build CI
[X] Builds impossibles si internet en panne
[X] Pas de traçabilité des versions utilisées
[X] Onboarding d'un nouveau développeur : 1-2 jours de configuration

════════════════════════
APRÈS NEXUS (gains)
════════════════════════

[OK] Alice publie sur Nexus -> Bob installe via pip -> Toujours synchronisé
[OK] Une seule source de vérité pour les règles de validation
[OK] 10-30 secondes de téléchargement (cache local Nexus)
[OK] Builds indépendants d'internet
[OK] Chaque version tracée, horodatée, immuable (releases)
[OK] Onboarding nouveau dev : 30 minutes


────────────────────────────────────────────────────────────────────────
H.2 - MÉTRIQUES CONCRÈTES
────────────────────────────────────────────────────────────────────────

TEMPS DE BUILD CI (moyen)
  Avant  : 7 minutes 34 secondes  (téléchargement Internet)
  Après  : 52 secondes            (téléchargement Nexus local)
  Gain   : -88%

BANDE PASSANTE (hebdomadaire)
  Avant  : 4.2 GB      (5 devs × 3 builds/jour × 280 MB dépendances)
  Après  : 280 MB      (premier téléchargement uniquement)
  Gain   : -93%

TAUX DE SUCCÈS BUILDS CI
  Avant  : 87%         (13% échouaient pour rate limits / timeouts PyPI / Maven Central)
  Après  : 99.8%       (Seuls pannes infra interne)

TEMPS RÉSOLUTION BUG PARTAGÉ
  Avant  : 2-3 jours   (partage manuel, intégration manuelle par Bob)
  Après  : < 1 heure   (Alice publie -> Bob installe la nouvelle version)


────────────────────────────────────────────────────────────────────────
H.3 - BONNES PRATIQUES APPRISES
────────────────────────────────────────────────────────────────────────

════════════════════════
POUR ALICE (Java Developer)
════════════════════════

[OK] TOUJOURS développer en -SNAPSHOT et ne passer en release qu'au bon moment
[OK] NE JAMAIS modifier une release déjà publiée -> Incrémenter la version
[OK] Publier sources ET javadoc (les collègues en ont besoin)
[OK] Maintenir STRICTEMENT la synchronisation entre le Java et le wrapper Python
[OK] Documenter les breaking changes dans le CHANGELOG
[OK] Tester le wrapper Python avant de publier (éviter que Bob soit bloqué)

════════════════════════
POUR BOB (Python Developer)
════════════════════════

[OK] Toujours utiliser un virtualenv par projet
[OK] Toujours figer les versions dans requirements.txt pour la production
[OK] Utiliser == pour les packages internes (critiques)
[OK] Utiliser >= pour les packages publics (flexibilité)
[OK] Vérifier dans Nexus UI avant de signaler un bug "package introuvable"
[OK] Ne jamais faire pip install sans venv activé

════════════════════════
POUR CHARLIE (DevOps)
════════════════════════

[OK] TOUJOURS utiliser des comptes dédiés (jamais partager les credentials admin)
[OK] Principle of least privilege pour chaque développeur
[OK] Cleanup policies configurées dès le début (avant que le disque sature)
[OK] Backups automatisés quotidiens testés mensuellement
[OK] Documentation d'onboarding tenue à jour
[OK] Utiliser des tokens API en CI/CD (jamais les vrais passwords)
[OK] Monitoring Nexus (espace disque, mémoire, uptime)

════════════════════════
POUR TOUTE L'ÉQUIPE
════════════════════════

[OK] Semantic Versioning strictement respecté (MAJOR.MINOR.PATCH)
[OK] Changelog maintenu à chaque release
[OK] Pull Requests obligatoires avant merge sur main
[OK] Les releases n'arrivent qu'après merge sur main + tag Git
[OK] Jamais de credentials dans le code source (Git)
[OK] Credentials dans GitHub Secrets pour CI/CD


────────────────────────────────────────────────────────────────────────
H.4 - RÉCAPITULATIF DES URLS NEXUS DE L'ÉQUIPE
────────────────────────────────────────────────────────────────────────

Interface Web Nexus     : http://192.168.1.50:8081

Repositories Maven :
  maven-public          : http://192.168.1.50:8081/repository/maven-public/
  maven-releases        : http://192.168.1.50:8081/repository/maven-releases/
  maven-snapshots       : http://192.168.1.50:8081/repository/maven-snapshots/
  maven-central-proxy   : http://192.168.1.50:8081/repository/maven-central-proxy/

Repositories PyPI :
  pypi-all              : http://192.168.1.50:8081/repository/pypi-all/simple
  pypi-hosted           : http://192.168.1.50:8081/repository/pypi-hosted/
  pypi-proxy            : http://192.168.1.50:8081/repository/pypi-proxy/

Repositories Docker :
  docker-hosted         : http://192.168.1.50:8082/
  docker-proxy          : http://192.168.1.50:8083/

Repositories Raw :
  raw-scripts           : http://192.168.1.50:8081/repository/raw-scripts/


────────────────────────────────────────────────────────────────────────
H.5 - COMMANDES RAPIDES DE RÉFÉRENCE
────────────────────────────────────────────────────────────────────────

## JAVA (Alice) ##

# Build et test
mvn clean test

# Déployer snapshot vers Nexus
mvn clean deploy

# Voir version actuelle
mvn help:evaluate -Dexpression=project.version -q -DforceStdout

# Forcer maj des snapshots
mvn clean install -U

# Debug Maven (voir toutes les requêtes vers Nexus)
mvn clean install -X 2>&1 | grep "nexus\|Downloading\|Downloaded"


## PYTHON (Bob) ##

# Créer venv et installer dépendances depuis Nexus
python3 -m venv venv && source venv/bin/activate
pip install -e ".[dev]"

# Installer package interne spécifique
pip install product-validator-py==1.0.0

# Forcer mise à jour d'un package
pip install --upgrade product-validator-py

# Publier sur Nexus
python -m build && twine upload -r nexus dist/*

# Vérifier quelle version est installée
pip show product-validator-py


## DOCKER (Charlie) ##

# Pull depuis Docker Hub via cache Nexus
docker pull 192.168.1.50:8083/nginx:alpine

# Login au registry privé
docker login 192.168.1.50:8082

# Push image vers Nexus
docker tag product-api:1.0.0 192.168.1.50:8082/product-api:1.0.0
docker push 192.168.1.50:8082/product-api:1.0.0

# Pull image privée depuis Nexus
docker pull 192.168.1.50:8082/product-api:latest


## NEXUS ADMIN (Charlie) ##

# Vérifier statut Nexus
curl -u admin:pass http://192.168.1.50:8081/service/rest/v1/status

# Lister tous les repositories
curl -u admin:pass http://192.168.1.50:8081/service/rest/v1/repositories

# Chercher un artifact
curl -u admin:pass "http://192.168.1.50:8081/service/rest/v1/search?name=product-validator"

# Voir les logs en temps réel
docker logs -f nexus | grep -E "ERROR|WARN"

# Relancer Nexus
cd /opt/nexus && docker-compose restart nexus


═══════════════════════════════════════════════════════════════════════════
* FIN DE LA SECTION - CAS PRATIQUE ÉQUIPE DE 3 DÉVELOPPEURS *
═══════════════════════════════════════════════════════════════════════════

Résumé de ce cas pratique :

  [UTILISATEUR] 3 développeurs : Alice (Java), Bob (Python), Charlie (DevOps)
  [CONSTRUCTION] 1 application : product-validator (Java) + product-api (Python)
  [LIEN] 1 lien fort  : product-validator-py (wrapper publié via Nexus)
  [PACKAGE] 10 repositories Nexus configurés et utilisés quotidiennement
  [RAPIDE] Gains mesurables : -88% temps de build, -93% bande passante

  La clé du succès : Nexus comme source unique de vérité pour tous les
  artifacts, quel que soit le langage ou l'outil utilisé par chaque
  développeur de l'équipe.

═══════════════════════════════════════════════════════════════════════════