feature: names refactoring, Jenkinsfile edit, example edit, bug fixes
This commit is contained in:
parent
7f3a76c984
commit
1b34bd5df1
@ -16,6 +16,8 @@ pipeline {
|
|||||||
steps {
|
steps {
|
||||||
script {
|
script {
|
||||||
env.CI_PROPERTIES_FILE_LOCATIONS = ".ci/ci.properties"
|
env.CI_PROPERTIES_FILE_LOCATIONS = ".ci/ci.properties"
|
||||||
|
env.CI_DOCKER_REGISTRY_USERNAME = env.GITEA_USER
|
||||||
|
env.CI_DOCKER_REGISTRY_PASSWORD = env.GITEA_TOKEN
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -35,44 +37,46 @@ pipeline {
|
|||||||
stage('Build: Regular') {
|
stage('Build: Regular') {
|
||||||
steps {
|
steps {
|
||||||
// TODO: Костыль. Надо скрипты адаптировать под сборку "На каждый коммит" и "Для релизов"
|
// TODO: Костыль. Надо скрипты адаптировать под сборку "На каждый коммит" и "Для релизов"
|
||||||
runGroovy 'docker_build'
|
runGroovy 'build_docker'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Дополнительная сборка для релизов
|
// Дополнительная сборка для релизов
|
||||||
stage('Build: Release Binaries And Deploy Image') {
|
stage('Build: Release Binaries And Deploy Image') {
|
||||||
when {
|
when {
|
||||||
tag "release-*"
|
anyOf {
|
||||||
|
tag "release-*"
|
||||||
|
tag "binaries-*"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
steps {
|
steps {
|
||||||
script {
|
script {
|
||||||
// Пример смены набора докерфайлов, которые используются по-умолчанию
|
|
||||||
env.CI_DOCKER_FILES_PRESET="release"
|
env.CI_DOCKER_FILES_PRESET="release"
|
||||||
}
|
}
|
||||||
runGroovy 'docker_build'
|
runGroovy 'release_docker_gitea_publish'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Пушим собранный образ в Docker Registry
|
// Пушим собранный образ в Docker Registry
|
||||||
stage('Publish: Publish a docker image') {
|
stage('Publish: Release docker image') {
|
||||||
when {
|
when {
|
||||||
tag "release-*"
|
anyOf {
|
||||||
|
tag "release-*"
|
||||||
|
tag "docker-*"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
steps {
|
steps {
|
||||||
// Переопределяем параметры скрипта из переменных окружения сборщика
|
runGroovy 'release_docker_build_push'
|
||||||
script {
|
|
||||||
env.CI_DOCKER_REGISTRY_USERNAME = env.GITEA_USER
|
|
||||||
env.CI_DOCKER_REGISTRY_PASSWORD = env.GITEA_TOKEN
|
|
||||||
}
|
|
||||||
|
|
||||||
runGroovy 'docker_push'
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Создаем в Gitea релиз с бинарями
|
// Создаем в Gitea релиз с бинарями
|
||||||
stage('Publish: Create gitea release') {
|
stage('Publish: Create gitea release') {
|
||||||
when {
|
when {
|
||||||
tag "release-*"
|
anyOf {
|
||||||
|
tag "release-*"
|
||||||
|
tag "binaries-*"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
steps {
|
steps {
|
||||||
// Переопределяем параметры скрипта из переменных окружения сборщика
|
// Переопределяем параметры скрипта из переменных окружения сборщика
|
||||||
@ -83,32 +87,24 @@ pipeline {
|
|||||||
env.CI_GITEA_TOKEN = env.GITEA_TOKEN
|
env.CI_GITEA_TOKEN = env.GITEA_TOKEN
|
||||||
}
|
}
|
||||||
|
|
||||||
runGroovy 'docker_gitea_release_publish'
|
runGroovy 'release_gitea_artifacts_publish_docker'
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Добавляем SSH профиль для последующего деплоя и проверяем его
|
|
||||||
stage('Prepare: Configuring SSH profile') {
|
|
||||||
when {
|
|
||||||
tag "release-*"
|
|
||||||
}
|
|
||||||
steps {
|
|
||||||
// Переопределяем параметры скрипта из переменных окружения сборщика
|
|
||||||
script {
|
|
||||||
env.CI_DEPLOY_SSH_PROFILE_PRIVATE_KEY_BASE64 = env.SSH_KEY_BASE64
|
|
||||||
}
|
|
||||||
|
|
||||||
runGroovy 'deploy_ssh_profile_setup'
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Подключаемся к серверу по SSH, обновляем тэг образа и перезапускаем
|
// Подключаемся к серверу по SSH, обновляем тэг образа и перезапускаем
|
||||||
stage('Deploy: Update PROD docker-compose environment tag') {
|
stage('Deploy: Update PROD docker-compose environment tag') {
|
||||||
when {
|
when {
|
||||||
tag "release-*"
|
anyOf {
|
||||||
|
tag "release-*"
|
||||||
|
tag "deploy-*"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
steps {
|
steps {
|
||||||
runGroovy 'deploy_compose_via_ssh'
|
script {
|
||||||
|
env.CI_DEPLOY_SSH_PROFILE_PRIVATE_KEY_BASE64 = env.SSH_KEY_BASE64
|
||||||
|
}
|
||||||
|
runGroovy 'deploy_ssh_profile_setup'
|
||||||
|
runGroovy 'deploy_docker_build_push_update_compose_via_ssh'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
# Git
|
# Git
|
||||||
ci.git.tag.deploy.prefixes=release-, deploy-
|
ci.git.tag.deploy.prefixes=deploy-, release-
|
||||||
ci.git.tag.release.prefixes=release-
|
ci.git.tag.release.prefixes=release-, binaries-, docker-
|
||||||
|
|
||||||
# Docker
|
# Docker
|
||||||
ci.docker.files.default=Deploy.Dockerfile
|
ci.docker.files.default=Deploy.Dockerfile
|
||||||
@ -31,5 +31,5 @@
|
|||||||
## Releases
|
## Releases
|
||||||
ci.release.artifacts.dockerfiles=Binaries.Extra.Dockerfile
|
ci.release.artifacts.dockerfiles=Binaries.Extra.Dockerfile
|
||||||
ci.release.artifacts.is-required=true
|
ci.release.artifacts.is-required=true
|
||||||
ci.release.artifacts.binaries.libc.dockerfile.grab=/build/app.linux-386
|
ci.release.artifacts.binaries.extra.dockerfile.grab=/build/app.linux-386
|
||||||
ci.release.artifact.app.linux-386.name=my-application-name.linux-386
|
ci.release.artifact.app.linux-386.name=my-application-name.linux-386
|
||||||
|
@ -32,7 +32,7 @@ ScriptLog.printf "Building docker image without tags..."
|
|||||||
|
|
||||||
for (def dockerfileName : Dockerfiles.getPresetDockerfiles()) {
|
for (def dockerfileName : Dockerfiles.getPresetDockerfiles()) {
|
||||||
def dockerCommand = DockerBuildCommandFactory.getBuildCommand(dockerfileName)
|
def dockerCommand = DockerBuildCommandFactory.getBuildCommand(dockerfileName)
|
||||||
println "Using dockerfile '${dockerfileName}', full command: '$dockerCommand'"
|
ScriptLog.printf "Building dockerfile '${dockerfileName}', full command: '$dockerCommand'"
|
||||||
sh dockerCommand
|
sh dockerCommand
|
||||||
}
|
}
|
||||||
|
|
@ -1,7 +1,9 @@
|
|||||||
package script
|
package script
|
||||||
|
|
||||||
import util.CIProperties
|
import util.CIProperties
|
||||||
|
import util.DockerBuildCommandFactory
|
||||||
import util.DockerImageNames
|
import util.DockerImageNames
|
||||||
|
import util.DockerLogin
|
||||||
import util.DockerTags
|
import util.DockerTags
|
||||||
import util.Dockerfiles
|
import util.Dockerfiles
|
||||||
|
|
||||||
@ -36,15 +38,15 @@ if (deployGitTags.isNullOrEmpty()) {
|
|||||||
def deployDockerfiles = Dockerfiles.getDeployDockerfiles()
|
def deployDockerfiles = Dockerfiles.getDeployDockerfiles()
|
||||||
|
|
||||||
def dockerComposeBaseCommand = CIProperties.findProperty("deploy.docker-compose.base-command")
|
def dockerComposeBaseCommand = CIProperties.findProperty("deploy.docker-compose.base-command")
|
||||||
.orElse("docker compose")
|
.orElse("docker compose")
|
||||||
|
|
||||||
def dockerComposeDirectory = CIProperties.getProperty("deploy.docker-compose.dir")
|
def dockerComposeDirectory = CIProperties.getProperty("deploy.docker-compose.dir")
|
||||||
|
|
||||||
def dockerComposeFileName = CIProperties.findProperty("deploy.docker-compose.filename")
|
def dockerComposeFileName = CIProperties.findProperty("deploy.docker-compose.filename")
|
||||||
.orElse("docker-compose.yml")
|
.orElse("docker-compose.yml")
|
||||||
|
|
||||||
def sshDeployProfile = CIProperties.findProperty("deploy.ssh.profile")
|
def sshDeployProfile = CIProperties.findProperty("deploy.ssh.profile")
|
||||||
.orElse("deploy")
|
.orElse("deploy")
|
||||||
|
|
||||||
if (deployGitTags.size() > 1) {
|
if (deployGitTags.size() > 1) {
|
||||||
throw new IllegalStateException("Can not deploy more than one git tag via docker-compose. Found multiple deploy tags: ${deployGitTags}")
|
throw new IllegalStateException("Can not deploy more than one git tag via docker-compose. Found multiple deploy tags: ${deployGitTags}")
|
||||||
@ -54,6 +56,8 @@ def deployGitTag = deployGitTags.first()
|
|||||||
|
|
||||||
ScriptLog.printf "Starting, total ${deployDockerfiles.size()} dockerfiles marked to be deployed... (tag: ${deployGitTag}, dockerfiles: ${deployDockerfiles})"
|
ScriptLog.printf "Starting, total ${deployDockerfiles.size()} dockerfiles marked to be deployed... (tag: ${deployGitTag}, dockerfiles: ${deployDockerfiles})"
|
||||||
|
|
||||||
|
DockerLogin.perform()
|
||||||
|
|
||||||
for (def dockerfileName : deployDockerfiles) {
|
for (def dockerfileName : deployDockerfiles) {
|
||||||
ScriptLog.printf "Deploying image for dockerfile named '${dockerfileName}' via ssh profile ${sshDeployProfile}"
|
ScriptLog.printf "Deploying image for dockerfile named '${dockerfileName}' via ssh profile ${sshDeployProfile}"
|
||||||
|
|
||||||
@ -61,25 +65,52 @@ for (def dockerfileName : deployDockerfiles) {
|
|||||||
|
|
||||||
ScriptLog.printf "Docker image ${dockerImageToUpdateTagBaseName} base name will be used for dockerfile named '${dockerfileName}'"
|
ScriptLog.printf "Docker image ${dockerImageToUpdateTagBaseName} base name will be used for dockerfile named '${dockerfileName}'"
|
||||||
|
|
||||||
def dockerReleaseTagToSetup = DockerTags.getDockerTagPostfixForDockerfile(
|
def dockerReleaseTagToSetup = DockerTags.getDockerTagForDockerfile(
|
||||||
GitTags.sanitizeTagFromPrefixes(deployGitTag, GitTags.deployPrefixes)
|
dockerfileName,
|
||||||
|
GitTags.sanitizeTagFromPrefixes(deployGitTag, GitTags.deployPrefixes)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def dockerReleaseImageToSetup = "${dockerImageToUpdateTagBaseName}:${dockerReleaseTagToSetup}"
|
||||||
|
|
||||||
|
def dockerImageBuildCommand = DockerBuildCommandFactory.getBuildCommand(dockerfileName, "-t ${dockerReleaseImageToSetup}")
|
||||||
|
ScriptLog.printf "Rebuilding docker image '${dockerReleaseImageToSetup}' before deploy by command '${dockerImageBuildCommand}'..."
|
||||||
|
|
||||||
|
sh dockerImageBuildCommand
|
||||||
|
|
||||||
|
ScriptLog.printf "Pushing docker image '${dockerReleaseImageToSetup}' before deploy..."
|
||||||
|
sh "docker push ${dockerReleaseImageToSetup}"
|
||||||
|
|
||||||
ScriptLog.printf "Updating docker-compose image tag to '${dockerReleaseTagToSetup}' in service '${dockerImageToUpdateTagBaseName}' in file '${dockerComposeFileName}' located in directory '${dockerComposeDirectory}'..."
|
ScriptLog.printf "Updating docker-compose image tag to '${dockerReleaseTagToSetup}' in service '${dockerImageToUpdateTagBaseName}' in file '${dockerComposeFileName}' located in directory '${dockerComposeDirectory}'..."
|
||||||
|
|
||||||
sh """ ssh -tt ${sshDeployProfile} "\\
|
if (dockerReleaseTagToSetup.isNullOrBlank()) {
|
||||||
cd '${dockerComposeDirectory}' && \\
|
throw new IllegalStateException("Build tag was not resolved (empty or null '${dockerReleaseTagToSetup}')")
|
||||||
sed -i 's|image: ${dockerImageToUpdateTagBaseName}:.*|image: ${dockerImageToUpdateTagBaseName}:${dockerReleaseTagToSetup}|g' '${dockerComposeFileName}'\\
|
}
|
||||||
"
|
|
||||||
"""
|
|
||||||
|
|
||||||
ScriptLog.printf "Service tag updated, restarting compose..."
|
def dockerComposeRollbackFileName = "${dockerComposeFileName}.backup.${System.currentTimeMillis()}.yml"
|
||||||
|
|
||||||
sh """ ssh -tt ${sshDeployProfile} "\\
|
try {
|
||||||
cd '${dockerComposeDirectory}' && \\
|
sh """ ssh -tt ${sshDeployProfile} "\\
|
||||||
${dockerComposeBaseCommand} -f '${dockerComposeFileName}' up -d \\
|
cd '${dockerComposeDirectory}' && \\
|
||||||
"
|
cp ${dockerComposeFileName} ${dockerComposeRollbackFileName} && \\
|
||||||
"""
|
sed -i 's|image: ${dockerImageToUpdateTagBaseName}:.*|image: ${dockerReleaseImageToSetup}|g' '${dockerComposeFileName}'\\ &&
|
||||||
|
${dockerComposeBaseCommand} -f '${dockerComposeFileName}' up -d \\
|
||||||
|
"
|
||||||
|
"""
|
||||||
|
} catch (Throwable e) {
|
||||||
|
ScriptLog.printf("Exception occurred while updating docker-compose tag on deploy, rolling back")
|
||||||
|
e.printStackTrace()
|
||||||
|
|
||||||
|
sh """ ssh -tt ${sshDeployProfile} "\\
|
||||||
|
cd '${dockerComposeDirectory}' && \\
|
||||||
|
rm ${dockerComposeFileName} && \\
|
||||||
|
cp ${dockerComposeRollbackFileName} ${dockerComposeFileName} \\
|
||||||
|
"
|
||||||
|
"""
|
||||||
|
|
||||||
|
ScriptLog.printf("Application restored from backup")
|
||||||
|
|
||||||
|
throw e
|
||||||
|
}
|
||||||
|
|
||||||
ScriptLog.printf "Deploying of docker tag '${dockerReleaseTagToSetup}' successfully completed!"
|
ScriptLog.printf "Deploying of docker tag '${dockerReleaseTagToSetup}' successfully completed!"
|
||||||
}
|
}
|
@ -1,7 +1,9 @@
|
|||||||
package script
|
package script
|
||||||
|
|
||||||
|
import util.CIProperties
|
||||||
import util.DockerImageNames
|
import util.DockerImageNames
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Скрипт для публикации докер образа при сборке.
|
* Скрипт для публикации докер образа при сборке.
|
||||||
*/
|
*/
|
||||||
@ -11,8 +13,10 @@ import util.DockerImageNames
|
|||||||
@Grab(group='io.tswf.groovy.better-groovy', module='better-groovy-scripting-shell', version='2.0.2-SNAPSHOT', changing = true),
|
@Grab(group='io.tswf.groovy.better-groovy', module='better-groovy-scripting-shell', version='2.0.2-SNAPSHOT', changing = true),
|
||||||
@Grab(group='io.tswf.groovy.better-groovy', module='better-groovy-scripting-gitea', version='2.0.2-SNAPSHOT', changing = true)
|
@Grab(group='io.tswf.groovy.better-groovy', module='better-groovy-scripting-gitea', version='2.0.2-SNAPSHOT', changing = true)
|
||||||
])
|
])
|
||||||
|
@groovy.transform.CompileStatic
|
||||||
import util.DockerLogin
|
import util.DockerLogin
|
||||||
import util.DockerBuildCommandFactory
|
import util.DockerBuildCommandFactory
|
||||||
|
import util.DockerTags
|
||||||
import util.Dockerfiles
|
import util.Dockerfiles
|
||||||
import util.GitTags
|
import util.GitTags
|
||||||
import util.GlobalProperties
|
import util.GlobalProperties
|
||||||
@ -39,7 +43,7 @@ ScriptLog.printf "Starting docker publishing..."
|
|||||||
|
|
||||||
DockerLogin.perform()
|
DockerLogin.perform()
|
||||||
|
|
||||||
def dockerfilesToPush = System.findGlobalPropertyList("ci.docker.image.push.files").orElse([])
|
def dockerfilesToPush = CIProperties.findListProperty("docker.image.push.files").orElse([])
|
||||||
|
|
||||||
gitReleaseTags.each { gitTag ->
|
gitReleaseTags.each { gitTag ->
|
||||||
Dockerfiles.getPresetDockerfiles().each { dockerfileName ->
|
Dockerfiles.getPresetDockerfiles().each { dockerfileName ->
|
||||||
@ -53,8 +57,12 @@ gitReleaseTags.each { gitTag ->
|
|||||||
def dockerImageBaseName = DockerImageNames.getImageName(dockerfileName)
|
def dockerImageBaseName = DockerImageNames.getImageName(dockerfileName)
|
||||||
|
|
||||||
// Для докер-тегов образов используем гит-теги с отброшенным префиксом
|
// Для докер-тегов образов используем гит-теги с отброшенным префиксом
|
||||||
def imageBaseTag = gitTag.replace(GitTags.getPrefix(), "")
|
def imageBaseTag = GitTags.sanitizeTagFromPrefixes(gitTag, GitTags.getReleasePrefixes())
|
||||||
def imageTag = DockerImageNames.getImageTag(imageBaseTag, dockerfileName)
|
def imageTag = DockerTags.getDockerTagForDockerfile(imageBaseTag, dockerfileName)
|
||||||
|
|
||||||
|
if (imageTag.isNullOrBlank()) {
|
||||||
|
throw new IllegalStateException("Build tag was not resolved (empty or null '${imageTag}')")
|
||||||
|
}
|
||||||
|
|
||||||
def imageFullName = "${dockerImageBaseName}:${imageTag}"
|
def imageFullName = "${dockerImageBaseName}:${imageTag}"
|
||||||
|
|
@ -12,7 +12,7 @@ class DockerBuildCommandFactory {
|
|||||||
]
|
]
|
||||||
|
|
||||||
return propertyNames.stream()
|
return propertyNames.stream()
|
||||||
.map { CIProperties.findProperty("ci.docker.build.additional-args").orNull() }
|
.map { CIProperties.findProperty(it).orNull() }
|
||||||
.filter { it != null }
|
.filter { it != null }
|
||||||
.findFirst()
|
.findFirst()
|
||||||
.orElse("")
|
.orElse("")
|
||||||
|
@ -5,11 +5,6 @@ import groovy.transform.CompileStatic
|
|||||||
@CompileStatic
|
@CompileStatic
|
||||||
class DockerImageNames {
|
class DockerImageNames {
|
||||||
|
|
||||||
static String getImageTag(String baseName, String dockerfile) {
|
|
||||||
def imageTagPostfix = CIProperties.findProperty("docker.image.tag.${dockerfile.toLowerCase()}.postfix").orElse("")
|
|
||||||
return "${baseName}${imageTagPostfix}"
|
|
||||||
}
|
|
||||||
|
|
||||||
static String getImageName(String dockerfileName) {
|
static String getImageName(String dockerfileName) {
|
||||||
def propertyNames = [
|
def propertyNames = [
|
||||||
"docker.image.${dockerfileName.toLowerCase()}.name",
|
"docker.image.${dockerfileName.toLowerCase()}.name",
|
||||||
|
@ -18,9 +18,20 @@ class DockerLogin {
|
|||||||
|
|
||||||
// Логинимся в Registry
|
// Логинимся в Registry
|
||||||
ScriptLog.printf "Performing login to registry..."
|
ScriptLog.printf "Performing login to registry..."
|
||||||
def registryName = System.getGlobalProperty("ci.docker.registry")
|
|
||||||
def registryUser = System.getGlobalProperty("ci.docker.registry.username")
|
def registryName = CIProperties.getProperty("docker.registry")
|
||||||
def registryPassword = System.getGlobalProperty("ci.docker.registry.password")
|
def registryUser = CIProperties.getProperty("docker.registry.username")
|
||||||
|
def registryPassword = CIProperties.getProperty("docker.registry.password")
|
||||||
|
|
||||||
|
if (registryName.isNullOrBlank()) {
|
||||||
|
throw new IllegalStateException("Docker registry name not set")
|
||||||
|
}
|
||||||
|
if (registryUser.isNullOrBlank()) {
|
||||||
|
throw new IllegalStateException("Docker registry user not set")
|
||||||
|
}
|
||||||
|
if (registryPassword.isNullOrBlank()) {
|
||||||
|
throw new IllegalStateException("Docker registry password not set")
|
||||||
|
}
|
||||||
|
|
||||||
sh "docker login $registryName -u $registryUser -p $registryPassword"
|
sh "docker login $registryName -u $registryUser -p $registryPassword"
|
||||||
|
|
||||||
|
@ -38,7 +38,16 @@ class GitTags {
|
|||||||
return getTagsWithPrefix(deployPrefixes)
|
return getTagsWithPrefix(deployPrefixes)
|
||||||
}
|
}
|
||||||
|
|
||||||
static String sanitizeTagFromPrefixes(String tag, Collection<String> prefixes) {
|
static List<String> getAllPrefixes() {
|
||||||
|
def result = new ArrayList<String>()
|
||||||
|
|
||||||
|
result.addAll(getReleasePrefixes())
|
||||||
|
result.addAll(getDeployPrefixes())
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
static String sanitizeTagFromPrefixes(String tag, Collection<String> prefixes = getAllPrefixes()) {
|
||||||
return tag.removeAll(prefixes)
|
return tag.removeAll(prefixes)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,19 @@
|
|||||||
package util
|
package util
|
||||||
|
|
||||||
|
import groovy.transform.CompileStatic
|
||||||
|
|
||||||
|
@CompileStatic
|
||||||
class ScriptLog {
|
class ScriptLog {
|
||||||
static void printf(Object text, Object... args) {
|
static void printf(Object text, Object... args) {
|
||||||
def callerClassName = new Throwable().getStackTrace()[1].getClassName().split("\\.").last()
|
def scriptPrefix = new Throwable().getStackTrace()
|
||||||
println "[${callerClassName}]: ${String.format(String.valueOf(text), args)}"
|
.stream()
|
||||||
|
.map { it.className }
|
||||||
|
.filter { it != null }
|
||||||
|
.map { it.split("\\.").last() }
|
||||||
|
.filter { it.isNotNullOrBlank() && it.charAt(0).isLowerCase() }
|
||||||
|
.toList()
|
||||||
|
.lastOrNull() ?: "ci script"
|
||||||
|
|
||||||
|
println "[${scriptPrefix}]: ${String.format(String.valueOf(text), args)}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user