1
0
mirror of https://github.com/firstBitMarksistskaya/jenkins-lib.git synced 2025-05-13 22:16:37 +02:00

Merge pull request #13 from firstBitSemenovskaya/vrunnerInit

This commit is contained in:
Nikita Gryzlov 2021-06-11 15:24:26 +03:00 committed by GitHub
commit 05f04be9e8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 606 additions and 40 deletions

View File

@ -23,18 +23,23 @@
1. Для запуска шага анализа SonarQube требуется агент с меткой `sonar`. 1. Для запуска шага анализа SonarQube требуется агент с меткой `sonar`.
1. Для запуска шага валидации EDT требуется агент с меткой `edt` (для собственно валидации) и агент с меткой `oscript` (для трансформации результатов с помощью библиотеки [stebi](https://github.com/Stepa86/stebi)). 1. Для запуска шага валидации EDT требуется агент с меткой `edt` (для собственно валидации) и агент с меткой `oscript` (для трансформации результатов с помощью библиотеки [stebi](https://github.com/Stepa86/stebi)).
1. Для запуска шагов, работающих с 1С (подготовка, синтаксический контроль и т.д.) требуется агент с меткой, совпадающей со значением в поле `v8version` файла конфигурации. 1. Для запуска шагов, работающих с 1С (подготовка, синтаксический контроль и т.д.) требуется агент с меткой, совпадающей со значением в поле `v8version` файла конфигурации.
1. В качестве ИБ используется файловая база, создаваемая в `./build/ib`, без данных авторизации. Переопределение "в следующих сериях". 1. В качестве ИБ используется файловая база, создаваемая в `./build/ib` на основании конфигурации из хранилища без пользователей. При необходимости вы можете создать пользователей на фазе инициализации ИБ.
1. Stage "Дымовые тесты" пока пустой. 1. Stage "Дымовые тесты" пока пустой.
1. Запуск `vrunner` на текущий момент происходит из локального каталога `oscript_modules`. Предполагается наличие в корне репозитория файла `packagedef`, в котором бы была указана зависимость от `vanessa-runner` 1. Запуск `vrunner` на текущий момент происходит из локального каталога `oscript_modules`. Предполагается наличие в корне репозитория файла `packagedef`, в котором бы была указана зависимость от `vanessa-runner`
## Возможности ## Возможности
1. Все шаги можно запустить на базе docker-образов из форка репозитория onec-docker. См. [памятку по слоям и последовательности сборки](https://github.com/firstBitSemenovskaya/onec-docker/blob/feature/first-bit/Layers.md) 1. Все шаги можно запустить на базе docker-образов из форка репозитория onec-docker. См. [памятку по слоям и последовательности сборки](https://github.com/firstBitSemenovskaya/onec-docker/blob/feature/first-bit/Layers.md)
1. Трансформация кода из формата конфигуратора в формат EDT (только если включен шаг `edtValidate`).
1. Подготовка информационной базы по версии из хранилища конфигурации. 1. Подготовка информационной базы по версии из хранилища конфигурации.
1. Запуск ИБ в режиме выполнения обработчиков обновления БСП.
1. Дополнительные шаги инициализации данных в ИБ.
1. Трансформация кода из формата конфигуратора в формат EDT (только если включен шаг `edtValidate`).
1. Запуск BDD сценариев с сохранением результатов в формате Allure.
1. Запуск синтаксического контроля средствами конфигуратора и сохранение результатов в виде отчета jUnit. 1. Запуск синтаксического контроля средствами конфигуратора и сохранение результатов в виде отчета jUnit.
1. Запуск валидации проекта средствами EDT и конвертация отчета в формате generic issues. 1. Запуск валидации проекта средствами EDT и конвертация отчета в формате generic issues.
1. Запуск статического анализа для SonarQube 1. Запуск статического анализа для SonarQube.
1. Публикация результатов junit и Allure в интерфейс Jenkins.
1. Конфигурирование логгера запускаемых oscript-приложений.
## Подключение ## Подключение
@ -60,7 +65,7 @@ pipeline1C()
## Внешний вид пайплайна в интерфейсе Blue Ocean ## Внешний вид пайплайна в интерфейсе 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,26 @@ 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".
* Все "шаги" по умолчанию выключены.
* Инициализация:
* Если включен шаг `initSteps`, то будет выполняться запуск ИБ с целью запуска обработчиков обновления из БСП. (`initInfobase` -> `runMigration`)
* TODO: Если в настройках шага инициализации не заполнен массив дополнительных шагов миграции (`initInfobase` -> `additionalInitializationSteps`), но в каталоге `tools` присутствуют файлы с именами, удовлетворяющими шаблону `vrunner.init*.json`, то автоматически выполняется запуск `vrunner vanessa` с передачей найденных файлов в качестве значения настроек (параметр `--settings`) в порядке лексиграфической сортировки имен файлов.
* BDD:
* TODO: Если в репозитории существует файл `tools/vrunner.json`, а настройка `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:
* По умолчанию из результатов анализа исключаются замечания, сработавшие на модулях с включенным запретом редактирования (желтый куб с замком)

View File

@ -78,7 +78,9 @@ sharedLibrary {
// TODO: retrieve downloaded plugin resource // TODO: retrieve downloaded plugin resource
pluginDependencies { pluginDependencies {
dependency("org.jenkins-ci.plugins", "pipeline-build-step", "2.12") dependency("org.jenkins-ci.plugins", "pipeline-build-step", "2.12")
dependency("org.jenkins-ci.plugins", "git", "4.4.4")
dependency("org.6wind.jenkins", "lockable-resources", "2.7") dependency("org.6wind.jenkins", "lockable-resources", "2.7")
dependency("ru.yandex.qatools.allure", "allure-jenkins-plugin", "2.28.1")
val declarativePluginsVersion = "1.6.0" val declarativePluginsVersion = "1.6.0"
dependency("org.jenkinsci.plugins", "pipeline-model-api", declarativePluginsVersion) dependency("org.jenkinsci.plugins", "pipeline-model-api", declarativePluginsVersion)
dependency("org.jenkinsci.plugins", "pipeline-model-declarative-agent", "1.1.1") dependency("org.jenkinsci.plugins", "pipeline-model-declarative-agent", "1.1.1")

View File

@ -6,11 +6,22 @@
"storage": "UNKNOWN_ID" "storage": "UNKNOWN_ID"
}, },
"stages": { "stages": {
"initSteps": false,
"sonarqube": false, "sonarqube": false,
"bdd": false,
"syntaxCheck": false, "syntaxCheck": false,
"edtValidate": false, "edtValidate": false,
"smoke": false "smoke": false
}, },
"initInfobase": {
"runMigration": true,
"additionalInitializationSteps": []
},
"bdd": {
"vrunnerSteps": [
"vanessa --settings ./tools/vrunner.json"
]
},
"sonarqube": { "sonarqube": {
"sonarQubeInstallation": "", "sonarQubeInstallation": "",
"useSonarScannerFromPath": true, "useSonarScannerFromPath": true,
@ -18,7 +29,7 @@
}, },
"syntaxCheck": { "syntaxCheck": {
"groupErrorsByMetadata": true, "groupErrorsByMetadata": true,
"pathToJUnitReport": "build/out/jUnit/syntax.xml", "pathToJUnitReport": "./build/out/jUnit/syntax.xml",
"checkModes": [ "checkModes": [
"-ThinClient", "-ThinClient",
"-WebClient", "-WebClient",
@ -31,10 +42,12 @@
"-CheckUseModality", "-CheckUseModality",
"-CheckUseSynchronousCalls", "-CheckUseSynchronousCalls",
"-DistributiveModules" "-DistributiveModules"
] ],
"vrunnerSettings": "./tools/vrunner.json"
}, },
"resultsTransform": { "resultsTransform": {
"removeSupport": false, "removeSupport": true,
"supportLevel": 0 "supportLevel": 0
} },
"logosConfig": ""
} }

View File

@ -45,6 +45,46 @@
"smoke" : { "smoke" : {
"type" : "boolean", "type" : "boolean",
"description" : "Дымовые тесты включены" "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" : { "properties" : {
"pathToJUnitReport" : { "pathToJUnitReport" : {
"type" : "string", "type" : "string",
"description" : "Путь к файлу отчета jUnit" "description" : "Путь к файлу отчета jUnit\n По умолчанию содержит значение \"./build/out/jUnit/syntax.xml\"\n "
}, },
"groupErrorsByMetadata" : { "groupErrorsByMetadata" : {
"type" : "boolean", "type" : "boolean",
"description" : "Группировать выявленные ошибки по объектам метаданных" "description" : "Группировать выявленные ошибки по объектам метаданных.\n По умолчанию включено.\n "
}, },
"checkModes" : { "checkModes" : {
"type" : "array", "type" : "array",
@ -86,6 +126,10 @@
"items" : { "items" : {
"type" : "string" "type" : "string"
} }
},
"vrunnerSettings" : {
"type" : "string",
"description" : "Путь к конфигурационному файлу vanessa-runner.\n По умолчанию содержит значение \"./tools/vrunner.json\".\n "
} }
} }
}, },
@ -96,13 +140,17 @@
"properties" : { "properties" : {
"removeSupport" : { "removeSupport" : {
"type" : "boolean", "type" : "boolean",
"description" : "Фильтровать замечания по уровню поддержки модуля" "description" : "Фильтровать замечания по уровню поддержки модуля. По умолчанию включено."
}, },
"supportLevel" : { "supportLevel" : {
"type" : "integer", "type" : "integer",
"description" : "Настройка фильтрации замечаний по уровню поддержки.\n 0 - удалить файлы на замке;\n 1 - удалить файлы на замке и на поддержке;\n 2 - удалить файлы на замке, на поддержке и снятые с поддержки.\n " "description" : "Настройка фильтрации замечаний по уровню поддержки.\n 0 - удалить файлы на замке;\n 1 - удалить файлы на замке и на поддержке;\n 2 - удалить файлы на замке, на поддержке и снятые с поддержки.\n "
} }
} }
},
"logosConfig" : {
"type" : "string",
"description" : "Конфигурация библиотеки logos. Применяется перед запуском каждой стадии сборки"
} }
} }
} }

View File

@ -21,7 +21,7 @@ public class JobConfigurationSchemaGenerator {
File jsonSchemaFile = new File("./resources/schema.json"); File jsonSchemaFile = new File("./resources/schema.json");
mapper.writeValue(jsonSchemaFile, jsonSchema); mapper.writeValue(jsonSchemaFile, jsonSchema);
System.out.println(json.toString()); System.out.println(json);
} }
} }

View File

@ -34,6 +34,8 @@ interface IStepExecutor {
def stash(String name, String includes) def stash(String name, String includes)
def stash(String name, String includes, boolean allowEmpty)
def unstash(String name) def unstash(String name)
def zip(String dir, String zipFile) def zip(String dir, String zipFile)
@ -45,4 +47,12 @@ interface IStepExecutor {
def unzip(String dir, String zipFile, quiet) def unzip(String dir, String zipFile, quiet)
def catchError(Closure body) def catchError(Closure body)
def httpRequest(String url, String outputFile, String responseHandle, boolean wrapAsMultipart)
def error(String errorMessage)
def allure(List<String> results)
def installLocalDependencies()
} }

View File

@ -1,6 +1,8 @@
package ru.pulsar.jenkins.library package ru.pulsar.jenkins.library
import org.jenkinsci.plugins.workflow.support.actions.EnvironmentAction import org.jenkinsci.plugins.workflow.support.actions.EnvironmentAction
import ru.yandex.qatools.allure.jenkins.config.ResultsConfig
class StepExecutor implements IStepExecutor { class StepExecutor implements IStepExecutor {
@ -80,8 +82,8 @@ class StepExecutor implements IStepExecutor {
} }
@Override @Override
def stash(String name, String includes) { def stash(String name, String includes, boolean allowEmpty = false) {
steps.stash name: name, includes: includes steps.stash name: name, includes: includes, allowEmpty: allowEmpty
} }
@Override @Override
@ -103,4 +105,31 @@ class StepExecutor implements IStepExecutor {
def catchError(Closure body) { def catchError(Closure body) {
steps.catchError 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<String> results) {
steps.allure([
commandline: 'allure',
includeProperties: false,
jdk: '',
properties: [],
reportBuildPolicy: 'ALWAYS',
results: ResultsConfig.convertPaths(results)
])
}
@Override
def installLocalDependencies() {
steps.installLocalDependencies()
}
} }

View File

@ -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 +
'}';
}
}

View File

@ -7,6 +7,8 @@ import org.apache.commons.beanutils.BeanUtils
import ru.pulsar.jenkins.library.IStepExecutor import ru.pulsar.jenkins.library.IStepExecutor
import ru.pulsar.jenkins.library.ioc.ContextRegistry import ru.pulsar.jenkins.library.ioc.ContextRegistry
import javax.annotation.CheckForNull
class ConfigurationReader implements Serializable { class ConfigurationReader implements Serializable {
private static ObjectMapper mapper private static ObjectMapper mapper
@ -41,12 +43,16 @@ class ConfigurationReader implements Serializable {
def nonMergeableSettings = Arrays.asList( def nonMergeableSettings = Arrays.asList(
"secrets", "secrets",
"stageFlags", "stageFlags",
"initInfobaseOptions",
"bddOptions",
"sonarQubeOptions", "sonarQubeOptions",
"syntaxCheckOptions", "syntaxCheckOptions",
"resultsTransformOptions" "resultsTransformOptions"
).toSet() ).toSet()
mergeObjects(baseConfiguration, configurationToMerge, nonMergeableSettings) mergeObjects(baseConfiguration, configurationToMerge, nonMergeableSettings)
mergeInitInfobaseOptions(baseConfiguration.initInfobaseOptions, configurationToMerge.initInfobaseOptions);
mergeBddOptions(baseConfiguration.bddOptions, configurationToMerge.bddOptions);
return baseConfiguration; 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()
}
} }

View File

@ -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 +
'}';
}
}

View File

@ -20,6 +20,14 @@ class JobConfiguration implements Serializable {
@JsonPropertyDescription("Идентификаторы сохраненных секретов") @JsonPropertyDescription("Идентификаторы сохраненных секретов")
Secrets secrets; Secrets secrets;
@JsonProperty("initInfobase")
@JsonPropertyDescription("Настройки шага инициализации ИБ")
InitInfobaseOptions initInfobaseOptions;
@JsonProperty("bdd")
@JsonPropertyDescription("Настройки шага запуска BDD сценариев")
BddOptions bddOptions;
@JsonProperty("sonarqube") @JsonProperty("sonarqube")
@JsonPropertyDescription("Настройки анализа SonarQube") @JsonPropertyDescription("Настройки анализа SonarQube")
SonarQubeOptions sonarQubeOptions; SonarQubeOptions sonarQubeOptions;
@ -32,6 +40,10 @@ class JobConfiguration implements Serializable {
@JsonPropertyDescription("Настройки трансформации результатов анализа") @JsonPropertyDescription("Настройки трансформации результатов анализа")
ResultsTransformOptions resultsTransformOptions; ResultsTransformOptions resultsTransformOptions;
@JsonProperty("logosConfig")
@JsonPropertyDescription("Конфигурация библиотеки logos. Применяется перед запуском каждой стадии сборки")
String logosConfig;
@Override @Override
@NonCPS @NonCPS
String toString() { String toString() {
@ -40,9 +52,12 @@ class JobConfiguration implements Serializable {
", srcDir='" + srcDir + '\'' + ", srcDir='" + srcDir + '\'' +
", stageFlags=" + stageFlags + ", stageFlags=" + stageFlags +
", secrets=" + secrets + ", secrets=" + secrets +
", initInfobaseOptions=" + initInfobaseOptions +
", bddOptions=" + bddOptions +
", sonarQubeOptions=" + sonarQubeOptions + ", sonarQubeOptions=" + sonarQubeOptions +
", syntaxCheckOptions=" + syntaxCheckOptions + ", syntaxCheckOptions=" + syntaxCheckOptions +
", resultsTransformOptions=" + resultsTransformOptions + ", resultsTransformOptions=" + resultsTransformOptions +
", logosConfig=" + logosConfig +
'}'; '}';
} }
} }

View File

@ -7,8 +7,8 @@ import com.fasterxml.jackson.annotation.JsonPropertyDescription
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
class ResultsTransformOptions implements Serializable { class ResultsTransformOptions implements Serializable {
@JsonPropertyDescription("Фильтровать замечания по уровню поддержки модуля") @JsonPropertyDescription("Фильтровать замечания по уровню поддержки модуля. По умолчанию включено.")
boolean removeSupport boolean removeSupport = true
@JsonPropertyDescription("""Настройка фильтрации замечаний по уровню поддержки. @JsonPropertyDescription("""Настройка фильтрации замечаний по уровню поддержки.
0 - удалить файлы на замке; 0 - удалить файлы на замке;

View File

@ -18,6 +18,12 @@ class StageFlags implements Serializable {
@JsonPropertyDescription("Дымовые тесты включены") @JsonPropertyDescription("Дымовые тесты включены")
boolean smoke boolean smoke
@JsonPropertyDescription("Предварительные шаги инициализации включены")
boolean initSteps
@JsonPropertyDescription("Запуск BDD сценариев включен")
boolean bdd
@Override @Override
@NonCPS @NonCPS
String toString() { String toString() {
@ -26,10 +32,12 @@ class StageFlags implements Serializable {
", syntaxCheck=" + syntaxCheck + ", syntaxCheck=" + syntaxCheck +
", edtValidate=" + edtValidate + ", edtValidate=" + edtValidate +
", smoke=" + smoke + ", smoke=" + smoke +
", initSteps=" + initSteps +
", bdd=" + bdd +
'}'; '}';
} }
boolean needInfobase() { boolean needInfobase() {
return smoke || syntaxCheck return smoke || syntaxCheck || initSteps || bdd
} }
} }

View File

@ -7,14 +7,23 @@ import com.fasterxml.jackson.annotation.JsonPropertyDescription
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
class SyntaxCheckOptions implements Serializable { class SyntaxCheckOptions implements Serializable {
@JsonPropertyDescription("Путь к файлу отчета jUnit") @JsonPropertyDescription("""Путь к файлу отчета jUnit
String pathToJUnitReport По умолчанию содержит значение "./build/out/jUnit/syntax.xml"
""")
String pathToJUnitReport = "./build/out/jUnit/syntax.xml"
@JsonPropertyDescription("Группировать выявленные ошибки по объектам метаданных") @JsonPropertyDescription("""Группировать выявленные ошибки по объектам метаданных.
boolean groupErrorsByMetadata; По умолчанию включено.
""")
boolean groupErrorsByMetadata = true
@JsonPropertyDescription("Режимы проверки конфигурации") @JsonPropertyDescription("Режимы проверки конфигурации")
String[] checkModes; String[] checkModes
@JsonPropertyDescription("""Путь к конфигурационному файлу vanessa-runner.
По умолчанию содержит значение "./tools/vrunner.json".
""")
String vrunnerSettings = "./tools/vrunner.json"
@Override @Override
@NonCPS @NonCPS
@ -23,6 +32,7 @@ class SyntaxCheckOptions implements Serializable {
"pathToJUnitReport='" + pathToJUnitReport + '\'' + "pathToJUnitReport='" + pathToJUnitReport + '\'' +
", groupErrorsByMetadata=" + groupErrorsByMetadata + ", groupErrorsByMetadata=" + groupErrorsByMetadata +
", checkModes=" + checkModes + ", checkModes=" + checkModes +
", vrunnerSettings=" + vrunnerSettings +
'}'; '}';
} }
} }

View File

@ -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<String> 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)
}
}

View File

@ -1,5 +1,6 @@
package ru.pulsar.jenkins.library.steps package ru.pulsar.jenkins.library.steps
import ru.pulsar.jenkins.library.IStepExecutor import ru.pulsar.jenkins.library.IStepExecutor
import ru.pulsar.jenkins.library.configuration.JobConfiguration import ru.pulsar.jenkins.library.configuration.JobConfiguration
import ru.pulsar.jenkins.library.ioc.ContextRegistry import ru.pulsar.jenkins.library.ioc.ContextRegistry
@ -47,4 +48,5 @@ class EdtTransform implements Serializable {
steps.zip(WORKSPACE, WORKSPACE_ZIP) steps.zip(WORKSPACE, WORKSPACE_ZIP)
steps.stash(WORKSPACE_ZIP_STASH, WORKSPACE_ZIP) steps.stash(WORKSPACE_ZIP_STASH, WORKSPACE_ZIP)
} }
} }

View File

@ -0,0 +1,59 @@
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 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<String> 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 {
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)
}
}

View File

@ -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<String> 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")
}
}
}

View File

@ -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()
}
}

View File

@ -31,14 +31,25 @@ class ConfigurationReaderTest {
// then // then
assertThat(jobConfiguration.getV8version()).isEqualTo("8.3.14.1944"); assertThat(jobConfiguration.getV8version()).isEqualTo("8.3.14.1944");
assertThat(jobConfiguration.getSonarQubeOptions().getSonarScannerToolName()).isEqualTo("sonar-scanner"); assertThat(jobConfiguration.getSonarQubeOptions().getSonarScannerToolName()).isEqualTo("sonar-scanner");
assertThat(jobConfiguration.getSecrets()) assertThat(jobConfiguration.getSecrets())
.hasFieldOrPropertyWithValue("storage", "1234") .hasFieldOrPropertyWithValue("storage", "1234")
.hasFieldOrPropertyWithValue("storagePath", "UNKNOWN_ID") .hasFieldOrPropertyWithValue("storagePath", "UNKNOWN_ID")
; ;
assertThat(jobConfiguration.getSyntaxCheckOptions().getCheckModes()).hasSize(1); 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");
} }
} }

View File

@ -6,10 +6,17 @@
"stages": { "stages": {
"syntaxCheck": true "syntaxCheck": true
}, },
"initInfobase": {
"runMigration": false,
"additionalInitializationSteps": [
"vanessa --settings ./tools/vrunner.first.json"
]
},
"syntaxCheck": { "syntaxCheck": {
"checkModes": ["-ThinClient"] "checkModes": ["-ThinClient"]
}, },
"resultsTransform": { "resultsTransform": {
"removeSupport": true "removeSupport": false
} },
"logosConfig": "logger.rootLogger=DEBUG"
} }

10
vars/bdd.groovy Normal file
View File

@ -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()
}

10
vars/initInfobase.groovy Normal file
View File

@ -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()
}

View File

@ -27,6 +27,7 @@ void call() {
agent { agent {
label 'agent' label 'agent'
} }
steps { steps {
script { script {
config = jobConfiguration() as JobConfiguration config = jobConfiguration() as JobConfiguration
@ -46,18 +47,41 @@ void call() {
expression { config.stageFlags.needInfobase() } expression { config.stageFlags.needInfobase() }
} }
stages {
stage('Создание ИБ') {
steps { steps {
printLocation() printLocation()
installLocalDependencies() installLocalDependencies()
dir("build/out") { echo '' } createDir('build/out')
// Создание базы загрузкой конфигурации из хранилища // Создание базы загрузкой конфигурации из хранилища
initFromStorage config initFromStorage config
}
}
stage('Инициализация ИБ') {
when {
beforeAgent true
expression { config.stageFlags.initSteps }
}
steps {
// Инициализация и первичная миграция
initInfobase config
}
}
stage('Архивация ИБ') {
steps {
printLocation()
zipInfobase() zipInfobase()
} }
}
}
} }
stage('Трансформация в формат EDT') { 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('Синтаксический контроль') { stage('Синтаксический контроль') {
agent { agent {
label agent1C label agent1C
@ -144,6 +183,14 @@ void call() {
} }
} }
} }
post('post-stage') {
always {
node('agent') {
saveResults config
}
}
}
} }
} }

32
vars/saveResults.groovy Normal file
View File

@ -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'
// ])
}

View File

@ -1,5 +1,7 @@
import hudson.FilePath
import ru.pulsar.jenkins.library.configuration.JobConfiguration import ru.pulsar.jenkins.library.configuration.JobConfiguration
import ru.pulsar.jenkins.library.ioc.ContextRegistry import ru.pulsar.jenkins.library.ioc.ContextRegistry
import ru.pulsar.jenkins.library.utils.FileUtils
def call(JobConfiguration config) { def call(JobConfiguration config) {
@ -20,27 +22,37 @@ def call(JobConfiguration config) {
unzipInfobase() unzipInfobase()
def outPath = new File(options.pathToJUnitReport).getParent() FilePath pathToJUnitReport = FileUtils.getFilePath("$env.WORKSPACE/$options.pathToJUnitReport")
String outPath = pathToJUnitReport.getParent()
createDir(outPath) 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 // См. https://github.com/vanessa-opensource/vanessa-runner/issues/361
// command += " --workspace $env.WORKSPACE" // command += " --workspace $env.WORKSPACE"
if (options.groupErrorsByMetadata) { 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) cmd(command, true)
junit allowEmptyResults: true, testResults: options.pathToJUnitReport junit allowEmptyResults: true, testResults: FileUtils.getLocalPath(pathToJUnitReport)
archiveArtifacts options.pathToJUnitReport archiveArtifacts FileUtils.getLocalPath(pathToJUnitReport)
} }