homelab_automation/documentation/ANSIBLE_EDITOR_DESIGN.md
Bruno Charest 05087aa380
Some checks failed
Tests / Backend Tests (Python) (3.10) (push) Has been cancelled
Tests / Backend Tests (Python) (3.11) (push) Has been cancelled
Tests / Backend Tests (Python) (3.12) (push) Has been cancelled
Tests / Frontend Tests (JS) (push) Has been cancelled
Tests / Integration Tests (push) Has been cancelled
Tests / All Tests Passed (push) Has been cancelled
Replace manual upsert logic with SQLite native upsert in Docker CRUD repositories, enhance Ansible backup playbook with better error handling and file permissions, add favicon endpoint, and improve playbook editor UI with syntax highlighting, lint integration, quality badges, and enhanced code editing features
2025-12-17 15:36:49 -05:00

16 KiB

🎯 Ansible Playbook Editor - Design & Architecture

Résumé Exécutif

Refonte complète de l'éditeur de playbooks Ansible pour offrir une expérience IDE-like avec:

  • Éditeur CodeMirror 6 avec coloration syntaxique Ansible/YAML avancée
  • Intégration ansible-lint non-bloquante avec feedback temps réel
  • Score de qualité et historique par playbook
  • UX professionnelle inspirée de VS Code/GitHub Actions

1. Vision UX Globale

Philosophie Design

  • Clarté avant tout: Interface sobre, focalisée sur le code
  • Feedback intelligent: Guidage proactif sans interruption du flux
  • Progressive disclosure: Complexité révélée au bon moment
  • Performance: Réactivité < 100ms pour les interactions

Principes UX

Principe Application
Non-bloquant Lint en arrière-plan, édition jamais interrompue
Contextuel Erreurs affichées à leur emplacement exact
Actionnable Chaque diagnostic propose une correction
Discret Pas de popup intrusif, indicateurs subtils

2. Design de l'Éditeur

2.1 Architecture de l'Éditeur

┌─────────────────────────────────────────────────────────────────┐
│ 📄 playbook-name.yml                     [Quality: 92/100 ✓]    │
├─────────────────────────────────────────────────────────────────┤
│ ┌──────┬────────────────────────────────────────────────────┐   │
│ │ LINT │ ERRORS (2)  WARNINGS (3)  INFO (1)                 │   │
│ ├──────┴────────────────────────────────────────────────────┤   │
│ │                                                            │   │
│ │  [Gutter]  [Code Editor Area]                             │   │
│ │    ⚠  1   ---                                             │   │
│ │       2   - name: Deploy application                      │   │
│ │       3     hosts: all                                    │   │
│ │    ⛔ 4     become: yes                                   │   │
│ │   ╭──────────────────────────────────────────╮            │   │
│ │   │ ⛔ risky-file-permissions: Missing mode  │            │   │
│ │   │    for file operation                    │            │   │
│ │   │    📖 Learn more  |  ✨ Auto-fix         │            │   │
│ │   ╰──────────────────────────────────────────╯            │   │
│ │       5     vars:                                         │   │
│ │       6       category: deploy                            │   │
│ │                                                            │   │
│ └────────────────────────────────────────────────────────────┘   │
├─────────────────────────────────────────────────────────────────┤
│ [✓ YAML Valid] [⚠ 5 issues] [Ln 4, Col 5]     [Annuler] [Save] │
└─────────────────────────────────────────────────────────────────┘

2.2 Coloration Syntaxique Ansible

Tokens à highlighter

Type Exemples Couleur (thème sombre)
Clés Ansible hosts, tasks, vars, handlers, become #c792ea (violet)
Modules ansible.builtin.copy, community.docker.docker_container #82aaff (bleu)
Variables Jinja {{ var }}, {% if %} #c3e88d (vert)
Valeurs booléennes true, false, yes, no #f78c6c (orange)
Commentaires # comment #546e7a (gris)
Strings "text", 'text' #c3e88d (vert clair)
Nombres 123, 0.5 #f78c6c (orange)
Ancres YAML &anchor, *reference #89ddff (cyan)

2.3 Fonctionnalités Éditeur

  1. Auto-indentation YAML (2 espaces)
  2. Bracket matching pour {{ }}, {% %}, [], {}
  3. Fold/Unfold des blocs YAML
  4. Minimap optionnelle
  5. Search & Replace avec regex
  6. Multi-curseur

3. Intégration ansible-lint

3.1 Bouton Lint Intelligent

États visuels

┌─────────────────────────────────────────────────────────────┐
│  État       │ Icône     │ Couleur    │ Animation          │
├─────────────┼───────────┼────────────┼────────────────────┤
│  Idle       │ 🔍        │ Gris       │ Aucune             │
│  Running    │ ⟳         │ Bleu       │ Rotation           │
│  Success    │ ✓         │ Vert       │ Pulse subtil       │
│  Warnings   │ ⚠ (3)     │ Jaune      │ Aucune             │
│  Errors     │ ⛔ (2)     │ Rouge      │ Aucune             │
└─────────────────────────────────────────────────────────────┘

3.2 Affichage des Résultats

Gutter Markers

  • Icône colorée dans la marge gauche à la ligne concernée
  • Survol = tooltip avec résumé
  • Clic = panneau détaillé

Inline Diagnostics

- name: Copy config
  ansible.builtin.copy:
    src: config.yml
    dest: /etc/app/config.yml
    # ⛔ risky-file-permissions: File permissions unset or incorrect
    # 💡 Ajouter: mode: '0644'

Panneau Problèmes (optionnel)

Liste scrollable de tous les problèmes, filtrable par sévérité.

3.3 Workflow Utilisateur

┌──────────────┐     ┌──────────────┐     ┌──────────────┐
│   Ouvrir     │────▶│   Éditer     │────▶│    Lint      │
│  playbook    │     │   contenu    │     │  (auto/btn)  │
└──────────────┘     └──────────────┘     └──────┬───────┘
                                                 │
        ┌────────────────────────────────────────┤
        ▼                                        ▼
┌──────────────┐                         ┌──────────────┐
│  Afficher    │                         │  Sauvegarder │
│  résultats   │                         │  (possible)  │
└──────────────┘                         └──────┬───────┘
                                                 │
                                                 ▼
                                         ┌──────────────┐
                                         │  Exécuter    │
                                         │ (bloqué si   │
                                         │  erreurs*)   │
                                         └──────────────┘
                                         * Configurable

3.4 Comportement Non-Bloquant

Action Lint bloque ? Sauvegarde bloque ? Exécution bloque ?
Lint en cours
Warnings
Errors ⚠️ Avertissement

4. Architecture Technique

4.1 Backend - API ansible-lint

Endpoint

POST /api/playbooks/{filename}/lint

Request

{
  "content": "---\n- name: My playbook\n  hosts: all\n...",
  "rules": ["yaml", "risky-file-permissions"],  // optionnel
  "skip_rules": ["no-changed-when"]  // optionnel
}

Response

{
  "success": true,
  "execution_time_ms": 342,
  "summary": {
    "total": 5,
    "errors": 2,
    "warnings": 2,
    "info": 1
  },
  "quality_score": 72,
  "issues": [
    {
      "rule_id": "risky-file-permissions",
      "severity": "error",
      "message": "File permissions unset or incorrect",
      "filename": "playbook.yml",
      "line": 12,
      "column": 5,
      "context": "    ansible.builtin.copy:",
      "help_url": "https://ansible-lint.readthedocs.io/rules/risky-file-permissions/",
      "fix_suggestion": "Add 'mode: \"0644\"' parameter"
    }
  ]
}

Implémentation Backend

# app/routes/lint.py

import asyncio
import json
import tempfile
from pathlib import Path
from fastapi import APIRouter, Depends, HTTPException
from pydantic import BaseModel

router = APIRouter()

class LintRequest(BaseModel):
    content: str
    rules: list[str] | None = None
    skip_rules: list[str] | None = None

class LintIssue(BaseModel):
    rule_id: str
    severity: str  # error, warning, info
    message: str
    line: int
    column: int
    context: str | None = None
    help_url: str | None = None
    fix_suggestion: str | None = None

class LintResponse(BaseModel):
    success: bool
    execution_time_ms: int
    summary: dict
    quality_score: int
    issues: list[LintIssue]

@router.post("/{filename}/lint")
async def lint_playbook(filename: str, request: LintRequest):
    """Exécute ansible-lint sur le contenu d'un playbook."""
    
    start_time = asyncio.get_event_loop().time()
    
    # Créer fichier temporaire
    with tempfile.NamedTemporaryFile(
        mode='w', suffix='.yml', delete=False
    ) as f:
        f.write(request.content)
        temp_path = f.name
    
    try:
        # Construire commande ansible-lint
        cmd = [
            'ansible-lint',
            '--format', 'json',
            '--nocolor',
            temp_path
        ]
        
        if request.skip_rules:
            for rule in request.skip_rules:
                cmd.extend(['--skip-list', rule])
        
        # Exécuter
        process = await asyncio.create_subprocess_exec(
            *cmd,
            stdout=asyncio.subprocess.PIPE,
            stderr=asyncio.subprocess.PIPE
        )
        stdout, stderr = await process.communicate()
        
        # Parser JSON
        issues = parse_ansible_lint_output(stdout.decode(), temp_path, filename)
        
        # Calculer score
        score = calculate_quality_score(issues, len(request.content.split('\n')))
        
        execution_time = int((asyncio.get_event_loop().time() - start_time) * 1000)
        
        return LintResponse(
            success=True,
            execution_time_ms=execution_time,
            summary={
                "total": len(issues),
                "errors": sum(1 for i in issues if i.severity == "error"),
                "warnings": sum(1 for i in issues if i.severity == "warning"),
                "info": sum(1 for i in issues if i.severity == "info")
            },
            quality_score=score,
            issues=issues
        )
    finally:
        Path(temp_path).unlink(missing_ok=True)

4.2 Frontend - Composants

Structure des composants

PlaybookEditor/
├── EditorContainer        # Container principal
├── CodeMirrorEditor       # Instance CodeMirror 6
├── LintButton             # Bouton avec états
├── LintResultsPanel       # Panneau latéral/bas
├── GutterMarkers          # Marqueurs dans la marge
├── InlineDiagnostics      # Diagnostics inline
├── QualityBadge           # Score de qualité
└── StatusBar              # Barre de statut

Gestion d'état

const editorState = {
  // Contenu
  content: '',
  originalContent: '',
  isDirty: false,
  
  // Lint
  lintStatus: 'idle', // idle | running | success | warnings | errors
  lintResults: null,
  lastLintTime: null,
  
  // Qualité
  qualityScore: null,
  
  // UI
  showProblemsPanel: false,
  activeIssueIndex: null,
  
  // Config
  lintOnSave: true,
  blockExecutionOnErrors: true
};

Stratégie de Performance

  1. Debounce: Lint auto après 1500ms d'inactivité
  2. Abort Controller: Annuler lint précédent si nouvelle édition
  3. Virtual Scrolling: Pour longs playbooks (>1000 lignes)
  4. Web Worker: Parser YAML en background (optionnel)

5. Idées Bonus à Forte Valeur

5.1 📊 Quality Score Dashboard

Score 0-100 basé sur:

  • Nombre d'erreurs (-20 pts/erreur)
  • Nombre de warnings (-5 pts/warning)
  • Bonnes pratiques suivies (+5 pts)
  • Historique (trend: amélioration ou dégradation)
┌────────────────────────────────────────┐
│  Quality Score: 87/100  ↑ +5          │
│  ████████████████████░░░░             │
│                                        │
│  ✓ No deprecated modules              │
│  ✓ FQCN used consistently             │
│  ⚠ 2 missing task names               │
│  ⚠ 1 risky permission                 │
└────────────────────────────────────────┘

5.2 🔧 Auto-Fix Suggestions

Pour certaines règles, proposer des corrections automatiques:

  • no-free-form: Convertir en syntaxe structurée
  • risky-file-permissions: Ajouter mode: '0644'
  • fqcn: Convertir copyansible.builtin.copy
  • yaml[truthy]: Convertir yestrue

5.3 📈 Lint History par Playbook

Stocker l'historique des scores pour chaque playbook:

{
  "playbook": "deploy-app.yml",
  "history": [
    {"date": "2025-01-15", "score": 65, "errors": 3, "warnings": 5},
    {"date": "2025-01-16", "score": 78, "errors": 1, "warnings": 3},
    {"date": "2025-01-17", "score": 92, "errors": 0, "warnings": 1}
  ]
}

5.4 💡 Assistant Bonnes Pratiques

Suggestions proactives basées sur le contexte:

  • "Cette tâche modifie des fichiers. Ajoutez un handler notify."
  • "Utilisez become_user au lieu de become pour plus de sécurité."
  • "Préférez ansible.builtin.command à shell pour les commandes simples."

5.5 🔄 Pré-validation Commit

Hook optionnel pour valider avant git commit (documentation fournie).


6. Plan d'Implémentation

Phase 1: Backend Lint API

  • Endpoint /api/playbooks/{filename}/lint
  • Parsing JSON ansible-lint
  • Calcul quality score
  • Suggestions de correction par règle
  • URLs de documentation

Phase 2: Éditeur CodeMirror 6

  • Intégration CDN CodeMirror 6 (avec fallback textarea)
  • Configuration YAML + thème personnalisé
  • Coloration Ansible avancée

Phase 3: UI ansible-lint

  • LintButton avec états (idle, running, success, warnings, errors)
  • Panneau problèmes avec navigation vers ligne
  • Badge de qualité coloré
  • Onglets Éditeur / Problèmes

Phase 4: Bonus

  • Quality Score (0-100) avec bonus/malus
  • Suggestions de fix par règle
  • Liens vers documentation ansible-lint

Tests

  • 24 tests unitaires (100% passing)
  • Tests parsing JSON/texte
  • Tests endpoint API
  • Tests calcul score qualité

7. Résumé Pitch Produit

Éditeur Ansible IDE-like pour votre Homelab

Passez de "ça marche peut-être" à "je suis sûr que c'est correct" avec notre éditeur de playbooks intelligent.

Coloration syntaxique Ansible - Modules, variables Jinja, clés YAML 🔍 ansible-lint intégré - Feedback en temps réel, sans bloquer 📊 Score de qualité - Suivez l'évolution de vos playbooks Non-bloquant - Éditez, sauvegardez, le lint tourne en arrière-plan 🎯 Actionnable - Chaque erreur avec explication et suggestion de fix