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>
14 KiB
CF7 Spam → Telegram — Self-Hosted Updates from Gitea (1.2.0) — DRAFT
Статус: DRAFT — дизайн обсуждён в брейнсторме, пользователь подтвердил разделы 1 и 2. Не финализировано (нет финального spec-документа без суффикса DRAFT, нет implementation plan, нет beads-подзадач).
Что нужно сделать в новой сессии:
- Перечитать этот документ.
- Спросить пользователя: «По дизайну self-hosted updates (1.2.0) всё ок? Если да — финализирую spec (без DRAFT), пробегусь по self-review, попрошу глянуть, потом writing-plans».
- Если ок —
git mv ...DRAFT.mdв финальное имя2026-04-21-cf7stg-self-hosted-updates-design.md, повторить self-review, попросить пользователя глянуть финал, потом invokesuperpowers: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 — структура
{
"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() изменение
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
- Локально: создать токен в Gitea (один раз), сохранить в
~/.gitea-token. - Локально: подготовить v1.2.0 (vendor PUC, добавить файлы, написать release.sh, тесты).
- PHPUnit зелёный →
./release.sh 1.2.0→ tag + push + Release + asset. - На домработница.рус (где сейчас 1.1.0) — один последний раз через
rsyncобновить до 1.2.0. Активировать плагин (auto-seed уже идемпотентный). PUC начинает работать. - Все будущие версии —
./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:
- Какую точно версию PUC v5 vendor'им? Скачать последний release с https://github.com/YahnisElsts/plugin-update-checker/releases (на 2026-04-21 это видимо
5.x.x). В spec зафиксировать конкретную версию для воспроизводимости. - Уважает ли 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— отлично. - Минификация PUC? Vendored PUC ~50 файлов / ~300KB. Не критично, но можно опционально удалить
examples/,tests/,docs/из vendored папки. Рассмотреть как nit. - Поддержка Gitea Actions — пользователь не запрашивал, но если есть желание авто-релизов на push tag — это +1 эпик в будущем.