Номер ревизии SVN в коде 1С 7.7

от автора

Добрый день.

Хочу поделиться способом подстановки в код 1С 7.7 номера ревизии и других значений из рабочей копии SVN.
Реализовать нижеописанное навеяло статьей, автору выражаю благодарность за вдохновение.

Предыстория

Торговая сеть средних размеров (примерно 150 магазинов), небольшой коллектив разработчиков, пишем на 1С 7.7, конфигурации в магазинах самописные.
Используется SVN сервер Collabnet Subversion, клиент TurtoiseSVN, компилятор/декомпилятор метаданных 1С gComp. Есть 1Сная база(мы зовем ее «копирка») из которой мы «раскидываем» обновления и обработки по магазинами. Магазины обновляются автоматом с помощью ConfStarter‘а.
Есть несколько самописных Java парсеров для подстановки функций (автологирования) и форматирования кода и пара bat’ников для упрощения работы с ними и с GComp:

  • Compile.bat — запускает компиляцию MD из исходников, на выходе 1cv7.compile.md. Его содержимое включает всего одну строчку:
    	"%GCOMP_PATH%\gcomp" -c -D .\REPO -F 1cv7.compile.md>.compile.log	

    где REPO — каталог с исходниками в каталоге БД

  • Build.bat — копирует исходники в отдельную папку BUILD и запускает Java-парсеры для автологирования и форматирования кода, потом GComp для компиляции и получаем на выходе 1cv7.build.md, такие MDшники идут в магазины.

Каталог, где они живут, включен в PATH, поэтому запускается все это добро прямо из каталога БД 1С, что вполне удобно.

Причина

Редко, но все же случались казусы — то не ту сборку (ревизию) закинут, то не с той ветки, то магазин не обновился, и т.п.
Необходимо было реализовать контроль за состоянием магазинов, а именно хотелось иметь достоверную информацию о том, какая именно сборка в каком магазине установлена.

Идея

По заданному шаблону получаем данные о рабочей копии с помощью TurtoiseSVN, на выходе получаем другой файл-шаблон со структурой «МаскаДляПоиска» = «ЗначениеДляЗамены» для программки-парсера.
Парсер перебирает файл-шаблон и ищет совпадения в файле с кодом. Маской для поиска является название функции. Если таковая находится — заменяется следующая после нее строчка (которая возвращает значение функции) на «ЗначениеДляЗамены» из шаблона. После этого файл передается дальше на компиляцию. Далее из магазина выгружается файл с данными и передается в офис, где полученная информация обрабатывается и консолидируется.

Реализация

Для получения данных о рабочей копии используется программа "SubWCRev.exe" из TurtoiseSVN. Она принимает три параметра:

  • Путь к рабочей копии SVN (это папка REPO в каталоге БД)
  • Путь к файлу-шаблону (файл shablon.txt в папке со скриптами)
  • Путь, куда выгружать готовый файл (я назвал его ReplacerSettings.txt, выгружается в каталог БД)

Файл shablon.txt имеет вышеупомянутую структуру «МаскаДляПоиска» = «ЗначениеДляЗамены» и следующие записи:

### getSVNRevision=	Return "$WCREV$"; getSVNURL=	Return "$WCURL$"; getSVNBuildTime=	Return "$WCNOW$"; 

На выходе получаем такой же файл, только вместо ключевых слов, указанных между знаками $ подставляются соответствующие значения:

  • $WCREV$ — Номер ревизии рабочей копии
  • $WCURL$ — адрес ветки
  • $WCNOW$ — Текущее время. Указывается в качестве времени сборки MD файла.

Подробнее о подстановке ключевых слов тут

Программка-парсер была написана на AutoIt. Я назвал ее Replacer. Она принимает два параметра:

  • путь к файлу с настройками (это ReplacerSettings.txt)
  • путь до файла, в котором будут подменяться значения функций (это ГлобальныйМодуль.1s)

Первая строчка в файле с настройками должна иметь значение ### для «защиты от дурака» — чтобы не перепутать порядок файлов. Именно поэтому так сделано в файле shablon.txt

Содержимое Replacer'а:

#include "file.au3" ;$CmdLine[0] - это количество переданных параметров.  if $CmdLine[0] <2 Then     MsgBox(0,"error!","Replacer have not all params!") 	Exit  EndIf   ;Добавляем глобальные переменные  Global $SettingsFilePath = $CmdLine[1] Global $WorkFilePath = $CmdLine[2]  ;Проверяем на существование файл настроек if FileExists ( $SettingsFilePath) = 0 Then  MsgBox(0, "Error", "Settings File is not exist!.")  exit EndIf  ;Проверяем на существование файл с кодом if FileExists ( $WorkFilePath ) = 0 Then  MsgBox(0, "Error", "Global Module File is not exist!.") exit EndIf  ;Открываем файл настроек   Local $Settingsfile = openFile($SettingsfilePath, 0)  Local $T = FileRead($SettingsFilePath,3)  if $T <> "###" Then 	MsgBox(0,"Error in the settings file!","Settings file not have in first line a secure code ###")    Exit  EndIf 	 	;Запускаем бесконечный цикл    While 1    ;Считываем строку файла настроек    Local $line = FileReadLine($Settingsfile)     ;Если неудачно, то выходим 	If @error = -1 Then ExitLoop    ;Ищем разделитель 	Local $SeparatorPos = StringInStr($line, "=") 	if $SeparatorPos = 0 Then 	   ;Если не нашли - продолжаем цикл 	   ContinueLoop 	EndIf    ;с помощью функции получаем из строки значение для поиска    Local $find = getFindString($line)    ;И для замены    Local $replace = getReplaceString($line) 	;С помощью функции подменяем значение        ___ReplaceStringInFile($WorkFilePath,$find,$replace)    WEnd   FileClose($Settingsfile) ;выходим из программы. Далее указанные функции выполняются только при вызове exit   ;В файле "file.au3" я нашел подходящую функцию, мне осталось только немного модифицировать ее. ;Она читает файл, создает массив строк, в массиве происходит поиск и замена, потом обратно запись из массива в файл. Func ___ReplaceStringInFile($szFileName, $szSearchString, $szReplaceString, $fCaseness = 0, $fOccurance = 1) 	Local $iRetVal = 0 	Local $nCount, $sEndsWith 	; Check if file is readonly .. 	If StringInStr(FileGetAttrib($szFileName), "R") Then Return SetError(6, 0, -1) 	;=============================================================================== 	;== Read the file into an array 	;=============================================================================== 	Local $hFile = FileOpen($szFileName, $FO_READ) 	If $hFile = -1 Then Return SetError(1, 0, -1) 	Local $s_TotFile = FileRead($hFile, FileGetSize($szFileName)) 	If StringRight($s_TotFile, 2) = @CRLF Then 		$sEndsWith = @CRLF 	ElseIf StringRight($s_TotFile, 1) = @CR Then 		$sEndsWith = @CR 	ElseIf StringRight($s_TotFile, 1) = @LF Then 		$sEndsWith = @LF 	Else 		$sEndsWith = "" 	EndIf 	Local $aFileLines = StringSplit(StringStripCR($s_TotFile), @LF) 	FileClose($hFile) 	;=============================================================================== 	;== Open the output file in write mode 	;=============================================================================== 	Local $iEncoding = FileGetEncoding($szFileName) 	Local $hWriteHandle = FileOpen($szFileName, $iEncoding + $FO_OVERWRITE) 	If $hWriteHandle = -1 Then Return SetError(2, 0, -1) 	;=============================================================================== 	;== Loop through the array and search for $szSearchString 	;=============================================================================== 	local $needReplace = 0 	For $nCount = 1 To $aFileLines[0] 	  ;тут я добавил проверку на необходимость замены строки 	  if $needReplace = 1 Then 		;подменяем содержимое строки и выходим из цикла 		$aFileLines[$nCount] = $szReplaceString 		 ExitLoop 	  EndIf 	  ;Если нашли значение в строке 		If StringInStr($aFileLines[$nCount], $szSearchString, $fCaseness) Then 			;Тут я закомментировал оригинальную строку 			;$aFileLines[$nCount] = StringReplace($aFileLines[$nCount], $szSearchString, $szReplaceString, 1 - $fOccurance, $fCaseness) 			;"Включаю" переменную needReplace и при следующей итерации цикла произойдет замена. Как раз то что нужно. 			$needReplace = 1 	 			$iRetVal = $iRetVal + 1  			;====================================================================== 			;== If we want just the first string replaced, copy the rest of the lines 			;== and stop 			;====================================================================== 			If $fOccurance = 0 Then 				$iRetVal = 1 				ExitLoop 			EndIf 		 EndIf 	Next 	;=============================================================================== 	;== Write the lines back to original file. 	;=============================================================================== 	For $nCount = 1 To $aFileLines[0] - 1 		If FileWriteLine($hWriteHandle, $aFileLines[$nCount]) = 0 Then 			FileClose($hWriteHandle) 			Return SetError(3, 0, -1) 		EndIf 	Next 	; Write the last record and ensure it ends with the same as the input file 	If $aFileLines[$nCount] <> "" Then FileWrite($hWriteHandle, $aFileLines[$nCount] & $sEndsWith) 	FileClose($hWriteHandle)  	Return $iRetVal EndFunc  ;открываем файл Func openFile($FilePath,$mode) Local $file = FileOpen($FilePath, $mode) If $file = -1 Then     MsgBox(0, "Error", "Unable to open file." $FilePath)    exit  Else 	 Return $file EndIf    EndFunc  ;ищем разделитель, возвращаем все, что до него Func getFindString($inString) 	Return StringLeft($inString, StringInStr($inString, "=")-1)  EndFunc   ;А тут все, что после него Func getReplaceString($inString) 	Return StringRight($inString, StringLen($inString)- StringInStr($inString, "=")) EndFunc 

Для автоматизации всех этих действий я создал bat’ник "getRevision.bat", параметром в него передается папка, где находится глобальный модуль (это BUILD или REPO).

Содержимое getRevision.bat:

REM На всякий пожарный проверяется существование папки с исходниками if not exist .\REPO (     echo FAIL: REPO folder isn't exist!  	pause     exit /b 1 	) REM и файла-шаблона. if not exist "%~dp0\shablon.txt" (     echo FAIL: Shablon isn't exist!  	pause     exit /b 2 	) 	 REM Запускаем SubWCRev.exe, получаем шаблон для парсера "c:\Program Files\TortoiseSVN\bin\SubWCRev.exe" .\REPO "%~dp0\shablon.txt" .\ReplacerSettings.txt -f REM Запускаем парсер, подменяем значение функций в глобальном модуле "%~dp0\Replacer.exe" ".\ReplacerSettings.txt" "%1\ГлобальныйМодуль.1s" REM выпиливаем шаблон для парсера del ".\ReplacerSettings.txt"

Вызов getRevision.bat был добавлен в Build.bat, а так как Build.bat предварительно копирует исходники в отдельную папку BUILD и она же указывается параметром для getRevision.bat подмена значений никак не отражается в папке REPO с исходниками и соответственно изменения не попадают в SVN, что для меня очень удобно.
По просьбе коллег был создан отдельный bat’ник — СompileSVN.bat
Как можно понять по называнию, он делает подмену значений в оригинальной папке с исходниками и запускает компиляцию. Его содержимое:

@echo Установка значений SVN в глобальный модуль... @echo off call getrevision.bat .\REPO @echo Компиляция файлов из папки REPO в 1cv7.compile.md... "%GCOMP_PATH%\gcomp" -c -D .\REPO -F 1cv7.compile.md>.compile.log

Compile.bat остался без изменений

Чтобы парсеру было что искать, вставляем в глобальный модуль функции «пустышки»

//------------------------------------------------- Функция getSVNRevision() Экспорт 	Возврат 0; КонецФункции //------------------------------------------------- Функция getSVNURL() Экспорт 	Возврат 0; КонецФункции //------------------------------------------------- Функция getSVNBuildTime() Экспорт 	Возврат 0; КонецФункции //-------------------------------------------------
Результат

Если запустить CompileSVN.bat и посмотреть изменения в коммите, можно увидеть следующее:
(некоторые данные изменены):

//------------------------------------------------- Функция getSVNRevision() Экспорт 	Return "5135"; КонецФункции //------------------------------------------------- Функция getSVNURL() Экспорт 	Return "https://сервер:порт/svn/trunk"; КонецФункции //------------------------------------------------- Функция getSVNBuildTime() Экспорт 	Return "2013/01/22 10:35:15"; КонецФункции //------------------------------------------------- 

Так же в глобальный модуль была добавлена процедура для выгрузки данных из магазина

глВыгрузитьMD_INFO

Процедура глВыгрузитьMD_INFO() Экспорт 	ВремяПоследнейЗаписи = ""; 	Попытка 		ФС.АтрибутыФайла( КаталогИБ() + "1CV7.MD", , , , , ВремяПоследнейЗаписи,  ); 	Исключение 	КонецПопытки; 	Текст = СоздатьОбъект( "Текст" ); 	Текст.ДобавитьСтроку( "###MD Info" ); 	Текст.ДобавитьСтроку( "SVN_Revision: " + getSVNRevision() ); 	Текст.ДобавитьСтроку( "SVN_URL: " + getSVNURL() ); 	Текст.ДобавитьСтроку(  		"SVN_BuildTime: " + 			СтрЗаменить( getSVNBuildTime(), "/", "." ) 		 ); 	Текст.ДобавитьСтроку( "MD_UpdateTime: " + ВремяПоследнейЗаписи ); 	Текст.ДобавитьСтроку( "### Константы: " ); 	Текст.ДобавитьСтроку( "Магазин: " + СокрЛП( Константа.Магазин ) ); 	Текст.ДобавитьСтроку( "КодМагазина: " + СокрЛП( Константа.КодМагазина ) ); 	Попытка 		Текст.Записать(  			глПолучитьПапкуОбмена_Исходящие() + 				"\" + 				глПравильноеИмяФайлаДляОбмена( "MDInfo", "" ) 			 ); 	Исключение 	КонецПопытки; КонецПроцедуры

Выгружается такой вот файлик (некоторые данные изменены):

### MD Info SVN_Revision: 5137 SVN_URL: https://сервер:порт/svn/branches/имя_ветки SVN_BuildTime: 2013.01.21 13:45:44 MD_UpdateTime: 2013.01.22 05:02:52 ### Константы:  Магазин: Имя_магазина  КодМагазина: 24  

Файл записывается в папку для обмена, потом передается на FTP сервер. Оттуда наша база «Копирка» будет собирать эти файлы и полученные данные записывать в базу.

Для удобства коллективного обновления скриптов был использован инсталятор inno setup, подробно описывать его нет смысла — там все просто и есть хороший help.

При небольших манипуляциях вышеописанное вполне легко можно приспособить под любой другой язык программирования.

Благодарю за внимание.

ссылка на оригинал статьи http://habrahabr.ru/post/166769/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *