Примерно через пятнадцать минут после push в feature-ветку мы уже видели, упадёт ли проведение документа в тестовой базе, а не узнавали об этом в ночной установке. Бизнес сравнивал цикл поставки с Java и web и попросил у 1С такую же прозрачность: статус сборки и ссылка на тесты в карточке MR, а не «зелёный свет» после ручного прогона на копии прода.
Сначала завели Jenkins и общий репозиторий пайплайнов, потом повесили MR в GitLab, потом - эталон на каждую сборку. Дальше - схема, которую мы действительно гоняем на агентах.
Запрос бизнеса
Инициатором стали не разработчики с конференции, а руководство, которое сравнивало циклы поставки. Java и web уже показывали статус сборки и ссылку на тесты в карточке MR; 1С отдавала результат после ручного прогона на копии прода. Аргумент, который прошёл в бюджет: сдвинуть поиск ошибок на этап до master, а не ловить регресс в релизной неделе.
Внедряли поэтапно: первый MR с полным контуром вышел не в первую неделю. Цель зафиксировали так: каждый MR получает воспроизводимую сборку, тот же набор проверок, что и релиз, и артефакты, которые ревьювер открывает без VPN в соседний отдел.
От хранилища к Git
Хранилище конфигурации учило дисциплине, но ломало изоляцию задач. Один разработчик вёл три темы, объекты смешивались, перенос между базами делался руками. CI «после коммита в общую ветку» опаздывал: конфликт метаданных всплывал, когда второй уже залил свою доработку.
EDT и Git стали условием нормального контура. MR в GitLab запускает пайплайн до слияния; ревьювер видит Allure, SonarQube и лог загрузки конфигурации в одной карточке. GitLab сначала был зеркалом выгрузки из хранилища, без живой разработки; позже стал точкой входа для MR и webhook на Jenkins. Jenkins выбрали в начале пути, потому что он уже стоял в компании; если бы выбирали сейчас, часть логики могла бы жить в native CI GitLab. Репозиторий настроек Jenkins, общие Jenkinsfile и Pipeline Libraries при этом переносимы между разными CI-серверами.
Ночную сборку cf и утренний ручной прогон заменили тем же сценарием в пайплайне MR: каждый push должен воспроизводить цепочку, близкую к релизному тегу, иначе зелёная ветка в Git не означает готовность комплекта поставки.
Ветки и релиз
master - только то, что уже прошло MR; задача живёт в feature-ветке. Регламентный job подтягивает master в открытые ветки; при конфликте слияния автор получает письмо, а не сюрприз в пятницу вечером.
Релиз: master блокируем, ставим тег на коммит, собираем комплект поставки (выгрузка cf и сопутствующих файлов из того же коммита, что и тег), гоняем финальный прогон, ставим в прод, снимаем блокировку. По тегу потом собираем тот же комплект, что ушёл в прод; на MR в Nexus уходят координаты промежуточных сборок для отладки, в релиз - только артефакты с тега.
| Этап | План, дни | Факт до CI на MR | После эталонной ИБ на MR |
|---|---|---|---|
| Исправление по ревью | 1 | 3–4 | 1–2 |
| Ручной прогон на копии | 2 | 5 | 0 (в пайплайне) |
| Сборка комплекта | 1 | 2 | 1 |
| Приёмка перед продом | 2 | 4 | 2–3 |
Цифры в таблице ниже - условные дни по внутреннему учёту этапов в календаре релиза: куда уходило время, пока обратная связь приходила после слияния. После переноса проверок на MR отдельный «ручной прогон на копии» перестал быть строкой в плане.
Репозиторий и пайплайн
Мы привели проекты к одному скелету: исходники конфигурации, расширений и обработок под EDT в одном дереве; каталог init-data с XML для инициализации; каталог tests с Vanessa и xUnitFor1C; Jenkinsfile на две страницы, остальное в Pipeline Libraries с версией библиотеки на проект. Отдельный git-репозиторий хранит vars и shared-модули Jenkins: сменили версию библиотеки в одном теге, откатили проект, не трогая продуктовый код.
На старте артефакты лежали на сетевых шарах; позже тяжёлые cf и комплекты уехали в Nexus, а пайплайн только ссылался на координаты. Allure с первых недель собирает и тесты, и шаги загрузки cf; SonarQube смотрит стиль и запахи по нашим правилам и не заменяет CheckConfig.
Чтобы новый проект не изобретал каталоги, зафиксировали эталонное дерево: основная поставка в src/Configuration, расширения в src-extensions, внешние обработки в src-epf, данные инициализации в init-data, Vanessa и xUnit в tests, служебные epf агента в tools. EDT открывает один workspace на весь репозиторий.

Скрипт prepareInfobase один; на MR меняется только имя эталонной копии в vars.
Что крутится на MR
Сборка последовательно-параллельная. Сразу после checkout параллельно идут SonarQube и подготовка тестовой ИБ. Когда база готова, параллельно стартуют дым Vanessa, серверные проверки конфигурации и валидация проекта EDT; сценарии по блокам (закупки, склад, закрытие месяца) едут на других агентах. Unit-тесты xUnitFor1C в конце, их мало и они быстрые. Все отчёты сливаются в Allure для MR.
Два слоя «синтаксиса» путают новичков. EDT-валидация ловит битые метаданные и ошибки модулей в проекте. На агенте гоняем отдельно: для EDT - headless через 1cedtcli (validate workspace) или ring-команду той же версии EDT, что у команды; для сервера - конфигуратор или ibcmd начиная с 8.3.20+: загрузка, обновление структуры, CheckConfig и CheckModules. Падение на втором слое при зелёной EDT обычно значит, что ветка не смержена с master или эталонная база устарела.
Загрузка ветки в ИБ: export из EDT в каталог выгрузки или cf, затем либо LoadCfg к каталогу или cf и UpdateDBCfg для серверной базы, либо ibcmd infobase config import и ibcmd infobase config apply. Расширения из src-extensions - отдельным шагом (LoadCfg к cfe или import расширения), не в одной пачке с основной конфигурацией. После применения конфигурации прогоняем обработчики обновления из модуля «Обновление информационной базы» (в типовых на БСП это штатный контур); на MR накатываем ветку на эталон актуальной версии, а не полный исторический лadder релизов с нуля. Именно здесь часто всплывают расхождения алгоритмов проведения, которые потом бьют дымовые тесты.
rem После export EDT: каталог src\Configuration или out\main.cf
rem Основная конфигурация (серверная ИБ, платформа 8.3.20+)
designer /S "srv\base_ci" /N"Agent" /P"" /LoadCfg "out\Configuration" /UpdateDBCfg -server
rem Расширения — отдельно, если есть src-extensions
designer /S "srv\base_ci" /N"Agent" /P"" /LoadCfg "out\extensions\MyExt.cfe" /UpdateDBCfg -server
designer /S "srv\base_ci" /N"Agent" /P"" /Execute "tools\RunUpdateHandlers.epf" /C"Run"
rem Linux-агент: ibcmd infobase config import + config apply вместо LoadCfg
На Linux-агентах импорт и apply через ibcmd, проверки - отдельными вызовами той же утилиты или конфигуратора из образа ВМ. Jenkinsfile один, без копий под Windows и Linux.
Дым и сценарии - Vanessa Automation и xUnitFor1C в headless-режиме (тонкий клиент или TestClient по сценарию), отчёт в JUnit и Allure. Ревьюверу хватает упавшего шага «Провести документ закупки» со скрином формы, без поиска rphost.log.
На схеме ниже три рабочих контура загрузки за одним шагом prepareInfobase в Pipeline Library.

Проект передаёт путь к выгрузке и имя эталона; в shared-модуле выбирается ветка под Windows или Linux.
Когда параллельных MR стало много, агенты выросли примерно с пяти до пятидесяти. Узкое место - не CPU, а одновременные копии ИБ на дисках; часть подготовки вынесли на отдельные ВМ с SSD. Лицензии сервера и политика «одна тестовая база - свой rphost на агенте» закладываются в план расширения, иначе пятьдесят job одновременно упираются не в Jenkins, а в ключи и место под копии.
Эталонная база и XML
На каждый MR: разворачиваем эталонную копию, накатываем конфигурацию ветки, выполняем обработчики обновления, заливаем данные из init-data. Для проверки реструктуризации - отдельный сценарий: откат копии к снимку эталона предыдущей версии, к файловому дампу или к копии «N минус 1», затем обновление с ветки; включаем технологический журнал с отбором по excp, по событиям СУБД (DBMSSQL или DBPOSTGRS) и по событиям обновления конфигурации.
Штатный контур - EnterpriseData через «Универсальный обмен данными» (внешняя обработка, правила в XML). Для узких наборов данных в нашей поставке - свои epf и пары процедур в духе ВыгрузитьДанныеВФайл и ЗагрузитьДанныеИзФайла в служебных обработках БСП; имена в типовых могут отличаться. Правило для разработчиков: поменял движения документа - обновил XML в init-data, иначе дым упадёт справедливо.
Процедура ЗагрузитьInitData(ПутьККаталогу) Экспорт
// Псевдокод: в CI — designer /Execute служебной epf, не объект метаданных типовой.
// Пример: designer /Execute "tools\LoadInitData.epf" /C"Загрузить;" + ПутьККаталогу + "init-enterprise.xml"
ПутьФайла = ПутьККаталогу + "init-enterprise.xml";
ВнешняяОбработка = ВнешниеОбработки.Создать("tools\LoadInitData.epf");
ВнешняяОбработка.ЗагрузитьEnterpriseData(ПутьФайла);
КонецПроцедуры
Инициализация идёт тем же механизмом, что разовая миграция, а не ручной набор документов в тонком клиенте.
Устаревшие XML копятся сами. Раз в неделю регламентный job прогружает свежий набор на чистый эталон и удаляет файлы, не участвовавшие в успешных сборках за восемь недель. Иначе init-data превращается в архив старых движений.
Когда дым ломается после смены алгоритма проведения, сначала смотрим обработчики обновления и XML, потом тест. Дым закрывает открытие форм, проведение, печать и пару регламентных отчётов; длинные сценарии Vanessa режем по подсистемам, чтобы один MR не монopolizировал все агенты.
Метрики и Kubernetes
Grafana с Prometheus смотрит долю упавших сборок по MR и сбои фоновых job: обновление эталонов, сборка комплектов, очистка XML. Алерт на тишину регламентного пайплайна так же важен, как на ошибку: мёртвый cron не кричит в чат, а эталон устаревает незаметно. На дашборде держим failed_builds, длительность подготовки ИБ и длину очереди агентов.
GitLab шлёт webhook на merge_request и push в master; Jenkins поднимает сборку с параметрами MR и публикует статус коммита обратно. Webhook обязателен: иначе сборку снова жмут вручную. Job только ставится в очередь; rphost и копирование эталона остаются на агентах.

K8s для rphost и копий ИБ мы пока не тащим: отладка сеанса, мусор от многократных копий, лицензии и образы из сообщества с оговорками по СУБД. В планах K8s для Nexus и мониторинга; тестовые агенты с платформой 1С по-прежнему на ВМ, где базе нужен постоянный диск.
// Упрощено для иллюстрации; на агенте — grep/скрипт по шаблону ТЖ (excp, DBMSSQL/DBPOSTGRS).
Функция ЕстьКритичнаяОшибкаТЖ(ПутьКФайлу) Экспорт
Текст = Новый ЧтениеТекста(ПутьКФайлу, КодировкаТекста.UTF8);
Пока Не Текст.КонецПотока() Цикл
Строка = Текст.ПрочитатьСтроку();
Если СтрНайти(Строка, "excp") > 0 Тогда
Возврат Истина;
КонецЕсли;
КонецЦикла;
Возврат Ложь;
КонецФункции
Фрагмент упрощён: полный разбор ТЖ - grep на агенте по шаблону под вашу СУБД; в 1С - служебная epf для локального прогона перед push.
Практические выводы
Зелёный Sonar без прогона на эталоне с init-data не показывает проведение на вашей структуре метаданных. Разводите EDT-validate и серверные CheckConfig после загрузки, включайте обработчики обновления в подготовку базы - иначе дым ловит следствие, а не причину.
Вступайте в нашу телеграмм-группу Инфостарт