release: cut 1.0.0
Pin compose and local trial to ghcr.io/mixeme/selfpost:1.0.0, close the CHANGELOG cut, retire implementation-plan and v1.x-closure-plan, and point e2e/CI comments at development.md. Co-Authored-By: Composer <noreply@cursor.com> Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -7,7 +7,7 @@ name: release
|
||||
# can never drift apart (the invariant restore's version check in spec 7.5.A
|
||||
# depends on).
|
||||
#
|
||||
# Native per-architecture builds (plan implementation-plan.md C.4), not qemu:
|
||||
# Native per-architecture builds (see docs/development.md), not qemu:
|
||||
# running the full Postfix/OpenDKIM stack under emulation for the e2e gate
|
||||
# below is impractically slow. Each arch builds, e2e-gates and pushes its own
|
||||
# tag on its own native runner; a merge job then combines them into the one
|
||||
@@ -68,7 +68,7 @@ jobs:
|
||||
go-version: "1.26"
|
||||
cache-dependency-path: test/e2e/go.sum
|
||||
|
||||
- name: e2e (gates publishing — see docs/implementation-plan.md C.4)
|
||||
- name: e2e (gates publishing — see docs/development.md)
|
||||
run: cd test/e2e && go test -v -timeout 20m ./...
|
||||
|
||||
- name: Log in to ghcr.io
|
||||
|
||||
+11
-4
@@ -5,6 +5,8 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); version
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [1.0.0] - 2026-08-09
|
||||
|
||||
### Added
|
||||
|
||||
- A **Delivery log** on each delivery's page (`/deliveries/{id}`): the
|
||||
@@ -61,10 +63,10 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); version
|
||||
layering and route table match `web`→`store` and `POST /domains/import`;
|
||||
OpenDKIM drops to `opendkim` via `UserID`; guide drops the archived
|
||||
"spec 7.5" pointer, clarifies `POSTFIX_SENDER_LOGIN_MAPS` vs panel writes,
|
||||
and states logrotate keeps 14 daily files. Intermediate CHANGELOG cuts vs
|
||||
the still-pinned compose `0.1.0` image are called out in the guide and
|
||||
`development.md`. Roadmap / implementation-plan point at CHANGELOG
|
||||
`[0.5.0]` Security and refreshed `internal/web` size / symbol links.
|
||||
and states logrotate keeps 14 daily files. Intermediate CHANGELOG cuts from
|
||||
before the published `1.0.0` image are called out in the guide and
|
||||
`development.md`. Roadmap points at CHANGELOG `[0.5.0]` Security and
|
||||
refreshed `internal/web` size / symbol links.
|
||||
- Full backups no longer carry `/data/log`. It is Postfix's raw log plus its
|
||||
fourteen rotated copies — diagnostic output rather than state to restore, and
|
||||
otherwise by far the largest thing in the archive.
|
||||
@@ -94,6 +96,11 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); version
|
||||
dev-host-specific workflow and `mixfed.ru` references removed from docs.
|
||||
- `docs/development.md` and `.cursor/rules/agent-rules.mdc` translated to
|
||||
English; `roadmap.md` remains Russian (internal tracker).
|
||||
- Deploy pin and local-trial image tag set to `ghcr.io/mixeme/selfpost:1.0.0`
|
||||
(compose and git tag `v1.0.0` cut together). Retired
|
||||
`docs/implementation-plan.md` and `docs/v1.x-closure-plan.md`; Makefile,
|
||||
`release.yml`, and e2e comments point at `docs/development.md`. Roadmap
|
||||
v1.x documentation/deploy tail closed.
|
||||
|
||||
## [0.6.0] - 2026-08-08
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ test:
|
||||
clean:
|
||||
rm -rf bin
|
||||
|
||||
# Hermetic container e2e (plan implementation-plan.md C.4): separate Go module
|
||||
# Hermetic container e2e (see docs/development.md): separate Go module
|
||||
# under test/e2e so its test-only dependencies (DKIM verification) never enter
|
||||
# this module's build graph. Builds the image fresh from this checkout, brings
|
||||
# up deploy/docker-compose.yml plus a test-only override on high ports and an
|
||||
|
||||
@@ -92,7 +92,7 @@ docker run --rm -d --name selfpost-try \
|
||||
-e SELFPOST_HOSTNAME=mail.local.test \
|
||||
-e PANEL_COOKIE_SECURE=false \
|
||||
-v selfpost-try-data:/data \
|
||||
ghcr.io/mixeme/selfpost:0.1.0
|
||||
ghcr.io/mixeme/selfpost:1.0.0
|
||||
```
|
||||
|
||||
**Get the setup URL** (pick one):
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
|
||||
services:
|
||||
selfpost:
|
||||
image: ghcr.io/mixeme/selfpost:0.1.0
|
||||
image: ghcr.io/mixeme/selfpost:1.0.0
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
SELFPOST_HOSTNAME: "${SELFPOST_HOSTNAME:?set the mail/panel hostname, e.g. mail.example.com}"
|
||||
|
||||
+12
-23
@@ -1,8 +1,7 @@
|
||||
# SelfPost — development
|
||||
|
||||
**What this file is.** How to build, test, document, and ship changes. Open
|
||||
work for v1.x and 2.x lives in [roadmap.md](roadmap.md) (and, until the tag,
|
||||
[v1.x-closure-plan.md](v1.x-closure-plan.md)). Product boundaries:
|
||||
work for 2.x lives in [roadmap.md](roadmap.md). Product boundaries:
|
||||
[product.md](product.md). As-built layout: [architecture.md](architecture.md).
|
||||
|
||||
---
|
||||
@@ -12,10 +11,8 @@ work for v1.x and 2.x lives in [roadmap.md](roadmap.md) (and, until the tag,
|
||||
After `/clear` or a fresh chat:
|
||||
|
||||
1. Read this file (process, docs rules, model routing).
|
||||
2. Open [roadmap.md](roadmap.md) for open work; until `v1.0.0`, also
|
||||
[v1.x-closure-plan.md](v1.x-closure-plan.md) for the remaining closure
|
||||
checklist. Accepted risks — [security.md](security.md); as-built —
|
||||
[architecture.md](architecture.md).
|
||||
2. Open [roadmap.md](roadmap.md) for open work. Accepted risks —
|
||||
[security.md](security.md); as-built — [architecture.md](architecture.md).
|
||||
3. Skim [product.md](product.md) if scope is in doubt.
|
||||
4. Continue from the next unchecked step in the active plan.
|
||||
|
||||
@@ -31,7 +28,7 @@ not duplicated here.
|
||||
| Security, infra, file permissions, Postfix/`postqueue`, open-relay risk | **Opus** | `mail.log` under `/data`, entrypoint permissions, queue reconcile |
|
||||
| UI / JS / CSS, templates, documentation (English), README | **Sonnet** | adaptive polling, this file's Documentation section |
|
||||
| Trivial mechanics: retarget links, grep, compose bump, CHANGELOG cut | **Haiku** | Makefile / release.yml comment fixes, deleting closed plan files |
|
||||
| Security **review** (not authorship) | **Fable** | pre-release checklist pass ([implementation-plan.md](implementation-plan.md) § D — done) |
|
||||
| Security **review** (not authorship) | **Fable** | pre-release checklist (CHANGELOG `[0.5.0]` Security / [security.md](security.md) — done) |
|
||||
|
||||
Default rule: risk-critical → Opus; UI / docs / boilerplate → Sonnet; trivial
|
||||
mechanics → Haiku. Reviewers must not be the author of the code under review.
|
||||
@@ -154,20 +151,16 @@ The release image is published **only on tag** `vX.Y.Z` (not on every push to
|
||||
|
||||
**Steps (on explicit request):**
|
||||
|
||||
1. Close `[Unreleased]` in [CHANGELOG.md](../CHANGELOG.md).
|
||||
2. Create and push git tag `vX.Y.Z`.
|
||||
1. Close `[Unreleased]` in [CHANGELOG.md](../CHANGELOG.md) and bump the pinned
|
||||
tag in [deploy/docker-compose.yml](../deploy/docker-compose.yml) (and any
|
||||
local-trial image references) in the **same** release commit.
|
||||
2. Create and push git tag `vX.Y.Z` on that commit.
|
||||
3. Workflow [release.yml](../.github/workflows/release.yml) builds, e2e-gates,
|
||||
and publishes `ghcr.io/mixeme/selfpost:X.Y.Z`.
|
||||
4. Update the pinned tag in
|
||||
[deploy/docker-compose.yml](../deploy/docker-compose.yml) in the **same**
|
||||
commit as the tag (see [roadmap.md](roadmap.md) § «v1.x — documentation and
|
||||
deploy tail»).
|
||||
|
||||
Ordinary commits **do not** publish an image. Intermediate CHANGELOG version
|
||||
cuts (`0.2.0`…`0.6.0`) document history on `main`; the compose pin and the only
|
||||
git release tag may lag until an explicit image publish (today: compose
|
||||
`0.1.0`, tag `v0.0.1` — see [roadmap.md](roadmap.md) § «v1.x — documentation
|
||||
and deploy tail»).
|
||||
Ordinary commits **do not** publish an image. The compose pin and the git tag
|
||||
must match (`1.0.0` / `v1.0.0` for the first published release). Intermediate
|
||||
CHANGELOG sections (`0.2.0`…`0.6.0`) record development history before that cut.
|
||||
|
||||
---
|
||||
|
||||
@@ -280,13 +273,9 @@ There is no `docs/archive/` directory.
|
||||
| As-built design | [architecture.md](architecture.md) |
|
||||
| Development process (this file) | [development.md](development.md) |
|
||||
| Security requirements and accepted risks | [security.md](security.md) |
|
||||
| Internal roadmap (v1.x tail, 2.x) | [roadmap.md](roadmap.md) |
|
||||
| Internal roadmap (2.x) | [roadmap.md](roadmap.md) |
|
||||
| Release history | [CHANGELOG.md](../CHANGELOG.md) |
|
||||
|
||||
`implementation-plan.md` remains only until the release cut (describes the
|
||||
closed release gate); delete it in the release commit. Temporary
|
||||
[v1.x-closure-plan.md](v1.x-closure-plan.md) goes away with that cut too.
|
||||
|
||||
### User-facing deliverables
|
||||
|
||||
| Artefact | Role |
|
||||
|
||||
+8
-9
@@ -375,12 +375,11 @@ but it can look like an open port in external scans.
|
||||
## Fixed image tag
|
||||
|
||||
`deploy/docker-compose.yml` pins an explicit version (`ghcr.io/mixeme/selfpost:X.Y.Z`),
|
||||
deliberately never `:latest`. Until the `v1.0.0` cut the shipped pin is still
|
||||
`0.1.0` — intermediate CHANGELOG sections (`0.2.0`…`0.6.0`) record development
|
||||
cuts and do not imply a published image of that tag. Pinning matters because of
|
||||
the backup version check above: the panel binary's embedded version and the
|
||||
image tag that produced it are the same value by construction (the release CI
|
||||
stamps both from one git tag — see `.github/workflows/release.yml`), so the
|
||||
pin is what makes "restore into the same version" a checkable fact rather than
|
||||
a guess. Upgrade by bumping the tag deliberately, not by riding a moving
|
||||
target.
|
||||
deliberately never `:latest`. The current pin is `1.0.0`. Intermediate
|
||||
CHANGELOG sections (`0.2.0`…`0.6.0`) record development cuts from before that
|
||||
image was published. Pinning matters because of the backup version check above:
|
||||
the panel binary's embedded version and the image tag that produced it are the
|
||||
same value by construction (the release CI stamps both from one git tag — see
|
||||
`.github/workflows/release.yml`), so the pin is what makes "restore into the
|
||||
same version" a checkable fact rather than a guess. Upgrade by bumping the tag
|
||||
deliberately, not by riding a moving target.
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
# План реализации: SelfPost
|
||||
|
||||
**Статус:** линия v1.0/v1.x до тега релиза **закрыта** — предрелизная ревизия
|
||||
безопасности (§ D) выполнена 2026-08-06, релизный гейт (e2e + ревизия) открыт.
|
||||
B.1–B.3 и C.4 закрыты — as-built в [architecture.md](architecture.md),
|
||||
e2e/CI в [development.md](development.md), принятые риски в
|
||||
[security.md](security.md). Текущее состояние и следующий шаг:
|
||||
[roadmap.md](roadmap.md) и [v1.x-closure-plan.md](v1.x-closure-plan.md).
|
||||
Объём 2.x.x — [roadmap.md](roadmap.md).
|
||||
|
||||
**Основа:** [product.md](product.md) v1.0.
|
||||
|
||||
---
|
||||
|
||||
## D. Предрелизная ревизия безопасности — ВЫПОЛНЕНО (2026-08-06)
|
||||
|
||||
**Проверка моделью Fable** по дифу от аудита v1.0 (Фаза 11, `bd64e80` — тега
|
||||
`v1.0.0` в репозитории нет, это его фактический эквивалент) до HEAD, плюс
|
||||
полный повторный проход по чек-листу безопасности (бывшее ТЗ 7.6, теперь
|
||||
[security.md](security.md)). Приоритеты из плана покрыты: аутентификация и
|
||||
сессии, валидация ввода, запись в конфиги/map-файлы, `os/exec`, права в
|
||||
`/data`, секреты.
|
||||
|
||||
**Результат.** Эксплуатируемых уязвимостей (high/medium) не найдено. Одна
|
||||
находка defence-in-depth закрыта правкой до тега: логин приложения передаётся
|
||||
в `saslpasswd2` после `--`, чтобы значение, начинающееся с `-` (допустимо
|
||||
whitelist'ом), не могло быть разобрано getopt как флаг
|
||||
([internal/app/sasl.go](../internal/app/sasl.go)). Принятые риски в
|
||||
[security.md](security.md) не пополнились — существующие записи (origin-check
|
||||
fallback, отсутствие CSRF-токенов, ложно-отрицательный `bounced` при сверке
|
||||
с `postqueue`) покрывают всё найденное.
|
||||
Сводка ревизии — в записи `Security` CHANGELOG `[0.5.0]`.
|
||||
+12
-71
@@ -11,84 +11,25 @@
|
||||
решением.
|
||||
|
||||
**Основа:** [product.md](product.md) v1.0. Процесс и правила документации —
|
||||
[development.md](development.md). Несделанное для v1.0/v1.x до тега — в
|
||||
[implementation-plan.md](implementation-plan.md) и
|
||||
[v1.x-closure-plan.md](v1.x-closure-plan.md). Хвост закрытого
|
||||
документационного прохода (D1–D9) — в секции ниже.
|
||||
[development.md](development.md). История закрытых фаз v1.x — в `git log` и
|
||||
[CHANGELOG.md](../CHANGELOG.md).
|
||||
|
||||
---
|
||||
|
||||
## v1.x — хвост документации и деплоя
|
||||
|
||||
**Статус:** не блокирует релизный тег; бывший хвост закрытого
|
||||
документационного прохода (D1–D9). Делать по желанию или в релизном коммите,
|
||||
где указано. Сводка чек-листов до тега —
|
||||
[v1.x-closure-plan.md](v1.x-closure-plan.md).
|
||||
**Статус: закрыто** в релизе `1.0.0` / git-тег `v1.0.0`
|
||||
(`ghcr.io/mixeme/selfpost:1.0.0`). План закрытия и `implementation-plan.md`
|
||||
удалены — история в git и CHANGELOG; `docs/archive/` не храним.
|
||||
|
||||
**Тег образа в compose + git tag — один релизный коммит (R1).** В
|
||||
[deploy/docker-compose.yml](../deploy/docker-compose.yml) поле `image:` бампить
|
||||
до версии релиза **в том же коммите**, что и git-тег `vX.Y.Z` — не раньше.
|
||||
Сейчас там `0.1.0`, то есть отстаёт от целевой версии; несовпадение мешает
|
||||
только до первого выката по тегу. Сам тег — последний шаг релизного гейта:
|
||||
содержательная часть (e2e C.4, ревизия § D) закрыта, режется по явной команде
|
||||
оператора ([development.md](development.md) § Commits and release build). После
|
||||
тега `release.yml` собирает и публикует `ghcr.io/mixeme/selfpost:X.Y.Z`,
|
||||
поэтому compose с новым тегом и сам тег обязаны появиться вместе — иначе
|
||||
compose неделю ссылается на несуществующий образ.
|
||||
| Тема | Итог |
|
||||
|---|---|
|
||||
| Адаптивный опрос мониторинга | 5 с / 30 с / 0 (скрытая вкладка) в `panel.js` |
|
||||
| `mail.log` + reconcile | `/data/log/mail.log`; сверка с `postqueue -p` |
|
||||
| Docs consolidation | процесс в [development.md](development.md); README Documentation |
|
||||
| Compose pin + git tag | `1.0.0` / `v1.0.0` в одном релизном коммите |
|
||||
|
||||
**Убрать `implementation-plan.md` — в релизном коммите.** Документ закрыт:
|
||||
уникального содержания в нём нет, § D (предрелизная ревизия безопасности)
|
||||
продублирован в [security.md](security.md) и CHANGELOG `[0.5.0]/Security`,
|
||||
а разделы B.1–B.3 и C.4 вырезаны ещё в `22f86d1`. Держится до тега только
|
||||
потому, что описывает релизный гейт, пока тот формально не закрыт. При резке
|
||||
версии:
|
||||
|
||||
1. Удалить файл (история § D — в git и CHANGELOG; `docs/archive/` не храним).
|
||||
2. Перецелить ссылки из кода и CI ([Makefile](../Makefile),
|
||||
[.github/workflows/release.yml](../.github/workflows/release.yml),
|
||||
[test/e2e/main_test.go](../test/e2e/main_test.go)) — они ссылаются на «план
|
||||
C.4», секцию, которой в файле уже нет; актуальное описание e2e — в
|
||||
[development.md](development.md).
|
||||
3. Перецелить оставшиеся ссылки из документации на
|
||||
[development.md](development.md) / [security.md](security.md) /
|
||||
[roadmap.md](roadmap.md).
|
||||
4. Удалить [v1.x-closure-plan.md](v1.x-closure-plan.md) в том же или следующем
|
||||
коммите.
|
||||
|
||||
**Готово, когда:** тег образа в compose совпадает с релизом и рядом стоит
|
||||
git-тег `vX.Y.Z`; `implementation-plan.md` и `v1.x-closure-plan.md` удалены,
|
||||
ссылок на них в активных документах и в коде/CI не осталось.
|
||||
|
||||
(Закрыто и действия не требует: `docs/logo` как каталога нет — критерию «либо
|
||||
содержит файлы, либо отсутствует» удовлетворяет; Quick start в
|
||||
[README.md](../README.md) тянет `docker-compose.yml` и `.env.example` с
|
||||
`raw.githubusercontent.com` — это и есть единственная площадка проекта, зеркал
|
||||
больше нет.)
|
||||
|
||||
**Сводный индекс документации в README.** ~~Ссылки на `docs/` разбросаны по
|
||||
тексту README…~~ **Закрыто (v1.x-closure Фаза 3):** секция Documentation в
|
||||
[README.md](../README.md) — единый список operator docs + roadmap.
|
||||
|
||||
**Опрос мониторинга у открытой, но незанятой вкладки.** ~~Скрытая вкладка уже не
|
||||
опрашивает сервер (фильтр на `htmx:beforeRequest` в
|
||||
[panel.js](../internal/web/static/panel.js)). Остаток: вкладка на переднем
|
||||
плане, с которой не работают, всё равно ходит раз в 5 с. Кандидат — адаптивный
|
||||
интервал (5 с при активности, 30 с при простое) по `htmx:afterRequest` без
|
||||
изменения `hx-trigger`. Ценность низкая: нагрузка — один SQL-запрос и рендер
|
||||
фрагмента, так что это скорее гигиена, чем экономия. Допустимый исход —
|
||||
осознанно не делать.~~ **Закрыто (v1.x-closure Фаза 1):** адаптивный интервал
|
||||
5 с / 30 с / 0 (скрытая вкладка) в `panel.js` через `data-poll`.
|
||||
|
||||
**Send-log vs `mail.log`.** ~~Persist позиции чтения сделан (таблица
|
||||
`logtail_state`, миграция `0003`): после рестарта панели log-tailer дочитывает
|
||||
пропущенный хвост. Остаётся пересоздание контейнера — `mail.log` не в `/data` и
|
||||
теряется вместе с ним, такие строки навсегда останутся `queued`. Кандидаты, если
|
||||
станет больно: volume для лога, сверка зависших строк через `postqueue`.~~
|
||||
**Закрыто (v1.x-closure Фаза 2):** сделаны оба кандидата — `mail.log` переехал в
|
||||
`/data/log/`, а строки, чьи delivery-строки потеряны безвозвратно, закрываются
|
||||
сверкой с `postqueue -p` (grace 2 мин → `bounced`). As-built и оставшийся риск
|
||||
(ложный `bounced`): [architecture.md](architecture.md) § Log tailer,
|
||||
[security.md](security.md).
|
||||
Открытая работа дальше — только секции 2.x ниже.
|
||||
|
||||
---
|
||||
|
||||
|
||||
+4
-4
@@ -94,8 +94,8 @@ Hardening сверх обязательного (security-заголовки, п
|
||||
|
||||
## Принятые риски
|
||||
|
||||
Здесь, а не в [implementation-plan.md](implementation-plan.md): план — про
|
||||
несделанную работу, принятый риск — решение с условием возврата.
|
||||
Принятый риск — решение с условием возврата, а не отложенная задача из
|
||||
дорожной карты.
|
||||
|
||||
- **`POST` без `Sec-Fetch-Site` и без `Origin` пропускается.**
|
||||
Клиент, не посылающий ни одного из двух — по-настоящему старый браузер или
|
||||
@@ -171,7 +171,7 @@ Origin-проверка закрывает это без изменения ни
|
||||
|
||||
## Как этот список пополняется
|
||||
|
||||
Предрелизная проверка на уязвимости ([implementation-plan.md](implementation-plan.md)
|
||||
§ D, модель Fable) закрывает каждую находку одним из двух способов: правка до
|
||||
Предрелизная проверка на уязвимости (модель Fable; история — CHANGELOG
|
||||
`[0.5.0]` Security) закрывает каждую находку одним из двух способов: правка до
|
||||
тега — либо запись сюда, с обоснованием и условием возврата, как у пунктов выше.
|
||||
Третьего варианта («посмотрели и ладно») нет.
|
||||
|
||||
@@ -1,209 +0,0 @@
|
||||
# План закрытия хвоста v1.x
|
||||
|
||||
**Статус:** в работе. **Целевой релиз:** `v1.0.0` / `ghcr.io/mixeme/selfpost:1.0.0`.
|
||||
|
||||
**Временный файл:** после закрытия v1.x удалить (история — git + CHANGELOG). Не часть постоянного пакета docs.
|
||||
|
||||
**Контекст:** релизный гейт по коду закрыт (B.1–B.3, C.4 e2e, § D Fable). Остаток — [roadmap.md](roadmap.md) § «v1.x — хвост документации и деплоя» + код из того же секции.
|
||||
|
||||
**Политика docs:** `docs/archive/` не храним; устаревшие планы удаляем после переноса полезного в [development.md](development.md).
|
||||
|
||||
---
|
||||
|
||||
## Рекомендация по модели
|
||||
|
||||
Общее правило (переносится в development.md):
|
||||
|
||||
| Тип работы | Модель | Примеры в этом плане |
|
||||
|------------|--------|---------------------|
|
||||
| Безопасность, инфра, права файлов, postfix/postqueue, open-relay-риски | **Opus** | mail.log в `/data`, entrypoint permissions, postqueue reconcile |
|
||||
| UI/JS/CSS, шаблоны, документация (English), README | **Sonnet** | adaptive polling, development.md § Documentation, README index |
|
||||
| Тривиальная механика: retarget ссылок, grep, compose bump, CHANGELOG cut | **Haiku** | Makefile/release.yml комментарии, удаление файлов |
|
||||
| Ревизия безопасности (**review**, не authorship) | **Fable** | не в этом плане (§ D уже выполнен) |
|
||||
|
||||
| Фаза / коммит | Модель | Почему |
|
||||
|---------------|--------|--------|
|
||||
| 1. Polling | Sonnet | panel.js + HTMX, CSP |
|
||||
| 2. mail.log + reconcile | Opus (+ Sonnet на unit-тесты) | postfix path, logrotate, entrypoint, postqueue |
|
||||
| 3. Docs consolidation | Sonnet | development.md, README, agent-rules |
|
||||
| 4. Release `1.0.0` | Haiku / Sonnet | механика релиза, без новой логики |
|
||||
| 5. Tag / push | **Оператор** | явная команда |
|
||||
|
||||
Сообщение коммита: трейлер `Co-Authored-By: Claude <модель> <noreply@anthropic.com>` с моделью, которая делала шаг.
|
||||
|
||||
---
|
||||
|
||||
## Общий чек-лист прогресса
|
||||
|
||||
- [x] **Фаза 1** — адаптивный опрос мониторинга
|
||||
- [x] **Фаза 2** — mail.log в `/data` + postqueue reconcile
|
||||
- [x] **Фаза 3** — docs: development.md, README, удаление планов и `docs/archive/`
|
||||
- [ ] **Фаза 4** — релизный коммит `1.0.0` (по явной команде)
|
||||
- [ ] **Фаза 5** — tag `v1.0.0` + push (по явной команде)
|
||||
- [ ] **Фаза 6** — выкат на прод (оператор)
|
||||
|
||||
**Гейт перед тегом:**
|
||||
|
||||
- [ ] `gofmt -l .` чистый
|
||||
- [ ] `go vet ./...` чистый
|
||||
- [ ] `go test ./...` чистый (Windows-падения domain/logtail — известны, не блокер)
|
||||
- [ ] `make e2e` зелёный (если Docker доступен)
|
||||
- [ ] grep: нет ссылок на удалённые docs в живых файлах
|
||||
|
||||
---
|
||||
|
||||
## Фаза 1 — Адаптивный опрос мониторинга
|
||||
|
||||
**Модель:** Sonnet.
|
||||
|
||||
**Цель:** 5 s при активности, 30 s при простое; скрытая вкладка — 0 запросов. Без `hx-trigger="every 5s [expr]"` (CSP / `unsafe-eval`).
|
||||
|
||||
### Чек-лист
|
||||
|
||||
- [x] `status_body.html`, `mail_queue_body.html`, `system_log_body.html`, `deliveries_rows.html` — `hx-trigger="load"` + маркер polling
|
||||
- [x] `panel.js` — `lastActivity`, schedule после `htmx:afterRequest` (5s / 30s)
|
||||
- [x] `htmx:afterSwap` — реинициализация polling-элементов
|
||||
- [x] `beforeRequest` для hidden tab — оставлен
|
||||
- [x] `architecture.md` — одна строка про polling
|
||||
- [x] CHANGELOG `[Unreleased]`
|
||||
- [ ] Стенд (опционально): idle → ~30s; активность → ~5s; hidden → 0 запросов
|
||||
|
||||
---
|
||||
|
||||
## Фаза 2 — Send-log vs `mail.log`
|
||||
|
||||
**Модель:** Opus (infra); Sonnet (тесты).
|
||||
|
||||
### 2.1 `mail.log` → `/data/log/mail.log`
|
||||
|
||||
- [x] `build/postfix-config.sh` — `maillog_file` из `MAIL_LOG`
|
||||
- [x] `build/logrotate-mail.conf` — путь + `create 0640 postfix selfpost`
|
||||
- [x] `build/entrypoint.sh` — `mkdir`, права (`2750 postfix:selfpost`, файл `0640`),
|
||||
исключение `/data/log` из общего `chown` на `panel`
|
||||
- [x] `cmd/panel/main.go` — default `MAIL_LOG`
|
||||
- [x] `internal/backup` — исключить `log/` из архива (+ тест)
|
||||
- [x] `test/e2e/mail_helpers.go` — путь в контейнере
|
||||
- [x] `guide.md` — `MAIL_LOG`, System log, бэкап `./data`; README — рост диска
|
||||
- [x] `envdoc_test` — `MAIL_LOG` в `buildScriptKeys`
|
||||
|
||||
### 2.2 Postqueue reconcile
|
||||
|
||||
- [x] `internal/postfix` — `QueueIDs` / парсер queue-id из `postqueue -p` + тест
|
||||
- [x] `internal/store` — `ListQueuedOlderThan(cutoff)`
|
||||
- [x] `internal/logtail` — periodic sweep (5 min), grace 2 min → `bounced`,
|
||||
старт только после того, как tailer дочитал лог до конца
|
||||
- [x] Тесты reconcile
|
||||
|
||||
### 2.3 Документация и риски
|
||||
|
||||
- [x] `architecture.md` — `/data/log`, reconcile; gap «container recreate» убран
|
||||
- [x] `security.md` — риск «вечный queued» снят; на его месте — ложный `bounced`
|
||||
и права на лог
|
||||
- [x] CHANGELOG `[Unreleased]`
|
||||
|
||||
**Стенд (остаток, для оператора):** Docker на машине разработки недоступен —
|
||||
сборка образа и старт контейнера не проверены. Проверить при выкате: `/data/log`
|
||||
создаётся с нужными правами, панель читает `mail.log`, logrotate проворачивает
|
||||
файл, `postqueue -p` читается из-под `panel`.
|
||||
|
||||
---
|
||||
|
||||
## Фаза 3 — Документация
|
||||
|
||||
**Модель:** Sonnet.
|
||||
|
||||
### 3.1 `documentation-plan.md` → development.md, затем delete
|
||||
|
||||
- [x] development.md § **Documentation**:
|
||||
- [x] Documentation map (без archive; история = git + CHANGELOG)
|
||||
- [x] User-facing deliverables
|
||||
- [x] Maintaining documentation (§3 правила)
|
||||
- [x] Verifying docs against code (полная таблица §2)
|
||||
- [x] `architecture.md` шапка → development.md
|
||||
- [x] Удалить `documentation-plan.md`
|
||||
- [x] Retarget `roadmap.md`
|
||||
|
||||
### 3.2 `progress.md` → development.md, затем delete
|
||||
|
||||
- [x] development.md § **Resuming work**
|
||||
- [x] development.md § **Model routing**
|
||||
- [x] development.md § **Commits** (слить с Release build)
|
||||
- [x] development.md § **Phase closure** (roadmap, не progress)
|
||||
- [x] `agent-rules.mdc` → development.md
|
||||
- [x] Удалить `progress.md`
|
||||
- [x] Retarget все ссылки
|
||||
|
||||
### 3.3 Удалить `docs/archive/`
|
||||
|
||||
- [x] Удалить `docs/archive/specification-v1.0.md`
|
||||
- [x] Удалить каталог `docs/archive/`
|
||||
- [x] Убрать ссылки на archive из живых docs (CHANGELOG историю ниже 0.6.0 не трогать)
|
||||
|
||||
### 3.4 README
|
||||
|
||||
- [x] § Documentation: operator docs + roadmap (internal, Russian)
|
||||
- [x] Без секции Archive
|
||||
|
||||
### 3.5 Проверка
|
||||
|
||||
- [x] `gofmt` / `vet` / `test`
|
||||
- [x] grep живых ссылок на удалённые файлы
|
||||
|
||||
---
|
||||
|
||||
## Фаза 4 — Релизный коммит R1
|
||||
|
||||
**Модель:** Haiku / Sonnet. **Только по явной команде оператора.**
|
||||
|
||||
- [ ] CHANGELOG: `[Unreleased]` → `[1.0.0] - дата`, новая `[Unreleased]`
|
||||
- [ ] `deploy/docker-compose.yml` → `ghcr.io/mixeme/selfpost:1.0.0`
|
||||
- [ ] Удалить `implementation-plan.md` (если ещё есть)
|
||||
- [ ] Makefile, `.github/workflows/release.yml`, `test/e2e/main_test.go` → development.md
|
||||
- [ ] `roadmap.md` — секция v1.x **закрыта**; убрать «переместить в archive»
|
||||
- [ ] Финальный grep ссылок
|
||||
- [ ] Удалить этот файл (`v1.x-closure-plan.md`) в том же или следующем коммите
|
||||
|
||||
---
|
||||
|
||||
## Фаза 5 — Tag и push
|
||||
|
||||
**Оператор.** Явная команда.
|
||||
|
||||
```sh
|
||||
git tag v1.0.0
|
||||
git push origin v1.0.0
|
||||
```
|
||||
|
||||
- [ ] `release.yml` — build amd64+arm64, e2e gate, push GHCR `1.0.0`
|
||||
- [ ] compose tag и git tag совпадают
|
||||
|
||||
---
|
||||
|
||||
## Фаза 6 — Прод (оператор)
|
||||
|
||||
- [ ] `docker compose pull` → `1.0.0`
|
||||
- [ ] Разлогин (cookie `__Host-`)
|
||||
- [ ] Reverse-proxy передаёт `Host`
|
||||
- [ ] `./data/log/` создался после upgrade
|
||||
|
||||
---
|
||||
|
||||
## Критерий «хвост v1.x закрыт»
|
||||
|
||||
- [ ] Все чек-листы фаз 1–4 отмечены
|
||||
- [ ] `compose` = `1.0.0`, тег `v1.0.0` в репо
|
||||
- [ ] Нет `implementation-plan.md`, `documentation-plan.md`, `progress.md`, `docs/archive/`, `v1.x-closure-plan.md`
|
||||
- [ ] Нет активных ссылок на удалённые пути
|
||||
- [ ] `development.md` — процесс + Documentation + model routing
|
||||
- [ ] `roadmap.md` — единственный internal tracker
|
||||
- [ ] Polling 5s/30s; mail.log в `/data`; reconcile; риск security снят
|
||||
|
||||
---
|
||||
|
||||
## Разбивка коммитов
|
||||
|
||||
1. **Polling** — panel.js, templates, architecture (+ CHANGELOG)
|
||||
2. **mail.log + reconcile** — build, logtail, store, postfix, docs (+ CHANGELOG)
|
||||
3. **Docs** — development.md, README, agent-rules; delete documentation-plan, progress, archive; retarget (+ CHANGELOG)
|
||||
4. **Release 1.0.0** — CHANGELOG cut, compose, delete implementation-plan, roadmap closure, delete `v1.x-closure-plan.md`
|
||||
5. **Tag/push** — оператор
|
||||
@@ -1,13 +1,13 @@
|
||||
// Package e2e is the hermetic container e2e gate (plan C.4): it drives the
|
||||
// shipped deploy/docker-compose.yml (plus a test-only override) exactly as an
|
||||
// Package e2e is the hermetic container e2e gate: it drives the shipped
|
||||
// deploy/docker-compose.yml (plus a test-only override) exactly as an
|
||||
// administrator and their applications would, so the class of failure unit
|
||||
// tests cannot see — broken container wiring — has one place to be caught
|
||||
// before an image is published.
|
||||
//
|
||||
// It is a separate module on purpose (see ../../docs/implementation-plan.md,
|
||||
// item C.4): `go test ./...` in the main module never pulls this in, and its
|
||||
// test-only dependencies (DKIM verification) never enter the shipped
|
||||
// binaries' build graph.
|
||||
// It is a separate module on purpose (see ../../docs/development.md):
|
||||
// `go test ./...` in the main module never pulls this in, and its test-only
|
||||
// dependencies (DKIM verification) never enter the shipped binaries' build
|
||||
// graph.
|
||||
package e2e
|
||||
|
||||
import (
|
||||
|
||||
Reference in New Issue
Block a user