1
0
mirror of https://github.com/firstBitMarksistskaya/jenkins-lib.git synced 2025-08-25 20:09:25 +02:00

Merge pull request #149 from ovcharenko-di/feature/save-dt

Сохранение ИБ в виде артефакта после выполнения всех шагов инциализации
This commit is contained in:
Nikita Fedkin
2025-01-24 09:19:29 +01:00
committed by GitHub
16 changed files with 223 additions and 15 deletions

View File

@@ -46,12 +46,12 @@
1. Запуск юнит-тестов с помощью фреймворка YAXUnit с сохранением результатов в формате jUnit и Allure. 1. Запуск юнит-тестов с помощью фреймворка YAXUnit с сохранением результатов в формате jUnit и Allure.
1. Запуск синтаксического контроля средствами конфигуратора и сохранение результатов в виде отчета jUnit. 1. Запуск синтаксического контроля средствами конфигуратора и сохранение результатов в виде отчета jUnit.
1. Валидация проекта средствами EDT и трансформация отчета EDT в формат BSL LS с помощью `edt-ripper` или Generic Issue с помощью `stebi`. 1. Валидация проекта средствами EDT и трансформация отчета EDT в формат BSL LS с помощью `edt-ripper` или Generic Issue с помощью `stebi`.
1. Запуск статического анализа для SonarQube. 1. Запуск статического анализа для SonarQube.
1. Публикация результатов junit и Allure в интерфейс Jenkins. 1. Публикация результатов junit и Allure в интерфейс Jenkins.
1. Рассылка результатов сборки на почту и в Telegram. 1. Рассылка результатов сборки на почту и в Telegram.
1. Конфигурирование логгера запускаемых oscript-приложений. 1. Конфигурирование логгера запускаемых oscript-приложений.
1. Замер покрытия при выполнении тестов. 1. Замер покрытия при выполнении тестов.
1. Возможность сохранить информационную базу в виде артефакта сборки после выполнения шагов инициализации и\или после выполнения сценарных тестов.
## Подключение ## Подключение
@@ -168,6 +168,7 @@ pipeline1C()
* Если разработка ведется с использованием подсистемы [БСП "Обновление версии ИБ"](https://its.1c.ru/db/bsp315doc#content:4:1:issogl1_обновление_версии_иб), то в значение параметра `sonar.projectVersion=$configurationVersion` утилиты `sonar-scanner` можно передавать версию из созданного общего модуля. Для этого необходимо заполнить параметр (`sonarqube` -> `infoBaseUpdateModuleName`). Если параметр не заполнен, версия передается из корня конфигурации. * Если разработка ведется с использованием подсистемы [БСП "Обновление версии ИБ"](https://its.1c.ru/db/bsp315doc#content:4:1:issogl1_обновление_версии_иб), то в значение параметра `sonar.projectVersion=$configurationVersion` утилиты `sonar-scanner` можно передавать версию из созданного общего модуля. Для этого необходимо заполнить параметр (`sonarqube` -> `infoBaseUpdateModuleName`). Если параметр не заполнен, версия передается из корня конфигурации.
* По умолчанию шаг анализа не дожидается окончания фонового задания на сервере SonarQube и не анализирует результат прохождения Порога качества (`sonarqube` -> `waitForQualityGate`). * По умолчанию шаг анализа не дожидается окончания фонового задания на сервере SonarQube и не анализирует результат прохождения Порога качества (`sonarqube` -> `waitForQualityGate`).
* Если выполнялась валидация EDT, результаты валидации передаются утилите `sonar-scanner` как значение параметра `sonar.externalIssuesReportPaths` при использовании `stebi` или как значение параметра `sonar.bsl.languageserver.reportPaths` при использовании `edt-ripper`. * Если выполнялась валидация EDT, результаты валидации передаются утилите `sonar-scanner` как значение параметра `sonar.externalIssuesReportPaths` при использовании `stebi` или как значение параметра `sonar.bsl.languageserver.reportPaths` при использовании `edt-ripper`.
* Информационная база по умолчанию не сохраняется в виде артефакта сборки.
* Рассылка уведомлений: * Рассылка уведомлений:
* Электронная почта: * Электронная почта:
* Для отправки используется плагин [`email-ext`](https://plugins.jenkins.io/email-ext). Шаблоны сообщений конфигурируются в настройках плагина. * Для отправки используется плагин [`email-ext`](https://plugins.jenkins.io/email-ext). Шаблоны сообщений конфигурируются в настройках плагина.
@@ -312,3 +313,12 @@ jobConfiguration.json
* При изменении портов отладки в jobConfiguration.json не забывайте менять порты в настройках соответствующих шагов (и наоборот) * При изменении портов отладки в jobConfiguration.json не забывайте менять порты в настройках соответствующих шагов (и наоборот)
* Настоятельно рекомендуется использовать не "постоянные" агенты Jenkins, а контейнеры docker. При выполнении билдов в контейнерах можно использовать исключительно стандартный порт 1550. * Настоятельно рекомендуется использовать не "постоянные" агенты Jenkins, а контейнеры docker. При выполнении билдов в контейнерах можно использовать исключительно стандартный порт 1550.
## Сохранение ИБ в виде артефакта сборки
Параметры `initInfobase` -> `archiveInfobase` и `bdd` -> `archiveInfobase` отвечают за сохранение информационной базы в виде артефакта сборки после выполнения соответствующих этапов.
Можно управлять тем, при каких статусах сборки ИБ будет сохранена, см. `onAlways`, `onFailure`, `onUnstable`, `onSuccess`.
Имя файла формируется следующим образом:
* для шага `initInfoBase`: '1Cv8.1CD.zip'
* для шага `bdd`: '1Cv8.1CD.bdd.zip'

View File

@@ -45,12 +45,24 @@
"additionalInitializationSteps": [], "additionalInitializationSteps": [],
"templateDBPath": "", "templateDBPath": "",
"vrunnerSettings": "./tools/vrunner.json", "vrunnerSettings": "./tools/vrunner.json",
"archiveInfobase": {
"onAlways": false,
"onFailure": false,
"onUnstable": false,
"onSuccess": false
},
"extensions": [] "extensions": []
}, },
"bdd": { "bdd": {
"vrunnerSteps": [ "vrunnerSteps": [
"vanessa --settings ./tools/vrunner.json" "vanessa --settings ./tools/vrunner.json"
], ],
"archiveInfobase": {
"onAlways": false,
"onFailure": false,
"onUnstable": false,
"onSuccess": false
},
"coverage": false, "coverage": false,
"dbgsPort": 1550 "dbgsPort": 1550
}, },
@@ -58,7 +70,7 @@
"sonarQubeInstallation": "", "sonarQubeInstallation": "",
"useSonarScannerFromPath": true, "useSonarScannerFromPath": true,
"sonarScannerToolName": "sonar-scanner", "sonarScannerToolName": "sonar-scanner",
"infoBaseUpdateModuleName" : "", "infoBaseUpdateModuleName": "",
"branchAnalysisConfiguration": "fromEnv", "branchAnalysisConfiguration": "fromEnv",
"waitForQualityGate": false "waitForQualityGate": false
}, },

View File

@@ -1,6 +1,27 @@
{ {
"$schema" : "http://json-schema.org/draft-07/schema#", "$schema" : "http://json-schema.org/draft-07/schema#",
"definitions" : { "definitions" : {
"ArchiveInfobaseOptions" : {
"type" : "object",
"properties" : {
"onAlways" : {
"type" : "boolean",
"description" : "Сохранять всегда"
},
"onFailure" : {
"type" : "boolean",
"description" : "Сохранять при падении сборки"
},
"onSuccess" : {
"type" : "boolean",
"description" : "Сохранять при успешной сборке"
},
"onUnstable" : {
"type" : "boolean",
"description" : "Сохранять при нестабильной сборке"
}
}
},
"EmailExtConfiguration" : { "EmailExtConfiguration" : {
"type" : "object", "type" : "object",
"properties" : { "properties" : {
@@ -28,6 +49,13 @@
"bdd" : { "bdd" : {
"type" : "object", "type" : "object",
"properties" : { "properties" : {
"archiveInfobase" : {
"allOf" : [ {
"$ref" : "#/definitions/ArchiveInfobaseOptions"
}, {
"description" : "Настройки сохранения базы после выполнения всех шагов\n "
} ]
},
"coverage" : { "coverage" : {
"type" : "boolean", "type" : "boolean",
"description" : "Выполнять замер покрытия", "description" : "Выполнять замер покрытия",
@@ -80,6 +108,13 @@
"type" : "string" "type" : "string"
} }
}, },
"archiveInfobase" : {
"allOf" : [ {
"$ref" : "#/definitions/ArchiveInfobaseOptions"
}, {
"description" : "Настройки сохранения базы после выполнения всех шагов\n "
} ]
},
"extensions" : { "extensions" : {
"description" : "Массив расширений для загрузки в конфигурацию.", "description" : "Массив расширений для загрузки в конфигурацию.",
"type" : "array", "type" : "array",
@@ -381,12 +416,12 @@
"publishToAllureReport" : { "publishToAllureReport" : {
"type" : "boolean", "type" : "boolean",
"description" : "Выполнять публикацию результатов в отчет Allure.\n По умолчанию выключено.\n ", "description" : "Выполнять публикацию результатов в отчет Allure.\n По умолчанию выключено.\n ",
"default": false "default" : false
}, },
"publishToJUnitReport" : { "publishToJUnitReport" : {
"type" : "boolean", "type" : "boolean",
"description" : "Выполнять публикацию результатов в отчет JUnit.\n По умолчанию включено.\n ", "description" : "Выполнять публикацию результатов в отчет JUnit.\n По умолчанию включено.\n ",
"default": true "default" : true
}, },
"vrunnerSettings" : { "vrunnerSettings" : {
"type" : "string", "type" : "string",

View File

@@ -33,7 +33,8 @@ public class JobConfigurationSchemaGenerator {
writer.write(jsonSchema.toPrettyString()); writer.write(jsonSchema.toPrettyString());
System.out.println(jsonSchema.toPrettyString()); System.out.println(jsonSchema.toPrettyString());
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); //noinspection CallToPrintStackTrace
e.printStackTrace();
} }
} }

View File

@@ -10,6 +10,7 @@ import org.jenkinsci.plugins.workflow.support.steps.build.RunWrapper
import ru.pulsar.jenkins.library.configuration.JobConfiguration import ru.pulsar.jenkins.library.configuration.JobConfiguration
import ru.pulsar.jenkins.library.configuration.StepCoverageOptions import ru.pulsar.jenkins.library.configuration.StepCoverageOptions
import ru.pulsar.jenkins.library.steps.Coverable import ru.pulsar.jenkins.library.steps.Coverable
import sp.sd.fileoperations.FileOperation
interface IStepExecutor { interface IStepExecutor {
@@ -34,6 +35,10 @@ interface IStepExecutor {
boolean fileExists(String file) boolean fileExists(String file)
void fileOperations(List<FileOperation> fileOperations)
void fileDeleteOperation(String includes)
void echo(message) void echo(message)
def cmd(String script, boolean returnStatus, boolean returnStdout) def cmd(String script, boolean returnStatus, boolean returnStdout)
@@ -89,6 +94,8 @@ interface IStepExecutor {
@SuppressWarnings('unused') @SuppressWarnings('unused')
def zip(String dir, String zipFile, String glob) def zip(String dir, String zipFile, String glob)
def zip(String dir, String zipFile, String glob, boolean archive)
def unzip(String dir, String zipFile) def unzip(String dir, String zipFile)
@SuppressWarnings('unused') @SuppressWarnings('unused')
@@ -120,4 +127,5 @@ interface IStepExecutor {
def brokenTestsSuspects() def brokenTestsSuspects()
RunWrapper currentBuild() RunWrapper currentBuild()
} }

View File

@@ -11,6 +11,7 @@ import ru.pulsar.jenkins.library.configuration.JobConfiguration
import ru.pulsar.jenkins.library.configuration.StepCoverageOptions import ru.pulsar.jenkins.library.configuration.StepCoverageOptions
import ru.pulsar.jenkins.library.steps.Coverable import ru.pulsar.jenkins.library.steps.Coverable
import ru.yandex.qatools.allure.jenkins.config.ResultsConfig import ru.yandex.qatools.allure.jenkins.config.ResultsConfig
import sp.sd.fileoperations.FileOperation
class StepExecutor implements IStepExecutor { class StepExecutor implements IStepExecutor {
@@ -55,6 +56,16 @@ class StepExecutor implements IStepExecutor {
steps.fileExists file steps.fileExists file
} }
@Override
void fileOperations(List<FileOperation> fileOperations) {
steps.fileOperations fileOperations
}
@Override
void fileDeleteOperation(String includes) {
steps.fileDeleteOperation includes: includes, excludes: '', useDefaultExcludes: true
}
@Override @Override
FileWrapper[] findFiles(String glob, String excludes = '') { FileWrapper[] findFiles(String glob, String excludes = '') {
steps.findFiles glob: glob, excludes: excludes steps.findFiles glob: glob, excludes: excludes
@@ -196,6 +207,11 @@ class StepExecutor implements IStepExecutor {
steps.zip dir: dir, zipFile: zipFile, glob: glob, overwrite: true steps.zip dir: dir, zipFile: zipFile, glob: glob, overwrite: true
} }
@Override
def zip(String dir, String zipFile, String glob = '', boolean archive) {
steps.zip dir: dir, zipFile: zipFile, glob: glob, overwrite: true, archive: archive
}
@Override @Override
def unzip(String dir, String zipFile, quiet = true) { def unzip(String dir, String zipFile, quiet = true) {
steps.unzip dir: dir, zipFile: zipFile, quiet: quiet steps.unzip dir: dir, zipFile: zipFile, quiet: quiet

View File

@@ -0,0 +1,31 @@
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 ArchiveInfobaseOptions implements Serializable {
@JsonPropertyDescription("Сохранять всегда")
Boolean onAlways = false
@JsonPropertyDescription("Сохранять при успешной сборке")
Boolean onSuccess = false
@JsonPropertyDescription("Сохранять при падении сборки")
Boolean onFailure = false
@JsonPropertyDescription("Сохранять при нестабильной сборке")
Boolean onUnstable = false
@Override
@NonCPS
String toString() {
return "ArchiveInfobaseOptions{" +
"onAlways=" + onAlways +
", onSuccess=" + onSuccess +
", onFailure=" + onFailure +
", onUnstable=" + onUnstable +
'}';
}
}

View File

@@ -16,11 +16,16 @@ class BddOptions extends StepCoverageOptions implements Serializable {
'vanessa --settings ./tools/vrunner.json' 'vanessa --settings ./tools/vrunner.json'
] ]
@JsonPropertyDescription("""Настройки сохранения базы после выполнения всех шагов
""")
ArchiveInfobaseOptions archiveInfobase
@Override @Override
@NonCPS @NonCPS
String toString() { String toString() {
return "BddOptions{" + return "BddOptions{" +
"vrunnerSteps=" + vrunnerSteps + "vrunnerSteps=" + vrunnerSteps +
"archiveInfobase=" + archiveInfobase +
"coverage=" + coverage + "coverage=" + coverage +
"dbgsPort=" + dbgsPort + "dbgsPort=" + dbgsPort +
'}' '}'

View File

@@ -69,6 +69,7 @@ class ConfigurationReader implements Serializable {
"yaxunitOptions", "yaxunitOptions",
"syntaxCheckOptions", "syntaxCheckOptions",
"resultsTransformOptions", "resultsTransformOptions",
"archiveInfobase",
"notificationsOptions", "notificationsOptions",
"emailNotificationOptions", "emailNotificationOptions",
"alwaysEmailOptions", "alwaysEmailOptions",

View File

@@ -40,6 +40,10 @@ class InitInfoBaseOptions implements Serializable {
""") """)
String templateDBPath String templateDBPath
@JsonPropertyDescription("""Настройки сохранения базы после выполнения всех шагов
""")
ArchiveInfobaseOptions archiveInfobase
@JsonPropertyDescription("Массив расширений для загрузки в конфигурацию.") @JsonPropertyDescription("Массив расширений для загрузки в конфигурацию.")
Extension[] extensions Extension[] extensions
@@ -80,6 +84,7 @@ class InitInfoBaseOptions implements Serializable {
", vrunnerSettings=" + vrunnerSettings + ", vrunnerSettings=" + vrunnerSettings +
", templateDBPath=" + templateDBPath + ", templateDBPath=" + templateDBPath +
", additionalInitializationSteps=" + additionalInitializationSteps + ", additionalInitializationSteps=" + additionalInitializationSteps +
", archiveInfobase=" + archiveInfobase +
", extensions=" + extensions + ", extensions=" + extensions +
'}' '}'
} }

View File

@@ -82,7 +82,7 @@ class Yaxunit implements Serializable, Coverable {
} }
if (options.publishToAllureReport) { if (options.publishToAllureReport) {
String allureReport = "./build/out/allure/yaxunit/junit.xml" String allureReport = "./build/out/allure/yaxunit/allure.xml"
FilePath pathToAllureReport = FileUtils.getFilePath("$env.WORKSPACE/$allureReport") FilePath pathToAllureReport = FileUtils.getFilePath("$env.WORKSPACE/$allureReport")
String allureReportDir = FileUtils.getLocalPath(pathToAllureReport.getParent()) String allureReportDir = FileUtils.getLocalPath(pathToAllureReport.getParent())

View File

@@ -0,0 +1,68 @@
package ru.pulsar.jenkins.library.steps
import hudson.model.Result
import ru.pulsar.jenkins.library.IStepExecutor
import ru.pulsar.jenkins.library.configuration.ArchiveInfobaseOptions
import ru.pulsar.jenkins.library.configuration.JobConfiguration
import ru.pulsar.jenkins.library.ioc.ContextRegistry
import ru.pulsar.jenkins.library.utils.Logger
class ZipInfobase implements Serializable {
private final JobConfiguration config
private final String stage
ZipInfobase(JobConfiguration config, String stage) {
this.config = config
this.stage = stage
}
def run() {
IStepExecutor steps = ContextRegistry.getContext().getStepExecutor()
Logger.printLocation()
def currentBuild = steps.currentBuild()
def currentResult = Result.fromString(currentBuild.getCurrentResult())
def archiveInfobaseOptions = getArchiveInfobaseOptionsForStage(config, stage)
def archiveName
if (stage == 'initInfoBase') {
archiveName = "1Cv8.1CD.zip"
} else {
archiveName = "1Cv8.1CD.${stage}.zip"
}
// опция отвечает только за то, будет ли файл сохранен в виде артефакта
def archiveInfobase = false
if (archiveInfobaseOptions.onAlways
|| (archiveInfobaseOptions.onFailure && (currentResult == Result.FAILURE || currentResult == Result.ABORTED))
|| (archiveInfobaseOptions.onUnstable && currentResult == Result.UNSTABLE)
|| (archiveInfobaseOptions.onSuccess && currentResult == Result.SUCCESS)) {
archiveInfobase = true
}
if (steps.fileExists(archiveName)) {
steps.fileOperations([steps.fileDeleteOperation(archiveName)])
}
steps.zip('build/ib', archiveName, '1Cv8.1CD', archiveInfobase)
steps.stash(archiveName, archiveName, false)
}
private static ArchiveInfobaseOptions getArchiveInfobaseOptionsForStage(JobConfiguration config, String stageName) {
def defaultOptions = new ArchiveInfobaseOptions()
if (!stageName) {
return defaultOptions
}
try {
return config."${stageName}Options".archiveInfobase
} catch(MissingPropertyException | NullPointerException e) {
Logger.println("Ошибка при получении настроек архивации для этапа ${stageName}: ${e.message}")
Logger.println(e.toString())
return defaultOptions
}
}
}

View File

@@ -2,7 +2,6 @@ package ru.pulsar.jenkins.library.configuration;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import ru.pulsar.jenkins.library.configuration.sonarqube.GenericIssueFormat; import ru.pulsar.jenkins.library.configuration.sonarqube.GenericIssueFormat;
import ru.pulsar.jenkins.library.utils.TestUtils; import ru.pulsar.jenkins.library.utils.TestUtils;
@@ -66,6 +65,7 @@ class ConfigurationReaderTest {
assertThat(jobConfiguration.getYaxunitOptions().getDbgsPort()).isEqualTo(1550); assertThat(jobConfiguration.getYaxunitOptions().getDbgsPort()).isEqualTo(1550);
assertThat(jobConfiguration.getInitInfoBaseOptions().getRunMigration()).isFalse(); assertThat(jobConfiguration.getInitInfoBaseOptions().getRunMigration()).isFalse();
assertThat(jobConfiguration.getInitInfoBaseOptions().getArchiveInfobase().getOnAlways()).isTrue();
assertThat(jobConfiguration.getInitInfoBaseOptions().getAdditionalInitializationSteps()).contains("vanessa --settings ./tools/vrunner.first.json"); assertThat(jobConfiguration.getInitInfoBaseOptions().getAdditionalInitializationSteps()).contains("vanessa --settings ./tools/vrunner.first.json");
assertThat(jobConfiguration.getBddOptions().getVrunnerSteps()).contains("vanessa --settings ./tools/vrunner.json"); assertThat(jobConfiguration.getBddOptions().getVrunnerSteps()).contains("vanessa --settings ./tools/vrunner.json");

View File

@@ -48,7 +48,10 @@
"initInfoBase" "initInfoBase"
] ]
} }
] ],
"archiveInfobase": {
"onAlways": true
}
}, },
"sonarqube": { "sonarqube": {
"sonarQubeInstallation": "qa", "sonarQubeInstallation": "qa",

View File

@@ -144,7 +144,7 @@ void call() {
timeout(time: config.timeoutOptions.zipInfoBase, unit: TimeUnit.MINUTES) { timeout(time: config.timeoutOptions.zipInfoBase, unit: TimeUnit.MINUTES) {
printLocation() printLocation()
zipInfobase() zipInfobase config, 'initInfoBase'
} }
} }
} }
@@ -237,6 +237,16 @@ void call() {
} }
} }
} }
stage('Архивация ИБ') {
steps {
timeout(time: config.timeoutOptions.zipInfoBase, unit: TimeUnit.MINUTES) {
printLocation()
zipInfobase config, 'bdd'
}
}
}
} }
} }

View File

@@ -1,7 +1,10 @@
def call() { import ru.pulsar.jenkins.library.configuration.JobConfiguration
if (fileExists('1Cv8.1CD.zip')) { import ru.pulsar.jenkins.library.ioc.ContextRegistry
fileOperations([fileDeleteOperation(includes: '1Cv8.1CD.zip')]) import ru.pulsar.jenkins.library.steps.ZipInfobase
}
zip dir: 'build/ib', glob: '1Cv8.1CD', zipFile: '1Cv8.1CD.zip' def call(JobConfiguration config, String stageName) {
stash name: "1Cv8.1CD.zip", includes: "1Cv8.1CD.zip", allowEmpty: false ContextRegistry.registerDefaultContext(this)
def zipInfobase = new ZipInfobase(config, stageName)
zipInfobase.run()
} }