12k
All articles

Введение в mise — менеджер версий для нескольких языков

Узнайте, как mise управляет версиями Node и Python для каждого проекта, учитывает файлы версий, фиксирует инструменты и запускает сборки локально и в CI.

OpenReplay Team
OpenReplay Team
Введение в mise — менеджер версий для нескольких языков

mise — это единый инструмент командной строки, который устанавливает Node, Python, Ruby и другие среды выполнения и переключает их версии для каждого каталога, считывая их из файла mise.toml в корне проекта.

Если в вашем репозитории есть фронтенд на Vite и Python-скрипт, который генерирует контент перед сборкой, вы, скорее всего, используете nvm и pyenv одновременно и надеетесь, что окружение каждого участника команды совпадает с CI. Обычно у кого-нибудь оно не совпадает.

Сравнение mise, nvm, pyenv, rustup и SDKMAN! приведено в статье 5 Version Managers Every Developer Should Know. Здесь мы обойдёмся без перечня возможностей. Вместо этого возьмём один репозиторий и проследим, как он ведёт себя при cd, при наличии файлов .nvmrc, при первом клонировании новым участником команды, в CI, в задаче сборки с двумя средами выполнения и при работе с переменными окружения.

Ключевые выводы

  • При активации в оболочке переход в каталог с mise.toml добавляет в PATH закреплённые за проектом версии сред выполнения, а выход из него восстанавливает прежние версии.
  • mise игнорирует .nvmrc и .python-version, если не включить их для каждого инструмента отдельно с помощью настройки idiomatic_version_file_enable_tools.
  • mise run самостоятельно загружает инструменты и переменные окружения проекта и по умолчанию устанавливает недостающие инструменты, поэтому работает в CI без активации в оболочке.
  • jdx/mise-action@v4 устанавливает mise и инструменты из mise.toml. Если в репозиторий закоммичен mise.lock, установка выполняется с флагом --locked.
  • Таблица [env] хранится в открытом виде и попадает в коммиты. Она предназначена для конфигурации, а не для секретов.

Проект: фронтенд на Vite с этапом сборки на Python

Пример репозитория, docs-preview, содержит две среды выполнения, один конфигурационный файл и один workflow:

docs-preview/
├── content/*.md
├── scripts/build_content.py
├── src/
├── package.json          # vite in devDependencies
├── mise.toml
└── .github/workflows/build.yml

Python-скрипт использует только стандартную библиотеку. Он преобразует Markdown-файлы в JSON, который импортирует фронтенд:

# scripts/build_content.py
import json, os, pathlib

src = pathlib.Path(os.environ.get("CONTENT_DIR", "content"))
out = pathlib.Path("src/generated/content.json")
out.parent.mkdir(parents=True, exist_ok=True)

items = [{"slug": p.stem, "body": p.read_text(encoding="utf-8")}
         for p in sorted(src.glob("*.md"))]
out.write_text(json.dumps(items, indent=2), encoding="utf-8")
print(f"wrote {len(items)} items to {out}")

Ниже приведён полный mise.toml. Последующие разделы ссылаются на него:

[tools]
node = "24"
python = "3.14"

[settings]
lockfile = true

[env]
CONTENT_DIR = "content"
VITE_SITE_NAME = "Docs Preview"

[tasks.install]
description = "Install JS dependencies"
run = "npm ci"

[tasks."build:content"]
description = "Convert content files to JSON for the frontend"
run = "python scripts/build_content.py"

[tasks.build]
description = "Generate content, then build the Vite app"
depends = ["install", "build:content"]
run = "npx vite build"

В таблице [tools] указаны префиксы версий (Node 24 LTS, Python 3.14). Lock-файл, о котором речь пойдёт ниже, фиксирует точные патч-версии, в которые они разрешаются.

Что происходит при входе в каталог и выходе из него

Когда mise активирован, переход в каталог с mise.toml добавляет в PATH закреплённые за проектом версии сред выполнения. Выход из каталога восстанавливает версии, которые были активны до этого. Если mise activate прописан в файле инициализации оболочки, mise переключает инструменты и переменные окружения при каждой смене каталога.

cd ~
node --version        # whatever your system or nvm provides
cd ~/code/docs-preview
node --version        # v24.x from mise.toml
python3 --version     # Python 3.14.x
echo "$CONTENT_DIR"   # content

Момент переключения версий зависит от оболочки. Согласно документации по шимам, в bash, zsh, fish и xonsh переключение происходит сразу после выполнения cd. Другие оболочки ждут появления следующего приглашения командной строки.

Если закреплённая версия ещё не установлена

На новой машине выполните mise install в каталоге проекта. Это явный и предсказуемый способ. Задачи также решают эту проблему за вас: задачи mise выполняются с инструментами и переменными окружения проекта, а настройка auto_install, включённая по умолчанию, устанавливает все недостающие инструменты из конфигурации при вызове mise run. С Python нужно быть осторожнее, если вы полагаетесь на шимы. В справочнике по настройкам поясняется, что шим, который не может разрешить управляемую mise версию (например, при отключённой автоустановке), переадресует вызов первой программе с тем же именем в другом месте PATH. Для инструмента, который поставляется и с ОС, например python3, это означает, что закреплённая версия так и не запустится, а ошибки вы не увидите.

Чтобы закрепить другую версию только в одном терминале, используйте mise shell node@<version>. Для этого mise должен быть уже активирован в оболочке.

Существующие файлы .nvmrc и .python-version

mise не читает .nvmrc и .python-version, если не включить эту возможность для каждого инструмента с помощью idiomatic_version_file_enable_tools. В FAQ mise подтверждается, что такие «идиоматические» файлы версий по умолчанию отключены. Это поведение появилось в mise 2025.10.0. Чтобы включить их:

[settings]
idiomatic_version_file_enable_tools = ["node", "python"]

В документации по конфигурации package.json указан среди файлов, которые mise читает для node, поэтому при включении node может подхватиться и он. Чтобы исключить отдельный файл, добавьте пару инструмент:имя_файла, например node:package.json, в настройку idiomatic_version_file_disable_files.

Когда mise.toml закрепляет обе среды выполнения, оставляйте .nvmrc только для тех участников команды, кто всё ещё пользуется nvm, и следите, чтобы оба файла совпадали. Если нужно освежить в памяти, как nvm обрабатывает этот файл, обратитесь к руководствам по переключению версий Node с помощью nvm для macOS, Linux и Windows.

Участники команды и CI

Ноутбук и CI-раннер читают один и тот же mise.toml, но добавляют инструменты в PATH по-разному:

НоутбукGitHub Actions
Инструменты в PATHХук активации в оболочкеjdx/mise-action устанавливает mise и закреплённые инструменты
Источник версийmise.toml + mise.lockТе же файлы
Команда сборкиmise run buildmise run build

Первое клонирование новым участником команды:

  1. Установите mise: curl https://mise.run | sh
  2. Добавьте eval "$(~/.local/bin/mise activate zsh)" (или аналогичную строку для вашей оболочки) в rc-файл оболочки.
  3. Просмотрите mise.toml, затем выполните mise install.
  4. Выполните mise run build.

К модели доверия mise нужно относиться внимательно, поскольку конфигурационные файлы могут выполнять код. Согласно руководству по началу работы, вне CI и режима paranoid любая команда, работающая с проектом, например mise install, mise exec или mise run, автоматически доверяет активной конфигурации. Поэтому просматривайте mise.toml, прежде чем запускать такие команды, и используйте mise trust, если хотите явно отметить конфигурацию как доверенную.

Фиксируйте точные версии. Lock-файл mise.lock создаётся командой mise lock или настройкой lockfile = true, показанной выше. Закоммитьте его. При mise install --locked отсутствие записи в lock-файле вызывает ошибку, поэтому mise никогда не выберет новую версию незаметно.

Workflow для CI:

# .github/workflows/build.yml
name: build
on: [push, pull_request]
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6
      - uses: jdx/mise-action@v4
      - run: mise run build

Шаг mise-action устанавливает mise и инструменты из mise.toml, поэтому workflow использует те же версии, что и разработчики локально. По умолчанию он выполняет mise install (install: true) и кеширует инструменты (cache: true). Если присутствует mise.lock, установка выполняется с --locked. Кроме того, он автоматически передаёт ${{ github.token }}. На ноутбуках задание GITHUB_TOKEN или MISE_GITHUB_TOKEN помогает, если при установке срабатывают ограничения частоты запросов, как описано на странице о токенах GitHub.

Если что-то сломалось, локализовать проблему помогут три команды:

СимптомКоманда
Активация или установка работает некорректноmise doctor
Похоже, применяется не тот конфигурационный файлmise config ls
Выбрана не та версияmise ls --current

Реальная задача: mise run build с двумя средами выполнения

Задача, определённая в mise.toml, выполняется с уже применёнными закреплёнными инструментами проекта и переменными из [env]. Это значит, что mise run build использует одни и те же Node и Python на ноутбуке и в CI — даже без активации в оболочке.

Ключ depends заставляет build дождаться завершения всех перечисленных задач, поэтому Python-этап успевает записать src/generated/content.json до того, как его прочитает Vite. Перечисленные задачи могут выполняться параллельно. В документации по задачам сказано, что зависимости могут запускаться параллельно и их порядок в depends не задаёт последовательность. В данном случае это безопасно: npm ci записывает node_modules, а Python-скрипт — src/generated, так что ни один этап не затрагивает результаты другого. Если когда-нибудь один этап начнёт использовать результаты другого, сделайте его напрямую зависимым от этого этапа.

mise tasks          # lists install, build:content, build
mise run build      # npm ci + Python step, then vite build

Согласно руководству по запуску задач, по умолчанию одновременно выполняется не более восьми задач.

Переменные окружения на уровне проекта в mise

Значения в таблице [env] хранятся в открытом виде в закоммиченном файле. Переменные окружения mise — это управление конфигурацией, а не управление секретами. В примере Python-скрипт читает CONTENT_DIR через os.environ, а фронтенд — import.meta.env.VITE_SITE_NAME.

Префикс VITE_ наглядно показывает риск хранения значений [env] в открытом виде. В документации Vite по переменным окружения поясняется, что любая переменная с префиксом VITE_ попадает в клиентский бандл, а её значение встраивается в код на этапе сборки. Любое значение VITE_ в mise.toml становится публичным, как только сайт выкатывается в продакшен.

Отсутствие переменной, нужной на этапе сборки, не приводит к ошибке сборки. Вы просто получаете бандл, который обращается не по тому URL. Session replay позволяет увидеть это глазами пользователя: вы наблюдаете, как запросы уходят на базовый URL undefined, и затем прослеживаете проблему до окружения, в котором выполнялась сборка.

Когда mise оправдан?

Если проекту нужен только Node, nvm и .nvmrc уже справляются с задачей, и mise не обязателен. Руководства по nvm хорошо описывают такую настройку. mise стоит внедрять, когда в одном репозитории закреплены две и более среды выполнения, а CI должен совпадать с окружением на каждом ноутбуке. Чтобы попробовать его на существующем репозитории, добавьте mise.toml с закреплёнными версиями в [tools], выполните mise lock, закоммитьте оба файла и замените шаг сборки в CI на mise run build.

Часто задаваемые вопросы

Где хранить секреты, если mise.toml коммитится в репозиторий?

Храните секреты в mise.local.toml, добавленном в .gitignore, или объявите их в mise.toml с параметром required = true — тогда mise проверяет, что переменная задана, не храня её значение. Параметр redact = true маскирует значения в перехваченном выводе задач, однако mise env по-прежнему выводит их в открытом виде. Для секретов, которые необходимо хранить в репозитории, mise умеет расшифровывать файлы SOPS, зашифрованные с помощью age (экспериментальная возможность), для mise exec, задач и активации в оболочке.

Читает ли mise файлы .tool-versions от asdf?

Да. В отличие от .nvmrc, mise читает .tool-versions без каких-либо дополнительных настроек. В Unix он также может запускать shell-плагины asdf, хотя работа всех команд и плагинов asdf не гарантируется. Если mise.toml и .tool-versions в одном каталоге объявляют один и тот же инструмент, приоритет у mise.toml. Документация mise не рекомендует использовать общие каталоги установки для asdf и mise, поэтому после перехода выполните mise install.

Почему редактор или скрипт использует не ту версию Node, что терминал?

Редакторы, git-хуки и планировщики часто не загружают rc-файл интерактивной оболочки, поэтому хук активации mise не срабатывает. Чтобы это исправить, добавьте mise activate zsh --shims (или аналогичную строку для вашей оболочки) в профиль входа, который читает редактор, например ~/.zprofile, а обычный mise activate оставьте в ~/.zshrc. В скриптах используйте mise exec -- команда или mise run. В режиме шимов недоступны некоторые возможности активации, например большинство хуков.

Understand every bug

Uncover frustrations, understand bugs and fix slowdowns like never before with OpenReplay — self-hosted, with full data ownership.

Star on GitHub

We use cookies to improve your experience. By using our site, you accept cookies.