приложение собрано каркасом и вшито в бинарник
- заведён каталог web/ — Vue 3, роутер пятой версии, сборка Vite; собранное вшивается через go:embed и раздаётся корневым маршрутом: разметка на неизвестном пути вне корней сервиса, отказ контракта внутри корня - перечень корней сервиса стал единой точкой и порождает регистрацию маршрутов, а не описывает её; журнал раздачи пишет исход и длину пути, но не сам путь - шаг front зовёт Node контейнером docker — Biome, юнит-тесты Vue и сборка входят в гейт, а в Dockerfile появилась ступень приложения
This commit is contained in:
@@ -0,0 +1,147 @@
|
||||
## 1. Каталог приложения и его сборка
|
||||
|
||||
- [x] 1.1 Завести `web/` с `package.json`, файлом замка, настройкой Vite и
|
||||
`index.html`; выходной каталог сборщика — `web/embed/dist`
|
||||
- [x] 1.2 Команда сборки приложения гоняет проверку типов и сборку одним вызовом
|
||||
(`vue-tsc` входит в команду сборки, а не стоит отдельным шагом)
|
||||
- [x] 1.3 В git лежит `web/embed/.gitkeep` — этажом выше выходного каталога, чтобы
|
||||
очистка перед сборкой его не уносила; собранное и каталог зависимостей внесены
|
||||
в `.gitignore`
|
||||
- [x] 1.4 Написан экран-заглушка: показывает имя вошедшего, на `401` ведёт ко
|
||||
входу, на прочий отказ показывает строку о неудаче; одна таблица маршрутов
|
||||
через `createRouter`, адреса обычные (`createWebHistory`)
|
||||
- [x] 1.5 Обращение к API идёт через одну свою обёртку над `fetch`; она же
|
||||
единственное место, где читается код ответа
|
||||
- [x] 1.6 Заведён Biome: форматирование и статический анализ кода приложения.
|
||||
Не покрывает разметку однофайловых компонентов — сказать строкой и остановиться,
|
||||
замена инструмента решается человеком
|
||||
- [x] 1.7 Заведены юнит-тесты Vue: показ вошедшего, увод ко входу на `401`,
|
||||
строка о неудаче на прочем отказе, учётная запись без имени
|
||||
|
||||
## 2. Раздача приложения из бинарника
|
||||
|
||||
- [x] 2.1 `web/embed.go` вшивает `web/embed` целиком (`all:`), включая метку
|
||||
пустого каталога
|
||||
- [x] 2.2 Перечень корней сервиса заведён единой точкой в пакете транспорта:
|
||||
`/api`, `/app`, `/auth`, `/_` и отдельные адреса `/health`, `/metrics`
|
||||
- [x] 2.2.1 Наши маршруты вешаются **из перечня**: проба здоровья, метрики,
|
||||
группа приложения, группа входа. Корни библиотеки остаются её литералами и
|
||||
держатся своим тестом; второе перечисление адресов наблюдения в уровне журнала
|
||||
снято
|
||||
- [x] 2.3 Принадлежность корню — точное совпадение либо префикс вместе с косой
|
||||
чертой; оба условия проверяются
|
||||
- [x] 2.4 Обработчик приложения зарегистрирован маршрутом `/` (тем самым занимает
|
||||
место библиотечного отказа) и принимает файловую систему параметром
|
||||
- [x] 2.5 Порядок обработчика: корень сервиса или адрес наблюдения — отказ формой
|
||||
библиотеки; метод не `GET` и не `HEAD` — `405`; несовпавший путь под каталогом
|
||||
ресурсов — `404`; совпавший файл — файл; прочее — разметка
|
||||
- [x] 2.6 Заголовки кэширования назначены по каталогу: ресурсы сборщика — год и
|
||||
«неизменяемо», всё прочее, включая разметку, — спрашивать заново
|
||||
- [x] 2.7 Отсутствие собранного приложения даёт `503` с честной страницей и
|
||||
строку в журнале при подъёме, а не `404` молча; проба здоровья остаётся зелёной
|
||||
- [x] 2.8 При подъёме в журнал уходит отпечаток вшитой разметки
|
||||
- [x] 2.9 Строка журнала корневого маршрута несёт исход и длину пути, но не сам
|
||||
путь
|
||||
- [x] 2.10 Разметка и ресурсы отдаются без сессии; адреса данных под корнем
|
||||
приложения сессии по-прежнему требуют
|
||||
|
||||
## 3. Сборка и выкладка
|
||||
|
||||
- [x] 3.1 Шаг сборки приложения заведён в `Taskfile.yml` и стоит **первым** в
|
||||
наборе проверок, до сборки Go; рядом — шаги Biome и юнит-тестов приложения
|
||||
- [x] 3.1.1 Проверки приложения гоняются тем же контейнером, что и сборка;
|
||||
второго окружения не заводится
|
||||
- [x] 3.2 Шаг зовёт установщик и сборщик контейнером, а не из `PATH`: имя образа
|
||||
берётся из `Dockerfile`, контейнер ходит под вызвавшим пользователем, кэш
|
||||
установщика уведён в каталог под `/tmp`
|
||||
- [x] 3.3 Зависимости ставятся из файла замка командой, которая его не правит; ту
|
||||
же команду зовёт ступень `Dockerfile`
|
||||
- [x] 3.4 Недостающий docker и отказ сети или реестра — отказ окружения, код 3;
|
||||
красная сборка приложения — код 1
|
||||
- [x] 3.5 В `Dockerfile` добавлена ступень сборки приложения перед сборкой
|
||||
бинарника; в рабочий слой Node не попадает
|
||||
- [x] 3.6 Вес выяснен, время замера снято с задачи решением владельца
|
||||
2026-08-15: финальный образ от ступени приложения не растёт вовсе (Node в него
|
||||
не копируется), вшитое добавляет к бинарнику 86 072 байта. Время сборки
|
||||
остаётся неизвестным сознательно
|
||||
- [x] 3.7 Последствие ADR-2026-08-11-spa-on-vue поправлено: требованием к машине
|
||||
разработчика становится docker, а не Node
|
||||
|
||||
## 4. Проверки
|
||||
|
||||
- [x] 4.1 Тест: неизвестный путь вне корней отдаёт разметку с кодом `200`
|
||||
- [x] 4.2 Тест: неизвестный путь под каждым корнем сервиса разметки не отдаёт
|
||||
- [x] 4.3 Тест: голый корень без косой черты разметки не отдаёт, а посторонний
|
||||
путь, начинающийся именем корня без косой черты, — отдаёт
|
||||
- [x] 4.4 Тест: неизвестный путь под корнем приложения отвечает `404` с сессией и
|
||||
`401` без неё
|
||||
- [x] 4.5 Тест: несуществующий файл под каталогом ресурсов отвечает `404`
|
||||
- [x] 4.6 Тест: проба здоровья и метрики приложением не подменяются
|
||||
- [x] 4.7 Тест: чужой метод отвечает `405`, а `HEAD` на разметку — `200`
|
||||
- [x] 4.8 Тест: разметка и ресурс отдаются без сессии, а список записей — нет
|
||||
- [x] 4.9 Тест: у разметки стоит требование спрашивать заново, у ресурса из
|
||||
каталога сборщика — год и «неизменяемо»
|
||||
- [x] 4.10 Тест: пустая сборка даёт `503`, а не `404`, и проба здоровья при этом
|
||||
зелёная
|
||||
- [x] 4.11 Юнит-тесты приложения краснеют на сломанном экране: намеренная поломка
|
||||
показа вошедшего роняет набор проверок
|
||||
- [x] 4.12 `task gate` зелёный целиком
|
||||
|
||||
## 5. Документы
|
||||
|
||||
- [x] 5.1 `docs/conventions/web-ui.md` — снята оговорка «кода приложения ещё
|
||||
нет»; дописаны правила: отпечаток в именах ресурсов, несовпавший ресурс отвечает
|
||||
`404`, зависимости ставятся из файла замка
|
||||
- [x] 5.2 `docs/architecture.md` — компонент приложения, внешняя зависимость
|
||||
сборки, новая capability и перечень корней в единых точках
|
||||
- [x] 5.3 `docs/database.md` — срок хранения ресурсов в настройках с числовым
|
||||
значением
|
||||
- [x] 5.4 `CLAUDE.md` — команды и требования к машине разработчика, состав гейта
|
||||
вместе с проверками приложения, второй сетезависимый шаг названный поимённо
|
||||
- [x] 5.5 `docs/review.md` — заведён род узла «раздача собранной статики и шаг
|
||||
сборки приложения» в типовых узлах
|
||||
- [x] 5.6 `docs/conventions/go-linters.md` либо соседняя запись — чем проверяется
|
||||
код приложения: Biome и юнит-тесты, что каждый ловит и где настроен
|
||||
|
||||
## Критерии приёмки
|
||||
|
||||
Дословно из записи задачи `spa-skeleton`:
|
||||
|
||||
- Статика собирается одной командой и вшивается в бинарник: запущенный бинарник
|
||||
отдаёт приложение без каталога рядом. Оракул — сборка, запуск бинарника из
|
||||
пустого каталога, запрос корня возвращает разметку приложения.
|
||||
- Образ собирается на чистой машине без предустановленного окружения фронтенда.
|
||||
Оракул — `task image` без локально установленных зависимостей фронтенда.
|
||||
- Приложение показывает, кто вошёл, и обращается к API живого сервера. Оракул —
|
||||
тест либо ручная проверка на запущенном сервере с сессией.
|
||||
- Шаг сборки статики входит в `task gate` и краснеет при ошибке сборки. Оракул —
|
||||
намеренно сломанный исходник роняет `task gate`.
|
||||
- Обновление страницы на любом маршруте приложения открывает тот же экран, а
|
||||
адрес внутри корня сервиса в приложение не проваливается. Оракул — тест на три
|
||||
запроса: неизвестный путь вне корней отдаёт разметку, неизвестный путь внутри
|
||||
`/api/` и внутри `/app/` отдаёт ошибку контракта.
|
||||
|
||||
Рубрика ревью дизайна, тем же списком:
|
||||
|
||||
- Перечень корней живёт одной точкой, и добавление корня не требует правки в двух
|
||||
местах.
|
||||
- Запрос несуществующего файла под каталогом ресурсов отвечает `404`, а не
|
||||
разметкой с `200`.
|
||||
- После прогона сборки приложения `git status` чист по `web/`.
|
||||
- После `task gate` `git status` чист по файлу замка зависимостей.
|
||||
- Отсутствие сборки громкое: код ответа, страница и строка журнала; «сборки нет»
|
||||
отличается от «файла нет».
|
||||
- Долгий неотзываемый срок ставится только файлу из каталога ресурсов сборщика.
|
||||
- Разметка и ресурсы отдаются анонимно, данные под корнем приложения — нет; по
|
||||
анонимному ответу нельзя узнать, вошёл ли кто-то.
|
||||
- В бинарник попадает только собранное: ни карт исходников, ни каталога
|
||||
зависимостей, ни файлов вне выходного каталога.
|
||||
- Отпечаток вшитой сборки виден строкой журнала при подъёме.
|
||||
- Шаг сборки следует словарю кодов: недостающий инструмент и отказ сети — 3,
|
||||
красная сборка — 1; молчаливого пропуска нет.
|
||||
- Ступень сборки не протекает в рабочий слой образа, а версия сборочного
|
||||
окружения объявлена одним местом.
|
||||
- Файловая система строится один раз, а не на каждый запрос; поведение на `HEAD`
|
||||
определено.
|
||||
- Запрос на путь длиной в килобайт не добавляет значения ни метке метрики, ни
|
||||
строке журнала.
|
||||
Reference in New Issue
Block a user