Einführung von .discourse-compatibility: Angeheftete Plugin-/Theme-Versionen für ältere Discourse-Versionen

Hallo zusammen :wave:, ich habe gerade ein neues Feature gemergt, das Plugins und Themes dabei hilft, bestimmte Versionen zu verankern, wenn sie auf älteren Discourse-Instanzen installiert werden.

Sie können nun eine .discourse-compatibility-Datei im Stammverzeichnis eines Plugin- oder Theme-Repository einfügen, die angibt, welche Version beim Installieren auf älteren Discourse-Versionen ausgecheckt werden soll.


Begründung

Es ist mühsam, sich zu merken, welche Plugins und Themes mit welchen Discourse-Versionen kompatibel sind. Als Administrator sollte es möglich sein, diese Änderungen einfach zu durchsuchen und eine Version zu finden, die für Ihre Discourse-Installation geeignet ist, ohne die Commit-Historie des Plugins lesen zu müssen. Als Autor eines Plugins oder Themes sollte es möglich sein, Installationsversionen zu verwalten, während Sie abwärtsinkompatible Änderungen vornehmen, damit Sie ältere Installationen nicht beschädigen.

Discourse-Software-Updates werden recht schnell ausgerollt, was zwar toll ist, aber die Wartung von Discourse-Instanzen mit vielen Plugins manchmal sehr schwierig macht, insbesondere wenn Sie anderen Release-Zyklen oder -Versionen folgen, wie beispielsweise der aktuellen stabilen Version. Mein Plan ist es, ein Ökosystem zu schaffen, das den Aktualisierungsprozess für diejenigen erleichtert, die entweder der stabilen Version oder einem anderen Release-Zyklus folgen, und diesen Site-Administratoren eine Methode an die Hand zu geben, schnell und automatisch die Plugin-Version zu erhalten, die mit der Discourse-Version kompatibel ist, die sie anvisieren.

Ursprüngliche Ankündigung (jetzt durch die oben verlinkte Dokumentation ersetzt)

Implementierung

Erstens: Wir verlassen uns hier auf die Tags von Discourse Core, da die Betas von Discourse häufig genug veröffentlicht werden, sodass wir Plugin-Versionen daran verankern können. Die Verankerung an Git-Hashes ist aus vielen Gründen ein Albtraum, daher können wir git describe verwenden, um das nächste Beta- oder Stable-Tag zu erhalten.

In einem Plugin oder Theme unterstützen wir nun eine Versionskompatibilitätsdatei mit dem Namen .discourse-compatibility im Stammverzeichnis. Diese Datei ist eine absteigend sortierte Liste (neuere Discourse-Versionen zuerst), die eine Kompatibilitätszuordnung spezifiziert.

Beispiel

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

Für jedes Plugin/Theme wird bei einem Upgrade oder Neuaufbau weiterhin ein späterer benannter Commit/Branch/Tag ausgecheckt, bis einer gefunden wird, der dem aktuellen Discourse-Version entspricht oder neuer ist.
Beispielsweise würde bei der obigen Versionsdatei, wenn die aktuelle Discourse-Version 2.4.6.beta12 wäre, die Datei gescannt und der Eintrag für 2.5.0.beta2 ausgewählt.

Wenn die aktuelle Discourse-Version 2.4.4.beta6 wäre, würde der entsprechende Eintrag für 2.4.4.beta6 ausgewählt.

Wenn keine spätere Version existiert, bleibt es bei der aktuell ausgecheckten Version.
Beispielsweise würde bei 2.5.0.beta3 keine Verankerung erfolgen.

Wenn keine frühere Version existiert, wird die früheste in der Versionsdatei aufgeführte Version ausgecheckt.
Beispielsweise würde bei 2.2.1.beta22 die frühestmögliche Version gemäß der „Version" ausgecheckt, nämlich der Eintrag für 2.4.2.beta1.


Das Ziel ist es, die Schmerzen bei der Wartung alternativer Bereitstellungen, die in Zukunft nicht strikt auf „tests-passed" basieren, zu lindern und Administratoren Flexibilität bei Zeit und Ort des Upgrades zu geben. Wir erreichen dies, indem wir Autoren von Plugins und Themes eine Möglichkeit bieten, abwärtsinkompatible Änderungen zu entwickeln, ohne Installationen auf älteren Discourse-Versionen zu beeinträchtigen.

50 „Gefällt mir“

This is a great feature, thank you :slight_smile:

I like the directionality of this, i.e. it makes it possible to manage this issue from within the plugin itself, not requiring the site admin to do anything per se.

I have a few initial questions:

  • Will the existing required_version plugin metadata check in plugin activation remain? And how do you see that interelating with this (if at all)?

  • I see this is added in the form of a rake task atm. How does it relate to, what is the intended use, with discourse_docker (i.e. the launcher) and docker_manager? I see you’ve made additions to both repos, but could you explain how it’s intended to work in both environments?

12 „Gefällt mir“

Yeah, that’s the idea - make it so plugin authors have the capability to add backwards compatibility so that admins don’t need to worry!

There are currently no plans to change or remove the required_version plugin metadata. They’re related, but are still separate in my mind - the required_version min/max fullstop disallows the plugin from being installed by throwing an error, and is loaded after this compatibility pull. If you want to prevent ancient Discourse instances from using your plugin, I’d say it’s still a good idea to include required_version for the first minimum version - no amount of compatibility hunting is going to fix that :wink:

There shouldn’t be any config changes needed in normal use, it’ll pick up changes automatically. The rake task is called in discourse_docker after the plugins are cloned, so in the launcher order:

  • Plugins cloned
  • Compatibility checked, and checked out (if applicable)
  • migrate

In docker_manager, older Discourse versions will be able to update plugins to a compatible version. The UI here stays the same, but an “up to date” plugin is now according to the compatibility file.

TLDR, no changes are needed for either use-case to start taking advantage of this, if that’s what you’re wondering.

Under the hood, it uses git show HEAD@{upstream}:.discourse-compatibility to read the latest file, and git reset --hard #{checkout_version} to checkout the correct version. That way we are able to use the latest compatibility, and older Discourse versions will not be stuck on an old (possible invalid) compat file.

11 „Gefällt mir“

Cool, thanks for explaining that.

So I poured myself a :wine_glass: and gave this a shot with the Custom Wizard Plugin.

I checked each tag out one by one in reverse order starting with v2.6.0.beta1. I found these git commands helpful:

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

It didn’t take too long to find a tag that wasn’t working with the current version of the plugin: v2.5.0.beta7 doesn’t include discourse/app/components/d-textarea which the custom wizard tries to import.

So, then I found the commit in the plugin that added that import, took the sha1 of the previous commit, checked that out and tested (worked fine), and added this to .discourse-compatibility:

v2.5.0.beta7: 802d74bab2ebe19a106f75275342dc2e9cc6066a

I then pushed that to a branch with the latest plugin code (a branch for testing, not necessary normally), and rebuilt a dockerized test server with that plugin branch and the version set at v2.5.0.beta7.

That didn’t work, then it hit me that, of course, the rake task plugin:pull_compatible_all doesn’t exist in v2.5.0.beta7, so this isn’t going to work retrospectively (I blame the :wine_glass:). Sure enough in the launcher logs I see

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

Is that the gist of how you imagine this being used though?

On the required_version front, I encountered that here as the test server had the discourse-legal-tools plugin installed, which has a required_version of v2.5.0, so it initially failed on v2.5.0.beta7. I think I’ll transfer that plugin over to this new system. I can still see required_version being useful to set an absolute baseline as you say.

11 „Gefällt mir“

Yeah that’s correct - because this isn’t baked into discourse core until now, it won’t work on anything older than 2.6.0.beta1 (currently). I still have to port this to stable + latest beta, so we’ll be able to use it against 2.5.0, but we’re not planning on porting it any earlier.

11 „Gefällt mir“

Nur eine kurze Ergänzung: Ich habe nun eine Kompatibilitätsdatei zum Custom Wizard-Master hinzugefügt und werde diese verwenden, um das Plugin zu verankern, einschließlich v2.6.0.beta1 (aktuelles Beta) und v2.5.0 (aktuell stabil), sobald dies portiert wird. Weitere Informationen findest du hier:

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

5 „Gefällt mir“

Hier nur eine Anmerkung und eine Frage.

Zunächst einmal nur zur Info: Die Syntax für Discourse-Versions-Tags in der Datei .discourse-compatibility lautet 2.5.0 (wie im Beispiel von @featheredtoast), nicht v2.5.0. Wenn Sie ein v verwenden, erhalten Sie diesen Fehler:

Malformed version number string v2.6.0.beta1

Zweitens hat @featheredtoast darauf hingewiesen, dass das Pinning-System nun auch auf stable zurückportiert wurde. Das habe ich übersehen, da ich den Tag v2.5.0 betrachtet habe.

Das wirft für mich eine leichte Frage auf. Die Branches von Discourse entsprechen nicht direkt den Versions-Tags von Discourse. Die meisten Sites, die ältere Versionen von Discourse betreiben, laufen wahrscheinlich auf einem Branch, also stable, und nicht auf einem Release, also v2.5.0.

Wenn eine Breaking Change (für ein Plugin) auch in einem „älteren

6 „Gefällt mir“

Versions correspond to the discourse version defined in the app, not the git tag.

So “2.5.0” in this context will mean any version declared as 2.5.0, up to the new version bump of 2.5.1 (or 2.6.0.beta1).

If a breaking change (for a plugin) is also added to the stable branch, that would indeed also break the plugin. The entire point of versioning is so we can be relatively sure we aren’t introducing breaking changes to that version, so that’s something to scream about separately. Our intent for stable is to backport only security and critical fixes (and minor features, when appropriate).

The intent of this is so we have the ability to calculate older working versions of plugins against older working versions of Discourse. This is by no means a silver bullet, but it does give us a platform in which to do it, if we are actually careful about choosing what to backport.

7 „Gefällt mir“

Das funktioniert größtenteils, es sei denn, Sie klonen mit --depth=1.

Ich werde bald einen Hook implementieren, der git fetch --depth 1 {upstream} commit aufruft, falls das Ziel zunächst nicht gefunden werden kann. Andernfalls wären wir gezwungen, einen partiellen oder vollständigen Klon durchzuführen, was nicht ideal ist. Wir sollten in der Lage sein, das Benötigte zu holen.

Edit: Hier aktualisiert:

Ich habe dies auch auf Beta und Stable zurückportiert – wir sollten also nun sogar mit flachen Klonen festlegen können.

6 „Gefällt mir“

Ich entschuldige mich im Voraus, da ich mich oft erst spät in der Nacht an dieses Thema wende, sodass ich nicht sicher bin, ob ich hier zu 100 % richtig liege. Es könnte auch sein, dass Ihnen das bereits bekannt ist. Ich halte dies hier fest, teilweise auch für meine eigene geistige Gesundheit, da es mich bereits mehrmals verwirrt hat.

Ich denke, dieser Mechanismus ist für den Zweig beta effektiv nicht funktionsfähig, wenn das Plugin auf einer Instanz verwendet wird, die Sie nicht kontrollieren (d. h. sie ist Open Source) und die auf übliche Weise aktualisiert wird. Die übliche Art der Aktualisierung besteht darin, dass der Site-Administrator dies tut, wenn er im Admin-UI dazu aufgefordert wird.

Angesichts dessen

Und dies

Und dass tests-passed und beta die gleiche Discourse-Version, aber nicht den gleichen Code haben, z. B. sind beide derzeit 2.6.0.beta2:

Daraus folgt:

  1. Um den Zweig beta zu unterstützen, müssen Sie einen Commit an das neueste Beta-Release anheften, da dies die Version ist, die Sites auf dem beta-Zweig verwenden werden.

  2. Wenn jedoch die neueste Beta-Version in der Kompatibilitätsdatei enthalten ist, verwenden Instanzen, die tests-passed ausführen, ebenfalls den angehefteten Commit.

Das bedeutet, dass Sie die Standardnutzung von tests-passed und beta gleichzeitig in einem Open-Source-Plugin nicht unterstützen können. Da die Mehrheit der Personen, die Plugins installieren, auf tests-passed läuft, können Sie beta mit dieser Methode effektiv nicht unterstützen.

Beachten Sie, dass dies in der Praxis für den Zweig stable funktioniert, da stable eine andere Discourse-Version als beta und tests-passed hat.

4 „Gefällt mir“

Ach ja, das ist mir bewusst. Es wird erst mit dem nächsten Beta-Schnitt wirklich behoben. Wir sollten auf jeden Fall auch einen Weg finden, zwischen Beta- und Test-bestandenen Versionen zu unterscheiden.

Als ich die Funktion eingerichtet habe, hatte ich stabile und inoffizielle Forks im Sinn und habe erst später bemerkt, dass ‘latest’ und ‘stable’ dieselben Versionen teilen.

6 „Gefällt mir“

Danke für die Bestätigung.

Es scheint, dass das wesentliche Ergebnis ist, dass der Prozess nicht ausgeführt werden sollte, wenn die Instanz tests-passed läuft. Die tests-passed-Version kann aus den oben beschriebenen Gründen nicht in die Datei aufgenommen werden, sodass das Aussperren des Prozesses bei tests-passed sein aktuelles Verhalten nicht ändern würde.

Eine Möglichkeit, dies zu implementieren, wäre:

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

   ...
end

Dies würde es ermöglichen, die neueste beta-Version in der Datei zu verwenden, um Plugins für Sites zu verankern, die beta ausführen. Und Sie könnten weiterhin tests-passed im neuesten Commit des Plugins unterstützen, also ohne diese Datei zu verwenden. Wenn Sie damit einverstanden sind, kann ich einen PR erstellen.

Alternativ habe ich das Gefühl, dass das zugrunde liegende Problem hier Folgendes ist:

Ich bin mir sicher, dass Sie das bereits diskutiert haben, aber wäre es möglich, etwas wie Folgendes zu tun:

  • tests-passed: 2.6.0.tests-passed, d. h. das PRE-Tag auf tests-passed im tests-passed-Branch setzen.

  • beta: 2.6.0.beta2, also wie bisher.

@jomaxro Könntest du mir das bitte näher erklären?

7 „Gefällt mir“

Ja, ich bin dafür, eine zusätzliche Kennzeichnung für Pre-Beta- und echte Beta-Releases einzuführen, sofern dies einfach umzusetzen ist. Das würde helfen, das zugrunde liegende Problem zu lösen. Ich habe bei anderen Softwareprojekten gesehen, dass die Versionsnummer vor dem Release auf die nächste Version hochgesetzt wird (z. B. nach der Veröffentlichung von Beta1 wird die „Version“ auf Beta2 erhöht).

Traditionell hat Discourse das jedoch nicht getan, also hängt es davon ab, welche Methode für das Projekt am einfachsten zu übernehmen ist.

6 „Gefällt mir“

Ich bin auf ein weiteres Problem gestoßen.

Diese Änderung führt $danger-low-mid in 2.6.0beta2 ein.

Das hat das discourse-styleguide-Plugin kaputt gemacht, sodass es aktualisiert wurde und eine .discourse-compatibility-Datei eingeführt wurde, um das Plugin beim vorherigen Commit für Discourse 2.5.0 zu halten.

Das bricht bei Discourse 2.5.1. Da diese Änderung nie auf die stabile Version zurückportiert wird, müsste die discourse-compatibility-Datei bei jeder neuen 2.5.x-Stable-Version aktualisiert werden.

Jede discourse-compatibility-Datei von jedem Plugin müsste bei jeder neuen 2.5.x-Stable-Version aktualisiert werden.

Alternativ könnte die Kompatibilitätsdatei auf Version 2.5.999 verweisen, wodurch das Plugin effektiv für die gesamte Lebensdauer von 2.5.x beim Commit 1f86468b2c81b40e97f1bcd16ea4bb780634e2c7 gehalten würde. Das kommt mir jedoch sehr hakelig vor.

6 „Gefällt mir“

Es klingt danach, als wäre es sicher, auf 2.6.0beta1 zu pinnen, was zudem korrekter wäre, da das Problem in beta2 eingeführt wurde. Ergibt das Sinn? Damit wären dann auch alle Versionen der 2.5er-Reihe abgedeckt.

Deine „hackige

6 „Gefällt mir“

Ja, das kommt mir auch weniger hackig vor.

Aber sowohl die Lösung mit 2.5.999 als auch die mit 2.6.0beta0 decken einen ähnlichen Fall nicht ab: Was ist, wenn in 2.6.0beta3 ein Problem eingeführt wird, das dann auf 2.5.2 zurückportiert wird?

Mehr Randfälle, mehr versteckte Funktionen: Sie können den Pin tatsächlich mit einem leeren Eintrag “aufheben”:

Mit einer Kompatibilitätsdatei wie:

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

Alles zwischen 2.4.2.beta1 und 2.4.4.beta6 wird nicht zurückgesetzt. Alles danach bis einschließlich 2.6.0.beta1 wird gepinnd. Danach wird es wieder entsperrt.

Im Hintergrund bleibt alles, was zu einem nil-Wert ausgewertet wird, unverändert bzw. bleibt auf der neuesten Version. Ein leerer Eintrag oder ~ wird als nil ausgewertet (über den Ruby-YAML-Parser), wodurch die Pin-Funktion umgangen wird.

8 „Gefällt mir“

Ist es möglich, ein Plugin einer älteren Version auf einer selbst gehosteten Instanz zu verwenden?

1 „Gefällt mir“

Ja, Sie können die Git-Clone-Befehle in Ihrer app.yml anpassen, um die gewünschte Version zu klonen. Beachten Sie, dass Sie auch sicherstellen müssen, dass Discourse auf die gewünschte Version gesetzt ist. Möglicherweise müssen Sie auch discourse_docker auf eine Version setzen, die mit der von Ihnen ausgeführten Version von Discourse kompatibel ist. Wenn Sie all das tun, ist es einfacher, nicht zu aktualisieren.

2 „Gefällt mir“