Архитектура
Две ветки — две базы:
- ветка develop → база ProdBase (рабочая, продуктивная)
- ветка test → база TestBase (тестовая)
Два режима деплоя:
- Hot deploy — динамическое обновление без остановки сеансов. Только для изменений BSL-кода.
- Full deploy — полное обновление с завершением сеансов. Для изменений метаданных.
Логика срабатывания:
|
Коммит в develop: → только BSL? → hot deploy ProdBase → auto-merge test → hot deploy TestBase → метаданные? → schedule 8:00 ProdBase → auto-merge test → full deploy TestBase
Коммит в test: → hot или full deploy TestBase → НЕТ автомержа в develop (только вручную)
Каждый день в 8:00: → full deploy ProdBase из develop |

Окружение и переменные
Все секреты хранятся в GitHub Actions Secrets и передаются в скрипт через переменные окружения. Ни один пароль не записан в файлах репозитория.
Переменные GitHub Actions Secrets
|
ADMIN_USER = deploy_svc # учётная запись администратора ИБ ADMIN_PASSWORD = *** # пароль (хранится зашифрованным в GitHub) |
Параметры сервера
|
# Адрес 1С-сервера (Windows Server с компонентами 1С) $ServerHost = "1c-app-01"
# Имя кластера (задаётся при установке сервера 1С) $Cluster = "MAIN-CLUSTER"
# Имена информационных баз в кластере $ProdBase = "ProdBase" $TestBase = "TestBase"
# Адрес RAS-сервиса (порт 1545 по умолчанию) $RasAddress = "1c-app-01:1545" |
Строки подключения к ИБ
Строка подключения к серверной ИБ состоит из имени сервера (Srvr) и имени базы в кластере (Ref). Кавычки внутри строки экранируются при передаче через командную строку.
|
# Рабочая база Srvr="1c-app-01";Ref="ProdBase";
# Тестовая база Srvr="1c-app-01";Ref="TestBase"; |
Пути к исполняемым файлам
|
# Платформа 1С (автоопределяется по последней версии в папке) $PlatformExe = "C:\Program Files\1cv8\8.3.27.1989\bin\1cv8.exe" $RacExe = "C:\Program Files\1cv8\8.3.27.1989\bin\rac.exe"
# 1C:EDT CLI (версия фиксируется при установке EDT на CI-агенте) $EdtCli = "C:\Program Files\1C\1CE\components\ 1c-edt-2025.1.4+15-x86_64\1cedtcli.exe"
# Путь к EDT-проекту (рабочая копия репозитория) $ProjectPath = "D:\CI\workspace\MyProject" |
Проблема 1: EDT и LoadConfigFromFiles
EDT-формат (.mdo-файлы) платформа не понимает напрямую. Команда /LoadConfigFromFiles ожидает XML-формат конфигуратора. Нужна конвертация.
Первый подход — через временную базу
Логика выглядела разумно: создать временную файловую базу, загрузить туда EDT через LoadConfigFromFiles, экспортировать XML через DumpConfigToFiles.
|
CREATEINFOBASE → LoadConfigFromFiles (EDT) → UpdateDBCfg → DumpConfigToFiles → XML → LoadConfigFromFiles (целевая БД) |
Изменения метаданных применялись корректно. BSL-код — нет. LoadConfigFromFiles для существующей базы загружает структуру метаданных, но модули существующих объектов не обновляет. DumpConfigToFiles экспортирует то, что загрузилось — XML без актуального BSL.
Решение — 1cedtcli напрямую
1cedtcli умеет экспортировать EDT-проект напрямую в XML-формат конфигуратора, минуя промежуточную базу. Полная строка вызова:
|
"C:\Program Files\1C\1CE\components\1c-edt-2025.1.4+15-x86_64\1cedtcli.exe" ^ -data "D:\CI\workspace\edt-ws-20250115-103000" ^ -command export ^ --project "D:\CI\workspace\MyProject" ^ --configuration-files "D:\CI\workspace\xml-20250115-103000" ^ -nl ru_RU |
Параметры:
- -data — временная рабочая папка EDT (создаётся скриптом, удаляется после)
- --project — путь к EDT-проекту (папка с файлом .project)
- --configuration-files — куда положить XML (папка с Configuration.xml)
- -nl ru_RU — локаль для сообщений
Этот XML содержит актуальный BSL из EDT-проекта. После этого стандартная цепочка:
|
# Шаг 1: загрузка XML в конфигурацию базы "C:\Program Files\1cv8\8.3.27.1989\bin\1cv8.exe" DESIGNER ^ /DisableStartupDialogs ^ /IBConnectionString "Srvr=""1c-app-01"";Ref=""ProdBase"";" ^ /N "deploy_svc" ^ /P "***" ^ /LoadConfigFromFiles "D:\CI\workspace\xml-20250115-103000" ^ /Out "D:\CI\logs\deploy-hot-ProdBase-20250115-103000.log"
# Шаг 2: динамическое обновление без остановки сеансов "C:\Program Files\1cv8\8.3.27.1989\bin\1cv8.exe" DESIGNER ^ /DisableStartupDialogs ^ /IBConnectionString "Srvr=""1c-app-01"";Ref=""ProdBase"";" ^ /N "deploy_svc" ^ /P "***" ^ /UpdateDBCfg -Dynamic+ ^ /Out "D:\CI\logs\deploy-hot-ProdBase-20250115-103000.log" |
О рисках динамического обновления
Динамическое обновление (-Dynamic+) не требует перезапуска сеансов, но у него есть обратная сторона: в момент применения изменений активные пользователи продолжают работать с предыдущей версией кода. Если модуль был изменён, а пользователь не перезашёл — он работает со «старым» кодом до конца сеанса. В редких случаях это может привести к временной несогласованности.
Второй риск — если платформа не смогла корректно переключить весь компилированный код, база может уйти в неконсистентное состояние. На практике это бывает крайне редко, но бывает.
Если после динамического обновления что-то пошло не так и база не отвечает штатно, вопрос решается двумя SQL-запросами к базе данных — сброс флагов dbschema и config приводит базу к исходному состоянию без динамики. Это занимает минуты.
Применять или нет динамическое обновление — каждая команда решает сама, исходя из своих требований к доступности и рисков конкретной конфигурации. Для нашего кейса (BSL-изменения, без схемы данных) это оправданный выбор.

Проблема 2: дедлок при управлении сеансами
Full deploy требует завершить активные сеансы перед обновлением. Первое очевидное решение — вызвать для этого HTTP-сервис, который уже был в конфигурации.
Что такое HTTP-сервис 1С
В конфигурации был написан стандартный HTTP-сервис (объект метаданных «HTTP-сервисы»). Он публиковался на веб-сервере IIS под URL вида:
|
http://1c-app-01/MyApp/hs/admin/ |
Сервис принимал команды от CI-скрипта через REST-запросы:
|
POST /hs/admin/block → запустить фоновое задание «заблокировать сеансы» POST /hs/admin/terminate → запустить фоновое задание «завершить сеансы» POST /hs/admin/unblock → запустить фоновое задание «снять блокировку» GET /hs/admin/status/{id} → проверить статус задания |
Логика: скрипт отправлял команду, получал ID задания, опрашивал /status пока не получал «выполнено». Так сделано, потому что BSL-код на сервере 1С не может выполниться мгновенно в рамках HTTP-запроса — нужен фоновый процесс.
Почему это не работает для блокировки
На практике всё вставало намертво. HTTP-сервис и фоновые задания живут в одном рабочем процессе rphost.exe.
Сценарий дедлока:
- 1. Скрипт вызывает POST /block — HTTP-запрос доходит до рабочего процесса, BSL-код запускает ФЗ «заблокировать сеансы».
- 2. Фоновое задание отрабатывает: ЗапретНачалаСеансов = Истина. Планировщик заданий останавливается.
- 3. Скрипт опрашивает /status — запрос тоже обрабатывается рабочим процессом, это работает.
- 4. Скрипт делает деплой, затем вызывает POST /unblock.
- 5. HTTP-запрос /unblock доходит до рабочего процесса. Код пытается создать фоновое задание «снять блокировку».
- 6. Планировщик заданий остановлен. Фоновое задание не запускается. Ответ не возвращается. База остаётся заблокированной навсегда.

Решение — rac.exe через RAS
rac.exe — утилита администрирования кластера серверов 1С. Она обращается к сервису RAS (Remote Administration Service) по порту 1545. RAS — это отдельный процесс 1CEnterprise83RAS.exe, полностью независимый от рабочих процессов баз данных.
Блокировка базы через rac.exe выполняется на уровне кластера, а не на прикладном уровне ИБ. Даже если база мёртвая — rac.exe всё равно пройдёт.
Полные строки вызова:
|
# Шаг 1: получить ID кластера "C:\Program Files\1cv8\8.3.27.1989\bin\rac.exe" 1c-app-01:1545 cluster list # → выводит: cluster : xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx # → name : MAIN-CLUSTER
# Шаг 2: найти UUID информационной базы по имени "C:\Program Files\1cv8\8.3.27.1989\bin\rac.exe" 1c-app-01:1545 ^ infobase summary list ^ --cluster=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx # → выводит список баз, ищем name=ProdBase, берём infobase : uuid
# Шаг 3: заблокировать начало новых сеансов "C:\Program Files\1cv8\8.3.27.1989\bin\rac.exe" 1c-app-01:1545 ^ infobase update ^ --cluster=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx ^ --infobase=yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy ^ --infobase-user=deploy_svc ^ --infobase-pwd=*** ^ --sessions-deny=on ^ --denied-message="Database update in progress. Start after 8:30." ^ --permission-code=CI_CD_Deploy
# Шаг 4: завершить конкретный активный сеанс "C:\Program Files\1cv8\8.3.27.1989\bin\rac.exe" 1c-app-01:1545 ^ session terminate ^ --cluster=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx ^ --session=zzzzzzzz-zzzz-zzzz-zzzz-zzzzzzzzzzzz
# Шаг 5: после деплоя — разблокировать "C:\Program Files\1cv8\8.3.27.1989\bin\rac.exe" 1c-app-01:1545 ^ infobase update ^ --cluster=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx ^ --infobase=yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy ^ --infobase-user=deploy_svc ^ --infobase-pwd=*** ^ --sessions-deny=off |
Параметр --permission-code=CI_CD_Deploy задаёт код разрешения. Скрипт, который запускает Designer для деплоя, передаёт этот же код в строке подключения — тогда конфигуратор подключается даже при активной блокировке:
|
# Строка подключения с кодом разрешения для сеансов-исключений Srvr="1c-app-01";Ref="ProdBase";Pwd="CI_CD_Deploy"; |
Предварительное условие: на сервере должен быть запущен RAS-сервис. Проверить и запустить:
|
# Проверить статус RAS Get-Service -Name "1CEnterprise83RAS"
# Запустить, если остановлен Start-Service -Name "1CEnterprise83RAS"
# Настроить автозапуск Set-Service -Name "1CEnterprise83RAS" -StartupType Automatic |

Что стоит запомнить
- LoadConfigFromFiles с временной базой не обновляет BSL существующих объектов. Используй 1cedtcli export --configuration-files напрямую из EDT-проекта.
- HTTP-сервис базы и rac.exe — принципиально разные инструменты. Первый зависит от работоспособности базы, второй нет. Для управления сеансами при деплое нужен второй.
- RAS на сервере — обязательное условие для rac.exe. Установи и настрой автозапуск заранее, а не в момент дедлока.
- Параметр --permission-code позволяет CI-скрипту подключиться к заблокированной базе для деплоя, не снимая блокировку раньше времени.
- Разделение деплоев на hot (BSL) и full (метаданные) позволяет рабочим базам получать исправления сразу, без ночного окна.
Вступайте в нашу телеграмм-группу Инфостарт
