Skip to content

Repository files navigation

📦 DevBox

Скоуп для нескольких репозиториев: один контейнер, один конфиг, окно, в котором каждый репозиторий сам по себе.

✨ Главное

🎯 Девбокс решает одну задачу: работать с десятком независимых репозиториев как с одним рабочим местом — не настраивая это место заново на каждой машине.

🧩 Что он даёт:

  • окружение целиком — образ: Node, сам девбокс, переменные. На машине остаётся один файл с описанием контейнера, всё остальное живёт в докер-томе;
  • состав по конфигу — какие репозитории, откуда, с какими адресами и тегами. Отдал конфиг напарнику — у него тот же скоуп;
  • окно, в котором репозиторий не теряется — у каждого свой корень: его команды, его ветка, его состояние видны отдельно от соседей;
  • связь между репозиториями — пакет соседа подставляется живой папкой, а не копией из реестра;
  • сквозные сценарии — «выпустить в одном, подтянуть в другом» одной задачей, причём каждый репозиторий выполняет свою обычную команду и о скоупе ничего не знает.

🪶 Скоуп — не репозиторий. В его корне нет .git: это рабочее пространство с настройками, рядом с которыми лежат обычные клоны. Каждый репозиторий остаётся самостоятельным и работает без девбокса.

🚀 Новый скоуп

На машине — один файл, и рядом с ним не нужно ничего:

// .devcontainer/devcontainer.json
{
  "image": "ghcr.io/egor6-66/devbox:0.4.0",

  // Скоуп живёт в докер-томе: на машине не остаётся ни репозиториев, ни их зависимостей.
  "workspaceMount": "source=мой-скоуп,target=/workspaces/tree,type=volume",
  "workspaceFolder": "/workspaces/tree",

  // Опциональные инструменты — фичами девконтейнеров: ассистент, `gh`, тулчейны. Образ их не
  // несёт, набор решает тот, кто работает в скоупе.
  "features": {
    "ghcr.io/anthropics/devcontainer-features/claude-code:1": {}
  },

  // Свои тома — сколько нужно: ключи, логин ассистента, кэш пакетов, свои настройки. Умолчаний нет
  // и образ их не навязывает: что подключить, решает человек. Нужен том только для чтения — добавь
  // `,readonly` сам; по умолчанию он на запись, иначе ассистент не сохранит логин.
  "mounts": [
    "source=мои-ключи,target=/home/node/.secrets,type=volume",
    "source=мой-стор,target=/home/node/.pnpm-store,type=volume"
  ]
}

👤 Скоуп работает пользователем 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 на двух платформах, с тегами полной, минорной и мажорной версии.

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

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages