Итак, что нам нужно:
- Конфигурация с поддержкой БСП.
- Jenkins который выведен «наружу».
- Python.
- Три или четыре кружки кофе(шутка)
Задумка следующая: у нас исходники хранятся на bitbucket, значит нужно интегрировать его из Jenkins, чтобы когда мы делали merge ветки develop в master, CI подхватывал все это добро и самостоятельно подключал к базе.
ШАГ 1. ОБРАБОТКА-ИНТЕГРАТОР НА 1С.
Данная обработка вызывается CI в пакетном режиме. Как параметр запуска передается список полных путей к файлам которые нужно подключить к базе. Многие штуки (интеграция из чатом Slack, подключение внешней обработки/отчета) у нас уже интегрированы в конфигурацию. Обработка парсит параметры запуска и подключает файлики к базе. После подключения приходит сообщение в корпоративный чат о результатах. Если файлика еще нет в базе – его необходимо подключить вручную. Вот небольшие фрагменты кода.
Код который парсит параметры запуска и помещает файлы во временное хранилище.
&НаКлиенте
Функция ПоместитьВнешниеОбработкиВХранилище(ПараметрЗапуска)
МассивСтрок = СтроковыеФункцииКлиентСервер.РазложитьСтрокуВМассивПодстрок(ПараметрЗапуска, ";");
МассивОшибок = Новый Массив;
МассивАдресов = Новый Массив;
Для каждого СтрокаИзМассива Из МассивСтрок Цикл
Сообщить(СтрокаИзМассива);
Файл = Новый Файл(СтрокаИзМассива);
Если Не Файл.Существует() Тогда
СообщениеОбОшибке = НСтр("ru='Файл не найден.'", ОбщегоНазначенияКлиентСервер.КодОсновногоЯзыка());
МассивОшибок.Добавить(Новый Структура("Сообщение, ПутьКФайлу", СообщениеОбОшибке, СтрокаИзМассива));
Продолжить;
КонецЕсли;
АдресФайла = "";
Если НЕ ПоместитьФайл(АдресФайла, СтрокаИзМассива, , Ложь, УникальныйИдентификатор) Тогда
СообщениеОбОшибке = НСтр("ru='Не удалось загрузить файл на сервер.'", ОбщегоНазначенияКлиентСервер.КодОсновногоЯзыка());
МассивОшибок.Добавить(Новый Структура("Сообщение, ПутьКФайлу", СообщениеОбОшибке, СтрокаИзМассива));
Продолжить;
КонецЕсли;
МассивАдресов.Добавить(Новый Структура("ПутьКФайлу, АдресФайла, ИмяФайла, РасширениеФайла", СтрокаИзМассива, АдресФайла, Файл.ИмяБезРасширения, Файл.Расширение));
КонецЦикла;
Возврат Новый Структура("МассивАдресов, МассивОшибок", МассивАдресов, МассивОшибок);
КонецФункции // ПоместитьВнешниеОбработкиВХранилище()
Здесь также происходит заполнение служебных структур и массив (МассивАдресов, МассивОшибок) для последующей обработки данных.
А вто код, который подключает обработки к базе с помощтью функционала БСП.
&НаСервере
Функция ВыполнитьПодключениеДоступныхОбработок(МассивАдресов)
ТекстСообщения = "";
Для каждого АдресОбработки Из МассивАдресов Цикл
ИмяВнОбработки = ВнешниеОбработки.Подключить(АдресОбработки.АдресФайла, , Ложь);
СозданныйОбъект = ВнешниеОбработки.Создать(ИмяВнОбработки);
Попытка
СведенияОВнешнейОбработке = СозданныйОбъект.СведенияОВнешнейОбработке();
Исключение
ШаблонСообщения = "Внешняя обработка _%1_ имеет неправильный формат данных.";
ШаблонСообщения = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(ШаблонСообщения, АдресОбработки.ПутьКФайлу);
ТекстСообщения = ТекстСообщения + ШаблонСообщения + Символы.ПС;
Продолжить;
КонецПопытки;
ОбработкаСсылка = Справочники.ДополнительныеОтчетыИОбработки.НайтиПоНаименованию(СведенияОВнешнейОбработке.Наименование);
Если ОбработкаСсылка.Пустая() Тогда
ШаблонСообщения = "Внешняя обработка _%1_ еще не подключена к базе. Первый раз обработку нужно подключить вручную.";
ШаблонСообщения = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(ШаблонСообщения, АдресОбработки.ПутьКФайлу);
ТекстСообщения = ТекстСообщения + ШаблонСообщения + Символы.ПС;
Продолжить;
КонецЕсли;
ПараметрыРегистрации = ПолучитьПараметрыРегистрацииОбработки();
Если ВРег(АдресОбработки.РасширениеФайла) = ".ERF" Тогда
ПараметрыРегистрации.ЭтоОтчет = Истина;
ИначеЕсли ВРег(АдресОбработки.РасширениеФайла) = ".EPF" Тогда
ПараметрыРегистрации.ЭтоОтчет = Ложь;
Иначе
ШаблонСообщения = "Внешняя обработка _%1_ не может быть подключена к базе. Расширение файла не соответствует расширению внешнего отчета (ERF) или обработки (EPF).";
ШаблонСообщения = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(ШаблонСообщения, АдресОбработки.ПутьКФайлу);
ТекстСообщения = ТекстСообщения + ШаблонСообщения + Символы.ПС;
Продолжить;
КонецЕсли;
ПараметрыРегистрации.ИмяФайла = АдресОбработки.ИмяФайла;
ПараметрыРегистрации.АдресДанныхОбработки = АдресОбработки.АдресФайла;
ОбработкаОбъект = ОбработкаСсылка.ПолучитьОбъект();
Попытка
ДополнительныеОтчетыИОбработки.ЗарегистрироватьОбработку(ОбработкаОбъект, ПараметрыРегистрации);
Исключение
ШаблонСообщения = "Обработку _%1_ не удалось автоматически подключить к базе.";
ШаблонСообщения = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(ШаблонСообщения, АдресОбработки.ПутьКФайлу);
ТекстСообщения = ТекстСообщения + ШаблонСообщения + Символы.ПС;
КонецПопытки;
ОбработкаОбъект.Записать();
ШаблонСообщения = "Обработка _%1_ успешно автоматически подключена к базе.";
ШаблонСообщения = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(ШаблонСообщения, АдресОбработки.ПутьКФайлу);
ТекстСообщения = ТекстСообщения + ШаблонСообщения + Символы.ПС;
КонецЦикла;
Возврат ТекстСообщения;
КонецФункции // ВыполнитьПодключениеДоступныхОбработок()
В коде сразу генерируются служебные сообщения, которые потом будут выведены в корпоративный чатик.
Шаг 2. Скрипт на Python.
Идея скрипта состоит в том, чтобы через команду git diff сравнить локальный master и master удаленный, а файлы которые изменились – записать в базу. Скрипт состоит из нескольких логических блоков. Первый блок делает пресловутый git diff и получает список измененных файлов.
def getStringWithChangedFiles(gitexepath, branchtocompare, repopath): try: fileList = subprocess.check_output([gitexepath, 'diff', branchtocompare, '--name-only']).decode("utf-8") except subprocess.CalledProcessError: raise Exception('Не удалось выполнить команду получения списка измененных файлов.') stringWithChangedFiles = '' print(fileList) for fileName in fileList.split("\n"): if not ('epf' in fileName or 'erf' in fileName): continue if not stringWithChangedFiles: stringWithChangedFiles = repopath + "\\" + fileName else: stringWithChangedFiles += ';' + repopath + "\\" + fileName return stringWithChangedFiles
Участок кода который запускает 1С в пакетном режиме и передает на вход обработки из шага №1 список измененных файлов.
def getStringWithChangedFiles(gitexepath, branchtocompare, repopath): try: fileList = subprocess.check_output([gitexepath, 'diff', branchtocompare, '--name-only']).decode("utf-8") except subprocess.CalledProcessError: raise Exception('Не удалось выполнить команду получения списка измененных файлов.') stringWithChangedFiles = '' print(fileList) for fileName in fileList.split("\n"): if not ('epf' in fileName or 'erf' in fileName): continue if not stringWithChangedFiles: stringWithChangedFiles = repopath + "\\" + fileName else: stringWithChangedFiles += ';' + repopath + "\\" + fileName return stringWithChangedFiles
В скрипте есть еще некоторые служебные процедуры, но в рамках данной статьи приводить их не будем.
ШАГ 3. КОНФИГУРИРУЕМ JENKINS.
Перейдем к самому вкусному - конфигурированию нашего job-а. Для начала укажем репозиторий откуда тянем изменения.
Далее необходимо указать что удаленный мастер будет копироваться в локальную ветку localmaster (в конце мы делаем merge).
Следующая настройка – самая важная для интеграции из bitbucket, а именно – генерирование токена аутентификации. Он нам нужен чтобы bitbucket мог запускать jobs на нашем CI удаленно. Токен должен быть сверхсекретным ;)
Ну и следующая настройка – непосредственный вызов скрипта через пункт Execute Windows batch command.
ШАГ 4. НАСТРОЙКИ BITBUCKET HOOK.
Чтобы bitbucket мог дергать наш CI нам нужно настроить так называемый Jenkins Hook. Перейдем в настройки репозитория в раздел Integrations подраздел Hooks. В меню Select a hook выберем пункт Jenkins.
Нам нужно заполнить всего три поля. Endpoint – сюда пишем адрес нашего CI сервера в формате http|s://username:password@domain:port/path, где username - User ID, а password - API Token. Посмотреть данные настройки можно если пойти в меню People, далее выбрать пользователя и открыть секцию API Token из настроек.
Project name – имя проекта, который мы выбрали в Jenkins. Здесь нужно заметить, что имена job-ам нужно стараться давать без пробелов, а если у вас установлен плагин для каталогов в списке проектов, путь к проекту будет выглядеть так Auto_deploy/job/Auto_deploy_test, где Auto_deploy – имя каталога, Auto_deploy_test – имя проекта.
Поле Token содержит наш токен аутентификации, который мы сгенерировали на шаге №3.
Вот собственно и все. Результаты работы будут примерно таковы
В подхода есть некоторые недостатки, а именно, Jenkins при каждом комите будет проверять наши ветки на изменения, что наверное затратно по производительности. С другой стороны процесс переноса наработок в продакшен хоть немного, но стает автоматическим.
Оригинал статьи. Happy Coding :-)