Скоуп для нескольких репозиториев: один контейнер, один конфиг, окно, в котором каждый репозиторий сам по себе.
- ✨ Главное
- 🚀 Новый скоуп
- 🧩 Команды
- 📄 Конфиг
- 🖥️ Окно
- 🔗 Шов зависимостей
- 🧱 Устройство
- 🛠️ Разработка девбокса
- ❓ FAQ · 🗺️ ROADMAP
🎯 Девбокс решает одну задачу: работать с десятком независимых репозиториев как с одним рабочим местом — не настраивая это место заново на каждой машине.
🧩 Что он даёт:
- окружение целиком — образ: Node, сам девбокс, переменные. На машине остаётся один файл с описанием контейнера, всё остальное живёт в докер-томе;
- состав по конфигу — какие репозитории, откуда, с какими адресами и тегами. Отдал конфиг напарнику — у него тот же скоуп;
- окно, в котором репозиторий не теряется — у каждого свой корень: его команды, его ветка, его состояние видны отдельно от соседей;
- связь между репозиториями — пакет соседа подставляется живой папкой, а не копией из реестра;
- сквозные сценарии — «выпустить в одном, подтянуть в другом» одной задачей, причём каждый репозиторий выполняет свою обычную команду и о скоупе ничего не знает.
🪶 Скоуп — не репозиторий. В его корне нет .git: это рабочее пространство с настройками, рядом
с которыми лежат обычные клоны. Каждый репозиторий остаётся самостоятельным и работает без девбокса.
На машине — один файл, и рядом с ним не нужно ничего:
👤 Скоуп работает пользователем node — образ объявляет его сам, потому что все подготовленные
им пути принадлежат node. Нужен другой — назови своего в этом же файле, он сильнее.
📂 Пути, по которым скоуп ищет чужое состояние, объявлены в образе: /home/node/.secrets (там же
CLAUDE_CONFIG_DIR, GIT_CONFIG_GLOBAL, GH_CONFIG_DIR, NPM_CONFIG_USERCONFIG) и
/home/node/.pnpm-store. Монтируешь туда свой том — логин, ключи и кэш переживают пересоздание
контейнера и делятся между скоупами.
Dev Containers: Reopen in Container — редактор создаст том, поднимет контейнер и позовёт девбокс.
В пустом томе он кладёт заготовку конфига и говорит, что в ней назвать:
# назвать свои репозитории в .devbox/devbox.json, затем
devbox up # склонировать, связать, поставить зависимости, собрать окно
devbox doctor # что не так, если что-то не так🤝 Поделиться скоупом — отдать конфиг. Это обычный файл: напарник кладёт его в свой том, зовёт
devbox up и получает тот же состав. Готовые конфиги с другой машины просто подкладываются вместо
заготовок.
🔄 Обновление девбокса — поправить версию образа в своём файле и пересобрать контейнер. Том со скоупом при этом цел.
| Команда | Что делает |
|---|---|
devbox up |
склонировать названное, прописать адреса, положить шов, поставить зависимости, собрать окно (--dry-run, --skip-install) |
devbox list |
состав скоупа: репозитории с тегами, описанием, объявленными командами, и сценарии |
devbox doctor |
чего не хватает: репозиториев, адресов, связи, переменных |
devbox open |
открыть окно скоупа (его же зовёт подключение контейнера) |
devbox init |
собрать конфиг по тому, что уже лежит рядом (--force — перезаписать) |
🚩 У любой команды есть --json: одна строка — конверт ответа для машины. Исход он же код возврата:
сделано и «делать нечего» — 0, отказ — 1, неверное употребление — 2.
📍 Корень скоупа — папка, в которой лежит .devbox/. Команду зовут откуда угодно внутри скоупа.
.devbox/devbox.json — один файл с тремя разделами.
{
"scope": {
"install": "pnpm install", // команда установки из корня; она же называет менеджер
"seam": "pnpm" // шов зависимостей: "pnpm", "go" или пусто
},
"repos": {
"base": { "path": "base", "url": "https://git.example.test/base.git", "tags": ["pkg"] },
"shop": {
"path": "apps/shop",
"url": "https://git.example.test/shop.git",
"desc": "магазин",
"tags": ["app"],
"remotes": { "work": "https://work.example.test/shop.git" },
"commands": { "check": "just check" }
}
},
"flows": {
"выпуск": {
"desc": "выпустить базу и подтянуть её в приложении",
"steps": [
{ "repo": "base", "run": "pnpm release" },
{ "repo": "shop", "run": "pnpm up @example/base@latest" }
]
}
}
}commands— только то, чего редактор найти не может:just, свои обёртки. Скрипты пакетов перечислять не нужно, их находит сам редактор, и репозиторий остаётся рабочим без девбокса.flows— шаги «репозиторий плюс его команда», по порядку, останов на первой ошибке. Вrunможно назвать имя изcommandsэтого репозитория или команду целиком.- Ключ, начинающийся на
//— комментарий или пример: в состав он не идёт.
.devbox/local.json — личное поверх общего. Его не передают:
{ "repos": { "shop": { "skip": true }, "base": { "url": "https://git.example.test/my-fork.git", "branch": "dev" } } }🔑 Секретов в конфиге нет. Имена переменных — .devbox/.env_example, значения — .devbox/.env.
Доктор сверяет имена и говорит, чего не хватает.
🌿 Ветка не записывается без нужды: клон берёт ветку по умолчанию репозитория. Нужна именно эта —
назови branch.
Окно — файл <имя скоупа>.code-workspace, девбокс собирает его по конфигу при каждом
развёртывании. Корни: каждый репозиторий и сам .devbox. Папки-обёртки над репозиториями нет,
поэтому ничего не дублируется и ничего не прячется.
- команды репозиториев находит редактор — обходит корни и показывает их скрипты. Это его механика, и девбокс в неё не лезет;
- задачи скоупа — в
.devbox/.vscode/tasks.json: развернуть, проверка, состав, установить зависимости, роль-сессия, пуш выбранного репозитория, сценарии из конфига и объявленные команды; - настройки окна лежат в самом файле окна: глубина поиска репозиториев по составу, менеджер пакетов из команды установки. Умолчания расширений девбокс не переписывает;
- рекомендации расширений — GitLens, Task Explorer, Tasks Shell Input: если их нет, редактор предложит поставить сам.
Открывать файл руками не нужно: подключение контейнера зовёт devbox open, и текущее окно
становится окном скоупа.
Включается полем scope.seam, и задача у него одна: пакет соседнего репозитория подставляется
живой папкой, а не копией из реестра. Репозиторий об этом не знает — ни один его файл не меняется.
seam |
что кладётся в корень | чем это канон |
|---|---|---|
pnpm |
pnpm-workspace.yaml с составом и связыванием соседей по версии |
штатный воркспейс pnpm |
go |
go.work — его собирает сам Go командой go work use |
штатный механизм Go для нескольких модулей |
| пусто | ничего | скоупу без общих пакетов шов не нужен |
Состав внутри файла девбокс держит между метками — свои строки пишите вне них, их он не трогает.
Убрал seam из конфига — файл больше не обновляется, зависимости возвращаются из реестра.
| Часть | Где | Что внутри |
|---|---|---|
| Вход | bin/devbox.ts |
объявление команд: имя, флаги, что зовут |
| Слой команд | src/cli/ |
конверт ответа и печать (answer.ts), разбор аргументов и коды возврата (program.ts) |
| Конфиг и развёртывание | src/devbox/ |
чтение конфига с личным слоем, заготовка и снимок диска, план, развёртывание, доктор, состав, шов, окно |
| Состав диска | src/repos/discover.ts |
кто считается репозиторием |
| Общее | src/git.ts, src/root.ts |
прогон git-команды, корень скоупа |
| Упаковка | Dockerfile |
образ окружения и метка с настройками контейнера |
| Проверки | test/ |
тесты на встроенном раннере Node |
| Черновой скоуп | tools/probe.ts |
представительный скоуп одной командой — им проверяется окно |
🪶 Ноль зависимостей у рантайма. Команды — TypeScript, который Node 24 исполняет сам: доктор обязан отвечать там, где ещё ничего не установлено.
node --test # проверки, ничего ставить не нужно
pnpm install # нужен только для tsc
pnpm run typecheck # типы
pnpm run probe # развернуть черновой скоуп и напечатать команду открытия окна🧪 Окно проверяется черновым скоупом, а не рабочим. pnpm run probe [путь] разворачивает
представительный скоуп — три репозитория с коммитами (один на just, чтобы видеть раннер, которого
редактор не знает), сценарий и шов — и печатает команду открытия окна. Рабочее окно при этом не
перезапускается.
🐳 Образ проверяет сборка, а не человек: на каждый пуш собирается образ и в нём гоняются команды. Выпуск — тегом: сборка публикует образ в GHCR на двух платформах, с тегами полной, минорной и мажорной версии.
📐 Работа написана голыми функциями, команда — тонкая оболочка над ней: та же механика годится и для другого фасада, и для теста без запуска процесса.