Введение в mise — менеджер версий для нескольких языков
Узнайте, как mise управляет версиями Node и Python для каждого проекта, учитывает файлы версий, фиксирует инструменты и запускает сборки локально и в CI.
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 build | mise run build |
Первое клонирование новым участником команды:
- Установите mise:
curl https://mise.run | sh - Добавьте
eval "$(~/.local/bin/mise activate zsh)"(или аналогичную строку для вашей оболочки) в rc-файл оболочки. - Просмотрите
mise.toml, затем выполнитеmise install. - Выполните
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. В режиме шимов недоступны некоторые возможности активации, например большинство хуков.