Один простой прием для сопровождающих пакетов JavaScript, позволяющий избежать взлома пользовательского программного обеспечения и выпускать стабильные версии.

Как использовать теги распространения пакетов npm для создания каналов выпуска, как в Google Chrome

Релизы программного обеспечения интересны как авторам пакетов, так и пользователям. Тяжелая работа последних недель наконец-то проявится в этой блестящей новой версии. Все пункты списка дел зачеркнуты, тесты зеленые, релизный блог готов. Скоро его будут читать и распространять тысячи, Твиттер наполнится похвалами и аплодисментами. Уф! Выполнено! 💪🎉

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

Помощь! Я был так взволнован, чтобы попробовать новую версию, но теперь я получаю эту странную ошибку ?! 😓

- Кто-то в Интернете - вероятно

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

Прежде чем раскрыть решение этой проблемы, я хочу привести недавний пример из реальной жизни, чтобы проиллюстрировать, насколько актуальна эта проблема. Обратите внимание, что я не пытаюсь выделить замечательных людей и их фантастический проект, о которых я сейчас упомяну. Это не нападение, а только конструктивный отзыв и действенный совет.

29 октября автор компилятора babel JavaScript, проекта, который имеет около 20 тысяч загрузок в день и тысячи проектов в зависимости от этого, провел заключительный доклад на EmberCamp London. В этом выступлении он выпустил новую версию 6.0.0 - прямо на сцене. Потребовалось три попытки и несколько принудительных нажатий, но в конце концов скрипт выпуска завершился. Захватывающие новости для всей экосистемы JavaScript 🎉

Помимо всех внесенных изменений, сообщение о выпуске подчеркивает интенсивные усилия, требуемые сопровождающими (выделено мной).

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

Себастьян не спал буквально всю ночь, чтобы закончить все дела в последнюю минуту, необходимые для запуска Babel 6, во время своего выступления на EmberCamp. В настоящее время для меня в Сан-Франциско час ночи, и я пишу этот пост в блоге как можно быстрее, слушаю музыку EDM, чтобы не уснуть.

Примерно через 14 минут после того, как должна была быть выпущена первоначальная версия 6.0.0 выпуска 6.0.2. В журнале изменений нет намека на то, на что конкретно обращалась эта версия, но в ретроспективе это не имеет значения, поскольку в ту же ночь вышла версия 6.0.12, а также 6.0.14 и 6.0.15 в ближайшие два дня . Среди исправлений - неправильное имя пакета. В их журнале изменений указано: Пробелы между версиями исправлений являются ошибочными, неработающими или тестовыми.

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

Тег «latest» dist используется npm publish по умолчанию.

К счастью, npm, предпочтительный менеджер пакетов и реестр JavaScript, имеет простую функцию, которая помогает избежать хаоса при выпуске: Теги распространения пакетов, или, короче, dist-tags. Фактически, если вы когда-либо ранее набирали npm install ‹package›, вы уже косвенно использовали эту функцию.

Когда мы набираем npm install ‹package›, то в итоге мы получаем не самую последнюю версию этого пакета, а версию с тегом dist «latest». прикреплен к нему. Тег «latest» dist используется npm publish по умолчанию, что означает, что обычно мы устанавливаем версию, которая была опубликована последней.

В большинстве случаев последняя и самая высокая версия совпадают, но представьте, что вы делаете исправление безопасности для более старой версии, например переход от 3.2.13 к 3.2.14, в то время как самый высокий стабильный - 4.6.12 уже . Если бы мы использовали только npm publish, обычные пользователи, набравшие npm install ‹package›, получат “ последняя »3.2.14 после этого, хотя 4.6.12 явно выше .

Сначала это может показаться немного запутанным, но как только мы задействуем поддержку нескольких треков версий, это именно то, что нам нужно, чтобы получить полный контроль над тем, какие пользователи пакета устанавливают.
Теперь с помощью npm publish - tag = previous для версии 3.2.14 все новые или обычные пользователи будут продолжать получать 4.6.12. Пользователи, которые все еще используют устаревшую версию 3.2.13, автоматически получат исправление ошибки (при условии, что они используют диапазоны версий, такие как ^ 3.0.0 или ~ 3.2.0 ). Явно прикрепив тег dist previous к версии 3.2.14, мы, по сути, создали новый канал выпуска, так что все в итоге получают ту версию, которую ожидают. Используя параметр командной строки tag, можно создавать эти каналы с произвольными именами, например, 3.x-latest тоже подойдет (имена тегов не может быть допустимым диапазоном SemVer, это единственное исключение).

  • npm install ‹package› (или npm install ‹package› @latest) по-прежнему приводит к версии 4.6.12 устанавливается
  • npm install ‹package› @previous приводит к установке версии 3.2.14.
  • Когда было «‹package›»: «^ 3.0.0» или «‹package›»: «~ 3.2.0» в package.json перед npm install автоматически получит 3.2.14

Теперь, когда мы знаем, как npm install неявно использует теги dist, давайте посмотрим, как мы можем использовать это для ✨ будущего наших пакетов

dist-tags работают точно так же для треков новых и более поздних версий, которые собираются выпустить впервые. Если мы будем придерживаться приведенного выше примера, это может быть версия 5.0.0, следующий крупный или критический выпуск. Опять же, вместо использования простого npm publish, мы будем использовать dist-теги, чтобы явно прикрепить к этому выпуску тег, отличный от «latest».
npm publish --tag = next. Ни один из обычных пользователей не получит эту новую версию автоматически, но теперь мы можем пригласить всех попробовать ее, запустив npm install ‹package› @next. В зависимости от объема и зрелости этого релиза мы могли бы просто объявить о выпуске в IRC или Slack, но ничто не мешает нам опубликовать сообщение о выпуске в блоге и высказаться об этом в Твиттере или даже на конференции.

  • npm install ‹package› @next приводит к установке версии 5.0.0
  • Но опять же: npm install ‹package› (или npm install ‹package› @latest) по-прежнему приводит к версии 4.6. .12 в процессе установки

Думайте об этом как о Google Chrome, Chrome Beta и Chrome Canary. Все это дистрибутивы одного и того же программного обеспечения, но в разной степени зрелости (каналы выпуска). Пользователи знают, какой уровень стабильности они могут ожидать, в то время как команда разработчиков может повторять и экспериментировать с очень высокой скоростью и ранней обратной связью.

Несомненно, сообщество, люди, вовлеченные в этот проект, немедленно попробуют «следующий». Внутри своих проектов они могут использовать существующие инструменты и рабочие процессы. npm делает этот процесс настолько простым для всех, что никаких дополнительных усилий не требуется, только осознанные решения и согласие.

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

И новичков, и постоянных клиентов не испугают сломанные версии по умолчанию. После выявления ошибок исправления могут быть опубликованы в канале следующий выпуск таким же образом, например, версия 5.0.1. Как только будет достаточно уверенности, можно с помощью одной команды переместить тег latest в самую высокую версию:
npm dist-tag add ‹package ›@ 5.0.1 последняя

  • Только сейчас: npm install ‹package› (или npm install ‹package› @latest) приводит к версии 5.0. 1 устанавливается

Еще одним эффектом этой техники является снижение давления, благодаря которому все будет готово и идеально с первого раза. Мы не можем взломать чей-либо код. Мы можем публиковать экспериментальные, неполные версии или версии, в которых мы еще не уверены на 100%. Так же, как у нас есть линтеры, обзоры кода, модульные и интеграционные тесты, тег «next» dist является еще одним уровнем безопасности, который позволяет нам выполнять итерацию быстрее, а не медленнее.

Точно так же мы не просто отправляем изменения в мастер, мы проходим процесс проверки на основе ответвлений и запросов на вытягивание. Примерно так же мы должны рассмотреть возможность публикации версий с тегом «next» dist-tag first, что является наилучшей практикой. Только после того, как толпа первых пользователей интегрировали их в свои проекты и предоставили положительные отзывы, мы должны повышать версию до «последней». Мне бы хотелось думать об этом как о массовом тестировании. Он хорошо поддерживается, интегрируется в существующие рабочие процессы, не нарушая их, и может использоваться всеми с минимальными накладными расходами или практически без них. Естественно, массовое тестирование может работать хорошо только в том случае, если ваш модуль использует большая, лояльная толпа, но независимо от того, что: Использование каналов выпуска действует как дополнительная безопасность net, чтобы выявлять ошибки любого рода и рано усвоить полезные привычки, вы хорошо подготовитесь к ошеломляющему успеху, который может последовать позже. Итак, это простой трюк, который позволяет нам отправлять стабильные выпуски нашим пользователям.

Эта стратегия выпуска используется самим npm, и глядя на их страницу выпусков, вы можете увидеть, насколько хорошо она работает для них, даже с двумя отдельными последней и следующей версиями для их стабильные и LTS треки соответственно. Та же концепция используется при разработке новой версии Hoodie, над которым я работаю.

Оглядываясь назад на приведенный выше пример с babel, это могло бы сэкономить немало стресса, бесценные часы сна и сломанные конструкции - без потери прогресса, волнения и заслуженного празднования. Публикация версии 6.0.0 babel с тегом dist «next» все равно привела бы ко всем положительным результатам для проекта, но без нарушения дефолт.

Я написал инструмент под названием semantic-release, который полностью автоматизирует публикацию пакетов. Он имеет полную встроенную поддержку dist-тегов, поэтому нет ограничений на автоматизацию рутинной работы. Есть несколько бесед там, где я говорю о зависимостях и управлении выпусками.

Если вы изо всех сил пытаетесь поддерживать актуальность и работоспособность своих зависимостей, вам следует изучить другой инструмент, над которым я работаю: Greenkeeper значительно сократит утомительную работу. Это бесплатно с открытым исходным кодом.

Я хотел бы услышать ваш опыт работы с dist-тегами и управлением или выпуском пакетов, поэтому, пожалуйста, не стесняйтесь рассказать мне об этом в Twitter.

Забавный факт: чтобы опубликовать ценный, хорошо написанный и понятный блог, я пригласил людей высказать свое мнение о моей первой версии, прежде чем публиковать ее для всех. То, что вы сейчас читаете, представляет собой значительно улучшенную и упрощенную версию. Как видите, каналы выпуска работают.
Спасибо, Джейк Чемпион, Льюис Каупер, Кент С. Доддс, Ян Ленардт, Soledad Penadés, maxogden и замечательная команда npm CLI за новаторство в этой технике.