Qualite : pyproject.toml (ruff/pytest/pyright) + CI Gitea (lint/tests/build)

pyproject.toml devient le point d'entree unique de configuration des outils
(pytest.ini supprime, sa config y est deplacee). Les dependances restent dans
requirements.txt/requirements-dev.txt (deja utilises par setup.bat) plutot que
dupliquees ici : ce projet se distribue par PyInstaller, pas par "pip install".

ruff : configure puis passe sur tout app/ + tests/ (154 corrections
automatiques, purement mecaniques - tri des imports, imports multiples sur une
ligne separes, alias depreciees) suivies de 7 corrections manuelles ciblees :
  - app/ssh_connection.py : `raise ... from None` pour le refus explicite de
    passphrase (ce n'est pas un echec a diagnostiquer).
  - app/controllers/workspace_manager.py : zip() sans strict= documente en
    commentaire (deliberement tolerant a un workspace legerement perime).
  - tests/test_ai_redaction.py : nom de variable ambigu renomme.
  - E702 (construction compacte "widget = X(); widget.setY(...)" dans
    sftp_panel.py/tunnel_dialog.py) et E402 (quelques tests qui importent un
    symbole juste avant la section qui l'utilise) : ignores explicitement, ce
    sont des styles deliberes preexistants, pas des oublis.
`ruff check app main.py tests` est desormais propre.

pyright : mode "basic" (le code n'a jamais ete type). Plus de 200 signalements
sur ~300 etaient le meme faux positif connu de l'ecosysteme PySide6 (acces
plat aux enums Qt, ex. Qt.UserRole, valide a l'execution mais que les stubs ne
reconnaissent que sous leur forme imbriquee) : desactive avec justification en
commentaire. Reste un socle de 77 signalements reels (surtout des acces
Optional non filtres dans app/ui/tabs.py et app/ssh_connection.py) : non
corrige dans cette phase - resserrer le typage la ou pyright a raison est un
travail a part, pas une consequence automatique de l'ajout de l'outil. La CI
le lance en mode informatif (continue-on-error) tant que ce socle n'est pas
resorbe.

.gitea/workflows/ci.yml : lint (ruff, bloquant), tests (pytest, bloquant),
typecheck (pyright, informatif), build (PyInstaller, sur main uniquement,
artefact du dossier portable). Necessite un runner Gitea Actions Windows
enregistre - PySide6/pywinpty/Credential Manager/ACL NTFS sont Windows
uniquement, comme l'application elle-meme ; non verifiable depuis ce poste.

Suite complete revalidee apres coup (33/33) pour confirmer qu'aucune des 154
corrections automatiques n'a modifie de comportement.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Florian
2026-09-05 07:46:50 +02:00
parent 09b7ed092c
commit c39ef9c038
72 changed files with 697 additions and 268 deletions

View File

@@ -6,7 +6,13 @@ from __future__ import annotations
import re
from PySide6.QtWidgets import (
QDialog, QVBoxLayout, QHBoxLayout, QTextEdit, QPushButton, QLabel, QApplication,
QApplication,
QDialog,
QHBoxLayout,
QLabel,
QPushButton,
QTextEdit,
QVBoxLayout,
)
_CODE_BLOCK_RE = re.compile(r"```(?:[a-zA-Z0-9_+-]*\n)?(.*?)```", re.DOTALL)

View File

@@ -6,8 +6,8 @@ from __future__ import annotations
from PySide6.QtWidgets import QMessageBox
from app.app_settings import get_setting, set_setting
from app.ai.redaction import detect_secrets
from app.app_settings import get_setting, set_setting
_NOTICE_ACK_KEY = "ai_notice_acknowledged"

View File

@@ -4,13 +4,21 @@ compatibles avec l'API OpenAI (OpenAI, OpenRouter, proxy interne, LLM local...).
from __future__ import annotations
from PySide6.QtWidgets import (
QDialog, QVBoxLayout, QFormLayout, QLineEdit, QCheckBox, QDoubleSpinBox,
QPushButton, QDialogButtonBox, QMessageBox, QLabel,
QCheckBox,
QDialog,
QDialogButtonBox,
QDoubleSpinBox,
QFormLayout,
QLabel,
QLineEdit,
QMessageBox,
QPushButton,
QVBoxLayout,
)
from app.ai.settings import AISettings, load_ai_settings, save_ai_settings, load_api_key, save_api_key
from app.ai.provider import OpenAICompatibleProvider
from app.ai.service import KIND_TEST, AIRequestWorker
from app.ai.settings import AISettings, load_ai_settings, load_api_key, save_ai_settings, save_api_key
from app.credentials import CredentialStore, NullCredentialStore

View File

@@ -3,17 +3,36 @@ d'une nouvelle session dans ce dossier (voir app.groups)."""
from __future__ import annotations
from PySide6.QtWidgets import (
QDialog, QFormLayout, QLineEdit, QSpinBox, QComboBox, QDialogButtonBox,
QHBoxLayout, QPushButton, QWidget, QVBoxLayout, QCheckBox, QStackedWidget, QLabel,
QCheckBox,
QComboBox,
QDialog,
QDialogButtonBox,
QFormLayout,
QHBoxLayout,
QLabel,
QLineEdit,
QPushButton,
QSpinBox,
QStackedWidget,
QVBoxLayout,
QWidget,
)
from app.session_manager import (
SessionStore, BastionConfig, AUTH_AUTO, AUTH_KEY, AUTH_PASSWORD, AUTH_AGENT,
BASTION_NONE, BASTION_SESSION, BASTION_OPENSSH, BASTION_MANUAL,
)
from app.key_manager import KeyManager
from app.groups import GroupSettings, GroupSettingsStore
from app.key_manager import KeyManager
from app.openssh.config import load_openssh_hosts
from app.session_manager import (
AUTH_AGENT,
AUTH_AUTO,
AUTH_KEY,
AUTH_PASSWORD,
BASTION_MANUAL,
BASTION_NONE,
BASTION_OPENSSH,
BASTION_SESSION,
BastionConfig,
SessionStore,
)
class GroupSettingsDialog(QDialog):

View File

@@ -5,13 +5,28 @@ from pathlib import Path
from PySide6.QtCore import Qt
from PySide6.QtWidgets import (
QDialog, QVBoxLayout, QHBoxLayout, QListWidget, QListWidgetItem, QPushButton,
QMessageBox, QFileDialog, QFormLayout, QLineEdit, QComboBox, QSpinBox,
QDialogButtonBox, QApplication, QLabel,
QApplication,
QComboBox,
QDialog,
QDialogButtonBox,
QFileDialog,
QFormLayout,
QHBoxLayout,
QLabel,
QLineEdit,
QListWidget,
QListWidgetItem,
QMessageBox,
QPushButton,
QSpinBox,
QVBoxLayout,
)
from app.key_manager import (
KeyManager, KEY_TYPE_ED25519, KEY_TYPE_RSA, KEY_TYPE_LABELS, KEY_TYPE_DESCRIPTIONS,
KEY_TYPE_DESCRIPTIONS,
KEY_TYPE_LABELS,
KEY_TYPE_RSA,
KeyManager,
)

View File

@@ -3,8 +3,15 @@ from __future__ import annotations
from PySide6.QtCore import Qt
from PySide6.QtWidgets import (
QDialog, QVBoxLayout, QHBoxLayout, QTreeWidget, QTreeWidgetItem, QPushButton,
QMessageBox, QLabel, QAbstractItemView,
QAbstractItemView,
QDialog,
QHBoxLayout,
QLabel,
QMessageBox,
QPushButton,
QTreeWidget,
QTreeWidgetItem,
QVBoxLayout,
)
from app.known_hosts_manager import KnownHostsStore

View File

@@ -2,12 +2,17 @@
snippets et actions, avec recherche floue et navigation entierement au clavier."""
from __future__ import annotations
from collections.abc import Callable
from dataclasses import dataclass, field
from typing import Callable
from PySide6.QtCore import Qt, QEvent
from PySide6.QtCore import QEvent, Qt
from PySide6.QtWidgets import (
QDialog, QVBoxLayout, QLineEdit, QListWidget, QListWidgetItem, QMessageBox,
QDialog,
QLineEdit,
QListWidget,
QListWidgetItem,
QMessageBox,
QVBoxLayout,
)
from app.openssh.config import load_openssh_hosts
@@ -111,9 +116,9 @@ class QuickConnectDialog(QDialog):
return results
def _quick_actions(self) -> list[tuple[str, Callable[[], None]]]:
from app.dialogs.workspace_dialog import WorkspaceDialog
from app.dialogs.snippet_dialog import SnippetManagerDialog
from app.dialogs.key_dialog import KeyManagerDialog
from app.dialogs.snippet_dialog import SnippetManagerDialog
from app.dialogs.workspace_dialog import WorkspaceDialog
return [
("Nouvelle session...", self.session_controller.new_session),

View File

@@ -3,20 +3,44 @@ from __future__ import annotations
from PySide6.QtCore import Qt
from PySide6.QtWidgets import (
QDialog, QFormLayout, QLineEdit, QSpinBox, QComboBox, QDialogButtonBox,
QHBoxLayout, QPushButton, QTextEdit, QFileDialog, QWidget, QVBoxLayout,
QCheckBox, QMessageBox, QGroupBox, QStackedWidget, QCompleter,
QCheckBox,
QComboBox,
QCompleter,
QDialog,
QDialogButtonBox,
QFileDialog,
QFormLayout,
QGroupBox,
QHBoxLayout,
QLineEdit,
QMessageBox,
QPushButton,
QSpinBox,
QStackedWidget,
QTextEdit,
QVBoxLayout,
QWidget,
)
from app.session_manager import (
Session, SessionStore, BastionConfig, AUTH_AUTO, AUTH_KEY, AUTH_PASSWORD, AUTH_AGENT,
BASTION_NONE, BASTION_SESSION, BASTION_OPENSSH, BASTION_MANUAL,
PASSWORD_STORAGE_NONE, PASSWORD_STORAGE_SECURE,
)
from app.credentials import CredentialStore, NullCredentialStore, session_credential_key
from app.groups import GroupSettingsStore
from app.key_manager import KeyManager
from app.openssh.config import load_openssh_hosts
from app.groups import GroupSettingsStore
from app.credentials import CredentialStore, NullCredentialStore, session_credential_key
from app.session_manager import (
AUTH_AGENT,
AUTH_AUTO,
AUTH_KEY,
AUTH_PASSWORD,
BASTION_MANUAL,
BASTION_NONE,
BASTION_OPENSSH,
BASTION_SESSION,
PASSWORD_STORAGE_NONE,
PASSWORD_STORAGE_SECURE,
BastionConfig,
Session,
SessionStore,
)
class SessionDialog(QDialog):

View File

@@ -4,8 +4,19 @@ from __future__ import annotations
from PySide6.QtCore import Qt
from PySide6.QtWidgets import (
QDialog, QVBoxLayout, QHBoxLayout, QTreeWidget, QTreeWidgetItem, QPushButton,
QMessageBox, QLineEdit, QLabel, QFormLayout, QTextEdit, QDialogButtonBox, QComboBox,
QComboBox,
QDialog,
QDialogButtonBox,
QFormLayout,
QHBoxLayout,
QLabel,
QLineEdit,
QMessageBox,
QPushButton,
QTextEdit,
QTreeWidget,
QTreeWidgetItem,
QVBoxLayout,
)
from app.snippets import Snippet, SnippetStore

View File

@@ -3,8 +3,19 @@ from __future__ import annotations
from PySide6.QtCore import Qt
from PySide6.QtWidgets import (
QDialog, QVBoxLayout, QHBoxLayout, QFormLayout, QTreeWidget, QTreeWidgetItem,
QPushButton, QComboBox, QSpinBox, QLineEdit, QLabel, QMessageBox, QAbstractItemView,
QAbstractItemView,
QComboBox,
QDialog,
QFormLayout,
QHBoxLayout,
QLabel,
QLineEdit,
QMessageBox,
QPushButton,
QSpinBox,
QTreeWidget,
QTreeWidgetItem,
QVBoxLayout,
)
from app.tunnels import DIRECTION_DYNAMIC, DIRECTION_LABELS, TunnelManager, describe

View File

@@ -3,12 +3,20 @@ from __future__ import annotations
from PySide6.QtCore import Qt
from PySide6.QtWidgets import (
QDialog, QVBoxLayout, QHBoxLayout, QListWidget, QListWidgetItem, QPushButton,
QMessageBox, QInputDialog, QLabel, QCheckBox,
QCheckBox,
QDialog,
QHBoxLayout,
QInputDialog,
QLabel,
QListWidget,
QListWidgetItem,
QMessageBox,
QPushButton,
QVBoxLayout,
)
from app.app_settings import get_setting, set_setting
from app.controllers.workspace_manager import WorkspaceManager, SETTING_RESTORE_ON_STARTUP
from app.controllers.workspace_manager import SETTING_RESTORE_ON_STARTUP, WorkspaceManager
class WorkspaceDialog(QDialog):