Files
iptvc/AGENTS.md
T
2026-08-03 17:57:21 +08:00

320 lines
26 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Инструкции для работы с репозиторием
## Обзор проекта
`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-файлы — источники правды (см. раздел «Источники правды»).