Раскладка репозитория — в module-structure.md, процесс — в CONTRIBUTING.md, установка глазами пользователя — в README.md.
В статье:
Раскладка репозитория — в module-structure.md, процесс — в CONTRIBUTING.md, установка глазами пользователя — в README.md.
build.sh — единственная точка входа./build.sh # проверки + архив shef.insync.zip
./build.sh --check # только проверки
./build.sh --version # напечатать версию модуля
CI зовёт её же. Это не украшение: если бы сервер гонял свой набор команд, локальный зелёный прогон и серверный красный означали бы разные вещи, и разбираться пришлось бы в двух местах сразу.
При сборке дополнительно: состав zip сверяется со списком SHIP, а первый
уровень внутри архива — с shef.insync/.
Пропущенная проверка выглядит как пройденная. Поэтому:
node, а JS или *_test.mjs в репозитории есть — отказ, а не примечание;run_tests сначала считает, сколько тестов ЕСТЬ, и сверяет с числом
прогнанных: переименованный файл или тест в подкаталоге иначе выпал бы из
прогона молча;check_composer_package сверяет индекс (git write-tree), а не HEAD, и
потому работает и на грязном дереве. Раньше он на ней пропускался — а локально
дерево грязное почти всегда, так что единственная проверка, ловящая
расхождения самого git archive, срабатывала только в CI.Символическая ссылка разводит каналы поставки молча. cp в архив
разыменовывает её и кладёт содержимое цели, git archive кладёт саму ссылку, а
обе сверки состава при этом зелёные — они сверяют имена. Поэтому ссылок под
контролем git просто не бывает: check_filenames роняет сборку.
php -l их не ловит. При short_open_tag=Off — а это значение по
умолчанию — <? открывающим тегом не считается, и файл целиком становится
инлайновым HTML. Синтаксически он остаётся правильным, в нём просто нет PHP.
А на портале классы из такого файла не определяются, зато исходник уезжает в
браузер.
Поэтому check_short_tags спрашивает сам PHP через token_get_all(), а не
grep: <? внутри строки или комментария лежит в своём токене, опасный же
остаётся куском T_INLINE_HTML. Наивный grep краснел бы на каждом регулярном
выражении вида /<?/.
Архив содержит каталог модуля целиком: первым уровнем внутри zip лежит
shef.insync/, иначе при распаковке файлы рассыплются прямо по
bitrix/modules/. Проверяется в самом скрипте, а не глазами.
Сверить поставку двумя путями установки можно так:
git archive --format=tar "$(git write-tree)" | tar -tf - | grep -v '/$' | sort > /tmp/composer.txt
./build.sh && unzip -Z1 shef.insync.zip | grep -v '/$' | sed 's#^shef.insync/##' | sort > /tmp/zip.txt
diff /tmp/composer.txt /tmp/zip.txt # должно быть пусто
.github/workflows/ci.yml, пять задач:
Skills краснеет, когда навыки поправили в shef.options. Это не поломка
этого репозитория, а сигнал: разложите навыки заново
(../options/.claude/skills/sync.sh --to .) и закоммитьте. Копию на месте не
правят — правка будет затёрта следующей раскладкой.
Версии PHP в матрице — не только про код модуля. tests/vendor_test.php
загружает из своей копии библиотек XML ровно то, что загружает модуль, с
error_reporting=-1: новая версия PHP с новыми deprecation покраснеет здесь, а
не в логе портала.
В защите ветки требуется ровно одна проверка — CI. Остальные её зависимости,
поэтому новая задача не потребует правки ruleset.
ci.yml выглядит ошибкой, но ею не являетсяif: always() у задачи CI — обязателен вместе с явной сверкой результатов
зависимостей. Без него задача была бы пропущена при падении зависимости, а
пропущенную проверку защита ветки засчитывает как пройденную: красный CI уехал
бы в main.
Подмывает заменить на !cancelled() — не надо. Тогда отменённый прогон стал бы
давать пропущенную проверку, и, отменив прогон вручную, можно было бы смержить
непроверенное.
Вытесненный по concurrency прогон краснеет на устаревшем коммите. Это шум,
а не поломка: защита смотрит на проверки головного коммита.
.github/workflows/release.yml, два входа.
Пуш тега v* — тег сверяется с VERSION из install/version.php.
Расхождение роняет прогон: тегу не доверяем, иначе на портал уедет архив,
версия которого врёт.
workflow_dispatch от main — тег выводится из VERSION и ставится
сам. Запуск от другой ветки отклоняется, занятый тег ловится до сборки.
Второй вход обязателен: пуш тегов бывает недоступен — другие права, прокси сессии, — а релиз выпускать надо.
Тег ставится после успешной сборки. Поставленный раньше, он пережил бы упавшую сборку, и следующая попытка упёрлась бы в занятый тег.
Примечания к релизу собираются из секции ## <версия> в CHANGELOG.md.
Последним шагом релиз дёргает update-package. Без секретов
PACKAGIST_USERNAME и PACKAGIST_TOKEN шаг пропускается, и релиз при этом
не падает: невыложенный релиз чинить нечем, а отставший Packagist
догоняется кнопкой Update за десять секунд.
Эндпойнт умеет только обновлять уже зарегистрированный пакет. Первую
регистрацию делают один раз руками: packagist.org → Submit →
https://github.com/bx-shef/insync.
composer.json требует sbwerewolf/xml-navigator — через Composer он и его
зависимости ложатся в vendor проекта. Архив несёт свою копию в
vendor/sbwerewolf/ (SHIP). Какую подключать, решает .settings.php при
каждой загрузке — по каждому namespace отдельно, через ShProjectContext из
shef.options.
Версий в самих пакетах нет, поэтому они записаны в vendor/versions.json.
Обновили копию — обновите и его: tests/vendor_test.php сверяет его с
ограничением в composer.json.
Ветка 7.2 выбрана не случайно. Ветки 8.x и новее требуют PHP 8.4, а модуль
поддерживает 8.2. А language-specific 8.4 переехал в namespace
SbWereWolf\LanguageSpecific, тогда как xml-navigator 7.2 зовёт
LanguageSpecific\: своя копия держит связку, которая работает.
Обновить свою копию:
mkdir /tmp/x && cd /tmp/x && echo '{}' > composer.json
composer require sbwerewolf/xml-navigator:<версия> sbwerewolf/language-specific:8.0.*
cd - && for p in xml-navigator language-specific json-serialize-trait; do
rm -rf vendor/sbwerewolf/$p/src && cp -a /tmp/x/vendor/sbwerewolf/$p/src vendor/sbwerewolf/$p/
done
# версии — в vendor/versions.json
./build.sh --check
composer.json: type = bitrix-module плюс
extra.installer-name = shef.insync. Тогда Composer разворачивает модуль в
bitrix/modules/shef.insync/ без настройки на стороне потребителя:
installer-name читается из пакета, а {$bitrix_dir} — только из корневого
composer.json, повлиять на него пакет не может.
bitrix-d7-module развернул бы модуль не туда. Шаблоны в
composer/installers:
bitrix-module → {$bitrix_dir}/modules/{$name}/bitrix-d7-module → {$bitrix_dir}/modules/{$vendor}.{$name}/а installer-name подменяет только {$name}. Для пакета bxshef/insync
второй вариант дал бы bitrix/modules/bxshef.shef.insync/ — каталог, которого
Битрикс не знает.
На стороне проекта-потребителя Composer 2.2+ требует явного разрешения
плагина, иначе в неинтерактивном режиме (CI) он не отработает и пакет ляжет в
vendor/bxshef/insync:
{
"config": {
"allow-plugins": {
"composer/installers": true
}
}
}
bitrix-module помечен в исходниках composer/installers как deprecated, remove on the major release, поэтому в require стоит потолок
"composer/installers": "^1.0 || ^2.0". Снимут потолок — модуль уедет в чужой
каталог.
Каталог модуля браузеру недоступен: в поставке nginx стоит deny all на
^/bitrix/(modules|local_cache|stack_cache|managed_cache|php_interface).
Поэтому фронт и раскладывается в /bitrix/js.
Проверить на стенде:
/bitrix/modules/shef.insync/install/js/shef-insync/ui-anchors/script.js -> 403
/bitrix/js/shef-insync/ui-anchors/script.js -> 200
/bitrix/components/shef.insync/import.stat.local/class.php -> есть на диске
/local/components/shef.insync/ -> нет (копия 1.x убрана)
Каталоги импорта — вне корня сайта, ссылки на них нет вовсе, см. security.md.
Полная процедура проверки на портале — в portal-check.md: шаги с ожидаемым результатом, отдельно обновление с 1.x и запуск примеров на живом ядре. Тестами рантайм Битрикса не покрыть, поэтому эта процедура и есть тест.