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>
This commit is contained in:
@@ -0,0 +1,221 @@
|
||||
# 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 эпик в будущем.
|
||||
Reference in New Issue
Block a user