О чём это
Год назад у меня была задача: править расширения боевой УТ 11.5 с ноутбука, находясь в другом часовом поясе от сервера, и делать это так, чтобы кассы не вставали. Конфигуратор — приложение с графическим интерфейсом, RDP до сервера был медленным, а любая ошибка стоила простоя магазинов.
Выяснилось, что весь цикл разработки расширения — выгрузка, правка, проверка компиляции, применение к базе и контроль результата — закрывается пакетным режимом DESIGNER без единого клика. Но по дороге лежит несколько мин, на которых я подорвался лично и о которых почти нигде не написано.
Главная из них: штатная команда загрузки конфигурации не компилирует код и возвращает нулевой код возврата на заведомо битом модуле. Если строить пайплайн наивно, вы будете уверены, что выкатили рабочий код, пока не позвонит кассир.
Статья описывает рабочий цикл целиком, с проверками, которые действительно ловят ошибки. Платформа 8.3.27, конфигурация УТ 11.5, сервер на Linux. Для Windows команды те же, отличается только обвязка.
Команды, из которых складывается цикл
| Задача | Команда |
|---|---|
| Выгрузить расширение в файлы | DESIGNER /DumpConfigToFiles <dir> -Extension <имя> |
| Загрузить расширение из файлов | DESIGNER /LoadConfigFromFiles <dir> -Extension <имя> |
| Применить изменения к базе | DESIGNER /UpdateDBCfg -Extension <имя> |
| Проверить компиляцию | DESIGNER /CheckModules -Extension <имя> -Server |
| Выгрузить внешнюю обработку в файлы | DESIGNER /DumpExternalDataProcessorOrReportToFiles <dir> <file.epf> |
| Собрать обработку из файлов | DESIGNER /LoadExternalDataProcessorOrReportFromFiles <root.xml> <out.epf> |
Ключ -AllExtensions вместо конкретного имени выгружает все расширения разом, каждое в подкаталог <dir>/<имя расширения>. Удобно, когда нужно снять слепок состояния базы целиком.
При выгрузке внешней обработки корневой XML пишется рядом с каталогом, а не внутрь него: указали /tmp/proc — получите каталог /tmp/proc и файл /tmp/proc.xml. Это неочевидно и ломает наивные скрипты, которые ожидают всё в одной папке.
Мина первая: загрузка не значит компиляция
Наивный пайплайн выглядит так: выгрузили, поправили Module.bsl, загрузили обратно, применили к базе. Проверять вроде нечего — команды отработали, коды возврата нулевые.
Проблема в том, что /LoadConfigFromFiles не компилирует модули. Она разбирает XML-структуру и складывает тексты модулей в конфигурацию как есть. Если в модуле синтаксическая ошибка — незакрытая скобка, опечатка в имени процедуры, лишняя точка с запятой — команда завершится с кодом 0 и напишет в лог, что всё хорошо.
Ошибка всплывёт позже: при /UpdateDBCfg, либо вообще в момент первого обращения пользователя к этому коду. То есть на кассе.
Единственная настоящая проверка — /CheckModules:
DESIGNER /CheckModules -Extension <имя> -Server
Она реально компилирует модули на стороне сервера и выдаёт ошибки с номерами строк и именами модулей. Занимает около минуты на среднее расширение. Это дорого для цикла «поправил-проверил», но абсолютно обязательно перед применением к бою.
Ключ -Server важен: без него проверяются не все контексты выполнения, и часть ошибок в серверных процедурах пройдёт мимо.
Правило, к которому я пришёл: между /LoadConfigFromFiles и /UpdateDBCfg всегда стоит /CheckModules, и пайплайн останавливается, если она вернула ошибки. Без этого гейта автоматизация опаснее ручного конфигуратора — тот хотя бы подсвечивает синтаксис при сохранении.
Мина вторая: без Xvfb ничего не запустится
На Linux-сервере без графической подсистемы любая команда DESIGNER падает с сообщением:
Unable to initialize GTK+
Платформа тянет за собой GTK даже в пакетном режиме, где никакого интерфейса нет и не предполагается. Лечится виртуальным X-сервером:
xvfb-run -a 1cv8 DESIGNER /S <сервер>\<база> /N <пользователь> /P <пароль> \
/DumpConfigToFiles /tmp/cfg -Extension <имя>
Ключ -a заставляет xvfb-run самому выбрать свободный номер дисплея — без него параллельные запуски дерутся за :99 и падают.
Отсюда же вытекает следующая проблема.
Мина третья: модальные диалоги вешают процесс молча
Раз есть виртуальный экран, значит на нём могут появляться окна. И они появляются.
Самый частый случай — «Предупреждение безопасности» при первом открытии внешней обработки. В обычном конфигураторе вы нажимаете «Да» и забываете. В Xvfb окно висит на невидимом экране, процесс ждёт ответа, а вы наблюдаете команду, которая «выполняется» третий час без единой строчки в логе.
Диагностика неприятная, потому что дисплеев в системе бывает несколько, и окно живёт только на одном из них:
for A in $(pgrep -a Xvfb | grep -oE "/tmp/xvfb-run[^ ]*/Xauthority"); do
D=$(pgrep -a Xvfb | grep "$A" | grep -oE ":[0-9]+" | head -1)
echo "$D -> $(XAUTHORITY=$A DISPLAY=$D xwininfo -root -tree 2>/dev/null | grep -c 1cv8)"
done
Цикл пробегает по всем живым Xvfb и печатает, на каком дисплее сколько окон платформы. Дальше можно посмотреть, что там:
XAUTHORITY=<auth> DISPLAY=:NNN import -window root /tmp/screen.png
И даже нажать кнопку, если очень нужно:
XAUTHORITY=<auth> DISPLAY=:NNN xdotool mousemove 967 484 click 1
Способ работающий, но это лечение симптома. Правильный вывод: в автоматизированном цикле не должно быть операций, способных открыть модальное окно. Если такая операция появилась — её надо убирать из пайплайна, а не учиться кликать вслепую.
Мина четвёртая: обрыв ssh убивает конфигуратор на середине
/UpdateDBCfg на большой конфигурации идёт минутами. Если запустить её обычной ssh-командой и потерять связь, процесс получит SIGHUP и умрёт посреди применения изменений к базе данных. Состояние после такого — отдельное приключение.
Запускать долгие команды нужно так, чтобы они пережили обрыв:
setsid nohup xvfb-run -a 1cv8 DESIGNER ... </dev/null >/tmp/upd.log 2>&1 &
setsid выносит процесс в отдельную сессию, nohup глушит SIGHUP, </dev/null отвязывает stdin — без него процесс может получить SIGTTIN при попытке чтения с закрытого терминала.
И отдельное правило: не плодить параллельные запуски. Два DESIGNER на одну базу начнут драться за блокировку конфигурации, и в лучшем случае один упадёт, в худшем — оба повиснут. Перед запуском стоит убедиться, что предыдущий завершился.
Причём убедиться внимательно.
Мина пятая: pgrep находит сам себя
Естественная проверка «конфигуратор ещё работает?» выглядит так:
pgrep -f "1cv8 DESIGNER"
Если запустить её внутри цикла ожидания, который сам был вызван как bash -c '... 1cv8 DESIGNER ...', то pgrep найдёт собственную ssh-команду: строка с этим текстом присутствует в командной строке обёртки.
Результат — вечный цикл ожидания процесса, который завершился полчаса назад. Я потерял на этом ровно столько.
Лечится матчингом по полному пути бинарника:
pgrep -af "x86_64/8.3.27.1688/1cv8 DESIGNER"
и обязательной проверкой вывода глазами, а не только кодом возврата.
Кто держит базу: диагностика через ras/rac
Рано или поздно DESIGNER упадёт с сообщением «Ошибка блокировки информационной базы для конфигурирования». Значит, кто-то держит конфигуратор открытым.
В логе /Out видно, чей это сеанс: имя компьютера, номер сеанса, время начала. Но дальше начинается интересное — надо понять, там работают или человек ушёл домой, забыв закрыть окно. В первом случае надо ждать, во втором — снимать.
Сначала поднимаем администрирование кластера. ras по умолчанию не запущен, поэтому rac отвечает «Connection refused»:
setsid nohup ras cluster --port=1545 localhost:1540 </dev/null >/dev/null 2>&1 &
Дальше смотрим сеанс:
rac session info --session=<uuid> --cluster=<uuid> localhost:1545
Как читать вывод — самое важное во всём разделе:
calls-last-5min,cpu-time-last-5min,duration-last-5min,bytes-last-5minравны нулю → сеанс висит, в нём никто не работает;last-active-atтикает даже у забытого окна, потому что клиент периодически пингует сервер. По этому полю судить об активности нельзя, и это ловушка, в которую попадают почти все;- суммарные
duration-allиcpu-time-totalв сотни миллисекунд за несколько часов означают, что человек поработал минуту и ушёл.
Забытый сеанс снимается:
rac session terminate --session=<uuid> --cluster=<uuid> localhost:1545
Отдельно стоит проверить, не ваш ли это собственный зависший DESIGNER из прошлого запуска — по моему опыту, виновником чаще всего оказывается именно он.
Мина шестая: после рестарта сервера умирает ras
Скрипты применения обычно закрывают базу от пользователей на время работ и открывают обратно:
rac infobase update --sessions-deny=on ... # закрыли
# ... работы ...
rac infobase update --sessions-deny=off ... # открыли
Если между этими шагами случился systemctl restart srv1cv8 — а он случается, например при смене параметров кластера — то ras умирает вместе с сервером. Команда снятия блокировки молча не выполняется, база остаётся закрытой, а HTTP-сервисы начинают отдавать 500.
Со стороны это выглядит как «мы всё сделали правильно, а приложение легло».
Вывод простой: финальное состояние проверять фактом, а не кодом возврата скрипта. Две проверки, обе обязательны:
rac infobase info --infobase=<uuid> ... | grep sessions-deny
curl -u <логин> http://127.0.0.1/<база>/hs/<корень>/status
Первая говорит, что база открыта по мнению кластера. Вторая — что она действительно отвечает. Ожидаем 200 или 401, но не 500.
Контроль: как убедиться, что в базе именно ваш код
/UpdateDBCfg отработала, ошибок нет. Но лежит ли в базе то, что вы правили?
Вопрос не праздный. Расширение могли параллельно менять с другой стороны — у нас, например, к базе имеет доступ подрядчик. Или ваш патч не доехал по какой-то из причин выше.
Надёжный способ — выгрузить расширение заново и сравнить с тем каталогом, который вы правили:
DESIGNER /DumpConfigToFiles /tmp/cfg_after -Extension <имя>
diff -r -x ConfigDumpInfo.xml /tmp/cfg_patched /tmp/cfg_after && echo ИДЕНТИЧНО
ConfigDumpInfo.xml исключать обязательно — там служебные версии выгрузки, они различаются всегда, и без -x вы будете вечно видеть расхождение.
Тот же приём работает и в обратную сторону: свежий дамп против дампа прошлой сессии показывает, менял ли расширение кто-то ещё, пока вас не было.
Мина седьмая: CRLF против LF
И вот здесь ждёт последняя засада. Вы делаете diff -rq двух дампов и получаете «файлы различаются» на весь файл целиком, хотя глазами разницы не видно.
Причина в переводах строк. 1С выгружает модули с CRLF. Если файл потом правился скриптом на Python, sed или любым другим инструментом с настройками по умолчанию, он превратится в LF. Содержательно файлы идентичны, побайтово — различаются в каждой строке.
Сравнивать надо с нормализацией:
diff <(tr -d '\r' < a.bsl) <(tr -d '\r' < b.bsl)
Для рекурсивного сравнения каталогов проще всего прогнать оба дерева через tr во временные копии, либо использовать diff --strip-trailing-cr.
Как выглядит цикл целиком
Собирая всё вместе, безопасный цикл правки боевого расширения получается такой:
- Снять дамп с боя —
/DumpConfigToFiles, это же ваш бэкап и точка отката. - Сравнить с дампом прошлой сессии — убедиться, что расширение не меняли без вас.
- Внести правку в копию каталога, сохраняя CRLF.
- Залить в тестовую базу —
/LoadConfigFromFiles. - Прогнать гейт —
/CheckModules -Server. Есть ошибки — цикл останавливается, в бой ничего не едет. - Закрыть базу от пользователей, применить в бою:
/LoadConfigFromFiles+/UpdateDBCfg. - Открыть базу, проверить фактом:
sessions-denyснят, HTTP-сервис отвечает. - Снять дамп заново и сравнить с патченным каталогом — «идентично» означает, что в базе действительно ваш код.
Шаги 5, 7 и 8 — это те самые проверки, которых нет в наивном варианте и без которых автоматизация превращается в рулетку.
Реальный простой касс при таком цикле — около двух минут: столько занимает закрытие базы, применение и открытие обратно. Всё остальное делается на тестовой базе и кассиров не касается.
Что нельзя сделать из командной строки
Честности ради, пакетный режим закрывает не всё:
- Внешнюю обработку с формой собрать из файлов не получится.
/LoadExternalDataProcessorOrReportFromFilesпадает с исключением XDTO при чтенииForm.xmlдаже на нетронутом дампе, сделанном той же версией платформы. Обработки без формы собираются нормально. - Выполнить произвольный код через
1cv8 ENTERPRISE /Executeне выйдет — команда лишь открывает обработку, тело модуля объекта при этом не исполняется. Это тема отдельной статьи. - Интерактивная отладка остаётся за конфигуратором.
Выводы
Пакетный режим DESIGNER полностью закрывает цикл разработки расширений и позволяет держать 1С в нормальном CI рядом с остальным стеком. Но три вещи надо принять как данность:
/CheckModules -Server— единственный настоящий гейт компиляции. Всё остальное молча пропустит битый код.- Успешный код возврата ничего не гарантирует. Финальное состояние проверяется фактом: дамп плюс
diff,sessions-denyплюс живойcurl. - Окружение важнее команд. Xvfb,
setsid nohup, отсутствие модальных окон и честныйpgrep— без этого цикл будет виснуть в местах, где вы не ожидаете.
Если выстроить это один раз, дальше правка боевого расширения занимает минуты и не требует ни RDP, ни присутствия в офисе, ни остановки торговли.
Платформа 8.3.27, УТ 11.5, сервер 1С на Linux. Все приёмы проверены на боевых базах розничной сети.
Вступайте в нашу телеграмм-группу Инфостарт