Интеграция с Git
Как DWE взаимодействует с Git: пайплайн рендеринга хуков по сервисам, проба рабочего пространства за dwe status git, опциональная проба обновления при dwe run и конвенции .gitignore для runtime-управляемых путей.
Содержание
Заголовок раздела «Содержание»- У двух сервисов есть рабочее дерево
- Рендеринг хуков
- Проба рабочего пространства (
dwe status git) - Проба обновления (
dwe run) - Конвенции
.gitignore - Никакого checkout, fetch или push
- Что читать дальше
У двух сервисов есть рабочее дерево
Заголовок раздела «У двух сервисов есть рабочее дерево»У проекта DWE есть собственный Git-репозиторий в корне проекта — тот, что отслеживает workspace.yml, дерево конфигурации workspace/ и оверлеи compose/. У большинства проектов также есть один или несколько прикладных сервисов, исходный код которых лежит в <svc.Dir>/src/. По конвенции у каждого такого сервиса — отдельная выгрузка Git в <svc.Dir>/src/.git/.
DWE обращается с этими двумя слоями одинаково: он никогда не предполагает, что в корне проекта обязательно конкретная VCS, и никогда не лезет в репозиторий одного сервиса, чтобы узнать о другом. Каждая Git-операция нацелена либо на корень проекта (проба обновления), либо ровно на один <svc.Dir>/src/ (рендеринг хуков, проба рабочего пространства). Общей поверхности «забрать всё разом» нет.
CLI вызывает хостовый бинарник git через shell. Путь к бинарнику разрешается через стандартный аксессор (config.GitBin(cfg)), так что пользователь при необходимости может закрепить конкретный путь через binary_git=/path в ~/.config/dwe/config. Пустое значение означает «искать git в $PATH».
Рендеринг хуков
Заголовок раздела «Рендеринг хуков»dwe render git рендерит shell-хуки в директорию <svc.Dir>/src/.git/hooks/ каждого включённого сервиса из template-пака под workspace/templates/git/.
Механизм такой же, как у render ide и render ai — та же модель выбора, та же цепочка разрешения пака, те же защитные проверки path-safety, та же схема манифеста. Рендеринг Git-хуков отличается тремя моментами:
- Назначение находится внутри
.git/, которое Git никогда не отслеживает. Отрендеренные файлы не коммитятся; повторный рендеринг — источник истины. Ручное редактированиеsrc/.git/hooks/pre-commitс расчётом, что изменения переживут, — концептуальная ошибка. toв манифесте ограничен basename’ами, потому что Git не заходит в поддиректорииhooks/. Строка манифеста видаto: subdir/pre-commitотклоняется на загрузке.- Дефолт активации совпадает с
render ide: только сервисыtype: appрендерят хуки по умолчанию. Tool и infra сервисы явно соглашаются черезservices.<name>.render.git.enabled: true.
Сквозной поток для одного сервиса:
- Гейт активации. И
services.<name>.enabled, иservices.<name>.render.git.enabledдолжны быть true (политический дефолт зависит от типа сервиса). - Преflight хаба.
<svc.Dir>должен разрешаться внутрь корня проекта и не достигаться через симлинк. - Проба директории git.
<svc.Dir>/src/.gitдолжен быть директорией. Обычный файл (указательgitdir:отgit worktreeили субмодуль) приводит к пропуску сервиса с предупреждением — см. Worktrees and submodules в справочнике команд. Отсутствующий.git/тоже пропускается с предупреждением. - Разрешение коллизий. Когда два сервиса делят один
<svc.Dir>(обычно базовыйmainиextends:-потомокmain-debug), побеждает самый глубокий extender. - Разрешение пака.
render.git.templateфиксирует пак; иначе цепочка пробуетworkspace/templates/git/<service-name>/, затемworkspace/templates/git/default/. - Рендер по каждому файлу. Каждая запись в
manifest.ymlпака читается, вычисляется как строгий Go text template, пишется в<svc.Dir>/src/.git/hooks/<basename>и получаетchmod 0755на каждом запуске.
Полный справочник по полям и проработанные примеры — в dwe render git. Справочник блока активации (services.<name>.render.git) — в config/services/fields.md.
Наследование шаблонов хуков
Заголовок раздела «Наследование шаблонов хуков»Пак — это просто директория. Композиция происходит через два механизма:
- Оверлей
<pack>.local/. Для любого пака вworkspace/templates/git/<pack>/соседняя директорияworkspace/templates/git/<pack>.local/может перекрывать отдельные файлы по относительному пути. Резолвер packroot пробует сначала<pack>.local/<rel>и затем переходит к<pack>/<rel>. Это точка кастомизации на разработчика —.local/обычно gitignored. extends:сервиса. Когдаservice-bрасширяетservice-a, и уservice-bнет своегоrender.git.template, он наследует значение родителя. Правило коллизии deepest-extends затем определяет, какой сервис побеждает для общего<svc.Dir>. Шаблонная переменная.Resolvedименует рендерящий сервис (самый глубокий extender — его оверлей решает container/dir), а.Serviceименует канонический корень конфигурации (используйте его, чтобы получить сырую конфигурацию по имени сервиса).
Сам пак не поддерживает директиву наследования «из другого пака». Если два пака разделяют содержимое, выносите общие части в тело шаблона через {{ template "name" . }} или дублируйте их. DWE не вводит языка композиции паков; модель файлового оверлея — единственная точка кастомизации.
Проба рабочего пространства (dwe status git)
Заголовок раздела «Проба рабочего пространства (dwe status git)»dwe status и dwe status git рендерят по одной строке на каждый сервис, у которого есть рабочее дерево в <svc.Dir>/src/. Проба только читает данные, выполняется параллельно и никогда не изменяет репозиторий.
flowchart TD
CFG["Загрузить смерженный конфиг"] --> ITER["Для каждого сервиса<br/>с выставленным svc.Dir"]
ITER --> ABS["Разрешить <abs>/src"]
ABS --> EX{"<abs>/src<br/>директория?"}
EX -- нет --> SKIP["Пропустить строку"]
EX -- да --> DEDUP["Сгруппировать по probe dir<br/>(потомки extends схлопываются)"]
DEDUP --> PICK["Выбрать корень цепочки<br/>(минимальная глубина)"]
PICK --> OWNGIT{"<abs>/src/.git<br/>существует?"}
OWNGIT -- нет --> BLANK["Строка с пустыми ячейками"]
OWNGIT -- да --> SHELL["git -C <abs>/src status -b --porcelain=v2"]
SHELL --> PARSE["Распарсить branch + oid + ahead/behind + dirty"]
PARSE --> ROW["Эмитировать строку"]
Граничные случаи:
- Нет собственного
.git/→ пустые ячейки, не ошибка. Сервис, у которого<svc.Dir>/src/существует, но нет своего.git/, получает строку с пустыми branch/SHA/ahead-behind. Проба намеренно завершает работу до любого вызова shell — иначеgit -Cподнялся бы вверх к ближайшему охватывающему репозиторию (часто корню проекта), и сообщать его как статус сервиса было бы неверно. - Отсутствующий
src/→ строки нет вовсе. Сервисы без рабочего дерева полностью опускаются из вывода. Отдельного флага «отказаться от секции workspace» помимо отсутствия директорииsrc/нет. - Потомки extends дедуплицируются. Когда два сервиса разделяют один
<svc.Dir>черезextends:, они пробуют одно и то же дерево. Коллектор группирует кандидатов по probe-директории и оставляет корень цепочки extends (минимальная глубина, ничьи разрешаются алфавитно). Sidecar-варианты вродеmain-debugне дают дублирующих строк. - Параллельные shell-вызовы с ограничением. Пробы запускаются внутри
errgroup, ограниченного 8 одновременными вызовами. Каждая горутина пишет в заранее выделенный слот строки, так что падения на строку изолированы и никогда не отменяют соседей. - Отображаемый путь. Пробуемая директория, показанная в таблице статуса, указывается относительно корня проекта с префиксом
…/(…/services/main/src). Сама проба использует абсолютные пути.
Проба никогда не делает fetch. Branch / OID / ahead-behind / dirty приходят из одного вызова git status -b --porcelain=v2. Актуальность относительно удалённого репозитория требует отдельной пробы обновления, описанной ниже.
Проба обновления (dwe run)
Заголовок раздела «Проба обновления (dwe run)»Верхнеуровневый блок update: в workspace.yml / local.yml управляет пробой самообновления. Когда mode: on, dwe run проверяет репозиторий корня проекта до выполнения любой фазы:
# workspace.yml (или workspace/local.yml)update: mode: on # on | offДва режима:
| Режим | Fetch | Pull |
|---|---|---|
on | да | по согласию (TTY-промпт; не-TTY понижается до check-семантики) |
off | нет | нет — проба отключена |
Проба запускается на корне проекта, никогда не на src/ сервиса. Она вызывает git fetch --quiet <remote> (таймаут 15 с), затем git rev-list --left-right --count, чтобы получить behind / ahead. Грязное дерево, отсутствующий upstream или сбой fetch выдают предупреждение и продолжают — пайплайн run никогда не блокируется.
Когда режим on, рабочее дерево чистое, behind > 0, ahead = 0 и сессия интерактивна, DWE запрашивает подтверждение перед запуском git pull --ff-only (таймаут 2 мин). Успешный pull перезагружает DweConfig, LifecycleConfig и реестр команд in-process до выполнения фаз, так что остаток dwe run видит состояние после обновления.
Приоритет в runtime: флаг --no-update > флаг --update <mode> > update.mode из смердженной конфигурации. Справочник по полю: config/workspace.md → блок update:.
Конвенции .gitignore
Заголовок раздела «Конвенции .gitignore»Типичный .gitignore проекта исключает четыре дерева, управляемых DWE:
# DWE runtime artifacts/.dwe/
# Service sources (root-anchored — keeps workspace/services/ tracked)/services/
# Unpacked snapshot stash/snapshots/
# Database and other dumps/backups/Обоснование, по папкам:
.dwe/содержит журнал деплоя (state.yml), блокировки проекта (deploy.lock,snapshot.lock), логи команд (logs/) и переопределения user-config на уровне проекта. Всё это перегенерируется из дерева конфигурации и запущенных контейнеров. Отслеживание этой папки только связало бы историю коммитов с локальными таймингами./services/содержит чекауты исходников сервисов-приложений (<hub>/src/и рабочие папки). Каждыйsrc/— это отдельный репозиторий, выкачиваемый на каждой машине; репозиторий проекта не должен отслеживать содержимое другого репозитория. Ведущий слеш привязывает паттерн к корню проекта, так что отслеживаемое деревоworkspace/services/не затрагивается.snapshots/содержит распакованную рабочую копию активного снапшота. Сами snapshot-архивы лежат там, куда их кладёт snapshot-workflow — обычно отдельный путь или общий том. Runtime-стэш отслеживать не нужно.backups/содержит дампы БД и прочее, создаваемые во время разработки. Это сгенерированные артефакты, различающиеся от машины к машине, поэтому отслеживание связало бы репозиторий с локальными данными.
Две связанные конвенции находятся в других местах:
<svc.Dir>/src/для каждого сервиса — это обычный вложенный репозиторий (или worktree). Его.gitignore— забота приложения, а не DWE.- Директории
workspace/templates/<kind>.local/по конвенции gitignored. Резолвер packroot ищет их первыми и переходит к каноническому паку — это точка переопределений на разработчика для шаблонов IDE, AI и Git.
Никакого checkout, fetch или push
Заголовок раздела «Никакого checkout, fetch или push»DWE не выполняет Git-операций, о которых пользователь не просил:
- Он никогда не запускает
git checkout,git switch,git reset --hard,git stashили что-либо ещё, что могло бы потерять работу. - Он запускает только
git fetchиgit pull --ff-onlyиз пробы обновления, и только когда настроенupdate.mode: on(или передан--update on) и рабочее дерево чистое. - Он никогда не делает push. Единственное место, где может произойти push, — это написанные пользователем Git-хуки, которые пользователь сам положил в template-пак; хуки — это пользовательский код, запускаемый Git, а не DWE.
- Проба рабочего пространства строго read-only —
git status -b --porcelain=v2без флагов, которые могли бы изменить индекс.
В сочетании с принципом отсутствия сети (Архитектура → Без сети на штатном пути) это означает, что любой вызов dwe, кроме dwe run с update.mode: on, не выполняет вообще никаких удалённых Git-операций.
Что читать дальше
Заголовок раздела «Что читать дальше»dwe render git— полный справочник по полям рендеринга хуков: схема манифеста, шаблонные переменные, защитные проверки path-safety, выходные сообщения.services.<name>.render.git— активация на сервис, закрепление шаблона, наследование.workspace.yml→ блокupdate:— конфигурация пробы обновления.- Шаблоны — Go text template движок, реестры хелперов, строгий режим.
- Раскладка проекта — где
workspace/templates/git/,<svc.Dir>/src/и.dwe/располагаются в дереве проекта.