1
0
mirror of https://github.com/firstBitMarksistskaya/jenkins-lib.git synced 2024-12-13 11:27:08 +02:00

Merge branch 'release/v0.3.0'

This commit is contained in:
Nikita Gryzlov 2021-06-11 17:36:52 +03:00
commit d39e9d1cf8
No known key found for this signature in database
GPG Key ID: E7AE91471C6FFE04
26 changed files with 628 additions and 40 deletions

View File

@ -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:
* По умолчанию из результатов анализа исключаются замечания, сработавшие на модулях с включенным запретом редактирования (желтый куб с замком)

View File

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

View File

@ -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": ""
}

View File

@ -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. Применяется перед запуском каждой стадии сборки"
}
}
}

View File

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

View File

@ -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<String> results)
def installLocalDependencies()
}

View File

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

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("Идентификаторы сохраненных секретов")
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 +
'}';
}
}

View File

@ -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 - удалить файлы на замке;

View File

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

View File

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

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

View File

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

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
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");
}
}

View File

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

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 {
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
}
}
}
}
}

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.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)
}