# Откат на прежнюю версию

Откат — это замена метки образа в `docker-compose.yml` на прежнюю и перезапуск.
Образы не удаляют до того, как новая версия отработала хотя бы день: удалённый
образ придётся снова везти архивом.

## Имена, которые понадобятся

`docker compose` добавляет к именам имя проекта (`name: a4-server` в
`docker-compose.yml`), поэтому на машине они такие:

| что | имя |
|---|---|
| контейнеры | `a4-server-f1-1`, `a4-server-a4-1` |
| тома с рабочими копиями | `a4-server_f1-cabins`, `a4-server_a4-cabins` |

Проверить у себя:

```sh
docker compose ps                                  # контейнеры этого комплекта
docker volume ls --format '{{.Name}}' | grep cabins
```

Это важно: команда с коротким именем (`-v f1-cabins:/from`) **не ошибётся
вслух** — Docker молча создаст новый пустой том с таким именем, и резервная
копия получится пустой.

## Порядок

```sh
# 1. посмотреть, какие версии образов уже лежат на машине
docker images | grep -E 'f1-webhost|a4-webhost'

# 2. в docker-compose.yml заменить метку на прежнюю, например
#      image: f1-webhost:2026.3.0-gaa7d3405   →   image: f1-webhost:<прежняя>
#    и поднять заново
docker compose up -d

# 3. проверить, что поднялось именно то
curl http://127.0.0.1:8092/f1/health
curl http://127.0.0.1:8090/a4/health
```

В ответе `/health` смотрите время сборки половин (`version.managerBuiltAt` и
`version.coreBuiltAt`) — оно обязано совпасть с тем выпуском, на который
откатились. Поле `version.commit` в образе **пустое** (`null`) и это нормально:
`git` внутрь образа не кладётся. Точный коммит сборки лежит в самом образе:

```sh
# таблицы (F1)
docker compose exec f1 cat /app/build-identity.json
# документы (A4) — файл называется ИНАЧЕ
docker compose exec a4 cat /app/release-identity.json
```

У таблиц это `commit`, `branch` и `dirtyFiles` (ноль значит «собрано из чистого
дерева»), у документов — `commit_sha`, `origin_main_sha` и `tracked_source_clean`
(`true` значит то же самое). Имена файлов и полей у двух половин разные; обе
команды проверены на образах этого комплекта.
Если время сборки не то, Docker взял другой образ: укажите метку полностью, без
`latest`.

## Про рабочие копии (`volumes`)

Рабочие копии книг и документов лежат в томах `a4-server_f1-cabins` и
`a4-server_a4-cabins`. При откате они **остаются** — `docker compose up -d` их
не трогает.

**Совместимость томов между версиями мы не проверяли** — говорю это прямо,
вместо обещания. Том содержит рабочие копии открытых файлов, а не ваш архив:
исходные файлы всё время лежат в вашей системе, сервер их только забирает и
отдаёт. Поэтому безопасный откат такой:

```sh
# 1. дать людям закрыть документы (или дождаться --idle-min) и остановить
docker compose stop

# 2. отложить тома прежней версии, не удаляя (имена — с приставкой проекта!)
docker run --rm -v a4-server_f1-cabins:/from -v /srv/backup:/to alpine \
  tar -czf /to/f1-cabins-$(date +%F).tar.gz -C /from .
docker run --rm -v a4-server_a4-cabins:/from -v /srv/backup:/to alpine \
  tar -czf /to/a4-cabins-$(date +%F).tar.gz -C /from .

# 3. откатить метку и поднять
docker compose up -d
```

Если после откатa редактор не открывает файл, а в журнале видно жалобу на
рабочую копию, — очистите том и откройте файл заново из вашей системы:

```sh
docker compose logs f1        # журнал таблиц; документы — docker compose logs a4

# ВАЖНО: именно down, а не stop. После `stop` том ещё занят контейнером, и
# `docker volume rm` отвечает «volume is in use».
docker compose down
docker volume rm a4-server_f1-cabins     # рабочие копии, не ваш архив
docker compose up -d
```

## Чего откат не делает

- Не возвращает файлы, которые люди успели сохранить в вашей системе: там
  хранение и версии — ваши, сервер их не держит.
- Не меняет настройки: адреса `--auth-url` и `--attach-url` остаются те же.
