Представляем .discourse-compatibility: зафиксированные версии плагинов и тем для старых версий Discourse

Всем привет :wave:, я только что объединил новую функцию, которая поможет плагинам и темам фиксировать определённые версии при установке на старых экземплярах Discourse.

Теперь вы можете добавить файл .discourse-compatibility в корень репозитория плагина или темы, который указывает, какую версию нужно использовать при установке на старые версии Discourse.


Обоснование

Запоминать, какие плагины и темы совместимы с какими версиями Discourse, — это боль. Как администратору, вам должно быть легко просматривать эти изменения и находить подходящую версию для вашей установки Discourse, не читая историю коммитов плагина. Как автору плагина или темы, вам должно быть удобно управлять версиями для установки, даже внося изменения, несовместимые с обратной совместимостью, чтобы не сломать старые установки.

Обновления программного обеспечения Discourse выходят довольно быстро, что, хотя и отлично, делает поддержку экземпляров Discourse с большим количеством плагинов иногда очень сложной, особенно если вы следуете другим циклам выпуска/версиям, например, текущей стабильной ветке. Моя цель здесь — создать экосистему, которая упростит процесс обновления для тех, кто следует стабильной или иной версии выпуска, и предоставит администраторам сайтов способ быстро и автоматически получать версию плагина, совместимую с целевой версией Discourse.

Оригинальное объявление (теперь заменено документацией, ссылка выше)

Реализация

Первое, что нужно отметить: мы полагаемся на теги ядра Discourse, поскольку бета-версии Discourse выходят достаточно часто, чтобы можно было фиксировать версии плагинов относительно них. Фиксация по хешам git — это кошмар по многим причинам, поэтому мы используем git describe, чтобы получить ближайший бета- или стабильный тег.

В плагине или теме теперь поддерживается файл совместимости версий с именем .discourse-compatibility в корне. Этот файл представляет собой упорядоченный список в порядке убывания (новые версии Discourse сначала), который определяет карту совместимости.

Пример

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

Для каждого плагина/темы при обновлении или пересборке будет продолжаться проверка более позднего именованного коммита/ветки/тега до тех пор, пока не будет найдена версия, равная или более поздняя, чем текущая версия Discourse.
Например, для файла версий выше, если текущая версия Discourse — 2.4.6.beta12, он просканирует файл и выберет запись для 2.5.0.beta2.

Если текущая версия Discourse — 2.4.4.beta6, будет выбрана соответствующая запись для 2.4.4.beta6.

Если более поздней версии не существует, останется текущая проверенная версия.
Например, для 2.5.0.beta3 фиксация не произойдёт.

Если более ранней версии не существует, будет проверена самая ранняя, указанная в файле версий.
Например, для 2.2.1.beta22 будет проверена самая ранняя возможная версия, то есть запись для 2.4.2.beta1.


Цель здесь — облегчить боль при поддержке альтернативных развёртываний, которые в будущем не будут строго соответствовать тестам tests-passed, и предоставить администраторам гибкость в том, когда и где обновляться. Мы делаем это, предоставляя авторам плагинов и тем возможность разрабатывать изменения, несовместимые с обратной совместимостью, не влияя на установки на старых версиях Discourse.

50 лайков

Это отличная функция, спасибо :slight_smile:

Мне нравится её направленность, то есть она позволяет управлять этой проблемой непосредственно внутри самого плагина, не требуя от администратора сайта каких-либо действий.

У меня есть несколько начальных вопросов:

  • Останется ли существующая проверка метаданных плагина required_version при активации плагина? И как вы видите её взаимодействие с этим (если вообще)?

  • Я вижу, что это добавлено в виде задачи rake на данный момент. Как это соотносится с discourse_docker (то есть лаунчером) и docker_manager? Каково intended использование? Я вижу, что вы внесли изменения в оба репозитория, но могли бы вы объяснить, как это должно работать в обеих средах?

12 лайков

Да, именно это и задумано — предоставить авторам плагинов возможность добавлять обратную совместимость, чтобы администраторам не о чем было беспокоиться!

В настоящее время не планируется изменение или удаление метаданных плагина required_version. Они связаны, но в моём сознании остаются отдельными: ограничение required_version (min/max) запрещает установку плагина, выбрасывая ошибку, и применяется после этого механизма совместимости. Если вы хотите предотвратить использование вашего плагина на очень старых экземплярах Discourse, по-прежнему рекомендуется указывать required_version для первой минимальной версии — никакое «охотничье» стремление к совместимости не решит эту проблему :wink:

В обычном режиме использования никаких изменений конфигурации не потребуется, всё будет определяться автоматически. Задача rake вызывается в discourse_docker после клонирования плагинов, поэтому в порядке запуска:

  • Плагины клонируются
  • Проверяется совместимость и осуществляется переход к нужной версии (если применимо)
  • Выполняется миграция

В docker_manager более старые версии Discourse смогут обновлять плагины до совместимой версии. Интерфейс остаётся прежним, но статус «актуально» теперь определяется на основе файла совместимости.

Если коротко: для начала использования этой функции не требуется никаких изменений ни в одном из сценариев, если именно об этом вы спрашиваете.

«Под капотом» используется команда git show HEAD@{upstream}:.discourse-compatibility для чтения последней версии файла и git reset --hard #{checkout_version} для перехода к нужной версии. Таким образом мы можем использовать актуальные данные о совместимости, а старые версии Discourse не будут застревать на устаревшем (возможно, некорректном) файле совместимости.

11 лайков

Круто, спасибо за объяснение.

Итак, я налил себе :wine_glass: и попробовал это с помощью плагина Custom Wizard.

Я проверил каждый тег по одному в обратном порядке, начиная с v2.6.0.beta1. Эти команды git оказались полезными:

git tag --list \\ например, git tag --list 'v2.5.0*'
git checkout tags/tag \\ например, git checkout tags/v2.5.0.beta7

Недолго пришлось искать тег, который не работал с текущей версией плагина: в v2.5.0.beta7 отсутствует discourse/app/components/d-textarea, который пытается импортировать кастомный мастер.

Таким образом, я нашел коммит в плагине, где был добавлен этот импорт, взял sha1 предыдущего коммита, переключился на него и протестировал (все работало отлично), а затем добавил это в .discourse-compatibility:

v2.5.0.beta7: 802d74bab2ebe19a106f75275342dc2e9cc6066a

Затем я отправил это в ветку с последним кодом плагина (ветка для тестирования, обычно не обязательная), и собрал заново тестовый сервер в контейнере Docker с этой веткой плагина и параметром version, установленным в v2.5.0.beta7.

Это не сработало, и тут меня осенило, что, конечно, задача rake plugin:pull_compatible_all не существует в v2.5.0.beta1, поэтому это не сработает ретроспективно (виноват :wine_glass:). И действительно, в логах запуска я вижу:

Неизвестно, как выполнить задачу 'plugin:pull_compatible_all' (см. список доступных задач с помощью `rake --tasks`)

Но это ли суть того, как вы представляете использование этого механизма?

Что касается required_version, я столкнулся с этим здесь, так как на тестовом сервере был установлен плагин discourse-legal-tools, у которого required_version равен v2.5.0, поэтому изначально он не работал с v2.5.0.beta7. Думаю, я перенесу этот плагин на новую систему. Я все еще вижу, что required_version может быть полезен для установки абсолютного базового уровня, как вы и говорили.

11 лайков

Да, это верно — поскольку эта функция до сих пор не была встроена в ядро Discourse, она не будет работать на версиях старше 2.6.0.beta1 (на данный момент). Мне ещё нужно перенести её в стабильную ветку и последнюю бета-версию, чтобы мы могли использовать её с версией 2.5.0, но мы не планируем переносить её на более ранние версии.

11 лайков

Дополнительно сообщаю, что я добавил файл совместимости в мастер Custom Wizard и буду использовать его для фиксации плагина, включая v2.6.0.beta1 (текущая бета-версия) и v2.5.0 (текущая стабильная версия), когда это будет перенесено. Подробнее см.:

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

5 лайков

Одно замечание и один вопрос.

Во-первых, просто замечание: синтаксис тегов версий Discourse в файле .discourse-compatibility — это 2.5.0 (как в примере от @featheredtoast), а не v2.5.0. Если вы используете префикс v, возникнет следующая ошибка:

Malformed version number string v2.6.0.beta1

Во-вторых, @featheredtoast указал, что система фиксации версий теперь перенесена в ветку stable. Я упустил это, так как смотрел на тег v2.5.0.

Это вызывает у меня небольшой вопрос. Ветки Discourse не напрямую соответствуют тегам релизов Discourse. Большинство сайтов, работающих на старых версиях Discourse, вероятно, находятся на ветке, например, stable, а не на релизе, например, v2.5.0.

Если разрушающее изменение (для плагина) также добавлено в «старую» ветку, например, stable, что это означает для фиксаций версий? Вероятно, я просто не до конца понимаю, как работает система версионирования.

6 лайков

Версии соответствуют версии Discourse, определённой в приложении, а не тегу git.

Таким образом, «2.5.0» в данном контексте означает любую версию, объявленную как 2.5.0, вплоть до нового повышения версии до 2.5.1 (или 2.6.0.beta1).

Если критическое изменение (для плагина) также добавляется в стабильную ветку, это действительно может нарушить работу плагина. Вся суть версионирования заключается в том, чтобы мы могли быть относительно уверены, что не вносим критические изменения в эту версию, поэтому это отдельный вопрос для обсуждения. Наша цель для стабильной ветки — переносить только исправления безопасности и критические исправления (а также небольшие функции, когда это уместно).

Цель этого подхода — иметь возможность вычислять старые рабочие версии плагинов в сочетании со старыми рабочими версиями Discourse. Это далеко не панацея, но это даёт нам платформу для такой работы, если мы будем тщательно подходить к выбору того, что переносить обратно.

7 лайков

Это работает в большинстве случаев, если только вы не клонируете с параметром --depth=1.

Вскоре я реализую хук, который будет выполнять команду git fetch --depth 1 {upstream} commit, если целевой коммит не будет найден изначально. Иначе мы окажемся в ситуации, когда придётся делать частичный или полный клон, что не является идеальным решением. Мы должны иметь возможность получить только необходимое.

Редактирование: Обновлено здесь:

Я также перенёс это исправление в ветки Beta и Stable — теперь мы должны иметь возможность фиксировать версии даже при использовании shallow-клонов.

6 лайков

Заранее приношу извинения, так как часто обращаюсь к этому поздно ночью, поэтому не уверен, что мои слова на 100% верны, и возможно, вы уже об этом знаете. Я фиксирую это здесь отчасти ради собственного спокойствия, так как этот вопрос уже несколько раз ставил меня в тупик.

Мне кажется, что этот механизм фактически неработоспособен для ветки beta, если плагин используется на экземпляре, которым вы не управляете (то есть он с открытым исходным кодом) и который обновляется обычным способом. Обычный способ обновления заключается в том, что администратор сайта делает это по запросу в интерфейсе администратора.

Учитывая это

И это

И тот факт, что tests-passed и beta имеют одну и ту же версию Discourse, но не один и тот же код, например, обе сейчас 2.6.0.beta2:

Следует следующее:

  1. Для поддержки ветки beta нужно зафиксировать (pin) коммит для последнего бета-релиза, так как именно его будут использовать сайты на ветке beta.

  2. Однако, если последняя бета-версия присутствует в файле совместимости, экземпляры, работающие на tests-passed, также будут использовать зафиксированный коммит.

Это означает, что вы не можете одновременно поддерживать стандартное использование как tests-passed, так и beta в плагине с открытым исходным кодом. Учитывая, что большинство людей, устанавливающих плагины, находятся на tests-passed, вы фактически не можете поддерживать beta этим методом.

Обратите внимание, что на практике это работает для ветки stable, так как у stable версия Discourse отличается от версий beta и tests-passed.

4 лайка

Да, я в курсе этого. Это действительно не решится до следующего бета-выпуска. Нам определённо стоит также найти способ различать бета-версии и версии, прошедшие тестирование.

Когда я настраивал эту функцию, я ориентировался на стабильные и экспериментальные ветки, и только позже заметил, что версии latest и stable совпадают.

6 лайков

Спасибо за подтверждение.

Похоже, что практический итог заключается в том, что процесс не должен запускаться, если экземпляр работает в режиме tests-passed. Вы не можете включить версию tests-passed в файл по причинам, описанным выше, поэтому исключение процесса для tests-passed не изменит его текущее поведение.

Один из способов реализовать это:

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

   ...
end

Это позволит использовать последнюю версию beta в файле для целей фиксации плагинов на сайтах, работающих в режиме beta. При этом вы сможете продолжать поддерживать tests-passed в последнем коммите плагина, то есть без использования этого файла. Если вы согласны с этим, я могу создать PR.

Или, как мне кажется, коренная проблема здесь заключается в следующем:

Уверен, вы уже обсуждали это, но возможно ли сделать что-то вроде:

  • tests-passed: 2.6.0.tests-passed, то есть установить PRE как tests-passed на ветке tests-passed.

  • beta: 2.6.0.beta2, то есть как сейчас.

@jomaxro, не могли бы вы помочь мне разобраться в этом?

7 лайков

Да, я поддерживаю идею добавить отдельную пометку для предварительных бета-версий в отличие от полноценных бета-выпусков, при условии, что это не составит большого труда. Это помогло бы решить основную проблему. Я видел, как другие проекты программного обеспечения повышали версию до следующей перед её выпуском (например, после выпуска beta1, версию меняли на beta2).

Однако традиционно Discourse так не делал, поэтому всё зависит от того, какой метод будет проще внедрить проекту.

6 лайков

Я столкнулся с ещё одной проблемой.

Это изменение добавляет $danger-low-mid в версии 2.6.0beta2.

Это сломало плагин discourse-styleguide, поэтому его обновили и добавили файл .discourse-compatibility, чтобы оставить плагин на предыдущем коммите для Discourse 2.5.0.

Это ломается в Discourse 2.5.1. Поскольку это изменение никогда не будет перенесено в стабильные версии, файл discourse-compatibility нужно будет обновлять с каждой новой стабильной версией 2.5.x.

Каждый файл discourse-compatibility каждого плагина нужно будет обновлять с каждой новой стабильной версией 2.5.x.

В качестве альтернативы файл совместимости может указывать на версию 2.5.999, фактически оставляя плагин на коммите 1f86468b2c81b40e97f1bcd16ea4bb780634e2c7 на протяжении всего жизненного цикла 2.5.x. Но это кажется мне очень костыльным решением.

6 лайков

Похоже, что безопаснее зафиксировать версию на 2.6.0beta1, что также будет более корректно, поскольку проблема была внесена в beta2. Это имеет смысл? Тогда это также охватит все версии 2.5.x.

Ваш «костыльный» вариант с 2.5.999 звучит как неплохое обходное решение (хотя и неаккуратное, как вы и отметили). Я старался максимально упростить фиксацию версий здесь, но вы правы: у нас всё ещё нет настоящего способа указать 2.5.x как допустимую зафиксированную версию.

Другое обходное решение для этого конкретного случая — зафиксировать последнюю стабильную версию, оставив бета-версии следующей версии незафиксированными. Это может быть чуть более тонким подходом, но для фиксации всей ветки 2.5.x он кажется мне гораздо менее «костыльным». Можно установить фиксацию на 2.6.0.beta0 или 2.6.0.alpha1, что семантически означает зафиксировать всё до и включая версию между 2.5.x и 2.6.0.beta1. Это означает:

Все версии 2.5.x будут охвачены фиксацией.
Все версии 2.6.0.beta(1+) останутся незафиксированными.

6 лайков

Да, мне тоже так кажется.

Однако ни решение с 2.5.999, ни с 2.6.0beta0 не покрывают схожий случай: что если в версии 2.6.0beta3 появится ошибка, которая будет перенесена обратно в 2.5.2?

Ещё больше частных случаев и скрытых возможностей: вы можете фактически «снять» привязку версии, оставив запись пустой:

При наличии файла совместимости:

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

Любые версии от 2.4.2.beta1 до 2.4.4.beta6 включительно не будут привязаны к конкретной версии. Все версии после 2.4.4.beta6 вплоть до 2.6.0.beta1 будут привязаны. После 2.6.0.beta1 привязка снова снимается.

На уровне реализации любые значения, которые вычисляются как nil, остаются без изменений и указывают на последнюю версию. Пустая запись или символ ~ вычисляются как nil (благодаря парсеру YAML в Ruby), что обходит функцию привязки версий.

8 лайков

Возможно ли использовать плагин старой версии на самохостинговом экземпляре?

1 лайк

Да, вы можете изменить команды git clone в вашем файле app.yml, чтобы клонировать нужную версию. Обратите внимание, что вам также нужно убедиться, что версия Discourse зафиксирована на требуемой версии. Возможно, вам также потребуется зафиксировать версию discourse_docker, совместимую с версией Discourse, которую вы используете. Если вы делаете всё это, проще не обновляться.

2 лайка