Presentando .discourse-compatibility: versiones fijadas de plugins/temas para versiones antiguas de Discourse

¡Hola a todos :wave:! Acabo de integrar una nueva función que ayudará a los plugins y temas a fijar ciertas versiones al instalarse en instancias más antiguas de Discourse.

Ahora puedes incluir un archivo .discourse-compatibility en la raíz del repositorio de un plugin o tema, que designa qué versión verificar al instalarla en versiones más antiguas de Discourse.


Fundamentación

Es molesto recordar qué plugins y temas son compatibles con qué versiones de Discourse. Como administrador, debería ser posible escanear fácilmente esos cambios y encontrar una versión adecuada para tu instalación de Discourse sin tener que leer el historial de commits del plugin. Como autor de un plugin o tema, debería ser posible gestionar las versiones de instalación mientras se realizan cambios incompatibles hacia atrás, para no romper instalaciones anteriores.

Las actualizaciones del software de Discourse se implementan bastante rápido, lo cual es increíble, pero hace que mantener instancias de Discourse con muchos plugins sea a veces muy difícil, especialmente si sigues otros ciclos de lanzamiento/versiones, como la versión estable actual. Mi plan aquí es permitir un ecosistema que facilite el proceso de actualización para quienes siguen la versión estable o algún otro ciclo de lanzamiento, y ofrecer a los administradores del sitio un método para obtener rápida y automáticamente cualquier versión del plugin que fuera compatible con la versión de Discourse que están apuntando.

Anuncio original (ahora sustituido por la documentación vinculada arriba)

Implementación

Lo primero que hay que tener en cuenta: estamos dependiendo de las etiquetas del núcleo de Discourse, ya que las versiones beta de Discourse se lanzan con suficiente frecuencia como para poder fijar las versiones de los plugins contra ellas. Fijar versiones contra hashes de git es una pesadilla por muchas razones, por lo que podemos usar git describe para obtener la etiqueta beta/estable más cercana.

En un plugin o un tema, ahora soportamos un archivo de compatibilidad de versiones llamado .discourse-compatibility en la raíz. Este archivo es una lista ordenada descendente (versiones más nuevas de Discourse primero) que especifica un mapa de compatibilidad.

Ejemplo

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

Para cada plugin/tema, una actualización o reconstrucción seguirá verificando un commit/rama/etiqueta nombrado posterior hasta encontrar uno que sea igual o posterior a la versión actual de Discourse.
Por ejemplo, para el archivo de versiones anterior, si la versión actual de Discourse fuera 2.4.6.beta12, escanearía el archivo y elegiría la entrada para 2.5.0.beta2.

Si la versión actual de Discourse fuera 2.4.4.beta6, elegiría la entrada coincidente para 2.4.4.beta6.

Si no existe una versión posterior, se mantiene en la versión verificada actualmente.
Por ejemplo, para 2.5.0.beta3 no se produciría ninguna fijación.

Si no existe una versión anterior, verifica la más antigua listada en el archivo de versiones.
Por ejemplo, para 2.2.1.beta22, verificaría la más antigua posible dada la “versión”, la entrada para 2.4.2.beta1.


El objetivo aquí es aliviar el dolor de mantener despliegues alternativos que no estén estrictamente en tests-passed en el futuro, y ofrecer flexibilidad a los administradores sobre cuándo y dónde actualizar. Estamos haciendo esto permitiendo a los autores de plugins y temas una forma de desarrollar cambios incompatibles hacia atrás sin afectar las instalaciones en versiones más antiguas de Discourse.

50 Me gusta

¡Esta es una gran característica, gracias! :slight_smile:

Me gusta la direccionalidad de esto, es decir, permite gestionar este problema desde el propio plugin, sin que el administrador del sitio tenga que hacer nada en particular.

Tengo algunas preguntas iniciales:

  • ¿Se mantendrá la verificación de metadatos del plugin required_version existente durante la activación del plugin? ¿Y cómo ves que esto se relacione con lo anterior (si es que se relaciona)?

  • Veo que esto se ha añadido en forma de una tarea rake por ahora. ¿Cómo se relaciona esto con discourse_docker (es decir, el lanzador) y docker_manager? ¿Cuál es el uso previsto? He visto que has realizado cambios en ambos repositorios, pero ¿podrías explicar cómo se supone que debe funcionar en ambos entornos?

12 Me gusta

Sí, esa es la idea: permitir que los autores de plugins añadan compatibilidad hacia atrás para que los administradores no tengan que preocuparse.

Actualmente no hay planes para cambiar o eliminar los metadatos del plugin required_version. Están relacionados, pero en mi mente siguen siendo conceptos distintos: el required_version con sus valores mínimo/máximo y punto final impide la instalación del plugin lanzando un error, y se carga después de esta verificación de compatibilidad. Si deseas evitar que instancias muy antiguas de Discourse utilicen tu plugin, seguiría siendo una buena idea incluir required_version para la primera versión mínima; por mucho que se busque compatibilidad, eso no se solucionará :wink:

No deberían ser necesarios cambios de configuración en el uso normal; detectará los cambios automáticamente. La tarea de rake se ejecuta en discourse_docker después de clonar los plugins, por lo que el orden en el lanzador es:

  • Clonación de plugins
  • Verificación de compatibilidad y checkout (si corresponde)
  • Migración

En docker_manager, las versiones más antiguas de Discourse podrán actualizar los plugins a una versión compatible. La interfaz de usuario se mantiene igual, pero un plugin “actualizado” ahora se define según el archivo de compatibilidad.

TLDR: no se necesitan cambios para ninguno de los dos casos de uso para empezar a aprovechar esta funcionalidad, por si te lo estabas preguntando.

Por debajo de la superficie, utiliza git show HEAD@{upstream}:.discourse-compatibility para leer el archivo más reciente y git reset --hard #{checkout_version} para hacer el checkout de la versión correcta. De esta manera, podemos utilizar la última información de compatibilidad y las versiones antiguas de Discourse no quedarán atascadas en un archivo de compatibilidad antiguo (y posiblemente inválido).

11 Me gusta

Genial, gracias por explicarlo.

Así que me serví una copa de :wine_glass: y lo probé con el Plugin de Asistente Personalizado.

Revisé cada etiqueta una por una en orden inverso, comenzando con v2.6.0.beta1. Encontré que estos comandos de git eran útiles:

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

No me tomó mucho tiempo encontrar una etiqueta que no funcionaba con la versión actual del plugin: v2.5.0.beta7 no incluye discourse/app/components/d-textarea, que el asistente personalizado intenta importar.

Entonces, encontré el commit en el plugin que agregó esa importación, tomé el sha1 del commit anterior, lo verifiqué, lo probé (funcionó bien) y agregué esto a .discourse-compatibility:

v2.5.0.beta7: 802d74bab2ebe19a106f75275342dc2e9cc6066a

Luego subí eso a una rama con el último código del plugin (una rama para pruebas, normalmente no es necesaria), y reconstruí un servidor de pruebas en Docker con esa rama del plugin y la version establecida en v2.5.0.beta7.

Eso no funcionó, luego me di cuenta de que, por supuesto, la tarea de rake plugin:pull_compatible_all no existe en v2.5.0.beta7, así que esto no funcionará de forma retrospectiva (la culpa es del :wine_glass:). Efectivamente, en los registros del lanzador veo:

No se sabe cómo construir la tarea 'plugin:pull_compatible_all' (Vea la lista de tareas disponibles con `rake --tasks`)

¿Es esa la esencia de cómo imaginas que se usaría esto?

En cuanto a required_version, me encontré con eso aquí porque el servidor de pruebas tenía instalado el plugin discourse-legal-tools, que tiene un required_version de v2.5.0, por lo que inicialmente falló en v2.5.0.beta7. Creo que transferiré ese plugin a este nuevo sistema. Todavía veo útil que required_version establezca una línea base absoluta, como mencionas.

11 Me gusta

Sí, eso es correcto: como esto no estaba integrado en el núcleo de Discourse hasta ahora, no funcionará en versiones anteriores a 2.6.0.beta1 (actualmente). Todavía tengo que portarlo a la versión estable y a la última beta, por lo que podremos usarlo con la 2.5.0, pero no planeamos portarlo a versiones anteriores.

11 Me gusta

Solo un seguimiento: ahora he agregado un archivo de compatibilidad al maestro de Custom Wizard y lo utilizaré para fijar el plugin, incluyendo v2.6.0.beta1 (beta actual) y v2.5.0 (estable actual) cuando esto sea portado. Ver más:

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

5 Me gusta

Una nota y una pregunta aquí.

En primer lugar, solo una nota: la sintaxis para las etiquetas de versión de Discourse en el archivo .discourse-compatibility es 2.5.0 (como en el ejemplo de @featheredtoast), no v2.5.0. Si incluyes una v, obtendrás este error:

Malformed version number string v2.6.0.beta1

En segundo lugar, @featheredtoast señaló que el sistema de fijación de versiones ya se ha retrotraído a la rama stable. No lo noté porque estaba buscando la etiqueta v2.5.0.

Esto me plantea una pequeña duda. Las ramas de Discourse no equivalen directamente a las etiquetas de versión de Discourse. La mayoría de los sitios que ejecutan versiones anteriores de Discourse probablemente estén en una rama, es decir, stable, en lugar de en una versión de lanzamiento, es decir, v2.5.0.

Si un cambio disruptivo (para un complemento) también se agrega a una rama “antigua”, es decir, stable, ¿qué significa esto para las fijaciones? Probablemente no entiendo del todo cómo funciona el sistema de control de versiones.

6 Me gusta

Las versiones corresponden a la versión de Discourse definida en la aplicación, no a la etiqueta de git.

Por lo tanto, “2.5.0” en este contexto significa cualquier versión declarada como 2.5.0, hasta el nuevo incremento de versión a 2.5.1 (o 2.6.0.beta1).

Si también se agrega un cambio incompatible (para un complemento) a la rama estable, eso efectivamente rompería el complemento. Todo el propósito del versionado es poder estar relativamente seguros de que no estamos introduciendo cambios incompatibles en esa versión, por lo que eso es algo que debe denunciarse por separado. Nuestra intención para la rama estable es hacer backport solo de correcciones de seguridad y críticas (y de funciones menores, cuando corresponda).

El propósito de esto es tener la capacidad de calcular versiones anteriores funcionales de complementos frente a versiones anteriores funcionales de Discourse. Esto no es de ninguna manera una solución mágica, pero sí nos brinda una plataforma para hacerlo, si realmente somos cuidadosos al elegir qué hacer backport.

7 Me gusta

Esto funciona en su mayoría, a menos que estés clonando con --depth=1.

Implementaré pronto un hook para ejecutar git fetch --depth 1 {upstream} commit si el objetivo no se encuentra inicialmente; de lo contrario, quedaríamos atrapados realizando una clonación parcial o completa, lo cual no es ideal. Deberíamos poder obtener lo que necesitamos.

Edición: Actualizado aquí:

También he hecho un backport de esto a Beta y Stable, así que ahora deberíamos poder fijar versiones incluso con clonaciones superficiales.

6 Me gusta

Me disculpo de antemano, ya que a menudo recurro a esto bastante tarde en la noche, así que no estoy seguro de tener el 100% de la razón y es posible que esto ya sea algo que ya conozcas. Lo estoy documentando aquí en parte por mi propia cordura, ya que me ha confundido un par de veces ahora.

Creo que este mecanismo es efectivamente inviable para la rama beta si el plugin se utiliza en una instancia que no controlas (es decir, es de código abierto) y que se actualiza de la manera habitual. La forma habitual de actualizar es que el administrador del sitio lo haga cuando se le indique en la interfaz de administración.

Dado esto

Y esto

Y que tests-passed y beta tienen la misma versión de Discourse pero no el mismo código, por ejemplo, ambos son actualmente 2.6.0.beta2:

De esto se deduce:

  1. Para admitir la rama beta, necesitas fijar un commit a la última versión beta, ya que esa es la que utilizarán los sitios en la rama beta.

  2. Sin embargo, si la última versión beta está en el archivo de compatibilidad, las instancias que ejecutan tests-passed también utilizarán el commit fijado.

Esto significa que no puedes admitir el uso estándar de tests-passed y beta al mismo tiempo en un plugin de código abierto. Dado que la mayoría de las personas que instalan plugins están en tests-passed, efectivamente no puedes admitir beta mediante este método.

Ten en cuenta que sí funciona en la práctica para la rama stable, ya que stable tiene una versión de Discourse diferente a beta y tests-passed.

4 Me gusta

Sí, esto es algo de lo que soy consciente; no se resuelve realmente hasta la siguiente versión beta. Definitivamente también deberíamos encontrar una manera de distinguir entre las versiones beta y las versiones en las que las pruebas han pasado.

Cuando configuré la función, tenía en mente las ramas estables y las ramas extrañas, y no me di cuenta hasta más tarde que la última versión y la estable compartían versiones.

6 Me gusta

Gracias por confirmarlo.

Parece que la conclusión efectiva es que el proceso no debe ejecutarse si la instancia está ejecutando tests-passed. No puedes incluir la versión tests-passed en el archivo por las razones descritas anteriormente, por lo que excluir el proceso en tests-passed no cambiaría su comportamiento actual.

Una forma de implementar esto sería

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

   ...
end

Esto permitiría usar la última versión beta en el archivo con el fin de fijar los plugins para sitios que ejecutan beta. Y podrías seguir dando soporte a tests-passed en el último commit del plugin, es decir, sin usar este archivo. Si estás de acuerdo con eso, puedo abrir un PR.

Alternativamente, siento que el problema subyacente aquí es este

Estoy seguro de que esto es algo que ya han discutido antes, pero ¿sería posible hacer algo como

  • tests-passed: 2.6.0.tests-passed, es decir, establecer el PRE como tests-passed en la rama tests-passed.

  • beta: 2.6.0.beta2, es decir, como está actualmente

@jomaxro ¿Podrías ayudarme a entender esto?

7 Me gusta

Sí, estoy a favor de añadir una marca adicional para la versión pre-beta frente a una versión beta real, siempre que sea lo suficientemente fácil de implementar. Eso ayudaría a resolver el problema subyacente aquí. He visto otros proyectos de software que incrementan la versión a la siguiente antes de su lanzamiento (por ejemplo, después de lanzar la beta1, cambian la «versión» a beta2).

Sin embargo, tradicionalmente Discourse no ha hecho eso, así que depende de qué método sea más fácil de adoptar para el proyecto.

6 Me gusta

Me encontré con otro problema.

Este cambio introduce $danger-low-mid en la versión 2.6.0beta2.

Esto rompió el plugin discourse-styleguide, por lo que se actualizó y se añadió un archivo .discourse-compatibility para mantener el plugin en el commit anterior para Discourse 2.5.0.

Esto falla en Discourse 2.5.1. Dado que este cambio nunca se retrotraerá a la versión estable, el archivo discourse-compatibility tendría que actualizarse en cada nueva versión estable 2.5.x.

Cada archivo discourse-compatibility de cada plugin tendría que actualizarse en cada nueva versión estable 2.5.x.

Alternativamente, el archivo de compatibilidad podría hacer referencia a la versión 2.5.999, manteniendo efectivamente el plugin en el commit 1f86468b2c81b40e97f1bcd16ea4bb780634e2c7 durante toda la vida útil de la serie 2.5.x. Pero esto me parece muy poco elegante.

6 Me gusta

Parece que sería seguro fijar la versión en 2.6.0beta1, lo cual también sería más correcto, ya que el problema se introdujo en beta2. ¿Tiene sentido? De esa manera también se cubrirían todas las versiones de la serie 2.5.

Tu solución “parcheada” de 2.5.999 suena como un buen truco (aunque desordenado, como mencionaste). Intenté mantener la fijación de versiones lo más sencilla posible, pero tienes razón en que todavía nos falta una forma verdadera de especificar 2.5.x como una versión fijada válida.

Otro truco para ese caso específico de fijar la última versión estable, pero dejando las betas de la siguiente versión sin fijar, que podría ser un poco más sutil pero que a mí me parece mucho menos “parcheado” para fijar toda la rama 2.5.x, es fijar en 2.6.0.beta0 o 2.6.0.alpha1, lo cual semánticamente significa fijar todo lo anterior y hasta la versión comprendida entre 2.5.x y 2.6.0.beta1. Esto significa:

Todo lo de la serie 2.5.x queda cubierto por la fijación.
Todo lo de 2.6.0.beta(1+) sigue sin estar fijado.

6 Me gusta

Sí, a mí también me parece menos «hacky».

Pero tanto la solución 2.5.999 como la 2.6.0beta0 no cubren un caso similar: ¿qué pasaría si se introdujera un problema en 2.6.0beta3 y se hiciera un backport a 2.5.2?

Más casos límite, más características ocultas: en realidad puedes “desactivar” el bloqueo con una entrada en blanco:

Con un archivo de compatibilidad como:

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

Cualquier versión entre 2.4.2.beta1 y 2.4.4.beta6 no se verá afectada por el bloqueo. Cualquier versión posterior hasta 2.6.0.beta1 será bloqueada. A partir de ahí, se desbloqueará nuevamente.

En el fondo, cualquier valor que se evalúe como nil permanecerá sin cambios o se mantendrá en la última versión. Una entrada en blanco o ~ se evalúa como nil (mediante el analizador YAML de Ruby), lo que evita la función de bloqueo.

8 Me gusta

¿Es posible usar un plugin de una versión anterior en una instancia autoalojada?

1 me gusta

Sí, puedes ajustar los comandos de git clone en tu app.yml para clonar la versión que desees. Ten en cuenta que también debes asegurarte de que Discourse esté fijado a la versión que deseas. Y también puede que necesites fijar discourse_docker a una versión compatible con la versión de Discourse que estás ejecutando. Si haces todo eso, es más fácil no actualizar.

2 Me gusta