Files
ad_trident/docs/appdaemon-upgrade-rollback-instructions.md
T
2026-11-13 13:15:03 +01:00

12 KiB

Instructions après mise à jour ou rollback AppDaemon

Préparé le 8 novembre 2026 pour cette installation /conf. À suivre par Codex après installation de 4.5.14, ou retour en 4.5.10. Ce document prépare l'intervention : sa rédaction n'a appliqué aucun patch ni redémarré le service.

1. Identifier l'installation avant toute modification

Lire ce document et docs/appdaemon-local-changes.md. Conserver les modifications applicatives existantes, les configurations YAML et les exclusions de dossiers. Ne pas restaurer les anciennes instrumentations des utility loops : elles ont été retirées volontairement et archivées dans tools/archive/.

Exécuter dans le conteneur, avec le Python réellement utilisé par AppDaemon :

python3 - <<'PY'
import inspect, sys
from importlib.metadata import version
import appdaemon
from appdaemon.utils import deep_compare
print('Python :', sys.executable)
print('Version :', version('appdaemon'))
print('Paquet :', appdaemon.__file__)
print('deep_compare :', inspect.getsourcefile(deep_compare))
print(inspect.getsource(deep_compare))
PY

Si le processus utilise un autre environnement, employer son interpréteur pour les inspections, modifications et tests. Ne pas déduire la version du seul tag Docker. Vérifier aussi l'Entrypoint/Cmd du conteneur et le script effectivement exécuté ; une image personnalisée peut différer de l'image officielle. Pour une autre version que 4.5.10 ou 4.5.14, refaire la comparaison avec ses sources.

Avant chaque édition, sauvegarder le fichier original dans un dossier daté sous /conf/tools/backups/appdaemon-<version>-<date>/, en notant son chemin absolu, sa version et son empreinte SHA-256. Ne pas écraser une sauvegarde précédente. N'appliquer chaque changement qu'une fois ; si déjà présent, le vérifier. Ne jamais remplacer un fichier 4.5.14 par une sauvegarde 4.5.10.

2. Tableau des changements

Élément 4.5.10 (rollback) 4.5.14
Timestamp de rafraîchissement HASS Appliquer le patch plugin.name Déjà intégré ; vérifier seulement
Comparaison des clés dans deep_compare Appliquer dans utils.py Appliquer dans utils/misc.py
Optimisation des logs ordinaires Conserver le raccourci de LoggerInterface Optimisation intégrée au core ; conserver le wrapper existant si compatible
Archivage au démarrage Docker Ajouter l'appel dans dockerStart.sh Ajouter l'appel dans /start.sh pour l'image officielle

3. Patch HASS : 4.5.10 seulement

Dans le paquet installé, fichier plugin_management.py, méthode update_plugin_state(), remplacer uniquement cet appel dans le finally :

-                    await self.refresh_update_time(plugin)
+                    await self.refresh_update_time(plugin.name)

Cela évite de redemander l'état complet de Home Assistant chaque seconde après expiration du délai de rafraîchissement (10 minutes par défaut). Le correctif est intégré depuis 4.5.12 et confirmé dans le tag 4.5.14. En 4.5.14, contrôler la ligne, sans ajouter un second patch.

Référence locale : APPDAEMON_BANDWIDTH_BUG_FIX.md.

4. Patch deep_compare : les deux versions

Localiser le fichier avec inspect.getsourcefile(deep_compare) :

  • 4.5.10 : appdaemon/utils.py.
  • 4.5.14 : appdaemon/utils/misc.py (le module a été réorganisé).

Dans deep_compare(), ajouter immédiatement après data = data or {} :

    if check.keys() != data.keys():
        return False

Conserver tout le reste de la fonction propre à la version installée. En particulier, garder current = data.get(k) en 4.5.14. Le début de fonction doit donc ressembler à ceci :

def deep_compare(check: dict, data: dict) -> bool:
    """Compares 2 nested dictionaries of values"""
    data = data or {}

    if check.keys() != data.keys():
        return False

    # Suite existante, différente selon la version : ne pas la remplacer.

La 4.5.14 évite le KeyError à l'ajout d'une clé, mais ne détecte toujours pas certaines suppressions : la fonction officielle renvoie True pour deep_compare({}, {'input_selection': {}}). Le patch corrige aussi les suppressions imbriquées et distingue une clé absente d'une valeur None.

Vérifier dans un nouveau processus Python après modification :

python3 - <<'PY'
from appdaemon.utils import deep_compare
assert deep_compare({'input_selection': {}}, {}) is False
assert deep_compare({}, {'input_selection': {}}) is False
assert deep_compare({'x': {'a': 1}}, {'x': {}}) is False
assert deep_compare({'x': {}}, {'x': {'a': 1}}) is False
assert deep_compare({'x': None}, {}) is False
assert deep_compare({'x': [1, 2]}, {'x': [1, 2]}) is True
assert deep_compare({'x': [1, 2]}, {'x': [2, 1]}) is False
assert deep_compare({'x': {'a': 1}}, {'x': {'a': 1}}) is True
assert deep_compare({'x': {}}, {'x': None}) is False
assert deep_compare({}, None) is True
assert deep_compare({'input_selection': {}}, None) is False
print('OK : 11 vérifications deep_compare')
PY

Référence locale : APPDAEMON_RELOAD_BUG_FIX.md. Ses anciens chemins de sauvegarde .bak ne sont plus actifs ; utiliser la sauvegarde de cette intervention.

5. LoggerInterface : conserver les fonctionnalités locales

Fichier persistant : apps/ad_toolbox/logger_interface.py, méthode _write_log().

En 4.5.10, conserver le raccourci existant : pour un message texte sans __module__, __line__ ou __function__, et avec les méthodes standard ADAPI, appeler api._log() sans passer par l'inspection de pile. Garder le repli vers api.log() pour les substitutions et les méthodes personnalisées. Préserver les options d'encodage, le routage des logs et les groupes de messages. Ne pas ajouter un autre patch dans adapi.py : il était volontairement revenu au code officiel 4.5.10.

En 4.5.14, _sub_stack() contient déjà le raccourci évitant inspect.stack() sans marqueurs. Aucun patch core de logging n'est nécessaire. Conserver le wrapper applicatif et ses fonctionnalités ; la suppression du raccourci local redondant est facultative, pas un prérequis de migration. Tester sa compatibilité.

Tests existants :

python3 -m unittest discover -s /conf/apps/ad_toolbox/unit_tests -p 'test_logger_interface_fast_path.py' -v
python3 -m unittest discover -s /conf/apps/ad_toolbox/unit_tests -p 'test_logger_interface_grouping.py' -v

Adaptation de test nécessaire en 4.5.14 : test_direct_log_is_not_modified attend actuellement stack.assert_called_once() pour api.log('Direct log'). Cette attente correspond à 4.5.10. Pour 4.5.14, attendre stack.assert_not_called(). Si le test doit couvrir les deux versions, choisir explicitement l'attente selon la version installée, en conservant l'assertion d'identité de _sub_stack. Ne pas modifier le core pour satisfaire l'ancienne attente. Vérifier que les tests d'intégration ne sont pas ignorés faute d'import AppDaemon.

6. Patch dockerStart / start.sh : les deux versions

Comportement local à préserver

Avant le lancement d'AppDaemon, appeler une seule fois :

sh "$CONF/archiveLogs.sh"

Ce script persistant archive les anciens *.log* et memory_usage.csv dans /conf/logs/archive/<horodatage>/, puis lance le suivi mémoire en arrière-plan. Conserver le contenu actuel de /conf/archiveLogs.sh. Ne pas lancer le script manuellement pendant qu'AppDaemon écrit ses logs pour le « tester » : il déplacerait les fichiers ouverts. Ne pas lancer le script de démarrage complet dans le conteneur déjà actif : cela lancerait un second AppDaemon.

4.5.10

Le script observé est /usr/src/app/dockerStart.sh. /conf/dockerStart.sh est un lien symbolique vers ce fichier, pas une copie persistante du patch. Après recréation du conteneur, la cible peut être remplacée. Ajouter l'appel après les installations de dépendances, juste avant le lancement :

sh "$CONF/archiveLogs.sh"

# Lets run it!
exec python3 -m appdaemon -c $CONF "$@"

4.5.14 : changement de point d'entrée confirmé dans le tag officiel

Le Dockerfile copie scripts/start.sh vers /start.sh et définit ENTRYPOINT ["/start.sh"]. Il n'utilise plus dockerStart.sh. Dans cette image, patcher /start.sh, pas l'ancien lien /conf/dockerStart.sh. Ajouter le même appel après l'installation des dépendances et avant l'exec :

sh "$CONF/archiveLogs.sh"

# Lets run it!
exec uv run --frozen appdaemon -c $CONF "$@"

Conserver le lancement uv, les installations via uv pip, le PYTHONPATH et le répertoire de configuration exemple /opt/conf propres à cette version. Ne pas recopier l'ancien script 4.5.10 par-dessus /start.sh. Si l'image déployée possède un autre point d'entrée, transposer cet appel dans le script réellement utilisé et noter le chemin dans le compte rendu. L'ancien lien peut rester inutilisé ; ne pas le prendre comme preuve du patch.

Vérification sans exécution

sh -n /conf/archiveLogs.sh
# Choisir le script réellement utilisé :
sh -n /usr/src/app/dockerStart.sh  # 4.5.10
# OU
sh -n /start.sh                  # 4.5.14 officielle

Inspecter le script : exactement un appel actif à archiveLogs.sh, avant l'exec. Ces modifications hors /conf sont perdues lors d'une recréation du conteneur, comme les patchs Python. Les réappliquer à chaque nouvelle image, ou les intégrer explicitement à une image dérivée si cela fait partie de l'intervention demandée.

7. Redémarrage et validation finale

Après application et vérifications, redémarrer le conteneur selon le mécanisme habituel de cette installation, dans le périmètre de l'intervention demandée. Un simple rechargement d'app ne remplace pas les fonctions core déjà importées et ne réexécute pas le script de démarrage.

Vérifier après redémarrage :

  1. Version réellement lancée et absence d'erreurs de démarrage/import.
  2. Patch deep_compare toujours présent et 11 vérifications réussies dans le même environnement que le service (une installation de dépendances au boot pourrait avoir remplacé le paquet).
  3. Message [archiveLogs], archive créée si des anciens logs existaient, nouveau memory_usage.csv, un seul processus archiveLogs.sh --watch-memory. Vérifier l'ajout de mesures à l'intervalle configuré (300 secondes par défaut). Avec uv, le PID 1 peut être uv : si les colonnes RSS AppDaemon sont vides, vérifier le processus réel ; les métriques du conteneur restent distinctes.
  4. Connexion Home Assistant et absence de rafraîchissements complets chaque seconde après le délai de refresh configuré (10 minutes par défaut).
  5. Tests du logger réussis avec l'attente adaptée à la version.
  6. Si un essai de reload réel est nécessaire, utiliser une app de test isolée sans action domotique ; vérifier ajout/suppression d'une clé imbriquée et nettoyer l'essai. Ne pas modifier chauffage/volets pour ce test.

Mettre à jour docs/appdaemon-local-changes.md avec la version, les fichiers réellement modifiés, les sauvegardes, résultats et statut du redémarrage. Séparer les vérifications exécutées de celles restant à observer ; ne pas annoncer une validation du service sur la seule base des tests unitaires.

8. Rollback vers 4.5.10

Revenir à l'image 4.5.10 prévue par le déploiement, puis reprendre les sections 1 à 7 pour 4.5.10 : patch HASS + clés deep_compare + raccourci applicatif logger + appel d'archivage dans le vrai dockerStart.sh. Les fichiers /conf et le sous-module apps/ad_toolbox doivent être conservés. Si le test du logger a été rendu compatible avec les deux versions, le conserver. Ne pas restaurer de fichiers core 4.5.14 dans 4.5.10, ni les anciennes sondes de diagnostic archivées. Vérifier à nouveau après le redémarrage du rollback.

Sources de référence