spec(wpmc S7): README + Gitea release + PUC v5.6 (mirror cf7stg flow)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,368 @@
|
||||
# S7 — README + Gitea release + plugin-update-checker
|
||||
|
||||
- Beads: `cp-7cf` (parent epic `cp-cw4`)
|
||||
- Depends on: `cp-la7` (S6 — CI + QA checklist, закрыт)
|
||||
- Дата: 2026-05-17
|
||||
|
||||
## Цель
|
||||
|
||||
Закрыть эпик `cp-cw4` (MVP-ядро) поставкой плагина в Gitea как self-hosted WordPress plugin с автоматическими обновлениями через Plugin Update Checker (PUC). Эталон flow — уже работающий `cf7-spam-to-telegram` (PUC v5.6 + `release.sh` + `wp-plugin-info.json` + Gitea releases).
|
||||
|
||||
Версия для первого релиза — **1.0.0** (MVP завершён, semantic).
|
||||
|
||||
S7 — infrastructure + docs only. Один новый приватный метод в `Plugin` (PUC bootstrap). Без новых функциональных классов.
|
||||
|
||||
## Артефакты
|
||||
|
||||
```
|
||||
wp-multi-city/
|
||||
├── wp-multi-city.php ← MODIFY: bump → 1.0.0, header Tested up to/Requires
|
||||
├── includes/class-plugin.php ← MODIFY: + register_update_checker()
|
||||
├── includes/lib/plugin-update-checker/ ← CREATE: vendored PUC v5.6 (copy из cf7stg)
|
||||
├── wp-plugin-info.json ← CREATE: метаданные для PUC
|
||||
├── readme.txt ← CREATE: WP.org формат, источник changelog для release.sh
|
||||
├── CHANGELOG.md ← CREATE: human-readable mirror
|
||||
├── README.md ← MODIFY: + Quick Start + Hook API + Migration guide
|
||||
├── release.sh ← CREATE: адаптация cf7stg/release.sh
|
||||
├── .gitattributes ← CREATE: export-ignore для git archive
|
||||
└── tests/
|
||||
├── PluginBootS7Test.php ← CREATE: boot() вызывает register_update_checker
|
||||
├── UpdateCheckerIntegrationTest.php ← CREATE: PucFactory loadable
|
||||
└── ZipArchiveBuildTest.php ← CREATE: git archive integrity
|
||||
```
|
||||
|
||||
## Plugin Update Checker integration
|
||||
|
||||
### Vendor
|
||||
|
||||
Скопировать `includes/lib/plugin-update-checker/` (~5 МБ, PUC v5.6 от Yahnis Elsts) из `cf7-spam-to-telegram`. Vendored код самодостаточен — нет composer-зависимости на проде. Namespace `YahnisElsts\PluginUpdateChecker\v5p6\*` версионирован — не конфликтует с другими плагинами, использующими PUC.
|
||||
|
||||
### Bootstrap в `Plugin::boot()`
|
||||
|
||||
```php
|
||||
private static function register_update_checker(): void
|
||||
{
|
||||
$loader = WPMC_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/wp-multi-city/raw/branch/main/wp-plugin-info.json',
|
||||
WPMC_PLUGIN_FILE,
|
||||
'wp-multi-city'
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
Вызов добавляется в `Plugin::boot()` ПОСЛЕ всех `*::register()` вызовов; защищён существующим `$booted` idempotency-flag-ом (двойной boot → второй `require_once` no-op, второй `buildUpdateChecker` не выполнится из-за `$booted` guard).
|
||||
|
||||
### `wp-plugin-info.json`
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "WP Multi City",
|
||||
"slug": "wp-multi-city",
|
||||
"version": "1.0.0",
|
||||
"download_url": "https://git.netranking.ru/bryzgalov/wp-multi-city/releases/download/v1.0.0/wp-multi-city-1.0.0.zip",
|
||||
"homepage": "https://git.netranking.ru/bryzgalov/wp-multi-city",
|
||||
"requires": "6.0",
|
||||
"tested": "6.9",
|
||||
"requires_php": "8.0",
|
||||
"last_updated": "2026-05-17 00:00:00",
|
||||
"author": "Vladimir Bryzgalov",
|
||||
"sections": {
|
||||
"description": "Мультигородская подсистема: taxonomy town, [shortcode_dmr] для подстановок, URL-роутер /{city}/{path}/, ACF-driven page clones.",
|
||||
"changelog": "<h4>1.0.0</h4><ul><li>Первый релиз. MVP-ядро S1–S7.</li></ul>"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`release.sh` перегенерирует `download_url`, `version`, `last_updated`, `sections.changelog` (HTML из `readme.txt`) после каждого релиза.
|
||||
|
||||
## release.sh
|
||||
|
||||
Адаптация `cf7-spam-to-telegram/release.sh` (9 шагов). Изменения:
|
||||
|
||||
```bash
|
||||
REPO_OWNER="bryzgalov"
|
||||
REPO_NAME="wp-multi-city"
|
||||
GITEA_BASE="https://git.netranking.ru"
|
||||
```
|
||||
|
||||
**STEP 2 — bump:**
|
||||
|
||||
```bash
|
||||
sed -i.bak -E \
|
||||
-e "s|^([[:space:]]*\\*[[:space:]]*Version:[[:space:]]*).*$|\\1${VERSION}|" \
|
||||
-e "s|define\\('WPMC_VERSION',[[:space:]]*'[^']+'\\)|define('WPMC_VERSION', '${VERSION}')|" \
|
||||
wp-multi-city.php
|
||||
rm wp-multi-city.php.bak
|
||||
|
||||
grep -q "^ \* Version: ${VERSION}$" wp-multi-city.php
|
||||
grep -q "define('WPMC_VERSION', '${VERSION}')" wp-multi-city.php
|
||||
|
||||
sed -i.bak -E "s|^(Stable tag:[[:space:]]*).*$|\\1${VERSION}|" readme.txt
|
||||
rm readme.txt.bak
|
||||
grep -q "^Stable tag: ${VERSION}$" readme.txt
|
||||
```
|
||||
|
||||
**STEP 4 — zip prefix `wp-multi-city/`**, sanity-list (in/out):
|
||||
|
||||
```bash
|
||||
# MUST be in zip:
|
||||
unzip -p ... wp-multi-city/wp-multi-city.php | grep "^ \* Version: ${VERSION}$"
|
||||
unzip -p ... wp-multi-city/wp-multi-city.php | grep "WPMC_VERSION', '${VERSION}'"
|
||||
grep "wp-multi-city/includes/lib/plugin-update-checker/plugin-update-checker.php" <<< "$ZIP_LISTING"
|
||||
grep "wp-multi-city/wp-plugin-info.json" <<< "$ZIP_LISTING"
|
||||
|
||||
# MUST NOT be in zip:
|
||||
for dev in tests/ docs/ composer.json composer.lock phpunit.xml.dist patchwork.json \
|
||||
release.sh CLAUDE.md AGENTS.md .gitattributes .gitignore .gitea/ ; do
|
||||
grep "wp-multi-city/${dev}" <<< "$ZIP_LISTING" && fail "dev file in zip: $dev"
|
||||
done
|
||||
```
|
||||
|
||||
Шаги 1, 3, 5–9 (валидация → git push → Gitea API create release → upload zip → regenerate `wp-plugin-info.json` → JSON commit + push → summary) — копия cf7stg без структурных изменений. Только `${REPO_NAME}` подставляется в URL.
|
||||
|
||||
### Prerequisites (валидируются в STEP 1)
|
||||
|
||||
- `~/.gitea-token` со scope `write:repository` (re-use от cf7stg).
|
||||
- Чистый git tree на `main`, in sync с `origin/main`.
|
||||
- `readme.txt` содержит секцию `= ${VERSION} =` (fail-fast перед side effects).
|
||||
- Команды в PATH: `git curl jq unzip awk sed`.
|
||||
|
||||
## .gitattributes
|
||||
|
||||
```
|
||||
# Exclude dev files from `git archive` (release.sh build)
|
||||
/tests/ export-ignore
|
||||
/docs/ export-ignore
|
||||
/phpunit.xml.dist export-ignore
|
||||
/patchwork.json export-ignore
|
||||
/composer.json export-ignore
|
||||
/composer.lock export-ignore
|
||||
/.phpunit.result.cache export-ignore
|
||||
/.beads/ export-ignore
|
||||
/.claude/ export-ignore
|
||||
/.gitea/ 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
|
||||
|
||||
# NOTE: includes/lib/plugin-update-checker/ MUST NOT be excluded —
|
||||
# required at runtime on target WP site.
|
||||
```
|
||||
|
||||
## Документация
|
||||
|
||||
Три файла, три аудитории:
|
||||
|
||||
| Файл | Формат | Аудитория | Назначение |
|
||||
|---|---|---|---|
|
||||
| `README.md` | Markdown | Разработчик в Gitea | Quick Start, Hook API, Migration guide, roadmap |
|
||||
| `readme.txt` | WP.org | `release.sh` + WP admin | `Stable tag`, `Tested up to`, Changelog в формате `= 1.0.0 =` |
|
||||
| `CHANGELOG.md` | Markdown | Human readers | Mirror `readme.txt` changelog в Markdown |
|
||||
|
||||
`readme.txt` — единственный источник правды для версии и changelog (`release.sh` парсит и `Stable tag`, и блоки `= X.Y.Z =`). `CHANGELOG.md` ведётся вручную при bump.
|
||||
|
||||
### README.md — целевая структура
|
||||
|
||||
```markdown
|
||||
# WP Multi City
|
||||
|
||||
Мультигородская подсистема для WordPress: один сайт обслуживает несколько городов
|
||||
по схеме `/{city}/{path}/` с подстановкой контента, ссылок и шорткодов.
|
||||
|
||||
## Требования
|
||||
- WordPress 6.0+
|
||||
- PHP 8.0+
|
||||
- Advanced Custom Fields Pro
|
||||
|
||||
## Установка
|
||||
1. Скачать zip с https://git.netranking.ru/bryzgalov/wp-multi-city/releases
|
||||
2. WP admin → Plugins → Add new → Upload (или распаковать в wp-content/plugins/)
|
||||
3. Активировать. Обновления приходят автоматически (PUC опрашивает Gitea hourly).
|
||||
|
||||
## Quick Start (5 минут)
|
||||
1. WP admin → Шорткоды → Города → создать term `tyumen`, `spb`.
|
||||
2. WP admin → WPMultiCity → main_town_slug = `spb`.
|
||||
3. WP admin → Шорткоды → Добавить пост, repeater row town=tyumen text="Тюменский тариф".
|
||||
4. Создать страницу с `[shortcode_dmr id=42]` → открыть на `/tyumen/<slug>/` → видна подстановка.
|
||||
|
||||
## Hook API
|
||||
### Процедурные функции (wpmc_*)
|
||||
- `wpmc_current_town(): ?string` — текущий town slug из URL.
|
||||
- `wpmc_get_term_by_slug(string $slug): ?\WP_Term`
|
||||
- `wpmc_alt_get_field(string $key, $post_id): mixed` — get_field с fallback.
|
||||
- `wpmc_remove_domain_link(string $url): string`
|
||||
- `wpmc_main_town_slug(): ?string`
|
||||
|
||||
### WP-фильтры (от плагина)
|
||||
- `the_title`/`single_cat_title` → `do_shortcode` (priority 9999)
|
||||
- `home_url`/`post_link`/`acf/load_value` → `LinkFilter::add_town_prefix`
|
||||
- `rest_url` → `RestUrlFilter::preserve_host`
|
||||
|
||||
### Bypass URL-фильтров
|
||||
\`\`\`php
|
||||
$canonical = \WPMultiCity\Helpers::without_url_filters(
|
||||
fn() => get_permalink($post_id)
|
||||
);
|
||||
\`\`\`
|
||||
|
||||
## Migration guide (из темы washanyanya)
|
||||
1. Закомментировать `require inc/town-functions.php` в теме.
|
||||
2. Активировать wp-multi-city.
|
||||
3. ACF Pro field group из темы → re-import в новую location (CPT shortcode-town).
|
||||
4. Заменить `current_town()` → `wpmc_current_town()` (или прописать алиас).
|
||||
5. Проверить: `/tyumen/about/` отдаёт правильный page; меню префиксится; `[shortcode_dmr]` рендерит.
|
||||
|
||||
## Testing
|
||||
[существующая секция: composer install; vendor/bin/phpunit; docs/QA-CHECKLIST.md]
|
||||
|
||||
## CI
|
||||
[существующая секция: .gitea/workflows/test.yml]
|
||||
|
||||
## Roadmap MVP
|
||||
| Этап | Содержание | Beads | Статус |
|
||||
| S1 | Скелет | cp-8ci | done |
|
||||
| S2 | Taxonomy + CPT + ACF | cp-q7g | done |
|
||||
| S3 | [shortcode_dmr] + хелперы | cp-arq | done |
|
||||
| S4 | URL-роутер | cp-83m | done |
|
||||
| S5 | Глобальные фильтры | cp-i17 | done |
|
||||
| S6 | CI + QA checklist | cp-la7 | done |
|
||||
| S7 | Gitea release + PUC | cp-7cf | done |
|
||||
|
||||
Эпик `cp-cw4` — CLOSED.
|
||||
```
|
||||
|
||||
### readme.txt — целевая структура
|
||||
|
||||
```
|
||||
=== WP Multi City ===
|
||||
Contributors: vbryzgalov
|
||||
Tags: multisite, multi-city, taxonomy, acf, shortcode
|
||||
Requires at least: 6.0
|
||||
Tested up to: 6.9
|
||||
Requires PHP: 8.0
|
||||
Stable tag: 1.0.0
|
||||
License: GPLv2 or later
|
||||
|
||||
== Description ==
|
||||
Мультигородская подсистема: одна WP-инсталляция обслуживает несколько городов
|
||||
по схеме /{city}/{path}/ с подстановками контента и URL.
|
||||
|
||||
== Installation ==
|
||||
1. Загрузить wp-multi-city в /wp-content/plugins/.
|
||||
2. Активировать в WP admin.
|
||||
3. Требуется ACF Pro.
|
||||
|
||||
== Changelog ==
|
||||
|
||||
= 1.0.0 =
|
||||
* Первый релиз. MVP-ядро завершено (S1–S7 эпика cp-cw4).
|
||||
* taxonomy town + CPT shortcode-town + ACF list_shortcode repeater.
|
||||
* [shortcode_dmr id=N] для подстановок по городу.
|
||||
* URL-роутер /{city}/{path}/ с поддержкой page_unic/main_page_alt/page_no_rep.
|
||||
* Глобальные фильтры the_title, home_url, post_link, acf/load_value, rest_url.
|
||||
* Self-hosted updates через Gitea + plugin-update-checker v5.6.
|
||||
```
|
||||
|
||||
### CHANGELOG.md — целевая структура
|
||||
|
||||
```markdown
|
||||
# Changelog
|
||||
|
||||
All notable changes to wp-multi-city. Format mirrors `readme.txt`.
|
||||
|
||||
## [1.0.0] — 2026-05-17
|
||||
|
||||
### Added
|
||||
- Первый релиз. MVP-ядро завершено (S1–S7 эпика cp-cw4).
|
||||
- `taxonomy town` + CPT `shortcode-town` + ACF `list_shortcode` repeater.
|
||||
- `[shortcode_dmr id=N]` для подстановок по городу.
|
||||
- URL-роутер `/{city}/{path}/` (page_unic, main_page_alt, page_no_rep).
|
||||
- Глобальные фильтры (the_title, home_url, post_link, acf/load_value, rest_url).
|
||||
- Self-hosted updates через Gitea + plugin-update-checker v5.6.
|
||||
```
|
||||
|
||||
## wp-multi-city.php — изменения
|
||||
|
||||
```php
|
||||
/**
|
||||
* Plugin Name: WP Multi City
|
||||
* Description: Мультигородская подсистема: taxonomy town, шорткоды, URL-роутер /{city}/{path}/, ACF page-clones.
|
||||
* Version: 1.0.0
|
||||
* Author: Vladimir Bryzgalov
|
||||
* Requires PHP: 8.0
|
||||
* Requires at least: 6.0
|
||||
* Tested up to: 6.9
|
||||
* License: GPLv2 or later
|
||||
* Text Domain: wp-multi-city
|
||||
*/
|
||||
...
|
||||
defined('WPMC_VERSION') || define('WPMC_VERSION', '1.0.0');
|
||||
```
|
||||
|
||||
## Тестовый план
|
||||
|
||||
### `tests/PluginBootS7Test.php` (2 теста)
|
||||
|
||||
1. `boot()` загружает PUC factory: после `Plugin::instance()->boot()` `class_exists('YahnisElsts\\PluginUpdateChecker\\v5\\PucFactory')` возвращает true.
|
||||
2. `boot()` идемпотентен для PUC: двойной `boot()` не бросает (защита от double-require + double-buildUpdateChecker — гарантируется `$booted` flag-ом).
|
||||
|
||||
### `tests/UpdateCheckerIntegrationTest.php` (2 теста)
|
||||
|
||||
3. `tests/UpdateCheckerIntegrationTest::test_puc_loader_exists`: файл `includes/lib/plugin-update-checker/plugin-update-checker.php` существует.
|
||||
4. `tests/UpdateCheckerIntegrationTest::test_puc_factory_loadable`: после `require_once` PUC loader-а, `PucFactory` класс загружается.
|
||||
|
||||
### `tests/ZipArchiveBuildTest.php` (2 теста)
|
||||
|
||||
5. `git archive HEAD --worktree-attributes --format=zip --prefix=wp-multi-city/`:
|
||||
- В zip присутствуют: `wp-multi-city.php`, `includes/lib/plugin-update-checker/plugin-update-checker.php`, `wp-plugin-info.json`, `readme.txt`, `README.md`.
|
||||
- В zip отсутствуют: `tests/`, `docs/`, `composer.json`, `phpunit.xml.dist`, `patchwork.json`, `release.sh`, `CLAUDE.md`, `.gitattributes`, `.gitea/`.
|
||||
6. `wp-plugin-info.json` — валидный JSON с обязательными ключами (`slug=wp-multi-city`, `version`, `download_url` содержит `git.netranking.ru/bryzgalov/wp-multi-city`).
|
||||
|
||||
**Итого:** 98 + 6 = **104 теста / ~185 assertions**.
|
||||
|
||||
## Acceptance verification
|
||||
|
||||
1. `vendor/bin/phpunit` — 104 теста зелёные.
|
||||
2. `git archive HEAD --worktree-attributes --format=zip --prefix=wp-multi-city/ -o /tmp/wpmc-test.zip && unzip -l /tmp/wpmc-test.zip` — никаких dev-файлов в архиве, PUC loader на месте, `wp-plugin-info.json` на месте.
|
||||
3. `wp-multi-city.php` header: `Version: 1.0.0`, константа `WPMC_VERSION = '1.0.0'`.
|
||||
4. `readme.txt` валидный: `Stable tag: 1.0.0`, секция `= 1.0.0 =` существует, `awk` извлечение возвращает не-пустой changelog body.
|
||||
5. README обновлён: Quick Start, Hook API, Migration guide на месте; roadmap S7 = done; эпик cp-cw4 помечен CLOSED.
|
||||
6. PHP-lint clean: `find includes -name '*.php' -not -path '*/lib/*' -exec php -l {} \;`.
|
||||
7. (Manual, after S7 merge) Gitea репо `bryzgalov/wp-multi-city` создан; `./release.sh 1.0.0` отрабатывает все 9 шагов без ошибок; в Gitea появляется release `v1.0.0` с zip-asset; URL `https://git.netranking.ru/bryzgalov/wp-multi-city/raw/branch/main/wp-plugin-info.json` отдаёт обновлённый JSON.
|
||||
|
||||
## Gitea репозиторий — ручные шаги (вне плана)
|
||||
|
||||
1. В Gitea UI (`git.netranking.ru/bryzgalov`) → New Repository → name `wp-multi-city`, public/private по выбору, без README (он у нас уже есть локально).
|
||||
2. На локальной машине после merge S7:
|
||||
```bash
|
||||
cd wp-multi-city
|
||||
git remote add origin https://git.netranking.ru/bryzgalov/wp-multi-city.git
|
||||
git push -u origin main
|
||||
```
|
||||
3. `~/.gitea-token` уже существует от cf7stg релизов — используется как есть. Token scope: `write:repository`.
|
||||
4. После первого push: `./release.sh 1.0.0` создаёт релиз `v1.0.0`.
|
||||
|
||||
## Что НЕ входит в S7
|
||||
|
||||
- Программное создание Gitea-репозитория (ручной шаг через UI).
|
||||
- PHP matrix в CI (8.0/8.1/8.2/8.3) — отложено.
|
||||
- Lint job (phpcs/phpstan) в CI — отложено.
|
||||
- Composer-based PUC (vendor-в-git проще: нет `composer install` на проде).
|
||||
- Migration guide для тем кроме washanyanya (универсальный — out of MVP).
|
||||
- Translations / `.pot` файл — out of MVP.
|
||||
|
||||
## Open questions
|
||||
|
||||
Нет. Все решения зафиксированы.
|
||||
Reference in New Issue
Block a user