diff --git a/README.md b/README.md index 3c08c25..d67fd24 100644 --- a/README.md +++ b/README.md @@ -23,18 +23,23 @@ 1. Для запуска шага анализа SonarQube требуется агент с меткой `sonar`. 1. Для запуска шага валидации EDT требуется агент с меткой `edt` (для собственно валидации) и агент с меткой `oscript` (для трансформации результатов с помощью библиотеки [stebi](https://github.com/Stepa86/stebi)). 1. Для запуска шагов, работающих с 1С (подготовка, синтаксический контроль и т.д.) требуется агент с меткой, совпадающей со значением в поле `v8version` файла конфигурации. -1. В качестве ИБ используется файловая база, создаваемая в `./build/ib`, без данных авторизации. Переопределение "в следующих сериях". +1. В качестве ИБ используется файловая база, создаваемая в `./build/ib` на основании конфигурации из хранилища без пользователей. При необходимости вы можете создать пользователей на фазе инициализации ИБ. 1. Stage "Дымовые тесты" пока пустой. 1. Запуск `vrunner` на текущий момент происходит из локального каталога `oscript_modules`. Предполагается наличие в корне репозитория файла `packagedef`, в котором бы была указана зависимость от `vanessa-runner` ## Возможности 1. Все шаги можно запустить на базе docker-образов из форка репозитория onec-docker. См. [памятку по слоям и последовательности сборки](https://github.com/firstBitSemenovskaya/onec-docker/blob/feature/first-bit/Layers.md) -1. Трансформация кода из формата конфигуратора в формат EDT (только если включен шаг `edtValidate`). 1. Подготовка информационной базы по версии из хранилища конфигурации. +1. Запуск ИБ в режиме выполнения обработчиков обновления БСП. +1. Дополнительные шаги инициализации данных в ИБ. +1. Трансформация кода из формата конфигуратора в формат EDT (только если включен шаг `edtValidate`). +1. Запуск BDD сценариев с сохранением результатов в формате Allure. 1. Запуск синтаксического контроля средствами конфигуратора и сохранение результатов в виде отчета jUnit. 1. Запуск валидации проекта средствами EDT и конвертация отчета в формате generic issues. -1. Запуск статического анализа для SonarQube +1. Запуск статического анализа для SonarQube. +1. Публикация результатов junit и Allure в интерфейс Jenkins. +1. Конфигурирование логгера запускаемых oscript-приложений. ## Подключение @@ -60,7 +65,7 @@ pipeline1C() ## Внешний вид пайплайна в интерфейсе Blue Ocean -![image](https://user-images.githubusercontent.com/1132840/80956084-27b14400-8e09-11ea-9e92-683818a14503.png) +![image](https://user-images.githubusercontent.com/1132840/121659170-b67f9e00-caaa-11eb-91be-107a689e893d.png) ## Конфигурирование @@ -89,3 +94,27 @@ pipeline1C() } } ``` + +## Параметры по умолчанию + +В библиотеке применяется принцип "соглашения по конфигурации" (convention over configuration): конфигурационный файл +содержит ряд настроек "по умолчанию". При соблюдении определенных правил структуры репозитория можно сократить +количество переопределений конфигурационного файла до минимума. + +* Общее: + * Исходники конфигурации ожидаются в каталоге `src/cf`. + * TODO: Имена "секретов" (jenkins credentials) по умолчанию высчитываются как `GROUP_REPO_KEY`, где `GROUP` и `REPO` - это группа проектов и имя проектов (например, `firstBitSemenovskaya` и `jenkins-lib`), а `KEY` - ключ секрета: + * `STORAGE_PATH` - путь к хранилищу конфигурации; + * `STORAGE_USER` - параметры авторизации в хранилище вида "username with password". + * Все "шаги" по умолчанию выключены. + * Результаты в формате `allure` ожидаются в каталоге `build/out/allure` или его подкаталогах. +* Инициализация: + * Если включен шаг `initSteps`, то будет выполняться запуск ИБ с целью запуска обработчиков обновления из БСП. (`initInfobase` -> `runMigration`) + * Если в настройках шага инициализации не заполнен массив дополнительных шагов миграции (`initInfobase` -> `additionalInitializationSteps`), но в каталоге `tools` присутствуют файлы с именами, удовлетворяющими шаблону `vrunner.init*.json`, то автоматически выполняется запуск `vrunner vanessa` с передачей найденных файлов в качестве значения настроек (параметр `--settings`) в порядке лексиграфической сортировки имен файлов. +* BDD: + * Если в конфигурационном файле проекта не заполнена настройка `bdd` -> `vrunnerSteps`, то автоматически выполняется запуск `vrunner vanessa --settings tools/vrunner.json`. +* Синтаксический контроль: + * Если в репозитории существует файл `tools/vrunner.json`, то синтаксический контроль конфигурации с помощью конфигуратора будет выполняться с передачей файла в параметры запуска `vrunner syntax-check --settings tools/vrunner.json`. + * TODO: Значение параметра `--mode` из конфигурационного файла vrunner имеют приоритет над `syntaxCheck` -> `checkModes`. Значение из `jobConfiguration.json` передается только в том случае, если параметр отсутствует в конфигурационном файле `vrunner`. +* Трансформация результатов валидации EDT: + * По умолчанию из результатов анализа исключаются замечания, сработавшие на модулях с включенным запретом редактирования (желтый куб с замком) \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts index 8e31f1f..903165c 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -78,7 +78,10 @@ sharedLibrary { // TODO: retrieve downloaded plugin resource pluginDependencies { dependency("org.jenkins-ci.plugins", "pipeline-build-step", "2.12") + dependency("org.jenkins-ci.plugins", "pipeline-utility-steps", "2.8.0") + dependency("org.jenkins-ci.plugins", "git", "4.4.4") dependency("org.6wind.jenkins", "lockable-resources", "2.7") + dependency("ru.yandex.qatools.allure", "allure-jenkins-plugin", "2.28.1") val declarativePluginsVersion = "1.6.0" dependency("org.jenkinsci.plugins", "pipeline-model-api", declarativePluginsVersion) dependency("org.jenkinsci.plugins", "pipeline-model-declarative-agent", "1.1.1") diff --git a/resources/globalConfiguration.json b/resources/globalConfiguration.json index 332119d..e2e2ca1 100644 --- a/resources/globalConfiguration.json +++ b/resources/globalConfiguration.json @@ -6,11 +6,22 @@ "storage": "UNKNOWN_ID" }, "stages": { + "initSteps": false, "sonarqube": false, + "bdd": false, "syntaxCheck": false, "edtValidate": false, "smoke": false }, + "initInfobase": { + "runMigration": true, + "additionalInitializationSteps": [] + }, + "bdd": { + "vrunnerSteps": [ + "vanessa --settings ./tools/vrunner.json" + ] + }, "sonarqube": { "sonarQubeInstallation": "", "useSonarScannerFromPath": true, @@ -18,7 +29,7 @@ }, "syntaxCheck": { "groupErrorsByMetadata": true, - "pathToJUnitReport": "build/out/jUnit/syntax.xml", + "pathToJUnitReport": "./build/out/jUnit/syntax.xml", "checkModes": [ "-ThinClient", "-WebClient", @@ -31,10 +42,12 @@ "-CheckUseModality", "-CheckUseSynchronousCalls", "-DistributiveModules" - ] + ], + "vrunnerSettings": "./tools/vrunner.json" }, "resultsTransform": { - "removeSupport": false, + "removeSupport": true, "supportLevel": 0 - } + }, + "logosConfig": "" } diff --git a/resources/schema.json b/resources/schema.json index 40bcd1c..7a61217 100644 --- a/resources/schema.json +++ b/resources/schema.json @@ -45,6 +45,46 @@ "smoke" : { "type" : "boolean", "description" : "Дымовые тесты включены" + }, + "initSteps" : { + "type" : "boolean", + "description" : "Предварительные шаги инициализации включены" + }, + "bdd" : { + "type" : "boolean", + "description" : "Запуск BDD сценариев включен" + } + } + }, + "initInfobase" : { + "type" : "object", + "id" : "urn:jsonschema:ru:pulsar:jenkins:library:configuration:InitInfobaseOptions", + "description" : "Настройки шага инициализации ИБ", + "properties" : { + "runMigration" : { + "type" : "boolean", + "description" : "Запустить миграцию ИБ" + }, + "additionalInitializationSteps" : { + "type" : "array", + "description" : "Дополнительные шаги, запускаемые через vrunner.\n В каждой строке передается отдельная команда \n vrunner и ее аргументы (например, \"vanessa --settings ./tools/vrunner.first.json\")\n ", + "items" : { + "type" : "string" + } + } + } + }, + "bdd" : { + "type" : "object", + "id" : "urn:jsonschema:ru:pulsar:jenkins:library:configuration:BddOptions", + "description" : "Настройки шага запуска BDD сценариев", + "properties" : { + "vrunnerSteps" : { + "type" : "array", + "description" : "Шаги, запускаемые через vrunner.\n В каждой строке передается отдельная команда \n vrunner и ее аргументы (например, \"vanessa --settings ./tools/vrunner.json\").\n По умолчанию содержит одну команду \"vanessa --settings ./tools/vrunner.json\".\n ", + "items" : { + "type" : "string" + } } } }, @@ -74,11 +114,11 @@ "properties" : { "pathToJUnitReport" : { "type" : "string", - "description" : "Путь к файлу отчета jUnit" + "description" : "Путь к файлу отчета jUnit\n По умолчанию содержит значение \"./build/out/jUnit/syntax.xml\"\n " }, "groupErrorsByMetadata" : { "type" : "boolean", - "description" : "Группировать выявленные ошибки по объектам метаданных" + "description" : "Группировать выявленные ошибки по объектам метаданных.\n По умолчанию включено.\n " }, "checkModes" : { "type" : "array", @@ -86,6 +126,10 @@ "items" : { "type" : "string" } + }, + "vrunnerSettings" : { + "type" : "string", + "description" : "Путь к конфигурационному файлу vanessa-runner.\n По умолчанию содержит значение \"./tools/vrunner.json\".\n " } } }, @@ -96,13 +140,17 @@ "properties" : { "removeSupport" : { "type" : "boolean", - "description" : "Фильтровать замечания по уровню поддержки модуля" + "description" : "Фильтровать замечания по уровню поддержки модуля. По умолчанию включено." }, "supportLevel" : { "type" : "integer", "description" : "Настройка фильтрации замечаний по уровню поддержки.\n 0 - удалить файлы на замке;\n 1 - удалить файлы на замке и на поддержке;\n 2 - удалить файлы на замке, на поддержке и снятые с поддержки.\n " } } + }, + "logosConfig" : { + "type" : "string", + "description" : "Конфигурация библиотеки logos. Применяется перед запуском каждой стадии сборки" } } } \ No newline at end of file diff --git a/src/JobConfigurationSchemaGenerator.java b/src/JobConfigurationSchemaGenerator.java index 68fe569..0a542b0 100644 --- a/src/JobConfigurationSchemaGenerator.java +++ b/src/JobConfigurationSchemaGenerator.java @@ -21,7 +21,7 @@ public class JobConfigurationSchemaGenerator { File jsonSchemaFile = new File("./resources/schema.json"); mapper.writeValue(jsonSchemaFile, jsonSchema); - System.out.println(json.toString()); + System.out.println(json); } } diff --git a/src/ru/pulsar/jenkins/library/IStepExecutor.groovy b/src/ru/pulsar/jenkins/library/IStepExecutor.groovy index 8942a1c..8c11196 100644 --- a/src/ru/pulsar/jenkins/library/IStepExecutor.groovy +++ b/src/ru/pulsar/jenkins/library/IStepExecutor.groovy @@ -1,5 +1,6 @@ package ru.pulsar.jenkins.library +import org.jenkinsci.plugins.pipeline.utility.steps.fs.FileWrapper import org.jenkinsci.plugins.workflow.support.actions.EnvironmentAction interface IStepExecutor { @@ -12,6 +13,10 @@ interface IStepExecutor { String libraryResource(String path) + FileWrapper[] findFiles(String glob) + + FileWrapper[] findFiles(String glob, String excludes) + String readFile(String file, String encoding) void echo(message) @@ -34,6 +39,8 @@ interface IStepExecutor { def stash(String name, String includes) + def stash(String name, String includes, boolean allowEmpty) + def unstash(String name) def zip(String dir, String zipFile) @@ -45,4 +52,12 @@ interface IStepExecutor { def unzip(String dir, String zipFile, quiet) def catchError(Closure body) + + def httpRequest(String url, String outputFile, String responseHandle, boolean wrapAsMultipart) + + def error(String errorMessage) + + def allure(List results) + + 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 2b425a5..573673b 100644 --- a/src/ru/pulsar/jenkins/library/StepExecutor.groovy +++ b/src/ru/pulsar/jenkins/library/StepExecutor.groovy @@ -1,6 +1,8 @@ package ru.pulsar.jenkins.library +import org.jenkinsci.plugins.pipeline.utility.steps.fs.FileWrapper import org.jenkinsci.plugins.workflow.support.actions.EnvironmentAction +import ru.yandex.qatools.allure.jenkins.config.ResultsConfig class StepExecutor implements IStepExecutor { @@ -35,6 +37,11 @@ class StepExecutor implements IStepExecutor { steps.readFile encoding: encoding, file: file } + @Override + FileWrapper[] findFiles(String glob, String excludes = '') { + steps.findFiles glob: glob, excludes: excludes + } + @Override void echo(Object message) { steps.echo message @@ -80,8 +87,8 @@ class StepExecutor implements IStepExecutor { } @Override - def stash(String name, String includes) { - steps.stash name: name, includes: includes + def stash(String name, String includes, boolean allowEmpty = false) { + steps.stash name: name, includes: includes, allowEmpty: allowEmpty } @Override @@ -103,4 +110,31 @@ class StepExecutor implements IStepExecutor { def catchError(Closure body) { steps.catchError body } + + @Override + def httpRequest(String url, String outputFile, String responseHandle = 'NONE', boolean wrapAsMultipart = false) { + steps.httpRequest responseHandle: responseHandle, outputFile: outputFile, url: url, wrapAsMultipart: wrapAsMultipart + } + + @Override + def error(String errorMessage) { + steps.error errorMessage + } + + @Override + def allure(List results) { + steps.allure([ + commandline: 'allure', + includeProperties: false, + jdk: '', + properties: [], + reportBuildPolicy: 'ALWAYS', + results: ResultsConfig.convertPaths(results) + ]) + } + + @Override + def installLocalDependencies() { + steps.installLocalDependencies() + } } diff --git a/src/ru/pulsar/jenkins/library/configuration/BddOptions.groovy b/src/ru/pulsar/jenkins/library/configuration/BddOptions.groovy new file mode 100644 index 0000000..95e7458 --- /dev/null +++ b/src/ru/pulsar/jenkins/library/configuration/BddOptions.groovy @@ -0,0 +1,26 @@ +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 BddOptions implements Serializable { + + @JsonPropertyDescription("""Шаги, запускаемые через vrunner. + В каждой строке передается отдельная команда + vrunner и ее аргументы (например, "vanessa --settings ./tools/vrunner.json"). + По умолчанию содержит одну команду "vanessa --settings ./tools/vrunner.json". + """) + String[] vrunnerSteps = [ + 'vanessa --settings ./tools/vrunner.json' + ] + + @Override + @NonCPS + String toString() { + return "BddOptions{" + + "vrunnerSteps=" + vrunnerSteps + + '}'; + } +} diff --git a/src/ru/pulsar/jenkins/library/configuration/ConfigurationReader.groovy b/src/ru/pulsar/jenkins/library/configuration/ConfigurationReader.groovy index d17ddf5..55b7ad2 100644 --- a/src/ru/pulsar/jenkins/library/configuration/ConfigurationReader.groovy +++ b/src/ru/pulsar/jenkins/library/configuration/ConfigurationReader.groovy @@ -7,6 +7,8 @@ import org.apache.commons.beanutils.BeanUtils import ru.pulsar.jenkins.library.IStepExecutor import ru.pulsar.jenkins.library.ioc.ContextRegistry +import javax.annotation.CheckForNull + class ConfigurationReader implements Serializable { private static ObjectMapper mapper @@ -41,12 +43,16 @@ class ConfigurationReader implements Serializable { def nonMergeableSettings = Arrays.asList( "secrets", "stageFlags", + "initInfobaseOptions", + "bddOptions", "sonarQubeOptions", "syntaxCheckOptions", "resultsTransformOptions" ).toSet() mergeObjects(baseConfiguration, configurationToMerge, nonMergeableSettings) + mergeInitInfobaseOptions(baseConfiguration.initInfobaseOptions, configurationToMerge.initInfobaseOptions); + mergeBddOptions(baseConfiguration.bddOptions, configurationToMerge.bddOptions); return baseConfiguration; } @@ -70,4 +76,20 @@ class ConfigurationReader implements Serializable { ) }) } + + @NonCPS + private static void mergeInitInfobaseOptions(InitInfobaseOptions baseObject, @CheckForNull InitInfobaseOptions objectToMerge) { + if (objectToMerge == null || objectToMerge.additionalInitializationSteps == null) { + return + } + baseObject.additionalInitializationSteps = objectToMerge.additionalInitializationSteps.clone() + } + + @NonCPS + private static void mergeBddOptions(BddOptions baseObject, @CheckForNull BddOptions objectToMerge) { + if (objectToMerge == null || objectToMerge.vrunnerSteps == null) { + return + } + baseObject.vrunnerSteps = objectToMerge.vrunnerSteps.clone() + } } diff --git a/src/ru/pulsar/jenkins/library/configuration/InitInfobaseOptions.groovy b/src/ru/pulsar/jenkins/library/configuration/InitInfobaseOptions.groovy new file mode 100644 index 0000000..6350339 --- /dev/null +++ b/src/ru/pulsar/jenkins/library/configuration/InitInfobaseOptions.groovy @@ -0,0 +1,27 @@ +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 InitInfobaseOptions implements Serializable { + + @JsonPropertyDescription("Запустить миграцию ИБ") + boolean runMigration = true + + @JsonPropertyDescription("""Дополнительные шаги, запускаемые через vrunner. + В каждой строке передается отдельная команда + vrunner и ее аргументы (например, "vanessa --settings ./tools/vrunner.first.json") + """) + String[] additionalInitializationSteps + + @Override + @NonCPS + String toString() { + return "InitInfobaseOptions{" + + "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 3368d8e..c404c9d 100644 --- a/src/ru/pulsar/jenkins/library/configuration/JobConfiguration.groovy +++ b/src/ru/pulsar/jenkins/library/configuration/JobConfiguration.groovy @@ -20,6 +20,14 @@ class JobConfiguration implements Serializable { @JsonPropertyDescription("Идентификаторы сохраненных секретов") Secrets secrets; + @JsonProperty("initInfobase") + @JsonPropertyDescription("Настройки шага инициализации ИБ") + InitInfobaseOptions initInfobaseOptions; + + @JsonProperty("bdd") + @JsonPropertyDescription("Настройки шага запуска BDD сценариев") + BddOptions bddOptions; + @JsonProperty("sonarqube") @JsonPropertyDescription("Настройки анализа SonarQube") SonarQubeOptions sonarQubeOptions; @@ -32,6 +40,10 @@ class JobConfiguration implements Serializable { @JsonPropertyDescription("Настройки трансформации результатов анализа") ResultsTransformOptions resultsTransformOptions; + @JsonProperty("logosConfig") + @JsonPropertyDescription("Конфигурация библиотеки logos. Применяется перед запуском каждой стадии сборки") + String logosConfig; + @Override @NonCPS String toString() { @@ -40,9 +52,12 @@ class JobConfiguration implements Serializable { ", srcDir='" + srcDir + '\'' + ", stageFlags=" + stageFlags + ", secrets=" + secrets + + ", initInfobaseOptions=" + initInfobaseOptions + + ", bddOptions=" + bddOptions + ", sonarQubeOptions=" + sonarQubeOptions + ", syntaxCheckOptions=" + syntaxCheckOptions + ", resultsTransformOptions=" + resultsTransformOptions + + ", logosConfig=" + logosConfig + '}'; } } \ 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 c159fd2..08168fc 100644 --- a/src/ru/pulsar/jenkins/library/configuration/ResultsTransformOptions.groovy +++ b/src/ru/pulsar/jenkins/library/configuration/ResultsTransformOptions.groovy @@ -7,8 +7,8 @@ import com.fasterxml.jackson.annotation.JsonPropertyDescription @JsonIgnoreProperties(ignoreUnknown = true) class ResultsTransformOptions implements Serializable { - @JsonPropertyDescription("Фильтровать замечания по уровню поддержки модуля") - boolean removeSupport + @JsonPropertyDescription("Фильтровать замечания по уровню поддержки модуля. По умолчанию включено.") + boolean removeSupport = true @JsonPropertyDescription("""Настройка фильтрации замечаний по уровню поддержки. 0 - удалить файлы на замке; diff --git a/src/ru/pulsar/jenkins/library/configuration/StageFlags.groovy b/src/ru/pulsar/jenkins/library/configuration/StageFlags.groovy index c73a158..2fdcbe7 100644 --- a/src/ru/pulsar/jenkins/library/configuration/StageFlags.groovy +++ b/src/ru/pulsar/jenkins/library/configuration/StageFlags.groovy @@ -18,6 +18,12 @@ class StageFlags implements Serializable { @JsonPropertyDescription("Дымовые тесты включены") boolean smoke + @JsonPropertyDescription("Предварительные шаги инициализации включены") + boolean initSteps + + @JsonPropertyDescription("Запуск BDD сценариев включен") + boolean bdd + @Override @NonCPS String toString() { @@ -26,10 +32,12 @@ class StageFlags implements Serializable { ", syntaxCheck=" + syntaxCheck + ", edtValidate=" + edtValidate + ", smoke=" + smoke + + ", initSteps=" + initSteps + + ", bdd=" + bdd + '}'; } boolean needInfobase() { - return smoke || syntaxCheck + return smoke || syntaxCheck || initSteps || bdd } } diff --git a/src/ru/pulsar/jenkins/library/configuration/SyntaxCheckOptions.groovy b/src/ru/pulsar/jenkins/library/configuration/SyntaxCheckOptions.groovy index dc765f4..6edbf9b 100644 --- a/src/ru/pulsar/jenkins/library/configuration/SyntaxCheckOptions.groovy +++ b/src/ru/pulsar/jenkins/library/configuration/SyntaxCheckOptions.groovy @@ -7,14 +7,23 @@ import com.fasterxml.jackson.annotation.JsonPropertyDescription @JsonIgnoreProperties(ignoreUnknown = true) class SyntaxCheckOptions implements Serializable { - @JsonPropertyDescription("Путь к файлу отчета jUnit") - String pathToJUnitReport + @JsonPropertyDescription("""Путь к файлу отчета jUnit + По умолчанию содержит значение "./build/out/jUnit/syntax.xml" + """) + String pathToJUnitReport = "./build/out/jUnit/syntax.xml" - @JsonPropertyDescription("Группировать выявленные ошибки по объектам метаданных") - boolean groupErrorsByMetadata; + @JsonPropertyDescription("""Группировать выявленные ошибки по объектам метаданных. + По умолчанию включено. + """) + boolean groupErrorsByMetadata = true @JsonPropertyDescription("Режимы проверки конфигурации") - String[] checkModes; + String[] checkModes + + @JsonPropertyDescription("""Путь к конфигурационному файлу vanessa-runner. + По умолчанию содержит значение "./tools/vrunner.json". + """) + String vrunnerSettings = "./tools/vrunner.json" @Override @NonCPS @@ -23,6 +32,7 @@ class SyntaxCheckOptions implements Serializable { "pathToJUnitReport='" + pathToJUnitReport + '\'' + ", groupErrorsByMetadata=" + groupErrorsByMetadata + ", checkModes=" + checkModes + + ", vrunnerSettings=" + vrunnerSettings + '}'; } } diff --git a/src/ru/pulsar/jenkins/library/steps/Bdd.groovy b/src/ru/pulsar/jenkins/library/steps/Bdd.groovy new file mode 100644 index 0000000..ebb8629 --- /dev/null +++ b/src/ru/pulsar/jenkins/library/steps/Bdd.groovy @@ -0,0 +1,49 @@ +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.Logger + +class Bdd implements Serializable { + + private final JobConfiguration config; + + Bdd(JobConfiguration config) { + this.config = config + } + + def run() { + IStepExecutor steps = ContextRegistry.getContext().getStepExecutor() + + Logger.printLocation() + + if (!config.stageFlags.bdd) { + Logger.println("BDD step is disabled") + return + } + + List logosConfig = ["LOGOS_CONFIG=$config.logosConfig"] + steps.withEnv(logosConfig) { + steps.installLocalDependencies() + + steps.createDir('build/out') + + // TODO: удалить после выхода VAS 1.0.35 + steps.httpRequest( + 'https://cloud.svc.pulsar.ru/index.php/s/WKwmqpFXSjfYjAH/download', + 'oscript_modules/vanessa-automation-single/vanessa-automation-single.epf' + ) + + steps.catchError { + config.bddOptions.vrunnerSteps.each { + Logger.println("Шаг запуска сценариев командой ${it}") + steps.cmd("oscript_modules/bin/vrunner ${it} --ibconnection \"/F./build/ib\"") + } + } + } + + steps.stash('bdd-allure', 'build/out/allure/**', true) + steps.stash('bdd-cucumber', 'build/out/cucumber/**', true) + } +} diff --git a/src/ru/pulsar/jenkins/library/steps/EdtTransform.groovy b/src/ru/pulsar/jenkins/library/steps/EdtTransform.groovy index 4b2a065..68e53e6 100644 --- a/src/ru/pulsar/jenkins/library/steps/EdtTransform.groovy +++ b/src/ru/pulsar/jenkins/library/steps/EdtTransform.groovy @@ -1,5 +1,6 @@ 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 @@ -47,4 +48,5 @@ class EdtTransform implements Serializable { steps.zip(WORKSPACE, WORKSPACE_ZIP) steps.stash(WORKSPACE_ZIP_STASH, WORKSPACE_ZIP) } + } diff --git a/src/ru/pulsar/jenkins/library/steps/InitInfobase.groovy b/src/ru/pulsar/jenkins/library/steps/InitInfobase.groovy new file mode 100644 index 0000000..eaef74b --- /dev/null +++ b/src/ru/pulsar/jenkins/library/steps/InitInfobase.groovy @@ -0,0 +1,69 @@ +package ru.pulsar.jenkins.library.steps + +import org.jenkinsci.plugins.pipeline.utility.steps.fs.FileWrapper +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.Logger + +class InitInfobase implements Serializable { + + private final JobConfiguration config; + + InitInfobase(JobConfiguration config) { + this.config = config + } + + def run() { + IStepExecutor steps = ContextRegistry.getContext().getStepExecutor() + + Logger.printLocation() + + steps.createDir('build/out') + + if (!config.stageFlags.initSteps) { + Logger.println("Init step is disabled") + return + } + + // TODO: удалить после выхода VAS 1.0.35 + steps.httpRequest( + 'https://cloud.svc.pulsar.ru/index.php/s/WKwmqpFXSjfYjAH/download', + 'oscript_modules/vanessa-automation-single/vanessa-automation-single.epf' + ) + + List logosConfig = ["LOGOS_CONFIG=$config.logosConfig"] + steps.withEnv(logosConfig) { + + if (config.initInfobaseOptions.runMigration) { + Logger.println("Запуск миграции ИБ") + + // Запуск миграции + steps.catchError { + steps.cmd('oscript_modules/bin/vrunner run --command "ЗапуститьОбновлениеИнформационнойБазы;ЗавершитьРаботуСистемы;" --execute \\$runnerRoot/epf/ЗакрытьПредприятие.epf --ibconnection "/F./build/ib"') + } + } else { + Logger.println("Шаг миграции ИБ выключен") + } + + steps.catchError { + if (config.initInfobaseOptions.additionalInitializationSteps.length == 0) { + FileWrapper[] files = steps.findFiles("tools/vrunner.init*.json") + files = files.sort new OrderBy( { it.name }) + files.each { + Logger.println("Первичная инициализация файлом ${it.path}") + steps.cmd("oscript_modules/bin/vrunner vanessa --settings ${it.path} --ibconnection \"/F./build/ib\"") + } + } else { + config.initInfobaseOptions.additionalInitializationSteps.each { + Logger.println("Первичная инициализация командой ${it}") + steps.cmd("oscript_modules/bin/vrunner ${it} --ibconnection \"/F./build/ib\"") + } + } + } + } + + steps.stash('init-allure', 'build/out/allure/**', true) + steps.stash('init-cucumber', 'build/out/cucumber/**', true) + } +} diff --git a/src/ru/pulsar/jenkins/library/steps/PublishAllure.groovy b/src/ru/pulsar/jenkins/library/steps/PublishAllure.groovy new file mode 100644 index 0000000..2c6360d --- /dev/null +++ b/src/ru/pulsar/jenkins/library/steps/PublishAllure.groovy @@ -0,0 +1,54 @@ +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 + +class PublishAllure implements Serializable { + + private final JobConfiguration config; + private IStepExecutor steps; + + PublishAllure(JobConfiguration config) { + this.config = config + } + + def run() { + steps = ContextRegistry.getContext().getStepExecutor() + + Logger.printLocation() + + safeUnstash('init-allure') + safeUnstash('bdd-allure') + + def env = steps.env(); + + FilePath allurePath = FileUtils.getFilePath("$env.WORKSPACE/build/out/allure") + if (!allurePath.exists()) { + Logger.println("Отсутствуют результаты allure для публикации") + return + } + + List results = new ArrayList<>(); + + allurePath.listDirectories().each { FilePath filePath -> + results.add(FileUtils.getLocalPath(filePath)) + } + if (results.isEmpty()) { + results.add(FileUtils.getLocalPath(allurePath)) + } + + steps.allure(results) + } + + private void safeUnstash(String stashName) { + try { + steps.unstash(stashName) + } catch (Exception ex) { + Logger.println("Can't unstash $stashName") + } + } +} diff --git a/src/ru/pulsar/jenkins/library/utils/FileUtils.groovy b/src/ru/pulsar/jenkins/library/utils/FileUtils.groovy new file mode 100644 index 0000000..43d475b --- /dev/null +++ b/src/ru/pulsar/jenkins/library/utils/FileUtils.groovy @@ -0,0 +1,35 @@ +package ru.pulsar.jenkins.library.utils + +import hudson.FilePath +import jenkins.model.Jenkins +import ru.pulsar.jenkins.library.IStepExecutor +import ru.pulsar.jenkins.library.ioc.ContextRegistry + +class FileUtils { + + static FilePath getFilePath(String path) { + + IStepExecutor steps = ContextRegistry.getContext().getStepExecutor() + + def env = steps.env(); + + String nodeName = env.NODE_NAME; + if (nodeName == null) { + steps.error 'Переменная среды NODE_NAME не задана. Запуск вне node или без agent?' + } + + if (nodeName == "master") { + return new FilePath(new File(path)); + } else { + return new FilePath(Jenkins.getInstanceOrNull().getComputer(nodeName).getChannel(), path); + } + } + + static String getLocalPath(FilePath filePath) { + IStepExecutor steps = ContextRegistry.getContext().getStepExecutor() + + def env = steps.env(); + + return filePath.getRemote().replaceAll("^$env.WORKSPACE/", "").toString() + } +} 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 01e4edc..a1fe3b2 100644 --- a/test/unit/groovy/ru/pulsar/jenkins/library/configuration/ConfigurationReaderTest.java +++ b/test/unit/groovy/ru/pulsar/jenkins/library/configuration/ConfigurationReaderTest.java @@ -31,14 +31,25 @@ class ConfigurationReaderTest { // then assertThat(jobConfiguration.getV8version()).isEqualTo("8.3.14.1944"); + assertThat(jobConfiguration.getSonarQubeOptions().getSonarScannerToolName()).isEqualTo("sonar-scanner"); + assertThat(jobConfiguration.getSecrets()) .hasFieldOrPropertyWithValue("storage", "1234") .hasFieldOrPropertyWithValue("storagePath", "UNKNOWN_ID") ; + assertThat(jobConfiguration.getSyntaxCheckOptions().getCheckModes()).hasSize(1); - assertThat(jobConfiguration.getResultsTransformOptions().isRemoveSupport()).isTrue(); - assertThat(jobConfiguration.getResultsTransformOptions().getSupportLevel()).isEqualTo(0); + + assertThat(jobConfiguration.getResultsTransformOptions().isRemoveSupport()).isFalse(); + assertThat(jobConfiguration.getResultsTransformOptions().getSupportLevel()).isZero(); + + 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"); } } \ No newline at end of file diff --git a/test/unit/resources/jobConfiguration.json b/test/unit/resources/jobConfiguration.json index 2cd66f2..947e8ba 100644 --- a/test/unit/resources/jobConfiguration.json +++ b/test/unit/resources/jobConfiguration.json @@ -6,10 +6,17 @@ "stages": { "syntaxCheck": true }, + "initInfobase": { + "runMigration": false, + "additionalInitializationSteps": [ + "vanessa --settings ./tools/vrunner.first.json" + ] + }, "syntaxCheck": { "checkModes": ["-ThinClient"] }, "resultsTransform": { - "removeSupport": true - } + "removeSupport": false + }, + "logosConfig": "logger.rootLogger=DEBUG" } \ No newline at end of file diff --git a/vars/bdd.groovy b/vars/bdd.groovy new file mode 100644 index 0000000..13d045a --- /dev/null +++ b/vars/bdd.groovy @@ -0,0 +1,10 @@ +import ru.pulsar.jenkins.library.configuration.JobConfiguration +import ru.pulsar.jenkins.library.ioc.ContextRegistry +import ru.pulsar.jenkins.library.steps.Bdd + +def call(JobConfiguration config) { + ContextRegistry.registerDefaultContext(this) + + def bdd = new Bdd(config) + bdd.run() +} \ No newline at end of file diff --git a/vars/initInfobase.groovy b/vars/initInfobase.groovy new file mode 100644 index 0000000..7eabdcb --- /dev/null +++ b/vars/initInfobase.groovy @@ -0,0 +1,10 @@ +import ru.pulsar.jenkins.library.configuration.JobConfiguration +import ru.pulsar.jenkins.library.ioc.ContextRegistry +import ru.pulsar.jenkins.library.steps.InitInfobase + +def call(JobConfiguration config) { + ContextRegistry.registerDefaultContext(this) + + def initInfobase = new InitInfobase(config) + initInfobase.run() +} \ No newline at end of file diff --git a/vars/pipeline1C.groovy b/vars/pipeline1C.groovy index d07006f..04ee349 100644 --- a/vars/pipeline1C.groovy +++ b/vars/pipeline1C.groovy @@ -27,6 +27,7 @@ void call() { agent { label 'agent' } + steps { script { config = jobConfiguration() as JobConfiguration @@ -46,18 +47,41 @@ void call() { expression { config.stageFlags.needInfobase() } } - steps { - printLocation() + stages { + stage('Создание ИБ') { + steps { + printLocation() - installLocalDependencies() + installLocalDependencies() - dir("build/out") { echo '' } + createDir('build/out') - // Создание базы загрузкой конфигурации из хранилища - initFromStorage config + // Создание базы загрузкой конфигурации из хранилища + initFromStorage config + } + } - zipInfobase() + stage('Инициализация ИБ') { + when { + beforeAgent true + expression { config.stageFlags.initSteps } + } + steps { + // Инициализация и первичная миграция + initInfobase config + } + } + + stage('Архивация ИБ') { + steps { + printLocation() + + zipInfobase() + } + + } } + } stage('Трансформация в формат EDT') { @@ -90,6 +114,21 @@ void call() { } } + stage('BDD сценарии') { + agent { + label agent1C + } + when { + beforeAgent true + expression { config.stageFlags.bdd } + } + steps { + unzipInfobase() + + bdd config + } + } + stage('Синтаксический контроль') { agent { label agent1C @@ -144,6 +183,14 @@ void call() { } } } + + post('post-stage') { + always { + node('agent') { + saveResults config + } + } + } } } diff --git a/vars/saveResults.groovy b/vars/saveResults.groovy new file mode 100644 index 0000000..c142b28 --- /dev/null +++ b/vars/saveResults.groovy @@ -0,0 +1,32 @@ +import ru.pulsar.jenkins.library.configuration.JobConfiguration +import ru.pulsar.jenkins.library.ioc.ContextRegistry +import ru.pulsar.jenkins.library.steps.PublishAllure + +def call(JobConfiguration config) { + ContextRegistry.registerDefaultContext(this) + + def publishAllure = new PublishAllure(config) + publishAllure.run() + +// step([ +// $class: 'CucumberReportPublisher', +// fileIncludePattern: '*.json', +// jsonReportDirectory: 'build/out/cucumber' +// ]) +// +// step([ +// $class: 'CukedoctorPublisher', +// featuresDir: 'build/out/cucumber', +// format: 'HTML', +// hideFeaturesSection: false, +// hideScenarioKeyword: false, +// hideStepTime: false, +// hideSummary: false, +// hideTags: false, +// numbered: true, +// sectAnchors: true, +// title: 'Living Documentation', +// toc: 'LEFT' +// ]) + +} \ No newline at end of file diff --git a/vars/syntaxCheck.groovy b/vars/syntaxCheck.groovy index 3d36d25..048cb6f 100644 --- a/vars/syntaxCheck.groovy +++ b/vars/syntaxCheck.groovy @@ -1,5 +1,7 @@ +import hudson.FilePath import ru.pulsar.jenkins.library.configuration.JobConfiguration import ru.pulsar.jenkins.library.ioc.ContextRegistry +import ru.pulsar.jenkins.library.utils.FileUtils def call(JobConfiguration config) { @@ -20,27 +22,37 @@ def call(JobConfiguration config) { unzipInfobase() - def outPath = new File(options.pathToJUnitReport).getParent() + FilePath pathToJUnitReport = FileUtils.getFilePath("$env.WORKSPACE/$options.pathToJUnitReport") + + String outPath = pathToJUnitReport.getParent() createDir(outPath) - String command = "oscript_modules/bin/vrunner syntax-check --ibconnection \"/F./build/ib\"" + String command = 'oscript_modules/bin/vrunner syntax-check --ibconnection "/F./build/ib"' // Временно убрал передачу параметра. // См. https://github.com/vanessa-opensource/vanessa-runner/issues/361 // command += " --workspace $env.WORKSPACE" if (options.groupErrorsByMetadata) { - command += " --groupbymetadata" + command += ' --groupbymetadata' } - command += " --junitpath " + options.pathToJUnitReport; + command += " --junitpath $pathToJUnitReport"; - command += " --mode " + options.checkModes.join(" ") + FilePath vrunnerSettings = FileUtils.getFilePath("$env.WORKSPACE/$options.vrunnerSettings") + if (vrunnerSettings.exists()) { + command += " --settings $vrunnerSettings"; + } + + if (options.checkModes.length > 0) { + def checkModes = options.checkModes.join(" ") + command += " --mode $checkModes" + } // Запуск синтакс-проверки cmd(command, true) - junit allowEmptyResults: true, testResults: options.pathToJUnitReport + junit allowEmptyResults: true, testResults: FileUtils.getLocalPath(pathToJUnitReport) - archiveArtifacts options.pathToJUnitReport + archiveArtifacts FileUtils.getLocalPath(pathToJUnitReport) }