Introduzione di .discourse-compatibility: versioni bloccate di plugin e temi per versioni più vecchie di Discourse

Ciao a tutti :wave:, ho appena unito una nuova funzionalità che aiuterà plugin e temi a fissare determinate versioni quando installati su istanze Discourse più vecchie.

Ora è possibile includere un file .discourse-compatibility nella radice di un repository di plugin o tema, che specifica quale versione controllare quando si esegue l’installazione su versioni precedenti di Discourse.


Motivazione

È frustrante dover ricordare quali plugin e temi sono compatibili con quali versioni di Discourse. Come amministratore, dovrebbe essere possibile scansionare facilmente queste modifiche e trovare una versione adatta alla propria installazione di Discourse senza dover leggere la cronologia dei commit del plugin. Come autore di plugin o temi, dovrebbe essere possibile gestire le versioni di installazione mentre si apportano modifiche incompatibili con le versioni precedenti, in modo da non rompere le installazioni esistenti.

Gli aggiornamenti del software Discourse vengono distribuiti piuttosto rapidamente, il che, sebbene sia fantastico, rende a volte molto difficile mantenere istanze di Discourse con molti plugin, specialmente se si seguono altri cicli di rilascio/versioni, come la versione stabile corrente. Il mio obiettivo qui è permettere un ecosistema che faciliti il processo di aggiornamento per chi segue la versione stabile o altri cicli di rilascio, offrendo agli amministratori dei siti un metodo per recuperare rapidamente e automaticamente qualsiasi versione del plugin fosse compatibile con la versione di Discourse che stanno utilizzando.

Annuncio originale (ora sostituito dalla documentazione collegata sopra)

Implementazione

La prima cosa da notare: ci basiamo sui tag del core di Discourse, dato che le versioni beta di Discourse vengono distribuite frequentemente enough da permetterci di fissare le versioni dei plugin contro di esse. Fissare le versioni contro hash git è un incubo per molte ragioni, quindi possiamo usare git describe per ottenere il tag beta/stabile più vicino.

In un plugin o in un tema, ora supportiamo un file di compatibilità delle versioni chiamato .discourse-compatibility nella radice. Questo file è un elenco ordinato in ordine decrescente (versioni più recenti di Discourse per prime) che specifica una mappa di compatibilità.

Esempio

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

Per ogni plugin/tema, un aggiornamento o una ricompilazione continuerà a controllare un commit/branch/tag nominato successivo fino a trovare uno uguale o successivo alla versione corrente di Discourse.
Ad esempio, per il file di versione sopra, se la versione corrente di Discourse fosse 2.4.6.beta12, scansionerebbe il file e sceglierebbe la voce per 2.5.0.beta2.

Se la versione corrente di Discourse fosse 2.4.4.beta6, sceglierebbe la voce corrispondente per 2.4.4.beta6.

Se non esiste una versione successiva, rimane sulla versione attualmente controllata.
Ad esempio, per 2.5.0.beta3 non avverrebbe alcun fissaggio.

Se non esiste una versione precedente, viene controllata la prima elencata nel file delle versioni.
Ad esempio, per 2.2.1.beta22 verrebbe controllata la versione più antica disponibile, ovvero la voce per 2.4.2.beta1.


L’obiettivo qui è ridurre il disagio nel mantenere distribuzioni alternative che non siano strettamente basate su “tests-passed” in futuro, offrendo flessibilità agli amministratori su quando e dove eseguire l’aggiornamento. Lo stiamo facendo consentendo agli autori di plugin e temi di sviluppare modifiche incompatibili con le versioni precedenti senza influenzare le installazioni su versioni più vecchie di Discourse.

50 Mi Piace

Questa è un’ottima funzionalità, grazie :slight_smile:

Mi piace la direzione presa, ovvero rende possibile gestire questo problema direttamente all’interno del plugin stesso, senza richiedere che l’amministratore del sito faccia nulla in particolare.

Ho alcune domande iniziali:

  • Rimarrà il controllo dei metadati del plugin esistente required_version durante l’attivazione del plugin? E come vedi l’interazione tra questo e tale controllo (se prevista)?

  • Vedo che al momento è implementato come un task Rake. Come si relaziona con, e qual è l’uso previsto in relazione a, discourse_docker (ovvero il launcher) e docker_manager? Ho notato che hai apportato modifiche a entrambi i repository, ma potresti spiegare come dovrebbe funzionare in entrambi gli ambienti?

12 Mi Piace

Sì, è proprio questo l’obiettivo: permettere agli autori dei plugin di aggiungere la retrocompatibilità, così gli amministratori non dovranno preoccuparsene!

Al momento non ci sono piani per modificare o rimuovere i metadati del plugin required_version. Sono correlati, ma nella mia mente rimangono separati: il required_version con i valori min/max e il punto fermo impedisce l’installazione del plugin generando un errore e viene caricato dopo questo controllo di compatibilità. Se vuoi impedire che istanze molto vecchie di Discourse utilizzino il tuo plugin, direi che è ancora una buona idea includere required_version per la prima versione minima: nessuna quantità di ricerca sulla compatibilità potrà risolvere quel problema :wink:

In condizioni d’uso normali non dovrebbero essere necessarie modifiche alla configurazione; rileverà automaticamente le variazioni. Il task Rake viene eseguito in discourse_docker dopo il clonaggio dei plugin, quindi nell’ordine di avvio del launcher:

  • I plugin vengono clonati
  • Viene verificata la compatibilità e, se necessario, viene effettuato il checkout
  • migrate

In docker_manager, le versioni più vecchie di Discourse potranno aggiornare i plugin a una versione compatibile. L’interfaccia utente rimane invariata, ma un plugin considerato “aggiornato” lo sarà ora in base al file di compatibilità.

TL;DR: non sono necessarie modifiche per nessuno dei due casi d’uso per iniziare a sfruttare questa funzionalità, se è questo che ti chiedevo.

Sotto il cofano, viene utilizzato git show HEAD@{upstream}:.discourse-compatibility per leggere l’ultimo file e git reset --hard #{checkout_version} per effettuare il checkout della versione corretta. In questo modo possiamo utilizzare l’ultima versione di compatibilità, e le versioni più vecchie di Discourse non rimarranno bloccate su un vecchio (e potenzialmente non valido) file di compatibilità.

11 Mi Piace

Bene, grazie per la spiegazione.

Quindi mi sono versato un :wine_glass: e ho provato con il plugin Custom Wizard.

Ho controllato ogni tag uno per uno in ordine inverso, partendo da v2.6.0.beta1. Ho trovato questi comandi git utili:

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

Non ci è voluto molto per trovare un tag che non funzionava con la versione corrente del plugin: v2.5.0.beta7 non include discourse/app/components/d-textarea, che il Custom Wizard cerca di importare.

Quindi ho trovato quel commit nel plugin che ha aggiunto quell’import, ho preso lo sha1 del commit precedente, ho fatto il checkout e ho testato (ha funzionato perfettamente), e ho aggiunto questo a .discourse-compatibility:

v2.5.0.beta7: 802d74bab2ebe19a106f75275342dc2e9cc6066a

Ho poi spinto tutto su un branch con l’ultima versione del codice del plugin (un branch per i test, non necessario normalmente), e ho ricompilato un server di test dockerizzato con quel branch del plugin e la version impostata su v2.5.0.beta7.

Non ha funzionato, poi mi è venuto in mente che, ovviamente, il task rake plugin:pull_compatible_all non esiste in v2.5.0.beta7, quindi questo non funzionerà retroattivamente (colpa del :wine_glass:). Come previsto, nei log del launcher vedo:

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

È questo il senso di come immagini che venga utilizzato?

Per quanto riguarda required_version, mi sono imbattuto in questo problema perché il server di test aveva installato il plugin discourse-legal-tools, che ha un required_version di v2.5.0, quindi inizialmente falliva su v2.5.0.beta7. Penso di trasferire quel plugin sul nuovo sistema. Credo ancora che required_version possa essere utile per impostare una base assoluta, come hai detto.

11 Mi Piace

Sì, è corretto: poiché questa funzionalità non è stata integrata nel nucleo di Discourse fino a ora, non funzionerà su versioni precedenti alla 2.6.0.beta1 (al momento). Devo ancora portarla alla versione stabile e all’ultima beta, così potremo utilizzarla con la 2.5.0, ma non abbiamo intenzione di adattarla a versioni precedenti.

11 Mi Piace

Un aggiornamento: ho appena aggiunto un file di compatibilità al master di Custom Wizard e lo userò per fissare il plugin, inclusa la versione v2.6.0.beta1 (beta attuale) e la v2.5.0 (stabile attuale), quando verrà trasferito. Per ulteriori informazioni, vedi:

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

5 Mi Piace

Una nota e una domanda.

Innanzitutto, solo una precisazione: la sintassi per i tag di versione di Discourse nel file .discourse-compatibility è 2.5.0 (come nell’esempio di @featheredtoast), non v2.5.0. Se includi la v, otterrai questo errore:

Malformed version number string v2.6.0.beta1

In secondo luogo, @featheredtoast ha fatto notare che il sistema di fissaggio delle versioni è stato ora retroportato su stable. Me ne sono perso conto perché stavo guardando il tag v2.5.0.

Questo mi pone una piccola domanda. I rami di Discourse non corrispondono direttamente ai tag di rilascio di Discourse. La maggior parte dei siti che eseguono versioni più vecchie di Discourse probabilmente si trova su un ramo, ovvero stable, piuttosto che su un rilascio, ovvero v2.5.0.

Se una modifica che interrompe la compatibilità (per un plugin) viene aggiunta anche a un ramo “più vecchio”, ovvero stable, cosa significa per i pin? Probabilmente non ho ancora compreso appieno il funzionamento del sistema di versionamento.

6 Mi Piace

Le versioni corrispondono alla versione di Discourse definita nell’applicazione, non al tag git.

Quindi, “2.5.0” in questo contesto indica qualsiasi versione dichiarata come 2.5.0, fino al nuovo aggiornamento di versione a 2.5.1 (o 2.6.0.beta1).

Se una modifica che interrompe la compatibilità (per un plugin) viene aggiunta anche al ramo stabile, ciò effettivamente interromperebbe anche il plugin. L’intero scopo della versionatura è garantire che non introduciamo modifiche che interrompono la compatibilità per quella versione; quindi, questo è un argomento da sollevare separatamente. La nostra intenzione per il ramo stabile è di backportare solo fix di sicurezza e critici (e funzionalità minori, quando appropriato).

Lo scopo di questo approccio è permetterci di calcolare le versioni precedenti funzionanti dei plugin rispetto alle versioni precedenti funzionanti di Discourse. Non è certamente una soluzione miracolosa, ma ci fornisce una piattaforma su cui farlo, se siamo effettivamente attenti nella scelta di cosa backportare.

7 Mi Piace

Funziona per la maggior parte dei casi, a meno che tu non stia clonando con --depth=1.

Implementerò presto un hook per eseguire git fetch --depth 1 {upstream} commit se il target non viene trovato inizialmente; altrimenti finiremmo per effettuare una clonazione parziale o completa, il che non è ideale. Dovremmo essere in grado di recuperare ciò di cui abbiamo bisogno.

Modifica: Aggiornato qui:

Ho eseguito il backport di questa modifica anche su Beta e Stable, quindi ora dovremmo essere in grado di fissare i riferimenti anche con clonazioni superficiali.

6 Mi Piace

Mi scuso in anticipo, dato che spesso mi rivolgo a questo argomento piuttosto tardi la notte, quindi non sono sicuro al 100% di essere corretto e potrebbe trattarsi di qualcosa di cui siete già a conoscenza. Lo sto documentando qui in parte per la mia stessa sanità mentale, dato che mi ha creato problemi un paio di volte finora.

Penso che questo meccanismo sia di fatto inutilizzabile per il ramo beta se il plugin viene utilizzato su un’istanza che non si controlla (cioè è open source) e che viene aggiornata nel modo consueto. Il modo consueto di aggiornamento consiste nell’interazione dell’amministratore del sito quando viene richiesto nell’interfaccia di amministrazione.

Considerando questo

E questo

E il fatto che tests-passed e beta abbiano la stessa versione di Discourse ma non lo stesso codice, ad esempio entrambe sono attualmente 2.6.0.beta2:

Ne consegue:

  1. Per supportare il ramo beta è necessario fissare (pin) un commit all’ultima versione beta, poiché è quella che utilizzeranno i siti sul ramo beta.

  2. Tuttavia, se l’ultima versione beta è presente nel file di compatibilità, le istanze in esecuzione su tests-passed utilizzeranno anch’esse il commit fissato.

Ciò significa che non è possibile supportare contemporaneamente l’uso standard di tests-passed e beta in un plugin open source. Dato che la maggior parte delle persone che installano plugin si trova su tests-passed, di fatto non è possibile supportare beta con questo metodo.

Si noti che funziona nella pratica per il ramo stable, poiché stable ha una versione di Discourse diversa da beta e tests-passed.

4 Mi Piace

Sì, sono a conoscenza di questo problema: non si risolve davvero fino al prossimo beta cut. Dovremmo sicuramente trovare un modo per distinguere tra le versioni beta e quelle in cui i test sono superati.

Quando ho configurato la funzionalità, avevo in mente fork stabili e problematici, e solo in seguito ho notato che ‘latest’ e ‘stable’ condividevano le stesse versioni.

6 Mi Piace

Grazie per la conferma.

Sembra che il risultato effettivo sia che il processo non dovrebbe essere eseguito se l’istanza è in esecuzione con tests-passed. Non è possibile includere la versione tests-passed nel file per i motivi descritti sopra, quindi escludere il processo su tests-passed non ne cambierebbe il comportamento attuale.

Un modo per implementare questo sarebbe

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

   ...
end

Questo renderebbe possibile utilizzare l’ultima versione beta nel file per fissare i plugin sui siti in esecuzione con beta. E potresti continuare a supportare tests-passed nell’ultimo commit del plugin, cioè senza usare questo file. Se sei d’accordo con questa soluzione, posso preparare una PR.

In alternativa, penso che il problema sottostante qui sia questo

Sono sicuro che si tratti di qualcosa che avete già discusso in precedenza, ma sarebbe possibile fare qualcosa come

  • tests-passed: 2.6.0.tests-passed, cioè impostare PRE come tests-passed sul ramo tests-passed.

  • beta: 2.6.0.beta2, cioè come è attualmente

@jomaxro Potresti aiutarmi a capire questo punto?

7 Mi Piace

Sì, sono favorevole all’aggiunta di un’indicazione aggiuntiva per distinguere le release pre-beta da quelle beta vere e proprie, a patto che sia abbastanza semplice da implementare. Questo aiuterebbe a risolvere il problema sottostante. Ho visto altri progetti software incrementare la versione alla prossima versione prima del rilascio (ad esempio, dopo aver rilasciato beta1, aggiornare la “versione” a beta2).

Tuttavia, tradizionalmente Discourse non ha seguito questa pratica, quindi dipende da quale metodo sia più semplice da adottare per il progetto.

6 Mi Piace

Ho riscontrato un altro problema.

Questa modifica introduce $danger-low-mid nella versione 2.6.0beta2.

Ciò ha interrotto il funzionamento del plugin discourse-styleguide, che è stato quindi aggiornato; è stato inoltre introdotto un file .discourse-compatibility per mantenere il plugin all’ultimo commit precedente per Discourse 2.5.0.

Ciò causa problemi su Discourse 2.5.1. Poiché questa modifica non verrà mai retroportata nelle versioni stabili, il file discourse-compatibility dovrebbe essere aggiornato ad ogni nuova versione stabile 2.5.x.

Ogni file discourse-compatibility di ogni plugin dovrebbe essere aggiornato ad ogni nuova versione stabile 2.5.x.

In alternativa, il file di compatibilità potrebbe fare riferimento alla versione 2.5.999, mantenendo di fatto il plugin al commit 1f86468b2c81b40e97f1bcd16ea4bb780634e2c7 per tutta la durata della serie 2.5.x. Tuttavia, questa soluzione mi sembra molto poco elegante.

6 Mi Piace

Sembra che sia sicuro fissare la versione su 2.6.0beta1, il che sarebbe anche più corretto dato che il problema è stato introdotto in beta2; ha senso? In questo modo si coprirebbero anche tutte le versioni 2.5.

La tua soluzione “artigianale” di 2.5.999 sembra un ottimo workaround (anche se disordinato, come hai giustamente notato). Ho cercato di mantenere il fissaggio della versione il più semplice possibile, ma hai ragione nel dire che manca ancora un modo reale per indicare 2.5.x come versione di fissaggio valida.

Un altro workaround per quel caso specifico, ovvero fissare l’ultima versione stabile ma lasciare le beta della prossima versione non fissate, potrebbe essere un po’ più sottile ma mi sembra molto meno artigianale per fissare l’intera branch 2.5.x: fissare la versione a 2.6.0.beta0 oppure 2.6.0.alpha1, il che semanticamente significa fissare tutto ciò che è precedente e fino alla versione compresa tra 2.5.x e 2.6.0.beta1. Questo significa:

Tutte le versioni 2.5.x sono coperte dal fissaggio.
Tutte le versioni 2.6.0.beta(1+) rimangono non fissate.

6 Mi Piace

Sì, anche per me sembra meno “hacky”.

Ma sia la soluzione 2.5.999 che quella 2.6.0beta0 non coprono un caso simile: cosa succede se un problema viene introdotto in 2.6.0beta3 e viene poi backportato alla 2.5.2?

Più casi limite, più funzionalità nascoste: puoi effettivamente “deselezionare” il pin con una voce vuota:

Con un file di compatibilità come:

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

Qualsiasi versione compresa tra 2.4.2.beta1 e 2.4.4.beta6 non verrà bloccata. Qualsiasi versione successiva fino a 2.6.0.beta1 verrà bloccata. Oltre tale versione, il blocco verrà nuovamente rimosso.

A livello interno, qualsiasi valore che si risolve in nil non verrà modificato e rimarrà sull’ultima versione disponibile. Una voce vuota o ~ viene valutata come nil (tramite il parser YAML di Ruby), bypassando così la funzione di blocco.

8 Mi Piace

È possibile utilizzare un plugin di vecchia versione su un’istanza self-hosted?

1 Mi Piace

Sì, puoi modificare i comandi git clone nel tuo file app.yml per clonare la versione desiderata. Tieni presente che devi assicurarti che anche Discourse sia bloccato alla versione desiderata. Potrebbe anche essere necessario bloccare discourse_docker a una versione compatibile con la versione di Discourse in esecuzione. Se stai facendo tutto questo, è più facile non aggiornare.

2 Mi Piace