Files
cf7-spam-to-telegram/docs/superpowers/specs/2026-04-21-cf7stg-self-hosted-updates-design-DRAFT.md
T
Vladimir Bryzgalov f7d81a4517 docs(spec): DRAFT design for self-hosted updates from Gitea (1.2.0)
Brainstorm session ran out of context — saving fully-formed design as
DRAFT so the next session can resume from spec self-review + finalize +
writing-plans without redoing the brainstorm.

Beads epic: cf7stg-5wy.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-21 18:46:35 +05:00

222 lines
14 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.
# CF7 Spam → Telegram — Self-Hosted Updates from Gitea (1.2.0) — DRAFT
**Статус:** DRAFT — дизайн обсуждён в брейнсторме, пользователь подтвердил разделы 1 и 2. Не финализировано (нет финального spec-документа без суффикса DRAFT, нет implementation plan, нет beads-подзадач).
**Что нужно сделать в новой сессии:**
1. Перечитать этот документ.
2. Спросить пользователя: «По дизайну self-hosted updates (1.2.0) всё ок? Если да — финализирую spec (без DRAFT), пробегусь по self-review, попрошу глянуть, потом writing-plans».
3. Если ок — `git mv ...DRAFT.md` в финальное имя `2026-04-21-cf7stg-self-hosted-updates-design.md`, повторить self-review, попросить пользователя глянуть финал, потом invoke `superpowers:writing-plans`.
---
## Контекст
**Trigger:** только что выпустили v1.1.0 (silent drop). Деплой 1.1.0 на домработница.рус сделан вручную через `rsync`. Этот же плагин будет деплоиться на другие сайты (washanyanya.ru, потенциально другие). Хочется, чтобы WP-сайты сами получали обновления через стандартный «Плагины → Обновления», без `rsync` и без знания внутренностей.
**Решено в брейнсторме:**
- Gitea репо `bryzgalov/cf7-spam-to-telegram` сделан **публичным** (пользователь подтвердил). В коде нет секретов.
- Используем **Plugin Update Checker v5** от YahnisElsts, vendored в `includes/lib/plugin-update-checker/`.
- В режиме «Generic JSON server»: PUC ходит в статичный `wp-plugin-info.json` в корне репо (через Gitea raw URL).
- Zip-релизы хранятся как Gitea Release assets (создаются вручную через скрипт `release.sh`, чистые благодаря `.gitattributes export-ignore`).
## Scope
**В scope:**
- Vendored PUC v5 в `includes/lib/plugin-update-checker/`.
- Файл `wp-plugin-info.json` в корне репо (Generic JSON для PUC).
- Инициализация PUC в `Plugin::boot()` через `register_update_checker()`.
- `.gitattributes` с `export-ignore` для всех dev-файлов.
- Bash-скрипт `release.sh` (bump version → commit → tag → push → build clean zip → Gitea Release + asset upload).
- Минимальные тесты на инвариант (PUC файл существует, JSON валидный, version в JSON совпадает с константой плагина).
- Версия 1.2.0 + changelog.
**Вне scope:**
- Кастомный `GiteaApi extends Api` (не нужен — публичный JSON).
- Update-сервер / прокси (не нужен — статичный JSON через raw URL).
- CI/Gitea Actions для авто-сборки релизов (вручную через release.sh достаточно).
- License key management.
- Авто-rollback на старую версию.
- Уведомления о доступном обновлении куда-либо кроме стандартной WP-админки.
## Архитектура
```
Локальная разработка
↓ ./release.sh 1.2.0
git push origin main --tags
git archive --worktree-attributes → cf7-spam-to-telegram-1.2.0.zip
POST Gitea API /releases → создаёт Release объект
POST Gitea API /releases/{id}/assets → uploads zip
WP-сайт (плагин 1.2.x уже установлен)
↓ Plugin::boot() → register_update_checker()
↓ PUC раз в 12 часов (стандартный WP transient)
GET https://git.netranking.ru/bryzgalov/cf7-spam-to-telegram/raw/branch/main/wp-plugin-info.json
↓ парсит, сравнивает version с CF7STG_VERSION
↓ если новее → WP «Плагины → Обновления» показывает кнопку «Обновить»
WP качает zip по download_url (Gitea release asset, public, без auth)
↓ распаковывает в wp-content/plugins/cf7-spam-to-telegram/
Плагин обновлён.
```
## Изменяемые файлы
| Файл | Что |
|---|---|
| `includes/lib/plugin-update-checker/` (новая папка, ~50 файлов) | Распакованный PUC v5 release с https://github.com/YahnisElsts/plugin-update-checker. Закоммичен целиком. |
| `wp-plugin-info.json` (новый, корень репо) | Статичная metadata, формат Generic JSON для PUC. |
| `includes/class-plugin.php` | В `boot()` добавляется `self::register_update_checker()`. Новый private method. |
| `.gitattributes` (изменяется) | `export-ignore` правила для всех dev-файлов. |
| `release.sh` (новый, корень, `chmod +x`) | Bash-скрипт релиза. |
| `cf7-spam-to-telegram.php` | Bump до `1.2.0` (header `Version:` + константа `CF7STG_VERSION`). |
| `readme.txt` | Bump `Stable tag: 1.2.0` + Changelog `= 1.2.0 =`. |
| `.gitignore` | Добавляется `dist/`. |
## `wp-plugin-info.json` — структура
```json
{
"name": "CF7 Spam → Telegram",
"slug": "cf7-spam-to-telegram",
"version": "1.2.0",
"download_url": "https://git.netranking.ru/bryzgalov/cf7-spam-to-telegram/releases/download/v1.2.0/cf7-spam-to-telegram-1.2.0.zip",
"homepage": "https://git.netranking.ru/bryzgalov/cf7-spam-to-telegram",
"requires": "6.0",
"tested": "6.9",
"requires_php": "7.4",
"last_updated": "2026-04-21 17:00:00",
"author": "Vladimir Bryzgalov",
"sections": {
"description": "Forwards CF7 spam submissions to Telegram with heuristic classification (client/unclear/spam/drop labels).",
"changelog": "<извлечённый из readme.txt блок == Changelog == ==>"
}
}
```
## `Plugin::boot()` изменение
```php
public function boot(): void
{
Settings::register_classifier_filters();
Dispatcher::register_hooks();
(new CF7Source())->register();
self::register_update_checker(); // ← новый
if (is_admin()) {
(new Settings_Page())->register();
(new Dashboard_Widget())->register();
}
}
private static function register_update_checker(): void
{
$loader = CF7STG_PLUGIN_DIR . 'includes/lib/plugin-update-checker/plugin-update-checker.php';
if (!is_file($loader)) return; // не падаем, если папка отсутствует
require_once $loader;
if (!class_exists('YahnisElsts\\PluginUpdateChecker\\v5\\PucFactory')) return;
\YahnisElsts\PluginUpdateChecker\v5\PucFactory::buildUpdateChecker(
'https://git.netranking.ru/bryzgalov/cf7-spam-to-telegram/raw/branch/main/wp-plugin-info.json',
CF7STG_PLUGIN_FILE,
'cf7-spam-to-telegram'
);
}
```
## `release.sh` (главные шаги)
```
release.sh 1.2.0 →
1. validate (semver, clean tree, на main, есть ~/.gitea-token)
2. bump version в cf7-spam-to-telegram.php (header + define) и readme.txt (Stable tag)
3. update wp-plugin-info.json (version, download_url, last_updated, sections.changelog ← из readme.txt)
4. git add + git commit -m "release: 1.2.0" + git tag v1.2.0 + git push origin main --tags
5. mkdir -p dist
git archive --worktree-attributes --format=zip --prefix=cf7-spam-to-telegram/ -o dist/cf7-spam-to-telegram-1.2.0.zip v1.2.0
6. POST /api/v1/repos/.../releases (создать Release объект)
7. POST /api/v1/repos/.../releases/{id}/assets (загрузить zip как asset)
8. echo URLs (release page, asset URL, PUC JSON URL)
```
Token хранится в `~/.gitea-token` (chmod 600), не в репо. Создание токена — единожды, в Gitea UI: `user/settings/applications` → scope `write:repository`.
## `.gitattributes` правила
```
# Исключаем dev-файлы из git archive (release zip)
tests/ export-ignore
docs/ export-ignore
composer.json export-ignore
composer.lock export-ignore
phpunit.xml export-ignore
.phpunit.result.cache export-ignore
.beads/ export-ignore
.claude/ export-ignore
CLAUDE.md export-ignore
AGENTS.md export-ignore
release.sh export-ignore
dist/ export-ignore
.gitignore export-ignore
.gitattributes export-ignore
.DS_Store export-ignore
```
**ВАЖНО:** `includes/lib/plugin-update-checker/` НЕ исключаем — она нужна в zip!
## Тесты
`tests/UpdateCheckerIntegrationTest.php` (extends `Cf7stgTestCase`):
- `test_puc_loader_file_exists()``includes/lib/plugin-update-checker/plugin-update-checker.php` существует.
- `test_puc_factory_class_loadable_after_require()` — после `require_once` лоадера класс `YahnisElsts\PluginUpdateChecker\v5\PucFactory` доступен.
- `test_wp_plugin_info_json_is_valid()``wp-plugin-info.json` парсится, содержит обязательные ключи `name, slug, version, download_url, sections`.
- `test_wp_plugin_info_json_version_matches_plugin_constant()``wp-plugin-info.json::version` совпадает с `CF7STG_VERSION` из `cf7-spam-to-telegram.php` (regex extract). Защита: release.sh забыл обновить JSON — тест поймает.
- `test_wp_plugin_info_json_download_url_matches_version()``download_url` содержит `/releases/download/v{version}/`.
`tests/ZipArchiveBuildTest.php` (опционально, требует git в `$PATH`):
- `test_git_archive_includes_runtime_dirs()``git archive HEAD --worktree-attributes -o /tmp/test.zip` распаковывается, содержит `cf7-spam-to-telegram.php`, `includes/`, `admin/`, `includes/lib/plugin-update-checker/plugin-update-checker.php`.
- `test_git_archive_excludes_dev_dirs()` — НЕТ `tests/`, `docs/`, `composer.json`, `release.sh`.
## Error handling
- Отсутствие PUC папки на сайте: `register_update_checker` делает `is_file` + `class_exists` → плагин не падает, обновлений просто нет (как до 1.2.0).
- Недоступность Gitea (network/down/4xx): PUC сам логирует через `error_log`, ничего не показывает в админке. Следующая итерация (12ч) повторит. Юзер не видит «обновлений» — всё.
- Bad JSON в `wp-plugin-info.json`: PUC скипает, error_log.
- Версия в JSON совпадает с локальной → no update notice. Норм.
## Rollout
1. Локально: создать токен в Gitea (один раз), сохранить в `~/.gitea-token`.
2. Локально: подготовить v1.2.0 (vendor PUC, добавить файлы, написать release.sh, тесты).
3. PHPUnit зелёный → `./release.sh 1.2.0` → tag + push + Release + asset.
4. На домработница.рус (где сейчас 1.1.0) — один последний раз через `rsync` обновить до 1.2.0. Активировать плагин (auto-seed уже идемпотентный). PUC начинает работать.
5. Все будущие версии — `./release.sh 1.x.y`, и через 12ч (или после ручного «Проверить обновления») WP-сайты сами обновятся.
## Версионирование
- `1.1.0``1.2.0` (semver minor: новая фича, обратная совместимость).
- Changelog в `readme.txt`:
```
= 1.2.0 =
* New: Self-hosted plugin updates from Gitea via plugin-update-checker (Yahnis Elsts v5).
WP «Плагины → Обновления» теперь показывает обновления автоматически (раз в 12ч),
скачивая чистый zip из Gitea Releases. Источник:
https://git.netranking.ru/bryzgalov/cf7-spam-to-telegram/raw/branch/main/wp-plugin-info.json.
* Dev: release.sh — однострочный релиз (bump version → commit → tag → push →
build clean zip via git archive --worktree-attributes → Gitea Release + asset upload).
* Dev: .gitattributes export-ignore для tests/, docs/, composer.*, phpunit.xml,
.beads/, .claude/, CLAUDE.md, AGENTS.md, release.sh — git archive собирает
чистый zip без dev-файлов.
```
## Открытые вопросы для следующей сессии
Эти моменты НЕ обсуждены с пользователем явно — могут потребовать одного-двух уточнений перед finalizing spec:
1. **Какую точно версию PUC v5 vendor'им?** Скачать последний release с https://github.com/YahnisElsts/plugin-update-checker/releases (на 2026-04-21 это видимо `5.x.x`). В spec зафиксировать конкретную версию для воспроизводимости.
2. **Уважает ли Gitea raw URL HTTPS на git.netranking.ru cache headers?** PUC ставит `If-Modified-Since`, важно чтобы сервер не ломал. Проверить вручную через `curl -I https://git.netranking.ru/.../raw/branch/main/wp-plugin-info.json` — если есть `Last-Modified` / `ETag` — отлично.
3. **Минификация PUC?** Vendored PUC ~50 файлов / ~300KB. Не критично, но можно опционально удалить `examples/`, `tests/`, `docs/` из vendored папки. Рассмотреть как nit.
4. **Поддержка Gitea Actions** — пользователь не запрашивал, но если есть желание авто-релизов на push tag — это +1 эпик в будущем.