Présentation de .discourse-compatibility : versions épinglées de plugins/thèmes pour les anciennes versions de Discourse

Bonjour à tous :wave:, je viens de fusionner une nouvelle fonctionnalité qui aidera les plugins et les thèmes à verrouiller certaines versions lors de leur installation sur d’anciennes instances Discourse.

Vous pouvez désormais inclure un fichier .discourse-compatibility à la racine d’un dépôt de plugin ou de thème, qui spécifie quelle version vérifier lors de l’installation sur d’anciennes versions de Discourse.


Justification

C’est pénible de se souvenir quels plugins et thèmes sont compatibles avec quelles versions de Discourse. En tant qu’administrateur, il devrait être possible de parcourir facilement ces changements et de trouver une version adaptée à votre installation Discourse sans avoir à lire l’historique des commits du plugin. En tant qu’auteur de plugin ou de thème, il devrait être possible de gérer les versions d’installation tout en apportant des modifications incompatibles avec les versions antérieures, afin de ne pas casser les installations existantes.

Les mises à jour logicielles de Discourse sont déployées assez rapidement, ce qui, bien que génial, rend parfois très difficile la maintenance d’instances Discourse comportant de nombreux plugins, surtout si vous suivez d’autres rythmes de publication ou versions, comme la version stable actuelle. Mon intention ici est de permettre un écosystème qui facilite le processus de mise à jour pour ceux qui suivent soit la version stable, soit un autre rythme de publication, et de fournir aux administrateurs de sites une méthode pour récupérer rapidement et automatiquement la version de plugin compatible avec la version de Discourse qu’ils ciblent.

Annonce originale (désormais remplacée par la documentation liée ci-dessus)

Mise en œuvre

Première chose à noter : nous nous appuyons sur les tags du cœur de Discourse, car les versions bêta de Discourse sont publiées fréquemment, ce qui nous permet de verrouiller les versions des plugins par rapport à elles. Le verrouillage par hachage git est un cauchemar pour de nombreuses raisons, nous utilisons donc git describe pour obtenir le tag bêta/stable le plus proche.

Dans un plugin ou un thème, nous prenons désormais en charge un fichier de compatibilité de version nommé .discourse-compatibility à la racine. Ce fichier est une liste ordonnée décroissante (versions plus récentes de Discourse en premier) qui spécifie une carte de compatibilité.

Exemple

2.5.0.beta2: git-hash-1234e5f5d
2.4.4.beta6: 4444ffff33dd
2.4.2.beta1: named-git-tag-or-branch

Pour chaque plugin/thème, une mise à niveau ou une reconstruction continuera de vérifier un commit/branche/tag nommé ultérieur jusqu’à ce qu’il trouve un élément égal ou postérieur à la version actuelle de Discourse.
Par exemple, pour le fichier de version ci-dessus, si la version actuelle de Discourse est 2.4.6.beta12, il parcourra le fichier et choisira l’entrée pour 2.5.0.beta2.

Si la version actuelle de Discourse est 2.4.4.beta6, il choisira l’entrée correspondante pour 2.4.4.beta6.

Si aucune version ultérieure n’existe, il reste sur la version actuellement vérifiée.
Par exemple, pour 2.5.0.beta3, aucun verrouillage ne se produira.

Si aucune version antérieure n’existe, il vérifie la plus ancienne répertoriée dans le fichier de version.
Par exemple, pour 2.2.1.beta22, il vérifiera la plus ancienne possible étant donné la « version », l’entrée pour 2.4.2.beta1.


L’objectif ici est de réduire la douleur liée à la maintenance de déploiements alternatifs qui ne sont pas strictement basés sur tests-passed à l’avenir, et de donner aux administrateurs la flexibilité de décider quand et où effectuer la mise à niveau. Nous le faisons en permettant aux auteurs de plugins et de thèmes de développer des modifications incompatibles avec les versions antérieures sans affecter les installations sur d’anciennes versions de Discourse.

50 « J'aime »

C’est une excellente fonctionnalité, merci :slight_smile:

J’aime l’approche adoptée ici, c’est-à-dire qu’elle permet de gérer ce problème directement depuis le plugin lui-même, sans que l’administrateur du site n’ait à intervenir.

J’ai quelques questions initiales :

  • La vérification des métadonnées du plugin via required_version lors de l’activation du plugin restera-t-elle en place ? Et comment voyez-vous son interaction avec cette nouvelle fonctionnalité (le cas échéant) ?

  • Je vois que cela est actuellement implémenté sous forme de tâche Rake. Comment cela se rapporte-t-il à discourse_docker (c’est-à-dire le lanceur) et à docker_manager ? Quelle est l’utilisation prévue ? J’ai remarqué que vous avez apporté des modifications aux deux dépôts, mais pourriez-vous expliquer comment cela est censé fonctionner dans les deux environnements ?

12 « J'aime »

Oui, c’est l’idée : permettre aux auteurs de plugins d’ajouter une compatibilité rétroactive afin que les administrateurs n’aient pas à s’en soucier !

Il n’est actuellement pas prévu de modifier ou de supprimer les métadonnées required_version des plugins. Elles sont liées, mais restent distinctes dans mon esprit : la version minimale/maximale définie par required_version empêche l’installation du plugin en générant une erreur, et elle est chargée après cette vérification de compatibilité. Si vous souhaitez empêcher les instances Discourse très anciennes d’utiliser votre plugin, il est toujours recommandé d’inclure required_version pour la première version minimale requise ; aucune recherche de compatibilité ne pourra résoudre ce problème :wink:

Aucune modification de configuration ne devrait être nécessaire en usage normal, les changements seront détectés automatiquement. La tâche Rake est appelée dans discourse_docker après le clonage des plugins, donc dans l’ordre de lancement du launcher :

  • Clonage des plugins
  • Vérification de compatibilité et passage à la version correspondante (le cas échéant)
  • Migration

Dans docker_manager, les anciennes versions de Discourse pourront mettre à jour les plugins vers une version compatible. L’interface utilisateur reste identique, mais un plugin « à jour » est désormais déterminé selon le fichier de compatibilité.

TLDR : aucune modification n’est nécessaire dans l’un ou l’autre cas d’usage pour commencer à bénéficier de cette fonctionnalité, si c’est ce que vous vous demandiez.

Sous le capot, cela utilise git show HEAD@{upstream}:.discourse-compatibility pour lire le fichier le plus récent, et git reset --hard #{checkout_version} pour passer à la version correcte. Cela nous permet d’utiliser la dernière compatibilité disponible, et les anciennes versions de Discourse ne resteront pas bloquées sur un ancien fichier de compatibilité (éventuellement invalide).

11 « J'aime »

Cool, merci pour ces explications.

Alors, je me suis servi un :wine_glass: et j’ai essayé avec le plugin Custom Wizard.

J’ai vérifié chaque tag un par un, dans l’ordre inverse, en commençant par v2.6.0.beta1. Ces commandes git m’ont été utiles :

git tag --list \\ par exemple git tag --list 'v2.5.0*'
git checkout tags/tag \\ par exemple git checkout tags/v2.5.0.beta7

Ça n’a pas pris trop de temps pour trouver un tag qui ne fonctionnait pas avec la version actuelle du plugin : v2.5.0.beta7 n’inclut pas discourse/app/components/d-textarea, que le wizard personnalisé tente d’importer.

Ensuite, j’ai trouvé le commit dans le plugin qui a ajouté cet import, j’ai récupéré le sha1 du commit précédent, je l’ai appliqué, j’ai testé (ça a fonctionné), et j’ai ajouté ceci à .discourse-compatibility :

v2.5.0.beta7: 802d74bab2ebe19a106f75275342dc2e9cc6066a

J’ai ensuite poussé cela sur une branche avec le code le plus récent du plugin (une branche de test, pas nécessaire normalement), et j’ai reconstruit un serveur de test dockerisé avec cette branche du plugin et la version définie sur v2.5.0.beta7.

Ça n’a pas fonctionné, puis il m’est venu à l’esprit que, bien sûr, la tâche rake plugin:pull_compatible_all n’existe pas dans v2.5.0.beta1, donc cela ne peut pas fonctionner rétrospectivement (je blâme le :wine_glass:). Effectivement, dans les journaux du launcher, je vois :

Don't know how to build task 'plugin:pull_compatible_all' (See the list of available tasks with `rake --tasks`)

Est-ce que c’est bien l’essentiel de la façon dont tu imagines que cela devrait être utilisé ?

Concernant le champ required_version, je l’ai rencontré ici car le serveur de test avait le plugin discourse-legal-tools installé, qui a un required_version de v2.5.0, donc il a échoué initialement sur v2.5.0.beta7. Je pense que je vais transférer ce plugin vers ce nouveau système. Je vois toujours l’utilité de required_version pour définir une base absolue, comme tu le dis.

11 « J'aime »

Oui, c’est exact : comme cette fonctionnalité n’est intégrée au cœur de Discourse que depuis maintenant, elle ne fonctionnera sur aucune version antérieure à 2.6.0.beta1 (pour l’instant). Je dois encore la porter vers la version stable et la dernière bêta, afin que nous puissions l’utiliser avec la 2.5.0, mais nous ne prévoyons pas de la porter sur des versions plus anciennes.

11 « J'aime »

Juste un petit rappel : j’ai désormais ajouté un fichier de compatibilité au maître de Custom Wizard et je l’utiliserai pour verrouiller le plugin, y compris v2.6.0.beta1 (bêta actuelle) et v2.5.0 (stable actuelle), lors de son portage. Voir plus bas :

https://meta.discourse.org/t/custom-wizard-plugin/73345/562?u=angus

5 « J'aime »

Une remarque et une question ici.

Premièrement, notez simplement que la syntaxe pour les balises de version Discourse dans le fichier .discourse-compatibility est 2.5.0 (comme dans l’exemple de @featheredtoast), et non v2.5.0. Si vous ajoutez un v, vous obtiendrez cette erreur :

Malformed version number string v2.6.0.beta1

Deuxièmement, @featheredtoast a souligné que le système de verrouillage (pinning) est désormais rétroporté sur stable. Je l’ai manqué car je regardais la balise v2.5.0.

Cela soulève une petite question pour moi. Les branches de Discourse ne correspondent pas directement aux balises de version de Discourse. La plupart des sites utilisant des versions plus anciennes de Discourse sont probablement sur une branche, c’est-à-dire stable, plutôt que sur une version publiée, c’est-à-dire v2.5.0.

Si un changement cassant (pour un plugin) est également ajouté à une branche « ancienne », c’est-à-dire stable, que signifie cela pour les verrous (pins) ? Je ne comprends probablement pas encore entièrement le fonctionnement du système de versioning.

6 « J'aime »

Les versions correspondent à la version de Discourse définie dans l’application, et non à la balise git.

Ainsi, « 2.5.0 » dans ce contexte désigne toute version déclarée comme 2.5.0, jusqu’à la nouvelle mise à niveau vers 2.5.1 (ou 2.6.0.beta1).

Si une modification incompatible (pour un plugin) est également ajoutée à la branche stable, cela briserait effectivement le plugin. Tout l’intérêt du versionnage est de nous permettre d’être relativement sûrs que nous n’introduisons pas de modifications incompatibles pour cette version ; c’est donc un point à soulever séparément. Notre intention pour la branche stable est de rétroporter uniquement des correctifs de sécurité et critiques (ainsi que des fonctionnalités mineures, le cas échéant).

L’objectif est de nous permettre de calculer les anciennes versions fonctionnelles des plugins par rapport aux anciennes versions fonctionnelles de Discourse. Ce n’est en aucun cas une solution miracle, mais cela nous offre une plateforme pour le faire, à condition que nous soyons réellement vigilants dans le choix de ce que nous rétroportons.

7 « J'aime »

Cela fonctionne généralement, sauf lorsque vous clonez avec l’option --depth=1.

Je vais bientôt implémenter un hook pour exécuter git fetch --depth 1 {upstream} commit si la cible n’est pas trouvée initialement. Sinon, nous serions contraints d’effectuer un clone partiel ou complet, ce qui n’est pas idéal. Nous devrions pouvoir récupérer ce dont nous avons besoin.

Édit : Mis à jour ici :

J’ai également rétroporté cette modification dans les versions Beta et Stable. Nous devrions donc désormais pouvoir verrouiller même avec des clones partiels.

6 « J'aime »

Désolé par avance, car je consulte souvent cela assez tard dans la nuit, donc je ne suis pas sûr à 100 % d’être exact ici, et il est possible que vous soyez déjà au courant de ce problème. Je le documente ici en partie pour ma propre tranquillité d’esprit, car cela m’a déjà posé problème à plusieurs reprises.

Je pense que ce mécanisme est effectivement inutilisable pour la branche beta si le plugin est utilisé sur une instance que vous ne contrôlez pas (c’est-à-dire qu’elle est open source) et qui est mise à jour de manière classique. La mise à jour classique consiste pour l’administrateur du site à le faire lorsqu’il est invité à le faire dans l’interface d’administration.

Compte tenu de ceci

Et ceci

Et le fait que tests-passed et beta aient la même version de Discourse mais pas le même code, par exemple tous deux sont actuellement 2.6.0.beta2 :

Il en découle :

  1. Pour prendre en charge la branche beta, vous devez figer un commit correspondant à la dernière version bêta, car c’est celle que les sites de la branche beta utiliseront.

  2. Cependant, si la dernière version bêta figure dans le fichier de compatibilité, les instances exécutant tests-passed utiliseront également le commit figé.

Cela signifie que vous ne pouvez pas prendre en charge l’utilisation standard de tests-passed et de beta simultanément dans un plugin open source. Étant donné que la majorité des personnes qui installent des plugins sont sur tests-passed, vous ne pouvez effectivement pas prendre en charge beta via cette méthode.

Notez que cela fonctionne en pratique pour la branche stable, car stable a une version de Discourse différente de beta et de tests-passed.

4 « J'aime »

Oui, c’est quelque chose dont j’ai connaissance, cela ne sera vraiment résolu qu’à la prochaine version bêta. Nous devrions également trouver un moyen de distinguer les versions bêta des versions où les tests ont réussi pour cela.

Lorsque j’ai configuré cette fonctionnalité, je pensais aux forks stables et étranges, et je n’ai réalisé que plus tard que les versions « dernière » et « stable » partageaient les mêmes numéros de version.

6 « J'aime »

Merci pour la confirmation.

Il semble que la conséquence effective soit que le processus ne doit pas s’exécuter si l’instance exécute tests-passed. Vous ne pouvez pas inclure la version tests-passed dans le fichier pour les raisons décrites ci-dessus, donc empêcher l’exécution du processus sur tests-passed ne modifierait pas son comportement actuel.

Une façon de mettre cela en œuvre serait :

def self.find_compatible_resource(version_list, version = ::Discourse::VERSION::STRING)
 
   return if Discourse.git_branch === 'tests-passed'

   ...
end

Cela rendrait possible l’utilisation de la dernière version beta dans le fichier dans le but de verrouiller les plugins pour les sites exécutant beta. Et vous pourriez continuer à prendre en charge tests-passed dans le dernier commit du plugin, c’est-à-dire sans utiliser ce fichier. Si cela vous convient, je peux préparer une PR.

Alternativement, je pense que le problème sous-jacent ici est le suivant :

Je suis sûr que c’est quelque chose que vous avez déjà discuté, mais serait-il possible de faire quelque chose comme :

  • tests-passed : 2.6.0.tests-passed, c’est-à-dire définir PRE sur tests-passed sur la branche tests-passed.

  • beta : 2.6.0.beta2, c’est-à-dire comme actuellement

@jomaxro, pourriez-vous m’aider à comprendre cela ?

7 « J'aime »

Oui, je suis favorable à l’ajout d’une mention supplémentaire pour distinguer une version pré-bêta d’une version bêta officielle, à condition que ce soit suffisamment simple à mettre en œuvre. Cela permettrait de résoudre le problème sous-jacent ici. J’ai vu d’autres projets logiciels passer à la prochaine version avant sa sortie (par exemple, après la sortie de la bêta 1, passer la « version » à bêta 2).

Cependant, Discourse n’a traditionnellement pas suivi cette approche, donc cela dépend de la méthode la plus facile à adopter pour le projet.

6 « J'aime »

J’ai rencontré un autre problème.

Cette modification introduit $danger-low-mid dans la version 2.6.0beta2.

Cela a cassé le plugin discourse-styleguide, qui a donc été mis à jour. Un fichier .discourse-compatibility a été ajouté pour maintenir le plugin à l’ancien commit pour Discourse 2.5.0.

Cela pose problème sur Discourse 2.5.1. Comme cette modification ne sera jamais rétroportée vers les versions stables, le fichier discourse-compatibility devrait être mis à jour à chaque nouvelle version stable 2.5.x.

Chaque fichier discourse-compatibility de chaque plugin devrait être mis à jour à chaque nouvelle version stable 2.5.x.

Une alternative consisterait à faire référence à la version 2.5.999 dans le fichier de compatibilité, ce qui maintiendrait le plugin au commit 1f86468b2c81b40e97f1bcd16ea4bb780634e2c7 pour toute la durée de vie de la série 2.5.x. Cependant, cela me semble très peu élégant.

6 « J'aime »

Cela semble indiquer qu’il serait sûr de se verrouiller sur la version 2.6.0beta1, ce qui serait également plus correct puisque le problème a été introduit dans la beta2. Est-ce que cela a du sens ? Cela couvrirait également toutes les versions de la série 2.5.

Votre solution « bidouillée » de la version 2.5.999 semble être une astuce élégante (bien que désordonnée, comme vous l’avez mentionné). J’ai essayé de rendre le verrouillage ici aussi simple que possible, mais vous avez raison : nous manquons toujours d’un moyen véritable de spécifier 2.5.x comme version verrouillée valide.

Une autre astuce pour ce cas particulier de verrouillage sur la dernière version stable, tout en laissant les bêtas de la prochaine version non verrouillées, qui pourrait être un peu plus subtile mais me semble beaucoup moins bidouillée pour verrouiller l’ensemble de la branche 2.5.x, consiste à effectuer le verrouillage sur 2.6.0.beta0 ou 2.6.0.alpha1. Cela signifie, sémantiquement, verrouiller tout ce qui se situe avant et jusqu’à la version comprise entre 2.5.x et 2.6.0.beta1. Cela implique :

Toutes les versions 2.5.x sont couvertes par le verrouillage.
Toutes les versions 2.6.0.beta(1+) restent non verrouillées.

6 « J'aime »

Oui, cela me semble aussi moins « bidouillé ».

Cependant, ni la solution 2.5.999 ni celle de 2.6.0beta0 ne couvrent un cas similaire : que se passerait-il si un problème était introduit dans 2.6.0beta3 et rétroporté vers 2.5.2 ?

Plus de cas limites, plus de fonctionnalités cachées : vous pouvez en réalité « désactiver » l’épinglage avec une entrée vide :

Avec un fichier de compatibilité comme :

        2.6.0.beta1: twofiveall
        2.4.4.beta6: ~
        2.4.2.beta1: twofourtwobetaone

Toute version comprise entre 2.4.2.beta1 et 2.4.4.beta6 ne sera pas épinglée. Toute version ultérieure jusqu’à 2.6.0.beta1 sera épinglée. Au-delà, elle sera à nouveau désépinglée.

En interne, toute valeur évaluée à nil restera inchangée/sera sur la dernière version. Une entrée vide ou ~ évalue à nil (via le parseur YAML de Ruby), ce qui contourne la fonction d’épinglage.

8 « J'aime »

Est-il possible d’utiliser un plugin d’ancienne version sur une instance auto-hébergée ?

1 « J'aime »

Oui, vous pouvez ajuster les commandes git clone dans votre app.yml pour cloner la version que vous souhaitez. Notez que vous devez également vous assurer que Discourse est épinglé à la version que vous souhaitez. Et vous devrez peut-être également épingler discourse_docker à une version compatible avec la version de Discourse que vous exécutez. Si vous faites tout cela, il est plus facile de ne pas mettre à niveau.

2 « J'aime »