320 lines
26 KiB
Markdown
320 lines
26 KiB
Markdown
# Инструкции для работы с репозиторием
|
||
|
||
## Обзор проекта
|
||
|
||
`iptvc` (IPTV Checker) — консольное приложение на Go для проверки IPTV-плейлистов в форматах M3U/M3U8. Программа умеет загружать плейлисты из локальных файлов, по URL и из ini-файла, проверять доступность плейлистов и отдельных каналов, выводить результаты в журнале или JSON, а также запускать встроенный веб-интерфейс.
|
||
|
||
Проект является частью экосистемы `m3u.su`. Исходный код размещается на Gitea: `https://git.axenov.dev/IPTV/iptvc`.
|
||
|
||
Основные технологии:
|
||
|
||
- Go 1.23.6 или новее согласно `go.mod` и README.
|
||
- Cobra для CLI-команд и флагов.
|
||
- YAML для конфигурации.
|
||
- INI для списка плейлистов.
|
||
- Кеш через `github.com/redis/go-redis/v9` для кеширования результатов.
|
||
- `godotenv` для автоматической загрузки `.env`.
|
||
- Docker и Docker Compose для контейнерного запуска.
|
||
- Встроенный HTTP-веб-сервер для просмотра плейлистов и результатов проверки.
|
||
|
||
## Структура репозитория
|
||
|
||
- `main.go` — точка входа, вызывает `cmd.Execute()`.
|
||
- `cmd/` — CLI-слой на Cobra:
|
||
- `root.go` — корневая команда, глобальные persistent-флаги (`--config`, `--verbose`, `--debug`, `--log-level`).
|
||
- `check.go` — команда `check`: одно-/многоитерационная проверка плейлистов с выводом в журнал или JSON.
|
||
- `serve.go` — команда `serve`: HTTP-веб-сервер, при необходимости запускает фоновую проверку через `--check` или `check.startOnServe`.
|
||
- `version.go` — команда `version`, печатает `iptvc v<VERSION>`.
|
||
- `flags.go` — определение общих флагов для `check`/`serve` и функции `applyAppOverrides`/`applyCacheOverrides`/`applyCheckOverrides` (используют `cmd.Flags().Changed()`, поэтому срабатывают только при явной передаче).
|
||
- `flags_test.go` — тесты применения CLI-переопределений к конфигурации.
|
||
- `app/` — прикладная логика приложения.
|
||
- `app/app.go` — инициализация приложения и глобальных структур `Args`/`Config`/`Cache`, точка входа `app.Init()`/`app.InitCache()`.
|
||
- `app/checker/` — подготовка и проверка плейлистов и каналов.
|
||
- `app/cache/` — абстракции и работа с кешем (через `go-redis/v9`).
|
||
- `app/config/` — структуры конфигурации, значения по умолчанию, YAML, переменные окружения и тесты.
|
||
- `app/inifile/` — чтение ini-файлов со списками плейлистов.
|
||
- `app/playlist/` — модели и обработка IPTV-плейлистов.
|
||
- `app/tagfile/` — чтение файла тегов каналов.
|
||
- `app/web/` — веб-сервер, маршруты, шаблоны, OpenAPI-спецификация и отображение данных (`server.go`, `handlers.go`, `views.go`, `templates.go`, `openapi.go`).
|
||
- `app/logger/` — настройка журналирования.
|
||
- `app/utils/` — вспомогательные функции.
|
||
- `docker/cache/` — конфигурация и данные для контейнера кеша (`valkey.conf`).
|
||
- `channels.json` — локальный файл тегов каналов.
|
||
- `playlists.ini.example` — пример ini-файла со списками плейлистов.
|
||
- `config.yml.example` — пример конфигурации приложения.
|
||
- `.env.example` — пример переменных окружения.
|
||
- `compose.yml` — окружение из приложения, документации и кеша.
|
||
- `Dockerfile` — минимальный runtime-образ на `alpine:3.22.5`; берёт уже собранный бинарь из `bin/linux_${TARGETARCH}/iptvc`.
|
||
- `Makefile` — очистка, кросс-компиляция бинарей и образов (см. раздел «Сборка и запуск»).
|
||
- `README.md` — пользовательская документация, команды запуска, конфигурация и API-маршруты.
|
||
- `LICENSE` — лицензия MIT.
|
||
|
||
## Архитектура и поток выполнения
|
||
|
||
1. `main.go` вызывает `cmd.Execute()`.
|
||
2. `cmd/root.go` регистрирует глобальные флаги и дочерние Cobra-команды.
|
||
3. Команда `check` инициализирует приложение, применяет CLI-переопределения, подключает кеш, получает плейлисты и запускает проверку.
|
||
4. Команда `serve` поднимает веб-сервер и при необходимости запускает фоновую проверку через флаг `--check` или настройку `check.startOnServe`.
|
||
5. Конфигурация загружается со следующим приоритетом: встроенные значения по умолчанию, `config.yml`, переменные окружения и явно переданные CLI-флаги.
|
||
6. Результаты проверки могут сохраняться в кеш и отображаться веб-сервером. При недоступном кеше веб-интерфейс показывает неизвестный статус.
|
||
|
||
Не следует помещать бизнес-логику в `main.go` или CLI-команды, если её можно разместить в соответствующем пакете `app/`. CLI-слой должен отвечать за разбор аргументов, инициализацию и координацию прикладных компонентов.
|
||
|
||
## Сборка и запуск
|
||
|
||
Для локальной разработки требуется Go версии 1.23.6 или новее. `Dockerfile` использует `alpine:3.22.5` как базовый runtime-образ; бинарный файл собирается на хосте через `go build` и копируется в образ.
|
||
|
||
Основные команды:
|
||
|
||
```bash
|
||
# Показать доступные цели Makefile
|
||
make help
|
||
|
||
# Быстро запустить приложение из исходников
|
||
go run .
|
||
|
||
# Запустить CLI-справку
|
||
go run . help
|
||
|
||
# Проверить локальный плейлист
|
||
go run . check -f mypls.m3u
|
||
|
||
# Проверить плейлист по URL
|
||
go run . check -u https://example.com/playlist.m3u
|
||
|
||
# Запустить веб-интерфейс на порту 8800
|
||
go run . serve -p 8800
|
||
|
||
# Запустить веб-интерфейс с фоновой проверкой
|
||
go run . serve -i playlists.ini -p 8800 --check
|
||
|
||
# Собрать текущую платформу
|
||
go build -o iptvc .
|
||
|
||
# Запустить тесты всех пакетов
|
||
go test ./...
|
||
|
||
# Проверить форматирование
|
||
|
||
gofmt -w .
|
||
```
|
||
|
||
В блоке выше команда форматирования приведена как ориентир: `gofmt` следует запускать по Go-файлам, например `gofmt -w main.go cmd app`, если оболочка или используемая версия инструмента не поддерживает обработку директорий так, как ожидается.
|
||
|
||
Цели Makefile:
|
||
|
||
- `make clear` — удалить каталог `bin/` с артефактами сборки.
|
||
- `make linux GOARCH=amd64` — собрать Linux-бинарный файл и ZIP-архив.
|
||
- `make win GOARCH=amd64` — собрать Windows-бинарный файл и ZIP-архив.
|
||
- `make darwin GOARCH=amd64` — собрать macOS-бинарный файл и ZIP-архив.
|
||
- `make release` — собрать архивы для Linux, Windows и macOS под `amd64` и `arm64`.
|
||
- `make test` — запустить все тесты (`go test ./...`).
|
||
- `make image` — собрать одноархитектурный runtime-образ `git.axenov.dev/iptv/iptvc:latest` (по умолчанию под `linux/amd64`; переопределяется через `GOARCH`).
|
||
- `make image-all` — собрать и запушить multi-arch манифест в registry (по умолчанию `linux/amd64,linux/arm64`; переопределяется через `IMAGE_PLATFORMS`).
|
||
|
||
По умолчанию `GOARCH=amd64`. Поддерживаемые значения: `amd64` и `arm64`.
|
||
|
||
**Бинарники собираются на хосте через `go build`** с кросс-компиляцией (`CGO_ENABLED=0` + `GOOS`/`GOARCH`). Это справедливо для всех шести целевых платформ (linux/darwin/windows × amd64/arm64) и работает одинаково на любом хосте с Go 1.23+.
|
||
|
||
**Образы собираются в Docker** через `docker build` (одноархитектурный) или `docker buildx build --platform ... --push` (multi-arch). `Dockerfile` берёт уже собранный бинарь из build context (`bin/linux_${TARGETARCH}/iptvc`), а не собирает его сам — поэтому build context должен содержать нужный бинарь перед запуском `make image`/`make image-all`. `make image` и `make image-all` автоматически зависят от `make linux`, чтобы контекст был полным.
|
||
|
||
**Multi-arch образы (`make image-all`)** используют `docker buildx` и **всегда пушат** результат в registry. Это ограничение buildx: multi-arch манифест нельзя положить в локальный Docker daemon (`--load` не работает с несколькими платформами одновременно). Если нужен только локальный образ одной архитектуры — используйте `make image`.
|
||
|
||
Сжатие UPX не используется намеренно: UPX ломает нотаризацию Mach-O на современных macOS и снижает надёжность запуска Windows-бинарей.
|
||
Экономия от `-ldflags="-s -w"` (~30%) сохраняется.
|
||
|
||
Версия и коммит вкомпилируются в бинарь через `-ldflags="-X axenov/iptv-checker/app.VERSION=... -X axenov/iptv-checker/app.COMMIT=..."`; переменные берутся из `git describe` и `git rev-parse` на хосте в момент запуска `make`.
|
||
|
||
### Сборка конкретной версии (по тегу или коммиту)
|
||
|
||
`Makefile` всегда берёт код из текущей рабочей копии (`COPY . .` в Dockerfile) и версию из `git describe`/`git rev-parse` **на хосте в момент запуска**. Поэтому, чтобы собрать произвольную версию, нужно сначала переключить рабочую копию на нужный тег или коммит, а **затем** запустить `make`.
|
||
|
||
`Makefile` сам не делает `git checkout` и не использует `git archive` — это осознанный выбор. Сборка остаётся детерминированной относительно того, что лежит в репозитории, а не относительно того, что пользователь хотел собрать. Любые попытки "обмануть" мету через `make VERSION=v1.0.0` без `git checkout` приведут к тому, что в бинарь попадёт код текущей ветки с чужой версией в ldflags.
|
||
|
||
#### Пример: собрать релиз по тегу `v1.0.0`
|
||
|
||
```bash
|
||
# 1. Убедиться, что рабочая копия чистая (без незакоммиченных правок).
|
||
# Иначе `git checkout` откажется переключаться.
|
||
git status
|
||
|
||
# 2. Запомнить, на чём мы были, чтобы вернуться.
|
||
git rev-parse --abbrev-ref HEAD # например: main
|
||
git rev-parse --short HEAD # например: 0de883f
|
||
|
||
# 3. Переключиться на нужный тег. Сборка по тегу даёт точно код этой версии.
|
||
git checkout v1.0.0
|
||
|
||
# 4. Собрать всё. VERSION и COMMIT будут вычислены из тега автоматически.
|
||
make release
|
||
|
||
# 5. Проверить, что в бинаре именно та версия.
|
||
./bin/linux_amd64/iptvc version # ожидаем: iptvc v1.0.0
|
||
|
||
# 6. Вернуться туда, где были.
|
||
git checkout main # или: git checkout 0de883f
|
||
```
|
||
|
||
#### Пример: собрать по конкретному коммиту
|
||
|
||
```bash
|
||
git checkout 0de883f
|
||
make release
|
||
git checkout main
|
||
```
|
||
|
||
#### Сборка в отдельной worktree (рекомендуется)
|
||
|
||
Переключение ветки в основной рабочей копии неудобно: IDE перезагружает индекс, открытые файлы могут стать невалидными, локальные правки блокируют checkout. Лучше использовать `git worktree` — это отдельная директория с привязкой к конкретному коммиту, основная рабочая копия не трогается.
|
||
|
||
```bash
|
||
# 1. Создать временный worktree на теге v1.0.0.
|
||
git worktree add /tmp/iptvc-v1.0.0 v1.0.0
|
||
|
||
# 2. Собрать там.
|
||
cd /tmp/iptvc-v1.0.0
|
||
make release
|
||
|
||
# 3. Убедиться, что бинарь содержит нужную версию.
|
||
./bin/linux_amd64/iptvc version # ожидаем: iptvc v1.0.0
|
||
|
||
# 4. Убрать worktree, когда он больше не нужен.
|
||
cd /path/to/main/repo
|
||
git worktree remove /tmp/iptvc-v1.0.0
|
||
```
|
||
|
||
Этот способ безопаснее `git checkout`: основная рабочая копия и все незакоммиченные изменения остаются нетронутыми.
|
||
|
||
#### Проверка, что собрано то, что нужно
|
||
|
||
После сборки в бинаре зашиты `app.VERSION` и `app.COMMIT`, которые выводятся командой `version`:
|
||
|
||
```bash
|
||
./bin/linux_amd64/iptvc version
|
||
# iptvc v1.0.0
|
||
```
|
||
|
||
Если версия не совпадает с ожидаемой — значит, рабочая копия была не на том коммите. Перепроверьте `git log -1 --oneline` в каталоге, из которого запускался `make release`.
|
||
|
||
#### Частые ошибки
|
||
|
||
- **"build failed: COPY failed: uncommitted changes"** — забыли `git status`. `COPY . .` в Dockerfile включает только файлы, отслеживаемые git'ом, но локальные изменения в `Dockerfile`/`go.mod` и т.п. могут запутать. Всегда делайте `git status` перед сборкой чужой версии.
|
||
- **"Permission denied" в `bin/`** — каталог `bin/` остался от предыдущей попытки с `--output=local` или от работы под другим пользователем. Решение: `make clear` (или `rm -rf bin`).
|
||
- **Бинарь собран, но версия "dev"/"unknown"** — скорее всего, вы в свежем клоне без тегов. Выполните `git fetch --tags` и повторите `git checkout v1.0.0`.
|
||
|
||
Перед запуском приложения для конфигурации обычно следует скопировать `.env.example` в `.env`, а `config.yml.example` — в `config.yml`, после чего адаптировать значения под окружение. Не добавляйте реальные пароли, токены или локальные URL в Git.
|
||
|
||
## Docker
|
||
|
||
Для контейнерного окружения используется файл `compose.yml`, а не устаревшее имя `docker-compose.yml`.
|
||
|
||
Ожидаемые локальные файлы для сервиса `iptvc`:
|
||
|
||
- `playlists.ini` монтируется в `/app/playlists.ini`.
|
||
- `channels.json` монтируется в `/app/channels.json`.
|
||
- `config.yml` монтируется в `/app/config.yml`.
|
||
- `.env` передаётся через `env_file`.
|
||
|
||
Основные команды:
|
||
|
||
```bash
|
||
# Собрать и запустить сервисы в фоне
|
||
docker compose up -d --build
|
||
|
||
# Посмотреть логи приложения
|
||
docker compose logs -f iptvc
|
||
|
||
# Остановить окружение
|
||
docker compose down
|
||
```
|
||
|
||
В compose-окружение входят:
|
||
|
||
- `iptvc` — веб-интерфейс и фоновая проверка плейлистов.
|
||
- `cache` — хранилище результатов проверки.
|
||
- `docs` — отдельный сервис документации, если его контекст доступен в окружении.
|
||
|
||
Обратите внимание: текущий `compose.yml` ссылается на контексты `./iptvc` и `./docs`, поэтому запуск из каталога, содержащего только этот репозиторий, может потребовать внешней структуры проекта или корректировки контекстов.
|
||
|
||
## Конфигурация
|
||
|
||
Поддерживаются `config.yml`, `.env` и CLI-флаги. CLI-флаги имеют наивысший приоритет только при явной передаче.
|
||
|
||
### Источники правды
|
||
|
||
Единственными источниками правды для значений по умолчанию являются `config.yml.example` и `.env.example`. Значения, встроенные в код (`app/config/config.go` → `defaults()`, `validate()`), и дефолты CLI-флагов (`cmd/flags.go`, `cmd/root.go`, `cmd/serve.go`) обязаны **полностью совпадать** с этими файлами.
|
||
|
||
Порядок внесения изменений в конфигурацию:
|
||
|
||
1. **Сначала** — правки в `config.yml.example` и `.env.example`.
|
||
2. **Затем** — правки в коде: `app/config/config.go` (`defaults()`, константы, `validate()`), `cmd/flags.go`, `cmd/root.go`, `cmd/serve.go`.
|
||
3. **Затем** — обновление тестов (`app/config/config_test.go`, `cmd/flags_test.go`).
|
||
4. **Затем** — обновление документации (`README.md`, `docs/content/...`).
|
||
|
||
При рефакторинге и работе с документацией сверяйтесь в первую очередь с `config.yml.example` и `.env.example`, а не с кодом или тестами. Если обнаружено расхождение между example-файлами и кодом — example-файлы считаются эталоном, код приводится к ним.
|
||
|
||
Ключевые группы настроек:
|
||
|
||
- `app` — часовой пояс, режим отладки, уровень журнала и пути к `playlists.ini` и `channels.json`.
|
||
- `server`/`site` — адрес и порт веб-сервера, базовый URL, размер страницы, ссылки сайта и заголовок.
|
||
- `check.playlists` — User-Agent, тайм-ауты, задержки и параллелизм проверки плейлистов.
|
||
- `check.channels` — User-Agent, тайм-аут, размер диапазона байтов, задержки и параллелизм проверки каналов.
|
||
- `cache` — включение кеша, адрес подключения, база и TTL.
|
||
|
||
Значения задержек и некоторых параметров могут задаваться как числом или диапазоном `[min, max]`. User-Agent может быть строкой или массивом строк; при массиве выбирается случайное значение.
|
||
|
||
Веб-команда поддерживает как минимум следующие сценарии:
|
||
|
||
```bash
|
||
# Сервер без фоновой проверки
|
||
./iptvc serve -i playlists.ini -p 8800
|
||
|
||
# Сервер с бесконечной фоновой проверкой каждые 60 секунд
|
||
./iptvc serve --check --repeat 0 --playlists-all-cooldown 60
|
||
```
|
||
|
||
Основные HTTP-маршруты описаны в README: `/`, `/page/{N}`, `/{code}`, `/{code}/details`, `/api/playlists/{code}`, `/api/version`, `/api/health` и `/api/stats`.
|
||
|
||
## Тестирование
|
||
|
||
Тесты находятся рядом с тестируемым кодом и используют стандартный пакет `testing`. Сейчас явно присутствуют тесты CLI-флагов в `cmd/flags_test.go`; они проверяют применение переопределений приложения, кеша и параметров проверки.
|
||
|
||
Перед изменением CLI или конфигурации запускайте:
|
||
|
||
```bash
|
||
go test ./...
|
||
go vet ./...
|
||
```
|
||
|
||
При обновлении конфигурационных структур синхронно проверяйте `config.yml.example`, `.env.example`, `README.md` и документацию в `docs/content/common/config/config.md`, `docs/content/iptvc/commands/check.md`, `docs/content/iptvc/commands/serve.md` и связанных страницах. См. также раздел «Источники правды» выше — правки начинаются с example-файлов.
|
||
|
||
При добавлении флага желательно добавить тесты на:
|
||
|
||
- значение по умолчанию без флага;
|
||
- явное включение и выключение boolean-флага;
|
||
- переопределение значения из конфигурации;
|
||
- корректный разбор чисел, диапазонов и списков;
|
||
- взаимодействие с приоритетом `config.yml`, окружения и CLI.
|
||
|
||
Отдельных интеграционных тестов веб-сервера, кеша и сетевой проверки плейлистов в просмотренных файлах не обнаружено. Такие тесты требуют изолированных фикстур и не должны зависеть от случайных внешних IPTV-ресурсов.
|
||
|
||
## Стиль разработки
|
||
|
||
- Соблюдайте стандартный стиль Go и форматируйте изменённые Go-файлы через `gofmt`.
|
||
- Используйте имена пакетов и экспортируемых сущностей в соответствии с идиомами Go.
|
||
- Сохраняйте существующие заголовки лицензии в файлах, где они уже присутствуют.
|
||
- Комментарии к экспортируемым типам и функциям должны объяснять их назначение.
|
||
- Не добавляйте секреты, реальные данные доступа к кешу и рабочие плейлисты в репозиторий.
|
||
- Для ошибок и сетевых операций сохраняйте существующую практику явной обработки ошибок и логирования.
|
||
- Не меняйте формат CLI, конфигурации или API-маршрутов без обновления README и соответствующих тестов.
|
||
- При работе с конфигурацией учитывайте различие между отсутствующим CLI-флагом и флагом, переданным явно: в `cmd/serve.go` это используется для корректного переопределения порта и хоста.
|
||
- Избегайте сетевых запросов и зависимости от внешних сервисов в модульных тестах.
|
||
|
||
## Важные ограничения и наблюдения
|
||
|
||
- `channels.json`, `playlists.ini`, `config.yml` и `.env` могут быть локальными входными данными; проверяйте их наличие перед запуском сценариев из документации.
|
||
- Кеш включён по умолчанию в `config.yml.example` и `.env.example`; compose-окружение запускает кеш и передаёт настройки через `.env`.
|
||
- Бинарные файлы и архивы сборки создаются в `bin/` и не должны вручную добавляться в исходный код.
|
||
- README указывает минимальную версию Go 1.23.6; Dockerfile использует `alpine:3.22.5` и не собирает Go-код внутри контейнера.
|
||
- При обновлении конфигурационных структур синхронно проверяйте `config.yml.example`, `.env.example`, README и тесты CLI-флагов. Example-файлы — источники правды (см. раздел «Источники правды»).
|