Stack qualité CI (MegaLinter, Trivy, ESLint) et purge du backlog lint web
Some checks failed
Qualité & Sécurité / MegaLinter (PMD · Ruff · Bandit · gitleaks · hadolint) (push) Successful in 1m9s
Build & Push Images / build (brain) (push) Successful in 1m10s
Qualité & Sécurité / Trivy (CVE dépendances Maven / pip / npm) (push) Failing after 2m36s
Build & Push Images / build (web) (push) Successful in 1m48s
Build & Push Images / build-switcher (push) Successful in 20s
Build & Push Images / build (core) (push) Successful in 3m9s
Qualité & Sécurité / Web (ESLint · angular-eslint + sonarjs) (push) Successful in 38s

CI (Gitea Actions) :
- Nouveau workflow quality.yml (séparé de ci.yml, non bloquant pour la release) :
  MegaLinter v9 (PMD, Ruff, Bandit, gitleaks, hadolint), ng lint (web) et
  Trivy (CVE des dépendances Maven/pip/npm), sur main + beta + PR
- Trivy : préchargement du cache Maven avant le scan — sans ça Maven Central
  rate-limite l'IP du runner (429) en résolvant les BOMs du parent Spring Boot
- upload-artifact v4 → v3 : l'API artifacts v4 n'est pas supportée par Gitea
  (corrige aussi l'upload du rapport Playwright de e2e.yml, cassé depuis toujours)
- Ruleset PMD projet (java-pmd-ruleset.xml, auto-détecté par MegaLinter) orienté
  bugs réels, sans le style Lombok-hostile du défaut : 11 337 → 65 findings ;
  PMD en mode rapport (JAVA_PMD_DISABLE_ERRORS) le temps de purger ce backlog

Web :
- angular-eslint 21 + eslint-plugin-sonarjs : les règles Sonar vivent désormais
  dans ESLint (config web/eslint.config.js, réglages justifiés en commentaire)
- tsconfig : skipLibCheck + types:[] + node_modules ré-exclu — répare les builds
  desktop Windows/Linux cassés par le conflit @types/eslint-scope vs ESLint 9
- Purge du backlog lint : 114 erreurs → 0 sur 66 fichiers (ngOnDestroy vides
  supprimés, any typés avec les vrais DTOs, outputs (close) → (closed), regex
  super-linéaires désamorcées, ternaires/fonctions imbriqués dépliés, intention
  documentée sur les catch/error volontairement vides)
- Bug latent corrigé au passage : license.service.disconnect() émettait
  undefined au lieu de true en cas de succès (masqué par un double cast)
This commit is contained in:
2026-07-07 01:05:00 +02:00
parent 3e9e828225
commit 42c4b53ced
71 changed files with 353 additions and 276 deletions

View File

@@ -92,7 +92,7 @@ export class CampaignGraphComponent implements OnInit, OnDestroy {
private readonly DRAG_THRESHOLD = 4;
// Adjacence (ids de nœuds) — sert à recalculer les arêtes après un drag.
private adjacency: Array<{ key: string; kind: NodeKind; a: string; b: string }> = [];
private adjacency: { key: string; kind: NodeKind; a: string; b: string }[] = [];
// Disposition personnalisée : positions sauvegardées sur la campagne, sauvegarde
// différée après un drag (évite un PUT par pixel déplacé).
@@ -174,7 +174,10 @@ export class CampaignGraphComponent implements OnInit, OnDestroy {
if (this.draggingId) return; // pas de changement de focus en plein drag
this.hoveredId = node.id;
this.hoverNeighbors = new Set(
this.adjacency.flatMap(e => e.a === node.id ? [e.b] : e.b === node.id ? [e.a] : []));
this.adjacency.flatMap(e => {
if (e.a === node.id) return [e.b];
return e.b === node.id ? [e.a] : [];
}));
}
onNodeLeave(): void {
@@ -230,7 +233,7 @@ export class CampaignGraphComponent implements OnInit, OnDestroy {
for (const [arcId, chapters] of Object.entries(treeData.chaptersByArc)) {
for (const ch of chapters) arcOfChapter.set(ch.id!, arcId);
}
const linkedScenes: Array<{ scene: Scene; chapterId: string }> = [];
const linkedScenes: { scene: Scene; chapterId: string }[] = [];
if (!this.hiddenKinds.has('scene')) {
for (const [chapterId, scenes] of Object.entries(treeData.scenesByChapter)) {
if (!arcOfChapter.has(chapterId)) continue;
@@ -268,7 +271,7 @@ export class CampaignGraphComponent implements OnInit, OnDestroy {
// Arêtes. Les liens page↔page sont dé-dupliqués par paire non-orientée
// (A→B et B→A = un seul trait) ; les liens entité→page portent le type de l'entité.
const adjacency: Array<{ key: string; kind: NodeKind; a: string; b: string }> = [];
const adjacency: { key: string; kind: NodeKind; a: string; b: string }[] = [];
const seenPairs = new Set<string>();
for (const p of pages) {
for (const targetId of p.relatedPageIds ?? []) {
@@ -530,14 +533,16 @@ export class CampaignGraphComponent implements OnInit, OnDestroy {
if (Math.abs(dx) >= needX || Math.abs(dy) >= needY) continue;
const overlapX = needX - Math.abs(dx);
const overlapY = needY - Math.abs(dy);
// Départage déterministe quand les centres coïncident (dx/dy = 0).
const tieBreak = i % 2 === 0 ? 1 : -1;
if (overlapX / needX < overlapY / needY) {
const shift = overlapX / 2 + 0.5;
const sign = dx !== 0 ? Math.sign(dx) : (i % 2 === 0 ? 1 : -1);
const sign = dx !== 0 ? Math.sign(dx) : tieBreak;
a.x -= sign * shift;
b.x += sign * shift;
} else {
const shift = overlapY / 2 + 0.5;
const sign = dy !== 0 ? Math.sign(dy) : (i % 2 === 0 ? 1 : -1);
const sign = dy !== 0 ? Math.sign(dy) : tieBreak;
a.y -= sign * shift;
b.y += sign * shift;
}