Что такое «Менеджер версий»
Системная установка
Для запуска кода на любом языке, необходимо установить его интерпретатор (или компилятор). В разных операционных системах это делается по-разному: где-то используются пакетные менеджеры, например, apt или yum, где-то скачивается установщик. Некоторые языки бывают предустановленны сразу, например, Python. На него многое завязано в дистрибутивах линукса.
# Ubuntu
sudo apt install nodejs # установит не самую свежую версию
Стандартный способ установки работает хорошо только в самом начале, когда происходит первичная настройка. Затем, с течением времени, начинают всплывать разные проблемы. Например, в какой-то момент выходит новая версия языка, которую нужно подключить к проекту. Обычно, должно пройти какое-то время, перед тем, как язык станет доступным для установки через пакетные менеджеры. И здесь либо придется ждать, что не всегда желательно, либо искать другой способ установки. Последнее нередко превращается в серьёзное испытание, с часами гугления и установкой дополнительных библиотек. Все это в конце концов засоряет систему и иногда ломает её.
Другая серьёзная проблема – установка сразу нескольких версий одного языка. Такое бывает нужно, когда разработчик переключается между разными проектами, требующими разные версии. Насколько часто такое происходит? Крайне часто. Чем дальше в разработку, тем больше вариантов: разные проекты в компании, свои проекты, опенсорс.
Здесь нужно упомянуть, что всё это мало касается тех, кто целиком ушел в Docker и Docker Compose. Однако даже в этом случае, нужны языки для работы с опенсорсом.
Менеджеры версий
Для решения этих проблем разработчики придумали менеджеры версий. Менеджер версий – это специальная программа, предназначенная для управления версиями языка. С его помощью устанавливают нужные версии и производят переключение между ними. В отличие от пакетных менеджеров, входящих в операционные системы, менеджеры версий всегда позволяют ставить последние версии языков, как только они выходят (включая установку альфа- и бета-версий).
Например, для Node.js это NVM (Node Version Manager):
# Установка NVM
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.35.3/install.sh | bash
# Установка не означает активацию. После установки активной останется та версия, что и была до установки
nvm install node # Установка последней доступной версии ноды
nvm install 6.14.4 # или 10.10.0, 8.9.1, и т.п.
nvm ls-remote # список доступных версий
nvm use node # Активация последней установленной версии ноды
nvm use node 17 # Активация нужной версии
Для упрощения работы менеджеры версий обычно позволяют создать специальный файл внутри проекта, который зафиксирует нужную версию языка. В некоторых случаях менеджеры версий сами отслеживают этот файл и переключают версию автоматически.
echo "17" > .nvmrc
# Эта команда увидела файл .nvmrc и использовала указанную там версию
$ nvm use
Found '/path/to/project/.nvmrc' with version <17>
Now using node v17
В современном мире сложно представить себе язык, у которого бы не было менеджера версий. Более того некоторые языки, такие как Ruby, имеют множество разных менеджеров версий, которые конкурируют между собой:
- go: gvm, g
- java: sdkman, jabba
- ruby: rbenv, rvm, chruby
- php: phpenv, phpbrew
- python: pyenv
Менеджеры версий позволили решить ещё несколько важных задач. Как правило, когда программист взаимодействует с языком, установленным напрямую, то ему приходится использовать sudo при установке глобальных пакетов. Дело в том, что стандартная схема установки языка предназначена для всех пользователей сразу. Поэтому все нужные файлы, включая глобальные пакеты, попадают в общие директории для которых нужны права администратора. С точки зрения безопасности это довольно большая дыра, которой могут воспользоваться (и иногда пользуются) разработчики опенсорс библиотек. Менеджеры версий устанавливают всё в домашнюю директорию текущего пользователя, где у него и так полные права. С одной стороны это позволяет не запускать установку пакетов от имени администратора, а с другой — не засоряется система. Удалить язык и все его пакеты через менеджер версий невероятно просто. Достаточно стереть директорию (правда, лучше это делать средствами самого менеджера версий).
Универсальный менеджер
Решив одни проблемы, менеджеры версий добавили другие. Во-первых, их слишком много, и они иногда меняются, сначала популярен один, затем другой. Во-вторых, сам процесс установки менеджера версий может оказаться сложнее установки языка. Проблема в том, что им нужно быть универсальными и работать везде, что крайне проблематично, учитывая разнообразие современных экосистем. Достаточно посмотреть размер документации NVM, чтобы оценить масштаб катастрофы. В-третьих, все эти менеджеры работают по-разному и имеют разные команды. Из-за этого усложняется процесс переключения между ними при работе с разными языками.
Всё это привело к следующему логическому шагу. В конце концов появился универсальный менеджер asdf, который, благодаря плагинам, способен работать с любыми языками. Неполный список его преимуществ:
- Одна единственная утилита командной строки для работы со всеми языками
- Единый интерфейс взаимодействия для всех языков
- Автоматическое переключение на нужную версию языка внутри каждого проекта
- Простая система плагинов, позволяющая добавить любые языки
Сейчас asdf набрал достаточно большую популярность и постепенно заменяет собой все остальные менеджеры версий (технически под капотом там часто используются менеджеры, специфические для конкретных языков). У него чуть более сложная система команд из-за необходимости поддерживать множество языков, но в остальном он резко упрощает весь процесс.
# У asdf прекрасная документация, где наглядно показано, как его установить,
# и какие могут понадобится зависимости в разных системах
# Установка
git clone https://github.com/asdf-vm/asdf.git ~/.asdf --branch v0.9
# После этого нужно перезапустить терминал
echo '. $HOME/.asdf/asdf.sh' >> ~/.bashrc
# Для работы с конкретным языком, сначала нужно подключить соответствующий плагин
# Список доступных плагинов есть на сайте проекта
asdf plugin add nodejs
# Установка языка
# Вместо nodejs нужно подставить название того плагина, с которым работаем
asdf install nodejs latest # latest означает последнюю версию указанного языка
# Установка указанной версии
asdf install nodejs latest
# Установка нужной версии языка версией по умолчанию
asdf global nodejs latest
# Показывает текущие версии для языков, установленных через asdf
asdf current
elixir 1.10.1-otp-22 (set by /Users/user/.tool-versions)
erlang 22.2.7 (set by /Users/user/.tool-versions)
nodejs 17.0.0 (set by /Users/user/.tool-versions)
php 7.4.5 (set by /Users/user/.tool-versions)
python 3.8.2 2.7.16 (set by /Users/user/.tool-versions)
ruby 2.7.0 (set by /Users/user/.tool-versions)
yarn 1.22.4 (set by /Users/user/.tool-versions)
Вывод
Работа с разными версиями языков – непростая задача, которую решают менеджеры версий и докер (для продвинутых). Среди всех менеджеров выделяется asdf, который становится универсальным инструментом для управления любыми языками и даже обычными программами.