Bash WP-CLI Update — bash-скрипт, который обновляет ядро, плагины, темы и схему базы WordPress на списке сайтов, заходя в каждый под его владельцем, с дампом базы до изменений, JSON-отчётом и путём восстановления. Это не «обёртка над wp plugin update --all»: у инструмента двадцать режимов, из которых десять ничего не пишут, конфигурация читается как данные (а не подключается через source), дамп снимается до первого изменения, а параллельный прогон отсекается блокировкой. Ниже — разбор возможностей версии 8.0.4 с примерами и с тем, что я реально прогнал 24 октября 2026 года на Debian 12 с настоящим WordPress 7.1.3: команды, вывод, коды возврата и найденные шероховатости. Вывод в примерах приведён с сокращениями: длинные пути заменены на /srv/www/example.com и /var/backups/wp-cli-update, служебные строки убраны, значимые — сохранены дословно.

TL;DR: что даёт менеджер и как проверить за десять минут
- Один bash-скрипт (201 706 байт) плюс искатель сайтов. Зависимости:
bash >= 4.2, GNU coreutils/findutils, WP-CLI;rootнужен потому, что каждый вызов идёт от владельца сайта черезrunuser/sudo/su. - Двадцать режимов: десять изменяющих (
--full,--core,--plugins,--themes,--db-optimize,--db-fix,--cron,--astra,--plugin-manage,--restore-db) и десять только читающих (--list-plugins,--verify,--health,--secrets,--check,--status,--doctor,--list-sites,--list-backups,--completion). - Дамп до изменений и обратный путь:
--backup dbснимаетwp db exportперед правкой сайта,--restore-db FILE --yesвозвращает базу и сам проверяет её черезwp db check. - Конфигурация — данные: файл
KEY=VALUEникогда не подключается черезsource; строка с$(, обратной кавычкой или;отклоняет файл целиком с кодом 4, групповые права — тоже. - Коды возврата — контракт:
0успех,1операционная ошибка,2ошибка аргументов,3ошибка окружения (в том числе занятый лок),4ошибка конфигурации. - npm-канал работает: пакет
bash-wp-cli-update@8.0.4опубликован 24 октября 2026 года, ставит два шимаwp-cli-updateиwp-find, версия из npm-копии отвечаетBash_WP-CLI_Update.sh 8.0.4. - Проверка за десять минут — четыре read-only команды, ничего не меняют и не берут блокировку:
sudo wp-cli-update --doctor # хост: wp, переключение пользователя, инструменты, место
sudo wp-cli-update --check # список сайтов и каждый сайт: wp-config.php, владелец
sudo wp-cli-update --health # версия ядра, ожидающие обновления, размер базы
sudo wp-cli-update --full --site /srv/www/example.com --dry-run # что именно будет выполнено
Ожидаемый результат: --doctor называет бинарник wp и механизм переключения пользователя, --check печатает по галочке на сайт, --health выводит таблицу, сухой прогон показывает конкретные команды wp. Если хоть один шаг отвечает не то — до парка не доходить.
Оглавление
- TL;DR: что даёт менеджер и как проверить за десять минут
- Проблема: цикл по сайтам, который ничего не гарантирует
- Что это за инструмент и чего он не делает
- Двадцать режимов
- Установка: три канала доставки
- Конфигурация как данные
- Безопасность по умолчанию
- Пример 1: инвентаризация парка без изменений
- Пример 2: один сайт — от сухого прогона до обновления
- Пример 3: парк целиком — параллельность и предохранитель
- Пример 4: ночной прогон и мониторинг
- Пример 5: восстановление базы
- Одновременные прогоны и блокировка
- Подводные камни
- Чек-лист внедрения
- FAQ
- Итог
Проблема: цикл по сайтам, который ничего не гарантирует
Типичный самодельный прогон выглядит так:
for site in $(cat /root/sites.txt); do
wp --path="$site" --allow-root plugin update --all
wp --path="$site" --allow-root core update
done
Он работает ровно до первого сбоя. Что в нём отсутствует по существу:
- владелец сайта.
wpпод root создаёт файлыroot:rootвwp-content— после этого плагин не может писать в свой каталог, а обновление из админки падает; - дамп до изменений. Обновление несовместимого плагина откатывать нечем: файлы прежней версии уже перезаписаны;
- границы отказа. Ошибка на третьем сайте не останавливает цикл и не видна в итоге: код возврата потерян,
stderrушёл в никуда; - отчёта. Утром нельзя ответить, что именно обновилось и на каком сайте;
- защиты от наложения. Ночной cron встречается с ручным прогоном — два процесса одновременно тянут один и тот же пакет;
- различения режимов. Проверить состояние парка нечем: любой запуск что-то меняет, поэтому «посмотреть» и «сделать» — одна и та же команда.
Bash WP-CLI Update закрывает именно эти шесть пунктов, и это его настоящая ценность — а не сам факт обновления плагинов, которое умеет и wp.
Что это за инструмент и чего он не делает
Устройство простое и от этого предсказуемое: один менеджер (Bash_WP-CLI_Update.sh, 201 706 байт), один искатель сайтов (Find_WP_Senior.sh, 33 136 байт), пример конфигурации, набор поведенческих тестов и каталог contrib/ с юнитом systemd, таймером, примером cron и правилом logrotate. Никакой сборки, зависимостей от Ruby/Python/Node и внешних библиотек: только bash и GNU-утилиты.
| Требование | Значение |
|---|---|
| ОС | Linux (пакет помечен os: linux) |
| Оболочка | bash >= 4.2 |
| Обязательное | GNU coreutils/findutils, WP-CLI, root, одна из runuser / sudo / su |
| Опциональное | flock, timeout (или perl), tar, gzip, nice, ionice, curl, mail |
| Node (только для npm-пути) | >= 16 |
| Лицензия | PolyForm Noncommercial 1.0.0 |
Что инструмент не делает и не обещает:
- не ставит WP-CLI (это отдельная установка, npm её тоже не выполняет);
- не лечит несовместимость плагинов с новой версией ядра — он даёт дамп и путь назад, а решение остаётся за администратором;
- не заменяет полноценный бэкап хостинга:
--backup full— этоtar.gzдерева сайта, полезный для откатов плагинов, но не для восстановления сервера; - не работает на macOS: встроенный там bash 3.2 не подходит, нужен контейнер или виртуальная машина.
Двадцать режимов
Режим задаётся ровно один на прогон; два режима сразу — ошибка аргументов с кодом 2 (проверено: --full --health → exit=2).
| Режим | Что делает | Пишет |
|---|---|---|
--full |
ядро + плагины + темы + схема базы + db optimize/db repair + cron (+ Astra при лицензии) |
да |
--core |
core update, core update-db --skip-plugins |
да |
--plugins |
обновление всех плагинов либо перечисленного набора (--only-active, --exclude-plugins, --hold-plugins) |
да |
--themes |
обновление тем с --exclude-themes / --hold-themes |
да |
--db-optimize |
db optimize, затем db repair |
да |
--db-fix |
только db repair |
да |
--cron |
cron event run --due-now |
да |
--astra |
обновление astra-addon с активацией лицензии при необходимости |
да |
--plugin-manage |
активировать, деактивировать или удалить один плагин (delete требует --yes) |
да |
--restore-db FILE |
импорт дампа в базу сайта; обязателен --yes, свежий дамп снимается первым |
да |
--list-plugins |
инвентаризация плагинов в table, json, csv или tsv | нет |
--verify |
core verify-checksums и plugin verify-checksums --all |
нет |
--health |
версия ядра, ожидающие обновления плагинов и тем, размер базы | нет |
--secrets |
имена секретных констант в wp-config.php, права файла, старые копии |
нет |
--check |
окружение и каждый сайт из списка | нет |
--status |
сводка последнего прогона и размеры журналов | нет |
--doctor |
диагностика хоста, список сайтов не нужен | нет |
--list-sites |
разрешённый список сайтов | нет |
--list-backups |
дампы, снятые инструментом, от свежих к старым | нет |
--completion |
скрипт дополнения для bash или zsh | нет |
Отдельно стоит правило: read-only значит read-only. --health, --secrets, --list-plugins, --verify, --list-backups и --check не берут блокировку, не снимают дамп и не пишут ничего, кроме строк журнала. Поэтому --health можно дёргать из мониторинга хоть каждую минуту.
Установка: три канала доставки
1. npm. Пакет bash-wp-cli-update опубликован 24 октября 2026 года (версия 8.0.4, распакованный размер 351 384 байта, лицензия PolyForm-Noncommercial-1.0.0, os: linux, node >= 16).
npm install -g bash-wp-cli-update
wp-cli-update --version # Bash_WP-CLI_Update.sh 8.0.4
wp-find --help
Проверено на Debian 12 с node 22.22.1 и npm 11.12.1: npm install --prefix /opt/wpcli-npm bash-wp-cli-update добавляет один пакет, кладёт wp-cli-update и wp-find в node_modules/.bin, обе команды работают, и весь прогон в этой статье выполнен именно npm-копией. Шим разрешает свой настоящий путь через readlink -f и делает exec bash .../Bash_WP-CLI_Update.sh "$@" — то есть аргументы доходят без искажений.
Важная граница: npm — канал доставки, а не установщик. Он не ставит WP-CLI, не создаёт каталоги /var/log/wp-cli-update, /var/backups/wp-cli-update, /var/lib/wp-cli-update, не пишет /etc/wp-cli-update.conf и ничего не запускает. Для промхоста это означает: после npm всё равно нужен шаг 2 или ручная подготовка каталогов и конфигурации.
2. Установщик из архива. Идемпотентный, с сухим прогоном и удалением:
curl -LO https://github.com/paulmann/Bash_WP-CLI_Update/releases/latest/download/bash-wp-cli-update-8.0.4.tar.gz
tar xzf bash-wp-cli-update-8.0.4.tar.gz
sudo ./install.sh --dry-run # показать шаги, ничего не делать
sudo ./install.sh --systemd # сделать и поставить ночной таймер
sudo ./install.sh --prefix /opt/wp --config /etc/wp-cli-update.conf
3. Вручную — клон репозитория, chmod 755 на оба скрипта, символические ссылки в /usr/local/bin, три каталога и конфигурация с правами 600.
Во всех трёх случаях после установки список сайтов строится искателем, а не руками:
sudo wp-find --output /var/lib/wp-cli-update/wp-found.txt /var/www /home
sudo wc -l /var/lib/wp-cli-update/wp-found.txt
sudo wp-cli-update --list-sites | head
wp-find умеет JSON, ограничение глубины и исключения; в моём случае он не понадобился только потому, что песочница — один сайт, переданный через --site.
Конфигурация как данные
Слои от слабого к сильному: встроенные значения → /etc/wp-cli-update.conf → wp-cli-update.conf рядом со скриптом → окружение WP_CLI_UPDATE_* → командная строка. Посмотреть, что победило и откуда пришло:
sudo wp-cli-update --print-config | grep -E 'WP_CLI_PATH|JOBS|BACKUP |MIN_FREE_MB'
WP_CLI_PATH /usr/local/bin/wp file:good.conf -
JOBS 2 file:good.conf -
BACKUP db file:good.conf -
MIN_FREE_MB 500 file:good.conf -
Две защиты важнее остальных и обе проверены:
# строка с метасимволом → файл отклонён целиком
Bash_WP-CLI_Update.sh: config: /etc/wp-cli-update.conf:3: shell metacharacter in a config line; refusing to read this file (use plain KEY=VALUE)
# exit=4
# групповые права на запись → отказ до чтения
Bash_WP-CLI_Update.sh: config: /etc/wp-cli-update.conf: group- or world-writable; chmod 600 or 640 it first (a writable config would be executable code with root privileges)
# exit=4
Это не паранойя, а разница между «конфигурация» и «скрипт от root»: файл, доступный на запись группе, — это исполняемый код с правами root.
Профиль для продакшена (пути те, что создаёт install.sh; без них встроенные значения указывают на каталог скрипта, и ночной прогон ищет список сайтов не там):
# /etc/wp-cli-update.conf
WP_CLI_PATH=/usr/local/bin/wp
SITES_FILE=/var/lib/wp-cli-update/wp-found.txt
LOG_FILE=/var/log/wp-cli-update/manager.log
ERROR_LOG_FILE=/var/log/wp-cli-update/errors.log
BACKUP_DIR=/var/backups/wp-cli-update
AUTO_DISCOVER=0
JOBS=4
BACKUP=db
KEEP_BACKUPS=5
BACKUP_AGE_DAYS=14
BACKUP_COMPRESS=1
MIN_FREE_MB=500
NICE_LEVEL=10
IONICE_CLASS=idle
TIMEOUT=600
RETRY=2
RETRY_DELAY=30
MAX_FAILURES=5
FAIL_ON=any
REPORT_FILE=/var/log/wp-cli-update/last-run.json
LOG_JSON=1
Ключи 6.1-эпохи (WP_TIMEOUT, WP_CLI_BIN, SITE_URL, BACKUP_KEEP, DEFAULT_JOBS) принимаются с предупреждением — конфигурация старой установки не ломается молча.
Безопасность по умолчанию
Семь правил, заложенных в инструмент, стоит знать до первого боевого прогона — они объясняют его поведение:
- Аргументы, а не строки. Каждый вызов WP-CLI — bash-массив; ничего не собирается конкатенацией,
evalне используется,printf %qтоже. Переключение пользователя передаёт аргументы позиционными параметрами, поэтому путь с кавычкой или пробелом ничего не ломает. - Конфигурация не исполняется.
sourceнет вовсе (см. выше). - Лицензия не становится аргументом. Ключ Astra пишется во временный файл, доступный root и группе владельца сайта, дочерний shell читает его во время вызова, файл удаляется при возврате. В
psключа не видно. - Дамп раньше изменения. Неудачный бэкап останавливает сайт, а не продолжает обновление без защиты (
--fail-on neverослабляет это осознанно). - Разрушающее — в два ключа.
--restore-dbтребует--yes, предварительно снимает текущую базу и после импорта выполняетwp db check. - Права дампа. Начиная с 8.0.3 дамп пишется в приватный временный каталог владельца сайта и переносится в хранилище root-ом, который ставит
root:root 0640. Проверено:-rw-r----- root:root 98606 .../db-20261024-151242-1968398.sql. До 8.0.3 дамп писался сразу в дерево бэкапов, и на каталоге0700(а именно так его создаётinstall.sh)wp db exportпадал на каждом сайте с владельцем не-root — то есть ночной прогон сBACKUP=dbпропускал весь парк, сообщая только предупреждение. - Коды возврата различают причину.
2— ошибка аргументов,4— ошибка конфигурации,3— окружение (в том числе занятый лок), а не «что-то пошло не так» с кодом 1.
Пример 1: инвентаризация парка без изменений
Начинать знакомство с инструментом на проде стоит с режимов, которые ничего не пишут.
sudo wp-cli-update --health
SITE CORE PLUGINS THEMES DB MiB
-------------------------------------------------------------------------------
example.com 7.1.3 0 0 1
Колонки PLUGINS и THEMES — число ожидающих обновлений, DB MiB — размер базы. Таблица идёт в stdout, служебные строки — в stderr, поэтому её можно завернуть в скрипт и сравнить с прошлой ночью.
Инвентаризация плагинов — в машинных форматах, без «прогресса» в потоке данных:
sudo wp-cli-update --list-plugins --format csv --site /srv/www/example.com
sudo wp-cli-update --list-plugins --format json --fields name,status,update,version | jq '.[] | select(.update != "none")'
Проверка целостности — единственный режим, который отвечает на вопрос «а файлы те, что ожидались»:
sudo wp-cli-update --verify --site /srv/www/example.com
Две операции: wp core verify-checksums и wp plugin verify-checksums --all; итог operations ok: 2, код 0. Ненулевой код здесь означает изменённые файлы ядра или плагина — то, что не покажет ни одна проверка версий.
Аудит секретов — с маскированием значений:
sudo wp-cli-update --secrets --site /srv/www/example.com
ERR /srv/www/example.com: wp-config.php is world-readable (mode 644); chmod 640 it
/srv/www/example.com/wp-config.php:30 DB_PASSWORD <redacted>
/srv/www/example.com/wp-config.php:52 AUTH_KEY <redacted>
...
INF /srv/www/example.com: 10 secret-bearing constant(s) found (values not printed)
Обратите внимание: это не список, а гейт. Найденные имена печатаются, значения — нет, а мировые права на wp-config.php дают код 1. Именно так это и должно работать в регулярной проверке парка: «посмотрел» и «всё в порядке» — разные вещи.
Проверка площадки целиком:
sudo wp-cli-update --check
== requested site ==
site : /srv/www/example.com
✓ wp-config.php
✓ wp-includes/version.php
✓ owner: siteowner
Пример 2: один сайт — от сухого прогона до обновления
Сухой прогон печатает не «намерения», а точные команды с окружением и приоритетом:
sudo wp-cli-update --full --site /srv/www/example.com --backup db --dry-run
INF [dry-run] would export the database of /srv/www/example.com
INF [dry-run] (siteowner@example.com) env DOCUMENT_ROOT=/srv/www/example.com HOME=/srv/www/example.com
nice -n 10 /usr/local/bin/wp --path=/srv/www/example.com core update
INF [dry-run] ... plugin update --all
INF [dry-run] ... theme update --all
INF [dry-run] ... core update-db --skip-plugins
INF [dry-run] ... db optimize
INF [dry-run] ... db repair
INF [dry-run] ... cron event run --due-now
INF [dry-run] ... plugin update astra-addon
Видно сразу три вещи, ради которых сухой прогон и нужен: порядок операций (ядро → плагины → темы → схема → оптимизация → cron → Astra), пользователь, от которого пойдёт каждая команда, и место дампа в последовательности.
Боевой прогон того же сайта:
sudo wp-cli-update --full --site /srv/www/example.com
--backup db --backup-dir /var/backups/wp-cli-update
--report /var/log/wp-cli-update/run.json
OK database backup: /var/backups/wp-cli-update/example.com/db-20261024-151242-1968398.sql (98606 bytes)
OK changed (plugin): akismet 5.3 -> 5.7.2
OK changed (theme): twentytwentyfour 1.1 -> 1.6
operations ok: 7
operations failed: 0
changes applied: 2
duration: 29s
✓ run finished without errors
Строка changed (plugin): akismet 5.3 -> 5.7.2 — это и есть ответ на утренний вопрос «что вчера обновилось», причём в том же виде он попадает в JSON-отчёт:
{"run":{"version":"8.0.4","mode":"full","dry_run":false,"host":"web01","duration_s":29,
"sites_total":42,"sites_ok":41,"sites_failed":1,"sites_skipped":0,
"ops_ok":180,"ops_failed":1,"warnings":2,"retries":1,"changes":6,"exit":1},
"sites":[{"path":"/srv/www/a","status":"OK"},{"path":"/srv/www/b","status":"FAILED"}],
"changes":[{"site":"/srv/www/a","kind":"plugin","name":"jetpack","from":"13.0","to":"13.5"}]}
Формат отчёта стабилен и плоский: сводка run, массив sites, массив changes. Этого достаточно, чтобы положить файл в сборщик логов и построить график «сколько сайтов обновляется за ночь».
Пример 3: парк целиком — параллельность и предохранитель
sudo wp-cli-update --full --jobs 4 --backup db --report /var/log/wp-cli-update/run.json --retry 2 --max-failures 5
Что здесь важно по смыслу, а не по синтаксису:
| Ключ | Зачем именно он |
|---|---|
--jobs 4 |
партия из четырёх сайтов; это не «4 потока на сайт», а четыре сайта параллельно |
--retry 2 |
повтор для таймаута загрузки или занятой базы; для неверного --url повтор бесполезен, поэтому по умолчанию 0 |
--max-failures 5 |
предохранитель: сломанный WP-CLI или полный диск останавливают прогон, а не пишут одну и ту же ошибку двести раз |
--run-timeout 3600 |
общий бюджет времени: остаток парка помечается пропущенным, а не «успешно завершённым» |
--min-free-mb 500 |
отказ писать дамп, если на диске меньше 500 МиБ — иначе wp db export добьёт файловую систему |
--nice 10 / --ionice idle |
обновление не должно выдавливать веб-сервер из процессора и диска |
Дисциплина повторов — отдельное достоинство: --retry не «пробует ещё раз всё подряд», а перезапускает именно упавший сайт, и каждый повтор виден в отчёте (retries).
Пример 4: ночной прогон и мониторинг
Штатный путь — таймер systemd из contrib/ (или пример для cron), с логом и ротацией:
sudo ./install.sh --systemd
systemctl list-timers 'wp-cli-update*'
Мониторинг держится на двух свойствах: --health не берёт блокировку и ничего не пишет, а коды возврата различают причину. Поэтому проверка из Zabbix или самописного дежурного скрипта выглядит так:
sudo wp-cli-update --health || notify "health exit=$?"
sudo wp-cli-update --status | tail -12
Уведомления — вебхук и почта, с условием отправки (--notify-on never|failure|always). Полезная деталь: полезная нагрузка содержит только счётчики — ни путей, ни лицензии, ни содержимого сайтов. Для сборщика логов есть --json-log: одна строка журнала — один JSON-объект.
--status показывает сводку последнего прогона и размеры журналов — этого хватает, чтобы заметить «лог вырос до 5 МБ и ротация не работает».
Коды возврата в дежурной практике читаются так:
| Код | Что значит | Действие дежурного |
|---|---|---|
0 |
при --fail-on any каждая операция на каждом сайте успешна |
ничего, свериться с .changes в отчёте |
1 |
хотя бы одна операция или сайт упали | sites_failed в отчёте, затем ERROR_LOG_FILE |
2 |
ошибка командной строки | ошибка в runbook или таймере, а не на хосте |
3 |
окружение: нет wp, занят лок, запуск не от root |
сначала --doctor, потом повтор |
4 |
конфигурация отклонена | права файла (600) или метасимвол в строке |
130 / 143 / 131 |
SIGINT / SIGTERM / SIGHUP | сводка и отчёт уже записаны; сайт, на котором прервалось, проверить --verify |
Пример 5: восстановление базы
Обратный путь встроен и намеренно неудобен. Сначала — что есть:
sudo wp-cli-update --list-backups --site /srv/www/example.com
/srv/www/example.com
2026-10-24 15:15 98550 /var/backups/wp-cli-update/example.com/db-20261024-151517-2058940.sql
Без подтверждения восстановление не пойдёт:
sudo wp-cli-update --restore-db /var/backups/.../db-20261024-151517-2058940.sql --site /srv/www/example.com
# ERR /srv/www/example.com: --restore-db overwrites the live database; re-run with --yes to confirm
# exit=1
С подтверждением — три шага: свежий дамп текущего состояния, импорт, проверка:
sudo wp-cli-update --restore-db /var/backups/.../db-20261024-151517-2058940.sql
--site /srv/www/example.com --yes
WRN /srv/www/example.com: restoring the database from .../db-20261024-151517-2058940.sql
OK database backup: .../db-20261024-152025-2155909.sql (98550 bytes)
OK /srv/www/example.com: database restored and db check passed
Ключевое: перед импортом снимается дамп того, что сейчас в базе. То есть неудачное восстановление не оставляет вас без обеих версий.
Одновременные прогоны и блокировка
Второй прогон при живом первом не начинает работу:
ERR another Bash_WP-CLI_Update.sh run holds /var/lib/wp-cli-update/.lock (pid 2127002); refusing to run concurrently
# exit=3
Если наложение возможно по расписанию (например, ручной прогон поверх ночного), есть ожидание:
sudo wp-cli-update --full --lock-wait 900 --backup db
Проверено: без --lock-wait второй прогон завершается кодом 3, с --lock-wait — ждёт и завершается успешно. Read-only режимы блокировку не берут, поэтому мониторинг не мешает ночной работе.
Подводные камни
1. Уведомления PHP в stdout ломают разбор. Инструмент читает вывод WP-CLI как данные. На хосте с PHP 8.5 и штатным wp-cli.phar сам WP-CLI печатает Deprecated: ... in phar:///usr/local/bin/wp/vendor/... в стандартный поток, и тогда:
ERR wp on /srv/www/example.com did not return a JSON plugin list
ERR site failed: /srv/www/example.com
а в --health остаётся пустой колонка CORE. Это не дефект менеджера, но диагностируется он не сразу. Лечится обёрткой, которая гасит уведомления, и указанием её через --wp (проверено, после этого --list-plugins и --health работают штатно):
cat > /usr/local/bin/wp-quiet <<'EOF'
#!/bin/sh
exec php -d error_reporting="E_ALL & ~E_DEPRECATED & ~E_STRICT" /usr/local/bin/wp "$@"
EOF
chmod 755 /usr/local/bin/wp-quiet
sudo wp-cli-update --health --wp /usr/local/bin/wp-quiet
Правильный порядок — сначала обновить WP-CLI и PHP-окружение, а обёртку держать как временную меру: любое сообщение в stdout — это всегда риск для любого инструмента, который парсит вывод.
2. --skip-if-no-updates не пропускает сайт в --full и --core. Замер: wp core check-update возвращает код 0 и когда сайт актуален (Success: WordPress is at the latest version.), и когда обновления есть (печатает таблицу версий, код тоже 0). Внутренняя проверка «ядро актуально» построена на этом коде, поэтому для режимов с ядром сайт считается «есть что делать» всегда. Практическое следствие: на полностью актуальном сайте --full --skip-if-no-updates выполнил 7 операций, sites_skipped: 0. Для --plugins и --themes тот же ключ работает как задумано: INF ... nothing pending, skipped, sites_skipped: 1, код 0. Если ночной прогон должен экономить время — делите его на --plugins и --themes либо полагайтесь на отчёт, а не на пропуск.
3. --full всегда делает шаг Astra. Даже если astra-addon не установлен, в прогоне появляются две строки предупреждений (plugin update astra-addon did not succeed, no Astra licence configured; the Astra step is skipped). Это предупреждения, не ошибки, но в отчёте warnings: 2 на каждом сайте. Если Astra в парке нет — либо не используйте --full, либо держите отдельные режимы, чтобы шум не маскировал настоящие предупреждения.
4. --secrets — это проверка с ненулевым кодом при находке. Мировые права на wp-config.php дают exit=1 и строку ERR ... world-readable (mode 644); chmod 640 it. В скрипте дежурного это надо учитывать: код 1 здесь означает «нашли проблему», а не «команда не сработала».
5. --restore-db без --yes — код 1, а не 2. Аргументы формально верны, отказ приходит на уровне операции: сайт помечается failed, итог — exit=1. Для автоматики различайте «не подтвердил» и «импорт сломался» по тексту журнала.
6. Права на хранилище дампов. Дамп создаётся как root:root 0640, а временный каталог — как приватный каталог владельца сайта. Если вы чистите BACKUP_DIR руками или монтируете его с nosuid/особыми ACL, проверьте, что root может писать, а владелец сайта — читать свой временный каталог.
7. macOS и BSD-утилиты не поддерживаются. Пакет помечен os: linux, инструменту нужны bash 4.2 и GNU coreutils. Для проб на ноутбуке — контейнер или виртуальная машина.
8. Лицензия не для коммерческой инфраструктуры. PolyForm Noncommercial 1.0.0 — это не MIT: обслуживание парка сайтов клиентов под неё не подпадает без отдельной лицензии. Проверять до раскатки, а не после.
9. WP-CLI должен быть найден. По умолчанию путь /usr/local/bin/wp. Если phar лежит иначе — --wp или WP_CLI_PATH; иначе --doctor покажет настроенный путь, а первый же прогон получит wp: command not found в журнале ошибок.
Чек-лист внедрения
bash --version | head -1— не ниже 4.2;id -u— 0 при боевом прогоне.command -v wp,command -v runuser || command -v sudo || command -v su— оба обязательны.- Установка одним каналом: npm,
install.shили вручную; каталоги/var/log,/var/backups,/var/libсозданы. /etc/wp-cli-update.conf— права600,AUTO_DISCOVER=0, пути абсолютные.sudo wp-find --output /var/lib/wp-cli-update/wp-found.txt /var/www— список сайтов собран.sudo wp-cli-update --print-config— фактическое состояние совпадает с ожидаемым.sudo wp-cli-update --doctor—wp, переключение пользователя,flock,timeout, место на диске.sudo wp-cli-update --check— код 0 и по галочке на каждый сайт.- Один сайт:
--dry-run, затем боевой прогон с--backup db. - Проверка результата:
--list-backups,--verify,--health, отчётrun.json. - Только теперь — парк:
--jobs 4 --max-failures 5 --retry 2 --report .... - Таймер включён,
--healthзаведён в мониторинг, журнал ротируется.
FAQ
Чем это отличается от wp plugin update --all в цикле?
Четырьмя вещами, которых в цикле нет: переключением на владельца сайта, дампом базы до изменений, отчётом с перечнем изменений и блокировкой от наложения. Сам факт обновления плагинов умеет и WP-CLI.
Нужен ли root?
Да, на время работы. Менеджер переключается в системного пользователя каждого сайта, иначе файлы в wp-content станут root:root и сайт потеряет возможность обновляться из админки. Для проб есть --no-user-switch, но на парке это не то поведение, которое нужно.
Можно ли ставить через npm на боевой хост?
Можно: с 24 октября 2026 года пакет bash-wp-cli-update@8.0.4 есть в реестре, установка проверена. Но npm приносит только два шима — каталоги, конфигурацию и WP-CLI он не создаёт, и root во время работы всё равно нужен.
Что будет, если бэкап не удался?
Сайт останавливается: инструмент не обновляет то, что не смог защитить. Это поведение по умолчанию; --fail-on never его ослабляет осознанно и должно применяться только там, где внешний бэкап гарантирован.
Обновление прервали по таймауту — что дальше?
Прогон завершится кодом прерывания (130/143/131), частичная сводка всё равно печатается, JSON-отчёт пишется. Сайт, на котором прервалось, проверяйте --verify и, при необходимости, откатывайте базу из дампа --list-backups.
Как понять, что инструмент не врёт про «всё ок»?
По коду возврата и по отчёту: exit 0 при fail-on any означает, что каждая операция на каждом сайте завершилась успешно. Отчёт дополнительно содержит ops_ok, ops_failed, changes и warnings — расхождение между «ноль ошибок» и «ноль изменений» видно сразу.
Итог
Bash WP-CLI Update решает задачу, которая обычно живёт в виде полусамодельного скрипта у каждого администратора парка: обновить WordPress на десятках сайтов, ничего не потеряв и сумев объяснить утром, что произошло. Ценность здесь не в скорости обновления, а в шести гарантиях — владелец сайта, дамп до изменений, отчёт, коды возврата, блокировка и обратный путь к базе. Версия 8.0.4 добавляет к этому третий канал доставки (npm) и честно оговаривает его границы.
Проверка на живом WordPress 7.1.3 подтвердила основной сценарий целиком: --doctor → --check → --health → сухой прогон → боевое обновление (7 операций, 2 изменения, дамп 0640) → восстановление базы с db check. Найденные шероховатости — чувствительность к уведомлениям PHP в stdout, неработающий пропуск сайта в режимах с ядром и неизбежный шаг Astra в --full — перечислены выше честно, потому что знать о них дешевле, чем выяснять ночью.
Цитата: Менеджер парка начинается не с команды обновления, а с ответа на вопрос «чем я откачусь, если обновление окажется неудачным»; всё остальное — удобство вокруг этого ответа.
Связанные материалы
- Bash WP-CLI Update — репозиторий — скрипты, тесты,
contrib/, пример конфигурации. - Релиз v8.0.4 — заметки, архив и
SHA256SUMS. - AGENTS.md — установка и проверка по шагам — предусловия, порядок проверки, таблица типовых сбоев.
- CHANGELOG.md — история линии 8.0 — что искали на продакшене в 8.0.2 и 8.0.3.
- Документация WP-CLI — поведение команд, на которых построены режимы.
- Пакет в npm — версия, лицензия, содержимое архива.
LLM Summary
- Тема:
Bash WP-CLI Update— bash-менеджер обслуживания парка WordPress через WP-CLI; разобрана версия8.0.4(релиз 21.10.2026, npm-публикация 24.10.2026). - Устройство: один менеджер (201 706 байт) и искатель сайтов (33 136 байт); зависимости —
bash >= 4.2, GNU coreutils/findutils, WP-CLI,root, одна изrunuser/sudo/su; лицензия PolyForm Noncommercial 1.0.0;os: linux. - Режимы: двадцать — десять изменяющих (
--full,--core,--plugins,--themes,--db-optimize,--db-fix,--cron,--astra,--plugin-manage,--restore-db) и десять read-only (--list-plugins,--verify,--health,--secrets,--check,--status,--doctor,--list-sites,--list-backups,--completion). - Ключевые команды:
wp-cli-update --doctor,--check,--health,--full --site X --backup db --dry-run,--full --jobs 4 --backup db --report run.json,--list-backups,--restore-db FILE --yes,--print-config. - Гарантии: дамп базы до изменений (
root:root 0640), отчёт JSON со списком изменений, блокировка от параллельного прогона, коды возврата0/1/2/3/4, конфигурация читается как данные безsource. - Замеры 24.10.2026: боевой прогон на WordPress 7.1.3 — 7 операций, 2 изменения, дамп 98 606 байт, 29 с; восстановление базы с
db check passed; второй прогон при живом первом — код 3. - Найденные ограничения: уведомления PHP в
stdoutломают--list-pluginsи колонкуCOREв--health(лечится обёрткой--wp);--skip-if-no-updatesне пропускает сайт в--full/--core, потому чтоwp core check-updateвозвращает 0 в обоих случаях;--fullвсегда выполняет шаг Astra;--secretsвозвращает 1 при находке. - Кому полезно: администраторам парков WordPress на Linux, хостингам и студиям, дежурным, которым нужен read-only обзор ожидающих обновлений без блокировки.
- Ссылки: https://github.com/paulmann/Bash_WP-CLI_Update, https://github.com/paulmann/Bash_WP-CLI_Update/releases/tag/v8.0.4, https://www.npmjs.com/package/bash-wp-cli-update
Промт для агента (установка)
Готовый промт: шаги и команды взяты из AGENTS.md репозитория — это инструкция автора для агента, а не пересказ. Скопируйте блок целиком.
Задача: поставить Bash WP-CLI Update 8.0.4 на Linux-хост, проверить его и обновить один сайт.
Материалы:
- репозиторий: https://github.com/paulmann/Bash_WP-CLI_Update
- инструкция для агентов (читать первой): https://github.com/paulmann/Bash_WP-CLI_Update/blob/main/AGENTS.md
- релиз: https://github.com/paulmann/Bash_WP-CLI_Update/releases/tag/v8.0.4
Два правила из AGENTS.md, которые важнее остальных:
1) не запускать изменяющий режим на боевом хосте с первого раза: установка, --doctor, --check,
один сайт с --dry-run, один сайт по-настоящему, и только потом парк;
2) не удалять бэкапы, файлы сайтов и базы «чтобы прибраться»: остановиться и доложить.
Предусловия — каждую строку сверить с ожидаемым ответом; расхождение это стоп и отчёт:
bash --version | head -1
id -u
command -v wp
command -v runuser || command -v sudo || command -v su
command -v flock; command -v timeout; command -v tar; command -v gzip
Установка (любой канал, один):
npm install -g bash-wp-cli-update
либо: ./install.sh --dry-run && sudo ./install.sh --systemd
либо вручную: chmod 755 оба скрипта, ссылки в /usr/local/bin, три каталога, конфиг 600.
Конфигурация: WP_CLI_PATH, SITES_FILE, AUTO_DISCOVER=0, LOG_FILE, ERROR_LOG_FILE, BACKUP=db,
BACKUP_DIR, KEEP_BACKUPS, MIN_FREE_MB, JOBS, TIMEOUT, RETRY, MAX_FAILURES. Файл не подключается
через source: обратная кавычка, $(, |, ;, &, < или > в строке отклоняют файл целиком.
Список сайтов и проверка по порядку, шаг не пропускать:
sudo wp-find --output /var/lib/wp-cli-update/wp-found.txt /var/www /home
sudo wp-cli-update --print-config
sudo wp-cli-update --doctor
sudo wp-cli-update --check
sudo wp-cli-update --health
sudo wp-cli-update --full --site /srv/www/example.com --backup db --dry-run
sudo wp-cli-update --full --site /srv/www/example.com --backup db
sudo wp-cli-update --status
Ограничения: не запускать тесты под root «чтобы прошли проверки переключения пользователя»;
не отключать fail-on и не менять JOBS/BACKUP «временно»; не вызывать --restore-db без причины
и без --yes; если команда падает — остановиться и показать команду, вывод и код возврата.
Что считать успехом: --doctor называет wp и механизм переключения пользователя; --check даёт
код 0 и галочку по каждому сайту; --health печатает таблицу; сухой прогон показывает ожидаемые
команды; боевой прогон одного сайта даёт код 0, а .changes в отчёте совпадают с увиденным.