diff --git a/.gitignore b/.gitignore index 3bb6fcec..0bd1052b 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ build/ bin/ lib/ +out/ *.iml .classpath diff --git a/README.md b/README.md index 959b0464..a2fe76ed 100644 --- a/README.md +++ b/README.md @@ -112,8 +112,9 @@ pipeline1C() * `STORAGE_USER` - параметры авторизации в хранилище вида "username with password" (для `secrets` -> `storage`). * Все "шаги" по умолчанию выключены (`stages`). * Если в корне репозитория существует файл `packagedef`, то в шагах, работающих с информационной базой, будет выполнена попытка установки локальных зависимостей средствами `opm`. - * Если после установки локальных зависимостей в каталоге `oscript_modules/bin` сушествует файл `vrunner`, то для выполнения команд работы с информационной базой будет использоваться он, а не глобально установленный `vrunner` из `PATH`. + * Если после установки локальных зависимостей в каталоге `oscript_modules/bin` существует файл `vrunner`, то для выполнения команд работы с информационной базой будет использоваться он, а не глобально установленный `vrunner` из `PATH`. * Результаты в формате `allure` ожидаются в каталоге `build/out/allure` или его подкаталогах. + * Каждый шаг имеет свой таймаут в минутах (от 10 до 240 в зависимости от "тяжёлости" шага сборки), но может быть переопределен в секции настроек `timeout`. * Инициализация: * Информационная база инициализируется только в том случае, если в сборочной линии включены шаги, работающие с базой (например, `bdd` или `syntaxCheck`). * Информационная база инициализируется конфигурацией из хранилища конфигурации (`initInfobase` -> `initMethod`). @@ -121,14 +122,20 @@ pipeline1C() * Первичный запуск информационной базы: * Если информационная база нужна для запуска в режиме "Предприятие" (например, для шагов `bdd` или `smoke`), то будет запущен шаг "Миграция ИБ". * После загрузки конфигурации в ИБ будет выполняться запуск ИБ с целью запуска обработчиков обновления из БСП (`initInfobase` -> `runMigration`). - * Если в настройках шага инициализации не заполнен массив дополнительных шагов миграции (`initInfobase` -> `additionalInitializationSteps`), но в каталоге `tools` присутствуют файлы с именами, удовлетворяющими шаблону `vrunner.init*.json`, то автоматически выполняется запуск `vrunner vanessa` с передачей найденных файлов в качестве значения настроек (параметр `--settings`) в порядке лексиграфической сортировки имен файлов. + * Если в настройках шага инициализации не заполнен массив дополнительных шагов миграции (`initInfobase` -> `additionalInitializationSteps`), но в каталоге `tools` присутствуют файлы с именами, удовлетворяющими шаблону `vrunner.init*.json`, то автоматически выполняется запуск `vrunner vanessa` с передачей найденных файлов в качестве значения настроек (параметр `--settings`) в порядке лексикографической сортировки имен файлов. * BDD: * Если в конфигурационном файле проекта не заполнена настройка `bdd` -> `vrunnerSteps`, то автоматически выполняется запуск `vrunner vanessa --settings tools/vrunner.json`. +* Дымовые тесты: + * Если в репозитории существует файл `tools/vrunner.json`, то запуск дымовых тестов будет выполняться с передачей файла в параметры запуска `vrunner xunit --settings tools/vrunner.json` (`smoke` -> `vrunnerSettings`). + * Если установка локальных зависимостей `opm` установит пакет `add`, то будет использоваться обработка `xddTestRunner.epf` из локальных зависимостей. + * Если в репозитории существует файл `tools/xUnitParams.json`, то этот путь к файлу будет передан в параметр запуска `vrunner xunit --xddConfig ./tools/xUnitParams.json` (`smoke -> xUnitParams`). + * Если используемый конфигурационный файл (`vrunner.json`) не содержит настройку `testsPath`, то запускается полный комплект дымовых тестов, расположенных в `$addRoot/tests/smoke`. + * По умолчанию включено формирование отчета в формате `jUnit` (`smoke` -> `publishToJUnitReport`) и выключено формирование отчета в формате Allure (`smoke` -> `publishToAllureReport`). * Синтаксический контроль: * Если в репозитории существует файл `tools/vrunner.json`, то синтаксический контроль конфигурации с помощью конфигуратора будет выполняться с передачей файла в параметры запуска `vrunner syntax-check --settings tools/vrunner.json` (`syntaxCheck` -> `vrunnerSettings`). * Применяется группировка ошибок по метаданным (`syntaxCheck` -> `groupErrorsByMetadata`). * Выгрузка результатов в формат `jUnit` осуществляется в файл `./build/out/jUnit/syntax.xml` (`syntaxCheck` -> `pathToJUnitReport`). - * Если в репозитории существует файл `./tools/syntax-check-exception-file.txt`, то команде запука синтаксического контроля конфигурации данный файл будет передаваться как файл с исключениями сообщений об ошибках (параметр `--exception-file`) (`syntaxCheck` -> `exceptionFile`). + * Если в репозитории существует файл `./tools/syntax-check-exception-file.txt`, то команде запуска синтаксического контроля конфигурации данный файл будет передаваться как файл с исключениями сообщений об ошибках (параметр `--exception-file`) (`syntaxCheck` -> `exceptionFile`). * Конфигурационный файл по умолчанию уже содержит ряд "режимов проверки" для синтаксического контроля конфигурации (`syntaxCheck` -> `checkModes`). * Трансформация результатов валидации EDT: * По умолчанию из результатов анализа исключаются замечания, сработавшие на модулях с включенным запретом редактирования (желтый куб с замком) (параметры `resultsTransform` -> `removeSupport` и `resultsTransform` -> `supportLevel`). @@ -136,5 +143,6 @@ pipeline1C() * Предполагается наличие единственной настройки `SonarQube installation` (`sonarqube` -> `sonarQubeInstallation`). * Используется `sonar-scanner` из переменной окружения `PATH` (`sonarqube` -> `useSonarScannerFromPath`). * Если использование `sonar-scanner` из переменной окружения `PATH` выключено, предполагается наличие настроенного глобального инструмента `SonarQube Scanner` с идентификатором инструмента `sonar-scanner` (`sonarqube` -> `sonarScannerToolName`). - * Версия из корня конфигурации передается утилите `sonar-scanner` как значение параметра `sonar.projectVersion=$configurationVersion`. + * Если разработка ведется с использованием подсистемы [БСП "Обновление версии ИБ"](https://its.1c.ru/db/bsp315doc#content:4:1:issogl1_обновление_версии_иб), то в значение параметра `sonar.projectVersion=$configurationVersion` утилиты `sonar-scanner` можно передавать версию из созданного общего модуля. + Для этого необходимо заполнить параметр (`sonarqube` -> `infoBaseUpdateModuleName`). Если параметр не заполнен, версия передается из корня конфигурации. * Если выполнялась валидация EDT, результаты валидации в формате `generic issues` передаются утилите `sonar-scanner` как значение параметра `sonar.externalIssuesReportPaths`. diff --git a/resources/globalConfiguration.json b/resources/globalConfiguration.json index 8f79812d..7ed9f033 100644 --- a/resources/globalConfiguration.json +++ b/resources/globalConfiguration.json @@ -16,6 +16,19 @@ "edtValidate": false, "smoke": false }, + "timeout": { + "smoke": 240, + "bdd": 120, + "createInfoBase": 60, + "designerToEdtFormatTransformation": 60, + "edtToDesignerFormatTransformation": 60, + "edtValidate": 240, + "initInfoBase": 60, + "resultTransformation": 10, + "sonarqube": 90, + "syntaxCheck": 240, + "zipInfoBase": 60 + }, "initInfobase": { "initMethod": "fromStorage", "runMigration": true, @@ -29,7 +42,8 @@ "sonarqube": { "sonarQubeInstallation": "", "useSonarScannerFromPath": true, - "sonarScannerToolName": "sonar-scanner" + "sonarScannerToolName": "sonar-scanner", + "infoBaseUpdateModuleName" : "" }, "syntaxCheck": { "groupErrorsByMetadata": true, @@ -50,6 +64,12 @@ ], "vrunnerSettings": "./tools/vrunner.json" }, + "smoke": { + "vrunnerSettings": "./tools/vrunner.json", + "xddConfigPath": "./tools/xUnitParams.json", + "publishToAllureReport": false, + "publishToJUnitReport": true + }, "resultsTransform": { "removeSupport": true, "supportLevel": 0 diff --git a/resources/schema.json b/resources/schema.json index 0532694c..5d2c0761 100644 --- a/resources/schema.json +++ b/resources/schema.json @@ -65,6 +65,57 @@ } } }, + "timeout" : { + "type" : "object", + "id" : "urn:jsonschema:ru:pulsar:jenkins:library:configuration:TimeoutOptions", + "description" : "Настройка таймаутов для шагов", + "properties" : { + "edtToDesignerFormatTransformation" : { + "type" : "integer", + "description" : "Таймаут шага трансформации исходников из формата EDT в формат Конфигуратора, в минутах.\n По умолчанию содержит значение 60.\n " + }, + "createInfoBase" : { + "type" : "integer", + "description" : "Таймаут шага создания информационной базы, в минутах.\n По умолчанию содержит значение 60.\n " + }, + "initInfoBase" : { + "type" : "integer", + "description" : "Таймаут шага инициализации информационной базы, в минутах.\n По умолчанию содержит значение 60.\n " + }, + "zipInfoBase" : { + "type" : "integer", + "description" : "Таймаут шага архивирования информационной базы, в минутах.\n По умолчанию содержит значение 60.\n " + }, + "designerToEdtFormatTransformation" : { + "type" : "integer", + "description" : "Таймаут шага трансформации исходников из формата Конфигуратора в формат EDT, в минутах.\n По умолчанию содержит значение 60.\n " + }, + "edtValidate" : { + "type" : "integer", + "description" : "Таймаут шага валидации EDT, в минутах.\n По умолчанию содержит значение 240.\n " + }, + "resultTransformation" : { + "type" : "integer", + "description" : "Таймаут шага трансформации результатов EDT, в минутах.\n По умолчанию содержит значение 10.\n " + }, + "bdd" : { + "type" : "integer", + "description" : "Таймаут шага проверки сценариев поведения, в минутах.\n По умолчанию содержит значение 120.\n " + }, + "syntaxCheck" : { + "type" : "integer", + "description" : "Таймаут шага синтаксического контроля, в минутах.\n По умолчанию содержит значение 240.\n " + }, + "smoke" : { + "type" : "integer", + "description" : "Таймаут шага дымовых тестов, в минутах.\n По умолчанию содержит значение 240.\n " + }, + "sonarqube" : { + "type" : "integer", + "description" : "Таймаут шага статического анализа SonarQube, в минутах.\n По умолчанию содержит значение 90.\n " + } + } + }, "initInfobase" : { "type" : "object", "id" : "urn:jsonschema:ru:pulsar:jenkins:library:configuration:InitInfobaseOptions", @@ -118,6 +169,10 @@ "sonarScannerToolName" : { "type" : "string", "description" : "Имя настроенной утилиты sonar-scanner.\nПрименяется, если useSonarScannerFromPath установлено в false." + }, + "infoBaseUpdateModuleName" : { + "type" : "string", + "description" : "Имя общего модуля (например, ОбновлениеИнформационнойБазыXXX), в котором указана версия библиотеки.\n Версия должна задаваться в виде присвоения `Описание.Версия = \"ваш номер версии\";`\n " } } }, @@ -151,6 +206,29 @@ } } }, + "smoke" : { + "type" : "object", + "id" : "urn:jsonschema:ru:pulsar:jenkins:library:configuration:SmokeTestOptions", + "description" : "Настройки дымового тестирования", + "properties" : { + "vrunnerSettings" : { + "type" : "string", + "description" : "Путь к конфигурационному файлу vanessa-runner.\n По умолчанию содержит значение \"./tools/vrunner.json\".\n " + }, + "xddConfigPath" : { + "type" : "string", + "description" : "Путь к конфигурационному файлу для xddTestRunner.\n По умолчанию содержит значение \"./tools/xUnitParams.json\".\n " + }, + "publishToAllureReport" : { + "type" : "boolean", + "description" : "Выполнять публикацию результатов в отчет Allure.\n По умолчанию выключено.\n " + }, + "publishToJUnitReport" : { + "type" : "boolean", + "description" : "Выполнять публикацию результатов в отчет JUnit.\n По умолчанию включено.\n " + } + } + }, "resultsTransform" : { "type" : "object", "id" : "urn:jsonschema:ru:pulsar:jenkins:library:configuration:ResultsTransformOptions", diff --git a/src/ru/pulsar/jenkins/library/IStepExecutor.groovy b/src/ru/pulsar/jenkins/library/IStepExecutor.groovy index 243763bc..a41cbeb8 100644 --- a/src/ru/pulsar/jenkins/library/IStepExecutor.groovy +++ b/src/ru/pulsar/jenkins/library/IStepExecutor.groovy @@ -17,6 +17,8 @@ interface IStepExecutor { FileWrapper[] findFiles(String glob, String excludes) + String readFile(String file) + String readFile(String file, String encoding) boolean fileExists(String file) @@ -73,5 +75,7 @@ interface IStepExecutor { def allure(List results) + def junit(String testResults, boolean allowEmptyResults) + def installLocalDependencies() } \ No newline at end of file diff --git a/src/ru/pulsar/jenkins/library/StepExecutor.groovy b/src/ru/pulsar/jenkins/library/StepExecutor.groovy index c5b63683..8d1ab2ae 100644 --- a/src/ru/pulsar/jenkins/library/StepExecutor.groovy +++ b/src/ru/pulsar/jenkins/library/StepExecutor.groovy @@ -33,7 +33,7 @@ class StepExecutor implements IStepExecutor { } @Override - String readFile(String file, String encoding) { + String readFile(String file, String encoding = 'UTF-8') { steps.readFile encoding: encoding, file: file } @@ -174,6 +174,11 @@ class StepExecutor implements IStepExecutor { ]) } + @Override + def junit(String testResults, boolean allowEmptyResults) { + steps.junit testResults: testResults, allowEmptyResults: allowEmptyResults + } + @Override def installLocalDependencies() { steps.installLocalDependencies() diff --git a/src/ru/pulsar/jenkins/library/configuration/ConfigurationReader.groovy b/src/ru/pulsar/jenkins/library/configuration/ConfigurationReader.groovy index 09776bda..ca75e17d 100644 --- a/src/ru/pulsar/jenkins/library/configuration/ConfigurationReader.groovy +++ b/src/ru/pulsar/jenkins/library/configuration/ConfigurationReader.groovy @@ -56,15 +56,17 @@ class ConfigurationReader implements Serializable { def nonMergeableSettings = Arrays.asList( "secrets", "stageFlags", - "initInfobaseOptions", + "timeoutOptions", + "initInfoBaseOptions", "bddOptions", "sonarQubeOptions", + "smokeTestOptions", "syntaxCheckOptions", "resultsTransformOptions" ).toSet() mergeObjects(baseConfiguration, configurationToMerge, nonMergeableSettings) - mergeInitInfobaseOptions(baseConfiguration.initInfobaseOptions, configurationToMerge.initInfobaseOptions); + mergeInitInfoBaseOptions(baseConfiguration.initInfoBaseOptions, configurationToMerge.initInfoBaseOptions); mergeBddOptions(baseConfiguration.bddOptions, configurationToMerge.bddOptions); return baseConfiguration; @@ -91,7 +93,7 @@ class ConfigurationReader implements Serializable { } @NonCPS - private static void mergeInitInfobaseOptions(InitInfobaseOptions baseObject, InitInfobaseOptions objectToMerge) { + private static void mergeInitInfoBaseOptions(InitInfoBaseOptions baseObject, InitInfoBaseOptions objectToMerge) { if (objectToMerge == null || objectToMerge.additionalInitializationSteps == null) { return } diff --git a/src/ru/pulsar/jenkins/library/configuration/InitInfobaseMethod.groovy b/src/ru/pulsar/jenkins/library/configuration/InitInfoBaseMethod.groovy similarity index 91% rename from src/ru/pulsar/jenkins/library/configuration/InitInfobaseMethod.groovy rename to src/ru/pulsar/jenkins/library/configuration/InitInfoBaseMethod.groovy index c13be07b..30986ad8 100644 --- a/src/ru/pulsar/jenkins/library/configuration/InitInfobaseMethod.groovy +++ b/src/ru/pulsar/jenkins/library/configuration/InitInfoBaseMethod.groovy @@ -2,7 +2,7 @@ package ru.pulsar.jenkins.library.configuration import com.fasterxml.jackson.annotation.JsonProperty -enum InitInfobaseMethod { +enum InitInfoBaseMethod { @JsonProperty("fromStorage") FROM_STORAGE, diff --git a/src/ru/pulsar/jenkins/library/configuration/InitInfobaseOptions.groovy b/src/ru/pulsar/jenkins/library/configuration/InitInfoBaseOptions.groovy similarity index 90% rename from src/ru/pulsar/jenkins/library/configuration/InitInfobaseOptions.groovy rename to src/ru/pulsar/jenkins/library/configuration/InitInfoBaseOptions.groovy index d36a3bfd..f0874a60 100644 --- a/src/ru/pulsar/jenkins/library/configuration/InitInfobaseOptions.groovy +++ b/src/ru/pulsar/jenkins/library/configuration/InitInfoBaseOptions.groovy @@ -5,7 +5,7 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties import com.fasterxml.jackson.annotation.JsonPropertyDescription @JsonIgnoreProperties(ignoreUnknown = true) -class InitInfobaseOptions implements Serializable { +class InitInfoBaseOptions implements Serializable { @JsonPropertyDescription(""" Способ инициализации информационной базы. @@ -14,10 +14,10 @@ class InitInfobaseOptions implements Serializable { * fromSource - инициализация информационной базы из исходников конфигурации; * defaultBranchFromStorage - инициализация основной ветки из хранилища конфигурации, остальных - из исходников конфигурации. По умолчанию содержит значение "fromStorage".""") - InitInfobaseMethod initMethod = InitInfobaseMethod.FROM_STORAGE; + InitInfoBaseMethod initMethod = InitInfoBaseMethod.FROM_STORAGE; @JsonPropertyDescription("Запустить миграцию ИБ") - boolean runMigration = true + Boolean runMigration = true @JsonPropertyDescription("""Дополнительные шаги, запускаемые через vrunner. В каждой строке передается отдельная команда @@ -28,7 +28,7 @@ class InitInfobaseOptions implements Serializable { @Override @NonCPS String toString() { - return "InitInfobaseOptions{" + + return "InitInfoBaseOptions{" + "initMethod=" + initMethod + ", runMigration=" + runMigration + ", additionalInitializationSteps=" + additionalInitializationSteps + diff --git a/src/ru/pulsar/jenkins/library/configuration/JobConfiguration.groovy b/src/ru/pulsar/jenkins/library/configuration/JobConfiguration.groovy index 4689a6f2..82b148ad 100644 --- a/src/ru/pulsar/jenkins/library/configuration/JobConfiguration.groovy +++ b/src/ru/pulsar/jenkins/library/configuration/JobConfiguration.groovy @@ -1,7 +1,6 @@ package ru.pulsar.jenkins.library.configuration import com.cloudbees.groovy.cps.NonCPS -import com.fasterxml.jackson.annotation.JsonEnumDefaultValue import com.fasterxml.jackson.annotation.JsonIgnoreProperties import com.fasterxml.jackson.annotation.JsonProperty import com.fasterxml.jackson.annotation.JsonPropertyDescription @@ -23,6 +22,10 @@ class JobConfiguration implements Serializable { @JsonPropertyDescription("Включение этапов сборок") StageFlags stageFlags; + @JsonProperty("timeout") + @JsonPropertyDescription("Настройка таймаутов для шагов") + TimeoutOptions timeoutOptions; + @JsonPropertyDescription("Имя ветки по умолчанию. Значение по умолчанию - main.") String defaultBranch @@ -31,7 +34,7 @@ class JobConfiguration implements Serializable { @JsonProperty("initInfobase") @JsonPropertyDescription("Настройки шага инициализации ИБ") - InitInfobaseOptions initInfobaseOptions; + InitInfoBaseOptions initInfoBaseOptions; @JsonProperty("bdd") @JsonPropertyDescription("Настройки шага запуска BDD сценариев") @@ -45,6 +48,10 @@ class JobConfiguration implements Serializable { @JsonPropertyDescription("Настройки синтаксического контроля") SyntaxCheckOptions syntaxCheckOptions; + @JsonProperty("smoke") + @JsonPropertyDescription("Настройки дымового тестирования") + SmokeTestOptions smokeTestOptions; + @JsonProperty("resultsTransform") @JsonPropertyDescription("Настройки трансформации результатов анализа") ResultsTransformOptions resultsTransformOptions; @@ -60,25 +67,27 @@ class JobConfiguration implements Serializable { "v8version='" + v8version + '\'' + ", srcDir='" + srcDir + '\'' + ", sourceFormat=" + sourceFormat + - ", defaultBranch=" + defaultBranch + ", stageFlags=" + stageFlags + + ", timeoutOptions=" + timeoutOptions + + ", defaultBranch='" + defaultBranch + '\'' + ", secrets=" + secrets + - ", initInfobaseOptions=" + initInfobaseOptions + + ", initInfoBaseOptions=" + initInfoBaseOptions + ", bddOptions=" + bddOptions + ", sonarQubeOptions=" + sonarQubeOptions + ", syntaxCheckOptions=" + syntaxCheckOptions + + ", smokeTestOptions=" + smokeTestOptions + ", resultsTransformOptions=" + resultsTransformOptions + - ", logosConfig=" + logosConfig + + ", logosConfig='" + logosConfig + '\'' + '}'; } - boolean infobaseFromFiles(){ + boolean infoBaseFromFiles() { IStepExecutor steps = ContextRegistry.getContext().getStepExecutor() def env = steps.env(); String branchName = env.BRANCH_NAME; - def initMethod = initInfobaseOptions.initMethod + def initMethod = initInfoBaseOptions.initMethod - return (initMethod == InitInfobaseMethod.FROM_SOURCE) || - (initMethod == InitInfobaseMethod.DEFAULT_BRANCH_FROM_STORAGE && branchName != defaultBranch) + return (initMethod == InitInfoBaseMethod.FROM_SOURCE) || + (initMethod == InitInfoBaseMethod.DEFAULT_BRANCH_FROM_STORAGE && branchName != defaultBranch) } } \ No newline at end of file diff --git a/src/ru/pulsar/jenkins/library/configuration/ResultsTransformOptions.groovy b/src/ru/pulsar/jenkins/library/configuration/ResultsTransformOptions.groovy index 08168fce..f450d84a 100644 --- a/src/ru/pulsar/jenkins/library/configuration/ResultsTransformOptions.groovy +++ b/src/ru/pulsar/jenkins/library/configuration/ResultsTransformOptions.groovy @@ -8,14 +8,14 @@ import com.fasterxml.jackson.annotation.JsonPropertyDescription class ResultsTransformOptions implements Serializable { @JsonPropertyDescription("Фильтровать замечания по уровню поддержки модуля. По умолчанию включено.") - boolean removeSupport = true + Boolean removeSupport = true @JsonPropertyDescription("""Настройка фильтрации замечаний по уровню поддержки. 0 - удалить файлы на замке; 1 - удалить файлы на замке и на поддержке; 2 - удалить файлы на замке, на поддержке и снятые с поддержки. """) - int supportLevel + Integer supportLevel @Override @NonCPS diff --git a/src/ru/pulsar/jenkins/library/configuration/SmokeTestOptions.groovy b/src/ru/pulsar/jenkins/library/configuration/SmokeTestOptions.groovy new file mode 100644 index 00000000..fd07c2ae --- /dev/null +++ b/src/ru/pulsar/jenkins/library/configuration/SmokeTestOptions.groovy @@ -0,0 +1,40 @@ +package ru.pulsar.jenkins.library.configuration + +import com.cloudbees.groovy.cps.NonCPS +import com.fasterxml.jackson.annotation.JsonIgnoreProperties +import com.fasterxml.jackson.annotation.JsonPropertyDescription + +@JsonIgnoreProperties(ignoreUnknown = true) +class SmokeTestOptions implements Serializable { + + @JsonPropertyDescription("""Путь к конфигурационному файлу vanessa-runner. + По умолчанию содержит значение "./tools/vrunner.json". + """) + String vrunnerSettings + + @JsonPropertyDescription("""Путь к конфигурационному файлу для xddTestRunner. + По умолчанию содержит значение "./tools/xUnitParams.json". + """) + String xddConfigPath; + + @JsonPropertyDescription("""Выполнять публикацию результатов в отчет Allure. + По умолчанию выключено. + """) + boolean publishToAllureReport + + @JsonPropertyDescription("""Выполнять публикацию результатов в отчет JUnit. + По умолчанию включено. + """) + boolean publishToJUnitReport + + @Override + @NonCPS + String toString() { + return "SmokeTestOptions{" + + "vrunnerSettings='" + vrunnerSettings + '\'' + + ", xddConfigPath='" + xddConfigPath + '\'' + + ", publishToAllureReport=" + publishToAllureReport + + ", publishToJUnitReport=" + publishToJUnitReport + + '}' + } +} diff --git a/src/ru/pulsar/jenkins/library/configuration/SonarQubeOptions.groovy b/src/ru/pulsar/jenkins/library/configuration/SonarQubeOptions.groovy index 7dcbb37e..dad49645 100644 --- a/src/ru/pulsar/jenkins/library/configuration/SonarQubeOptions.groovy +++ b/src/ru/pulsar/jenkins/library/configuration/SonarQubeOptions.groovy @@ -13,13 +13,18 @@ class SonarQubeOptions implements Serializable { String sonarQubeInstallation; @JsonPropertyDescription("Использовать sonar-scanner, доступный в PATH") - boolean useSonarScannerFromPath + Boolean useSonarScannerFromPath @JsonPropertyDescription( "Имя настроенной утилиты sonar-scanner.\nПрименяется, если useSonarScannerFromPath установлено в false." ) String sonarScannerToolName + @JsonPropertyDescription("""Имя общего модуля (например, ОбновлениеИнформационнойБазыXXX), в котором указана версия библиотеки. + Версия должна задаваться в виде присвоения `Описание.Версия = "ваш номер версии";` + """) + String infoBaseUpdateModuleName + @Override @NonCPS String toString() { @@ -27,6 +32,7 @@ class SonarQubeOptions implements Serializable { "useSonarScannerFromPath=" + useSonarScannerFromPath + ", sonarScannerToolName='" + sonarScannerToolName + '\'' + ", sonarQubeInstallation='" + sonarQubeInstallation + '\'' + + ", infoBaseUpdateModuleName='" + infoBaseUpdateModuleName + '\'' + '}'; } } diff --git a/src/ru/pulsar/jenkins/library/configuration/StageFlags.groovy b/src/ru/pulsar/jenkins/library/configuration/StageFlags.groovy index 2fdcbe74..f52cb76a 100644 --- a/src/ru/pulsar/jenkins/library/configuration/StageFlags.groovy +++ b/src/ru/pulsar/jenkins/library/configuration/StageFlags.groovy @@ -7,22 +7,22 @@ import com.fasterxml.jackson.annotation.JsonPropertyDescription @JsonIgnoreProperties(ignoreUnknown = true) class StageFlags implements Serializable { @JsonPropertyDescription("Анализ SonarQube включен") - boolean sonarqube + Boolean sonarqube @JsonPropertyDescription("Синтаксический контроль включен") - boolean syntaxCheck + Boolean syntaxCheck @JsonPropertyDescription("Валидация EDT включена") - boolean edtValidate + Boolean edtValidate @JsonPropertyDescription("Дымовые тесты включены") - boolean smoke + Boolean smoke @JsonPropertyDescription("Предварительные шаги инициализации включены") - boolean initSteps + Boolean initSteps @JsonPropertyDescription("Запуск BDD сценариев включен") - boolean bdd + Boolean bdd @Override @NonCPS @@ -37,7 +37,7 @@ class StageFlags implements Serializable { '}'; } - boolean needInfobase() { + boolean needInfoBase() { return smoke || syntaxCheck || initSteps || bdd } } diff --git a/src/ru/pulsar/jenkins/library/configuration/TimeoutOptions.groovy b/src/ru/pulsar/jenkins/library/configuration/TimeoutOptions.groovy new file mode 100644 index 00000000..4def6ec1 --- /dev/null +++ b/src/ru/pulsar/jenkins/library/configuration/TimeoutOptions.groovy @@ -0,0 +1,63 @@ +package ru.pulsar.jenkins.library.configuration + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties +import com.fasterxml.jackson.annotation.JsonPropertyDescription + +@JsonIgnoreProperties(ignoreUnknown = true) +class TimeoutOptions implements Serializable { + + @JsonPropertyDescription('''Таймаут шага трансформации исходников из формата EDT в формат Конфигуратора, в минутах. + По умолчанию содержит значение 60. + ''') + Integer edtToDesignerFormatTransformation + + @JsonPropertyDescription('''Таймаут шага создания информационной базы, в минутах. + По умолчанию содержит значение 60. + ''') + Integer createInfoBase + + @JsonPropertyDescription('''Таймаут шага инициализации информационной базы, в минутах. + По умолчанию содержит значение 60. + ''') + Integer initInfoBase + + @JsonPropertyDescription('''Таймаут шага архивирования информационной базы, в минутах. + По умолчанию содержит значение 60. + ''') + Integer zipInfoBase + + @JsonPropertyDescription('''Таймаут шага трансформации исходников из формата Конфигуратора в формат EDT, в минутах. + По умолчанию содержит значение 60. + ''') + Integer designerToEdtFormatTransformation + + @JsonPropertyDescription('''Таймаут шага валидации EDT, в минутах. + По умолчанию содержит значение 240. + ''') + Integer edtValidate + + @JsonPropertyDescription('''Таймаут шага трансформации результатов EDT, в минутах. + По умолчанию содержит значение 10. + ''') + Integer resultTransformation + + @JsonPropertyDescription('''Таймаут шага проверки сценариев поведения, в минутах. + По умолчанию содержит значение 120. + ''') + Integer bdd + + @JsonPropertyDescription('''Таймаут шага синтаксического контроля, в минутах. + По умолчанию содержит значение 240. + ''') + Integer syntaxCheck + + @JsonPropertyDescription('''Таймаут шага дымовых тестов, в минутах. + По умолчанию содержит значение 240. + ''') + Integer smoke + + @JsonPropertyDescription('''Таймаут шага статического анализа SonarQube, в минутах. + По умолчанию содержит значение 90. + ''') + Integer sonarqube +} diff --git a/src/ru/pulsar/jenkins/library/steps/DesignerToEdtFormatTransformation.groovy b/src/ru/pulsar/jenkins/library/steps/DesignerToEdtFormatTransformation.groovy index bfee6f00..e62a3368 100644 --- a/src/ru/pulsar/jenkins/library/steps/DesignerToEdtFormatTransformation.groovy +++ b/src/ru/pulsar/jenkins/library/steps/DesignerToEdtFormatTransformation.groovy @@ -4,6 +4,7 @@ package ru.pulsar.jenkins.library.steps import ru.pulsar.jenkins.library.IStepExecutor import ru.pulsar.jenkins.library.configuration.JobConfiguration import ru.pulsar.jenkins.library.ioc.ContextRegistry +import ru.pulsar.jenkins.library.utils.Constants import ru.pulsar.jenkins.library.utils.Logger class DesignerToEdtFormatTransformation implements Serializable { @@ -38,9 +39,9 @@ class DesignerToEdtFormatTransformation implements Serializable { Logger.println("Конвертация исходников из формата конфигуратора в формат EDT") - def ringCommand = "ring edt workspace import --configuration-files '$configurationRoot' --project-name $PROJECT_NAME --workspace-location '$workspaceDir'" + def ringCommand = "ring edt workspace import --configuration-files \"$configurationRoot\" --project-name $PROJECT_NAME --workspace-location \"$workspaceDir\"" - def ringOpts = ['RING_OPTS=-Dfile.encoding=UTF-8 -Dosgi.nl=ru -Duser.language=ru'] + def ringOpts = [Constants.DEFAULT_RING_OPTS] steps.withEnv(ringOpts) { steps.cmd(ringCommand) } diff --git a/src/ru/pulsar/jenkins/library/steps/EdtToDesignerFormatTransformation.groovy b/src/ru/pulsar/jenkins/library/steps/EdtToDesignerFormatTransformation.groovy index a925b647..57ccf620 100644 --- a/src/ru/pulsar/jenkins/library/steps/EdtToDesignerFormatTransformation.groovy +++ b/src/ru/pulsar/jenkins/library/steps/EdtToDesignerFormatTransformation.groovy @@ -43,9 +43,9 @@ class EdtToDesignerFormatTransformation implements Serializable { Logger.println("Конвертация исходников из формата EDT в формат Конфигуратора") - def ringCommand = "ring edt workspace export --workspace-location '$workspaceDir' --project '$projectDir' --configuration-files '$configurationRoot'" + def ringCommand = "ring edt workspace export --workspace-location \"$workspaceDir\" --project \"$projectDir\" --configuration-files \"$configurationRoot\"" - def ringOpts =[Constants.DEFAULT_RING_OPTS] + def ringOpts = [Constants.DEFAULT_RING_OPTS] steps.withEnv(ringOpts) { steps.cmd(ringCommand) } diff --git a/src/ru/pulsar/jenkins/library/steps/EdtValidate.groovy b/src/ru/pulsar/jenkins/library/steps/EdtValidate.groovy index 4f30a225..1e29990e 100644 --- a/src/ru/pulsar/jenkins/library/steps/EdtValidate.groovy +++ b/src/ru/pulsar/jenkins/library/steps/EdtValidate.groovy @@ -4,6 +4,7 @@ import ru.pulsar.jenkins.library.IStepExecutor import ru.pulsar.jenkins.library.configuration.JobConfiguration import ru.pulsar.jenkins.library.configuration.SourceFormat import ru.pulsar.jenkins.library.ioc.ContextRegistry +import ru.pulsar.jenkins.library.utils.Constants import ru.pulsar.jenkins.library.utils.Logger class EdtValidate implements Serializable { @@ -46,8 +47,8 @@ class EdtValidate implements Serializable { Logger.println("Выполнение валидации EDT") - def ringCommand = "ring edt workspace validate --workspace-location '$workspaceLocation' --file '$resultFile' $projectList" - def ringOpts = ['RING_OPTS=-Dfile.encoding=UTF-8 -Dosgi.nl=ru -Duser.language=ru'] + def ringCommand = "ring edt workspace validate --workspace-location \"$workspaceLocation\" --file \"$resultFile\" $projectList" + def ringOpts = [Constants.DEFAULT_RING_OPTS] steps.withEnv(ringOpts) { steps.catchError { steps.cmd(ringCommand) diff --git a/src/ru/pulsar/jenkins/library/steps/InitFromFiles.groovy b/src/ru/pulsar/jenkins/library/steps/InitFromFiles.groovy index a8ce35d3..e08fa32a 100644 --- a/src/ru/pulsar/jenkins/library/steps/InitFromFiles.groovy +++ b/src/ru/pulsar/jenkins/library/steps/InitFromFiles.groovy @@ -20,7 +20,7 @@ class InitFromFiles implements Serializable { Logger.printLocation() - if (!config.infobaseFromFiles()) { + if (!config.infoBaseFromFiles()) { Logger.println("init infoBase from files is disabled") return } diff --git a/src/ru/pulsar/jenkins/library/steps/InitFromStorage.groovy b/src/ru/pulsar/jenkins/library/steps/InitFromStorage.groovy index 6ed2b390..18b310d4 100644 --- a/src/ru/pulsar/jenkins/library/steps/InitFromStorage.groovy +++ b/src/ru/pulsar/jenkins/library/steps/InitFromStorage.groovy @@ -27,7 +27,7 @@ class InitFromStorage implements Serializable { Logger.printLocation() - if (config.infobaseFromFiles()) { + if (config.infoBaseFromFiles()) { Logger.println("init infoBase from storage is disabled") return } diff --git a/src/ru/pulsar/jenkins/library/steps/InitInfobase.groovy b/src/ru/pulsar/jenkins/library/steps/InitInfoBase.groovy similarity index 76% rename from src/ru/pulsar/jenkins/library/steps/InitInfobase.groovy rename to src/ru/pulsar/jenkins/library/steps/InitInfoBase.groovy index f85c9eaf..981e52e4 100644 --- a/src/ru/pulsar/jenkins/library/steps/InitInfobase.groovy +++ b/src/ru/pulsar/jenkins/library/steps/InitInfoBase.groovy @@ -7,11 +7,11 @@ import ru.pulsar.jenkins.library.ioc.ContextRegistry import ru.pulsar.jenkins.library.utils.Logger import ru.pulsar.jenkins.library.utils.VRunner -class InitInfobase implements Serializable { +class InitInfoBase implements Serializable { private final JobConfiguration config; - InitInfobase(JobConfiguration config) { + InitInfoBase(JobConfiguration config) { this.config = config } @@ -32,19 +32,27 @@ class InitInfobase implements Serializable { String vrunnerPath = VRunner.getVRunnerPath(); - if (config.initInfobaseOptions.runMigration) { + if (config.initInfoBaseOptions.runMigration) { Logger.println("Запуск миграции ИБ") + String command = vrunnerPath + ' run --command "ЗапуститьОбновлениеИнформационнойБазы;ЗавершитьРаботуСистемы;" --execute ' + String executeParameter = '$runnerRoot/epf/ЗакрытьПредприятие.epf' + if (steps.isUnix()) { + executeParameter = '\\' + executeParameter + } + command += executeParameter; + command += ' --ibconnection "/F./build/ib"' + // Запуск миграции steps.catchError { - VRunner.exec(vrunnerPath + ' run --command "ЗапуститьОбновлениеИнформационнойБазы;ЗавершитьРаботуСистемы;" --execute \\$runnerRoot/epf/ЗакрытьПредприятие.epf --ibconnection "/F./build/ib"') + VRunner.exec(command) } } else { Logger.println("Шаг миграции ИБ выключен") } steps.catchError { - if (config.initInfobaseOptions.additionalInitializationSteps.length == 0) { + if (config.initInfoBaseOptions.additionalInitializationSteps.length == 0) { FileWrapper[] files = steps.findFiles("tools/vrunner.init*.json") files = files.sort new OrderBy( { it.name }) files.each { @@ -52,7 +60,7 @@ class InitInfobase implements Serializable { VRunner.exec("$vrunnerPath vanessa --settings ${it.path} --ibconnection \"/F./build/ib\"") } } else { - config.initInfobaseOptions.additionalInitializationSteps.each { + config.initInfoBaseOptions.additionalInitializationSteps.each { Logger.println("Первичная инициализация командой ${it}") VRunner.exec("$vrunnerPath ${it} --ibconnection \"/F./build/ib\"") } diff --git a/src/ru/pulsar/jenkins/library/steps/PublishAllure.groovy b/src/ru/pulsar/jenkins/library/steps/PublishAllure.groovy index 2c6360d0..8d55fe79 100644 --- a/src/ru/pulsar/jenkins/library/steps/PublishAllure.groovy +++ b/src/ru/pulsar/jenkins/library/steps/PublishAllure.groovy @@ -23,6 +23,9 @@ class PublishAllure implements Serializable { safeUnstash('init-allure') safeUnstash('bdd-allure') + if (config.smokeTestOptions.publishToAllureReport) { + safeUnstash(SmokeTest.SMOKE_ALLURE_STASH) + } def env = steps.env(); @@ -47,7 +50,7 @@ class PublishAllure implements Serializable { private void safeUnstash(String stashName) { try { steps.unstash(stashName) - } catch (Exception ex) { + } catch (Exception ignored) { Logger.println("Can't unstash $stashName") } } diff --git a/src/ru/pulsar/jenkins/library/steps/SmokeTest.groovy b/src/ru/pulsar/jenkins/library/steps/SmokeTest.groovy new file mode 100644 index 00000000..3fbf107a --- /dev/null +++ b/src/ru/pulsar/jenkins/library/steps/SmokeTest.groovy @@ -0,0 +1,120 @@ +package ru.pulsar.jenkins.library.steps + +import hudson.FilePath +import ru.pulsar.jenkins.library.IStepExecutor +import ru.pulsar.jenkins.library.configuration.JobConfiguration +import ru.pulsar.jenkins.library.ioc.ContextRegistry +import ru.pulsar.jenkins.library.utils.FileUtils +import ru.pulsar.jenkins.library.utils.Logger +import ru.pulsar.jenkins.library.utils.VRunner + +class SmokeTest implements Serializable { + + public static final String SMOKE_ALLURE_STASH = 'smoke-allure' + + private final JobConfiguration config + + SmokeTest(JobConfiguration config) { + this.config = config + } + + def run() { + IStepExecutor steps = ContextRegistry.getContext().getStepExecutor() + + Logger.printLocation() + + if (!config.stageFlags.smoke) { + Logger.println("Smoke test step is disabled") + return + } + + List logosConfig = ["LOGOS_CONFIG=$config.logosConfig"] + steps.withEnv(logosConfig) { + steps.installLocalDependencies() + } + + def options = config.smokeTestOptions + def env = steps.env() + + String vrunnerPath = VRunner.getVRunnerPath() + String command = "$vrunnerPath xunit --ibconnection \"/F./build/ib\"" + + String vrunnerSettings = options.vrunnerSettings + if (steps.fileExists(vrunnerSettings)) { + command += " --settings $vrunnerSettings" + } + + String xddTestRunnerPath = "./oscript_modules/add/xddTestRunner.epf" + if (steps.fileExists(xddTestRunnerPath)) { + command += " --pathxunit $xddTestRunnerPath" + } + + if (steps.fileExists(options.xddConfigPath)) { + command += " --xddConfig $options.xddConfigPath" + } + + String junitReport = "build/out/jUnit/smoke/smoke.xml" + FilePath pathToJUnitReport = FileUtils.getFilePath("$env.WORKSPACE/$junitReport") + String junitReportDir = FileUtils.getLocalPath(pathToJUnitReport.getParent()) + + String allureReport = "build/out/allure/smoke/allure.xml" + FilePath pathToAllureReport = FileUtils.getFilePath("$env.WORKSPACE/$allureReport") + String allureReportDir = FileUtils.getLocalPath(pathToAllureReport.getParent()) + + StringBuilder reportsConfigConstructor = new StringBuilder() + + if (options.publishToJUnitReport) { + steps.createDir(junitReportDir) + + String junitReportCommand = "ГенераторОтчетаJUnitXML{$junitReport}" + + reportsConfigConstructor.append(junitReportCommand) + } + + if (options.publishToAllureReport) { + steps.createDir(allureReportDir) + + String allureReportCommand = "ГенераторОтчетаAllureXMLВерсия2{$allureReport}" + + if (reportsConfigConstructor.length() > 0) { + reportsConfigConstructor.append(';') + } + reportsConfigConstructor.append(allureReportCommand) + } + + if (reportsConfigConstructor.length() > 0) { + String reportsConfig = reportsConfigConstructor.toString() + command += " --reportsxunit \"$reportsConfig\"" + } + + if (steps.isUnix()) { + command = command.replace(';', '\\;') + } + + if (!VRunner.configContainsSetting(vrunnerSettings, "testsPath")) { + String testsPath = "oscript_modules/add/tests/smoke" + if (!steps.fileExists(testsPath)) { + testsPath = '$addRoot/tests/smoke' + if (steps.isUnix()) { + testsPath = '\\' + testsPath + } + } + command += " $testsPath" + } + + steps.withEnv(logosConfig) { + VRunner.exec(command) + } + + if (options.publishToAllureReport) { + steps.stash(SMOKE_ALLURE_STASH, "$allureReportDir/**", true) + steps.archiveArtifacts("$allureReportDir/**") + } + + if (options.publishToJUnitReport) { + steps.junit("$junitReportDir/*.xml", true) + steps.archiveArtifacts("$junitReportDir/**") + } + + } +} diff --git a/src/ru/pulsar/jenkins/library/steps/SonarScanner.groovy b/src/ru/pulsar/jenkins/library/steps/SonarScanner.groovy index bac85327..022e187a 100644 --- a/src/ru/pulsar/jenkins/library/steps/SonarScanner.groovy +++ b/src/ru/pulsar/jenkins/library/steps/SonarScanner.groovy @@ -10,15 +10,9 @@ import ru.pulsar.jenkins.library.utils.VersionParser class SonarScanner implements Serializable { private final JobConfiguration config; - private final String rootFile SonarScanner(JobConfiguration config) { this.config = config - if (config.sourceFormat == SourceFormat.EDT){ - this.rootFile = "$config.srcDir/src/Configuration/Configuration.mdo" - } else { - this.rootFile = "$config.srcDir/Configuration.xml" - } } def run() { @@ -44,15 +38,9 @@ class SonarScanner implements Serializable { String sonarCommand = "$sonarScannerBinary -Dsonar.branch.name=$env.BRANCH_NAME" - String configurationVersion - if (config.sourceFormat == SourceFormat.EDT) { - configurationVersion = VersionParser.edt(rootFile) - } else { - configurationVersion = VersionParser.configuration(rootFile) - } - - if (configurationVersion) { - sonarCommand += " -Dsonar.projectVersion=$configurationVersion" + String projectVersion = computeProjectVersion() + if (projectVersion) { + sonarCommand += " -Dsonar.projectVersion=$projectVersion" } if (config.stageFlags.edtValidate) { @@ -69,4 +57,27 @@ class SonarScanner implements Serializable { steps.cmd(sonarCommand) } } + + private String computeProjectVersion() { + String projectVersion + String nameOfModule = config.sonarQubeOptions.infoBaseUpdateModuleName + + if (!nameOfModule.isEmpty()) { + String rootFile + if (config.sourceFormat == SourceFormat.EDT) { + rootFile = "$config.srcDir/src/CommonModules/$nameOfModule/Module.bsl" + } else { + rootFile = "$config.srcDir/CommonModules/$nameOfModule/Ext/Module.bsl" + } + projectVersion = VersionParser.ssl(rootFile) + } else if (config.sourceFormat == SourceFormat.EDT) { + String rootFile = "$config.srcDir/src/Configuration/Configuration.mdo" + projectVersion = VersionParser.edt(rootFile) + } else { + String rootFile = "$config.srcDir/Configuration.xml" + projectVersion = VersionParser.configuration(rootFile) + } + + return projectVersion + } } diff --git a/src/ru/pulsar/jenkins/library/utils/Constants.groovy b/src/ru/pulsar/jenkins/library/utils/Constants.groovy index 82dc25f2..1443474c 100644 --- a/src/ru/pulsar/jenkins/library/utils/Constants.groovy +++ b/src/ru/pulsar/jenkins/library/utils/Constants.groovy @@ -1,6 +1,6 @@ package ru.pulsar.jenkins.library.utils final class Constants { - public static final String DEFAULT_RING_OPTS = "RING_OPTS=-Dfile.encoding=UTF-8 -Dosgi.nl=ru -Duser.language=ru" + public static final String DEFAULT_RING_OPTS = "RING_OPTS=-Dfile.encoding=UTF-8 -Dsun.jnu.encoding=UTF8 -Dosgi.nl=ru -Duser.language=ru" } diff --git a/src/ru/pulsar/jenkins/library/utils/FileUtils.groovy b/src/ru/pulsar/jenkins/library/utils/FileUtils.groovy index 43d475bb..e7a0f6c2 100644 --- a/src/ru/pulsar/jenkins/library/utils/FileUtils.groovy +++ b/src/ru/pulsar/jenkins/library/utils/FileUtils.groovy @@ -5,6 +5,8 @@ import jenkins.model.Jenkins import ru.pulsar.jenkins.library.IStepExecutor import ru.pulsar.jenkins.library.ioc.ContextRegistry +import java.nio.file.Path + class FileUtils { static FilePath getFilePath(String path) { @@ -18,7 +20,7 @@ class FileUtils { steps.error 'Переменная среды NODE_NAME не задана. Запуск вне node или без agent?' } - if (nodeName == "master") { + if (nodeName == "master" || nodeName == "built-in") { return new FilePath(new File(path)); } else { return new FilePath(Jenkins.getInstanceOrNull().getComputer(nodeName).getChannel(), path); @@ -30,6 +32,13 @@ class FileUtils { def env = steps.env(); - return filePath.getRemote().replaceAll("^$env.WORKSPACE/", "").toString() + Path workspacePath = new File(env.WORKSPACE).toPath() + Path rawFilePath = new File(filePath.getRemote()).toPath() + + return workspacePath.relativize(rawFilePath) + .toString() + .replaceAll('\\\\\\\\', '/') + .replaceAll('\\\\', '/') + .toString() } } diff --git a/src/ru/pulsar/jenkins/library/utils/VRunner.groovy b/src/ru/pulsar/jenkins/library/utils/VRunner.groovy index d9b48ab9..fdf37e2f 100644 --- a/src/ru/pulsar/jenkins/library/utils/VRunner.groovy +++ b/src/ru/pulsar/jenkins/library/utils/VRunner.groovy @@ -27,4 +27,15 @@ class VRunner { return steps.cmd(command, returnStatus) } as int } + + static boolean configContainsSetting(String configPath, String settingName) { + IStepExecutor steps = ContextRegistry.getContext().getStepExecutor() + + if (!steps.fileExists(configPath)) { + return false + } + + String fileContent = steps.readFile(configPath) + return fileContent.contains("\"$settingName\"") + } } diff --git a/src/ru/pulsar/jenkins/library/utils/VersionParser.groovy b/src/ru/pulsar/jenkins/library/utils/VersionParser.groovy index 71839672..4914cdf8 100644 --- a/src/ru/pulsar/jenkins/library/utils/VersionParser.groovy +++ b/src/ru/pulsar/jenkins/library/utils/VersionParser.groovy @@ -8,6 +8,7 @@ import java.util.regex.Pattern class VersionParser implements Serializable { final static VERSION_REGEXP = ~/(?i)(.*)<\/version>/ + final static VERSION_REGEXP_SSL = ~/(?i)Описание.Версия = "(.*)";/ static String configuration(rootFile = 'src/cf/Configuration.xml') { return extractVersionFromFile(rootFile, VERSION_REGEXP) @@ -21,6 +22,10 @@ class VersionParser implements Serializable { return extractVersionFromFile(versionFile, VERSION_REGEXP) } + static String ssl(versionFile) { + return extractVersionFromFile(versionFile, VERSION_REGEXP_SSL) + } + private static String extractVersionFromFile(String filePath, Pattern regexp) { IStepExecutor steps = ContextRegistry.getContext().getStepExecutor() @@ -28,7 +33,7 @@ class VersionParser implements Serializable { return "" } - def configurationText = steps.readFile(filePath, 'UTF-8'); + def configurationText = steps.readFile(filePath) return version(configurationText, regexp) } diff --git a/test/unit/groovy/ru/pulsar/jenkins/library/configuration/ConfigurationReaderTest.java b/test/unit/groovy/ru/pulsar/jenkins/library/configuration/ConfigurationReaderTest.java index a1fe3b2e..64d2d3b9 100644 --- a/test/unit/groovy/ru/pulsar/jenkins/library/configuration/ConfigurationReaderTest.java +++ b/test/unit/groovy/ru/pulsar/jenkins/library/configuration/ConfigurationReaderTest.java @@ -33,6 +33,8 @@ void testCreateJobConfigurationObject() throws IOException { assertThat(jobConfiguration.getV8version()).isEqualTo("8.3.14.1944"); assertThat(jobConfiguration.getSonarQubeOptions().getSonarScannerToolName()).isEqualTo("sonar-scanner"); + assertThat(jobConfiguration.getSonarQubeOptions().getSonarQubeInstallation()).isEqualTo("qa"); + assertThat(jobConfiguration.getSonarQubeOptions().getUseSonarScannerFromPath()).isTrue(); assertThat(jobConfiguration.getSecrets()) .hasFieldOrPropertyWithValue("storage", "1234") @@ -41,15 +43,22 @@ void testCreateJobConfigurationObject() throws IOException { assertThat(jobConfiguration.getSyntaxCheckOptions().getCheckModes()).hasSize(1); - assertThat(jobConfiguration.getResultsTransformOptions().isRemoveSupport()).isFalse(); + assertThat(jobConfiguration.getResultsTransformOptions().getRemoveSupport()).isFalse(); assertThat(jobConfiguration.getResultsTransformOptions().getSupportLevel()).isZero(); - assertThat(jobConfiguration.getInitInfobaseOptions().getRunMigration()).isFalse(); - assertThat(jobConfiguration.getInitInfobaseOptions().getAdditionalInitializationSteps()).contains("vanessa --settings ./tools/vrunner.first.json"); + assertThat(jobConfiguration.getSmokeTestOptions().getVrunnerSettings()).contains("./tools/vrunner-smoke.json"); + assertThat(jobConfiguration.getSmokeTestOptions().isPublishToAllureReport()).isFalse(); + assertThat(jobConfiguration.getSmokeTestOptions().isPublishToJUnitReport()).isTrue(); + + assertThat(jobConfiguration.getInitInfoBaseOptions().getRunMigration()).isFalse(); + assertThat(jobConfiguration.getInitInfoBaseOptions().getAdditionalInitializationSteps()).contains("vanessa --settings ./tools/vrunner.first.json"); assertThat(jobConfiguration.getBddOptions().getVrunnerSteps()).contains("vanessa --settings ./tools/vrunner.json"); assertThat(jobConfiguration.getLogosConfig()).isEqualTo("logger.rootLogger=DEBUG"); + + assertThat(jobConfiguration.getTimeoutOptions().getBdd()).isEqualTo(120); + assertThat(jobConfiguration.getTimeoutOptions().getZipInfoBase()).isEqualTo(123); } } \ No newline at end of file diff --git a/test/unit/groovy/ru/pulsar/jenkins/library/utils/TestUtils.java b/test/unit/groovy/ru/pulsar/jenkins/library/utils/TestUtils.java index 6d2e88b6..92f5a4b4 100644 --- a/test/unit/groovy/ru/pulsar/jenkins/library/utils/TestUtils.java +++ b/test/unit/groovy/ru/pulsar/jenkins/library/utils/TestUtils.java @@ -34,6 +34,11 @@ public static IStepExecutor getMockedStepExecutor() { return FileUtils.readFileToString(new File(file), encoding); }); + when(steps.readFile(anyString())).thenAnswer(invocation -> { + String file = invocation.getArgument(0); + return FileUtils.readFileToString(new File(file), StandardCharsets.UTF_8); + }); + when(steps.fileExists(anyString())).thenAnswer(invocation -> { String file = invocation.getArgument(0); return new File(file).exists(); diff --git a/test/unit/resources/jobConfiguration.json b/test/unit/resources/jobConfiguration.json index 947e8ba8..a4205c11 100644 --- a/test/unit/resources/jobConfiguration.json +++ b/test/unit/resources/jobConfiguration.json @@ -6,17 +6,28 @@ "stages": { "syntaxCheck": true }, + "timeout": { + "zipInfoBase": 123 + }, "initInfobase": { "runMigration": false, "additionalInitializationSteps": [ "vanessa --settings ./tools/vrunner.first.json" ] }, + "sonarqube": { + "sonarQubeInstallation": "qa" + }, "syntaxCheck": { "checkModes": ["-ThinClient"] }, "resultsTransform": { "removeSupport": false }, + "smoke": { + "vrunnerSettings": "./tools/vrunner-smoke.json", + "publishToAllureReport": false, + "publishToJUnitReport": true + }, "logosConfig": "logger.rootLogger=DEBUG" } \ No newline at end of file diff --git a/vars/initInfobase.groovy b/vars/initInfobase.groovy index 7eabdcba..bb6bdd8e 100644 --- a/vars/initInfobase.groovy +++ b/vars/initInfobase.groovy @@ -1,10 +1,10 @@ import ru.pulsar.jenkins.library.configuration.JobConfiguration import ru.pulsar.jenkins.library.ioc.ContextRegistry -import ru.pulsar.jenkins.library.steps.InitInfobase +import ru.pulsar.jenkins.library.steps.InitInfoBase def call(JobConfiguration config) { ContextRegistry.registerDefaultContext(this) - def initInfobase = new InitInfobase(config) + def initInfobase = new InitInfoBase(config) initInfobase.run() } \ No newline at end of file diff --git a/vars/pipeline1C.groovy b/vars/pipeline1C.groovy index efab2c46..227924d9 100644 --- a/vars/pipeline1C.groovy +++ b/vars/pipeline1C.groovy @@ -19,7 +19,6 @@ void call() { options { buildDiscarder(logRotator(numToKeepStr: '30')) - timeout(time: 2, unit: TimeUnit.HOURS) timestamps() } @@ -29,6 +28,9 @@ void call() { agent { label 'agent' } + options { + timeout(time: 1, unit: TimeUnit.HOURS) + } steps { script { @@ -46,7 +48,7 @@ void call() { } when { beforeAgent true - expression { config.stageFlags.needInfobase() } + expression { config.stageFlags.needInfoBase() } } stages { @@ -56,25 +58,28 @@ void call() { } when { beforeAgent true - expression { config.stageFlags.needInfobase() && config.infobaseFromFiles() && config.sourceFormat == SourceFormat.EDT } + expression { config.stageFlags.needInfoBase() && config.infoBaseFromFiles() && config.sourceFormat == SourceFormat.EDT } } steps { - edtToDesignerFormatTransformation config + timeout(time: config.timeoutOptions.edtToDesignerFormatTransformation, unit: TimeUnit.MINUTES) { + edtToDesignerFormatTransformation config + } } } stage('Создание ИБ') { steps { - createDir('build/out') - - script { - if (config.infobaseFromFiles()){ - // Создание базы загрузкой из файлов - initFromFiles config - } - else{ - // Создание базы загрузкой конфигурации из хранилища - initFromStorage config + timeout(time: config.timeoutOptions.createInfoBase, unit: TimeUnit.MINUTES) { + createDir('build/out') + + script { + if (config.infoBaseFromFiles()) { + // Создание базы загрузкой из файлов + initFromFiles config + } else { + // Создание базы загрузкой конфигурации из хранилища + initFromStorage config + } } } } @@ -86,16 +91,20 @@ void call() { expression { config.stageFlags.initSteps } } steps { - // Инициализация и первичная миграция - initInfobase config + timeout(time: config.timeoutOptions.initInfoBase, unit: TimeUnit.MINUTES) { + // Инициализация и первичная миграция + initInfobase config + } } } stage('Архивация ИБ') { steps { - printLocation() + timeout(time: config.timeoutOptions.zipInfoBase, unit: TimeUnit.MINUTES) { + printLocation() - zipInfobase() + zipInfobase() + } } } @@ -109,10 +118,12 @@ void call() { } when { beforeAgent true - expression { config.sourceFormat == SourceFormat.DESIGNER && config.stageFlags.edtValidate} + expression { config.sourceFormat == SourceFormat.DESIGNER && config.stageFlags.edtValidate } } steps { - designerToEdtFormatTransformation config + timeout(time: config.timeoutOptions.designerToEdtFormatTransformation, unit: TimeUnit.MINUTES) { + designerToEdtFormatTransformation config + } } } } @@ -131,7 +142,9 @@ void call() { label 'edt' } steps { - edtValidate config + timeout(time: config.timeoutOptions.edtValidate, unit: TimeUnit.MINUTES) { + edtValidate config + } } } @@ -140,7 +153,9 @@ void call() { label 'oscript' } steps { - transform config + timeout(time: config.timeoutOptions.resultTransformation, unit: TimeUnit.MINUTES) { + transform config + } } } } @@ -155,9 +170,11 @@ void call() { expression { config.stageFlags.bdd } } steps { - unzipInfobase() - - bdd config + timeout(time: config.timeoutOptions.bdd, unit: TimeUnit.MINUTES) { + unzipInfobase() + + bdd config + } } } @@ -170,7 +187,9 @@ void call() { expression { config.stageFlags.syntaxCheck } } steps { - syntaxCheck config + timeout(time: config.timeoutOptions.syntaxCheck, unit: TimeUnit.MINUTES) { + syntaxCheck config + } } } @@ -183,7 +202,11 @@ void call() { expression { config.stageFlags.smoke } } steps { - smoke config + timeout(time: config.timeoutOptions.smoke, unit: TimeUnit.MINUTES) { + unzipInfobase() + + smoke config + } } } } @@ -198,7 +221,9 @@ void call() { expression { config.stageFlags.sonarqube } } steps { - sonarScanner config + timeout(time: config.timeoutOptions.sonarqube, unit: TimeUnit.MINUTES) { + sonarScanner config + } } } } diff --git a/vars/smoke.groovy b/vars/smoke.groovy index fc204f64..f43802fe 100644 --- a/vars/smoke.groovy +++ b/vars/smoke.groovy @@ -1,23 +1,12 @@ import ru.pulsar.jenkins.library.configuration.JobConfiguration import ru.pulsar.jenkins.library.ioc.ContextRegistry +import ru.pulsar.jenkins.library.steps.SmokeTest def call(JobConfiguration config) { ContextRegistry.registerDefaultContext(this) - // TODO: Вынести в отдельный класс по аналогии с SonarScanner - - printLocation() - - if (!config.stageFlags.smoke) { - echo("Smoke tests step is disabled") - return - } - - def options = config.syntaxCheckOptions - - installLocalDependencies() - - unzipInfobase() + def smokeTest = new SmokeTest(config) + smokeTest.run() } diff --git a/vars/syntaxCheck.groovy b/vars/syntaxCheck.groovy index 636b8370..b879fdb4 100644 --- a/vars/syntaxCheck.groovy +++ b/vars/syntaxCheck.groovy @@ -46,15 +46,15 @@ def call(JobConfiguration config) { command += " --settings $vrunnerSettings"; } + if (!options.exceptionFile.empty && fileExists(options.exceptionFile)) { + command += " --exception-file $options.exceptionFile" + } + if (options.checkModes.length > 0) { def checkModes = options.checkModes.join(" ") command += " --mode $checkModes" } - if (!options.exceptionFile.empty && fileExists(options.exceptionFile)) { - command += " --exception-file $options.exceptionFile" - } - // Запуск синтакс-проверки VRunner.exec(command, true)