initial commit
This commit is contained in:
85
src/main/groovy/script/deploy_compose_via_ssh.groovy
Normal file
85
src/main/groovy/script/deploy_compose_via_ssh.groovy
Normal file
@@ -0,0 +1,85 @@
|
||||
package script
|
||||
|
||||
import util.CIProperties
|
||||
import util.DockerImageNames
|
||||
import util.DockerTags
|
||||
import util.Dockerfiles
|
||||
|
||||
/**
|
||||
* Скрип для деплоя докер образа на сервер посредством обновления docker-compose файла и его перезапуска.
|
||||
*/
|
||||
|
||||
@GrabResolver(name='gitea', root='https://git.tswf.io/api/packages/public-repos/maven')
|
||||
@Grapes([
|
||||
@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)
|
||||
])
|
||||
import util.GitTags
|
||||
import util.GlobalProperties
|
||||
import util.ScriptLog
|
||||
|
||||
GlobalProperties.loadGlobalProperties()
|
||||
|
||||
println """
|
||||
#############################################################################
|
||||
DEPLOY via docker-compose script
|
||||
#############################################################################
|
||||
"""
|
||||
|
||||
def deployGitTags = GitTags.getDeployTags()
|
||||
|
||||
if (deployGitTags.isNullOrEmpty()) {
|
||||
ScriptLog.printf "There is no tags to remote restart."
|
||||
System.exit(1)
|
||||
}
|
||||
|
||||
def deployDockerfiles = Dockerfiles.getDeployDockerfiles()
|
||||
|
||||
def dockerComposeBaseCommand = CIProperties.findProperty("deploy.docker-compose.base-command")
|
||||
.orElse("docker compose")
|
||||
|
||||
def dockerComposeDirectory = CIProperties.getProperty("deploy.docker-compose.dir")
|
||||
|
||||
def dockerComposeFileName = CIProperties.findProperty("deploy.docker-compose.filename")
|
||||
.orElse("docker-compose.yml")
|
||||
|
||||
def sshDeployProfile = CIProperties.findProperty("deploy.ssh.profile")
|
||||
.orElse("deploy")
|
||||
|
||||
if (deployGitTags.size() > 1) {
|
||||
throw new IllegalStateException("Can not deploy more than one git tag via docker-compose. Found multiple deploy tags: ${deployGitTags}")
|
||||
}
|
||||
|
||||
def deployGitTag = deployGitTags.first()
|
||||
|
||||
ScriptLog.printf "Starting, total ${deployDockerfiles.size()} dockerfiles marked to be deployed... (tag: ${deployGitTag}, dockerfiles: ${deployDockerfiles})"
|
||||
|
||||
for (def dockerfileName : deployDockerfiles) {
|
||||
ScriptLog.printf "Deploying image for dockerfile named '${dockerfileName}' via ssh profile ${sshDeployProfile}"
|
||||
|
||||
def dockerImageToUpdateTagBaseName = DockerImageNames.getImageName(dockerfileName)
|
||||
|
||||
ScriptLog.printf "Docker image ${dockerImageToUpdateTagBaseName} base name will be used for dockerfile named '${dockerfileName}'"
|
||||
|
||||
def dockerReleaseTagToSetup = DockerTags.getDockerTagPostfixForDockerfile(
|
||||
GitTags.sanitizeTagFromPrefixes(deployGitTag, GitTags.deployPrefixes)
|
||||
)
|
||||
|
||||
ScriptLog.printf "Updating docker-compose image tag to '${dockerReleaseTagToSetup}' in service '${dockerImageToUpdateTagBaseName}' in file '${dockerComposeFileName}' located in directory '${dockerComposeDirectory}'..."
|
||||
|
||||
sh """ ssh -tt ${sshDeployProfile} "\\
|
||||
cd '${dockerComposeDirectory}' && \\
|
||||
sed -i 's|image: ${dockerImageToUpdateTagBaseName}:.*|image: ${dockerImageToUpdateTagBaseName}:${dockerReleaseTagToSetup}|g' '${dockerComposeFileName}'\\
|
||||
"
|
||||
"""
|
||||
|
||||
ScriptLog.printf "Service tag updated, restarting compose..."
|
||||
|
||||
sh """ ssh -tt ${sshDeployProfile} "\\
|
||||
cd '${dockerComposeDirectory}' && \\
|
||||
${dockerComposeBaseCommand} -f '${dockerComposeFileName}' up -d \\
|
||||
"
|
||||
"""
|
||||
|
||||
ScriptLog.printf "Deploying of docker tag '${dockerReleaseTagToSetup}' successfully completed!"
|
||||
}
|
||||
69
src/main/groovy/script/deploy_ssh_profile_setup.groovy
Normal file
69
src/main/groovy/script/deploy_ssh_profile_setup.groovy
Normal file
@@ -0,0 +1,69 @@
|
||||
package script
|
||||
|
||||
/**
|
||||
* Скрипт для начальной конфигурации SSH профиля для деплоя
|
||||
* Создает профиль для подключения к стенду, а также настраивает пользователя для этих целей.
|
||||
*/
|
||||
|
||||
@GrabResolver(name='gitea', root='https://git.tswf.io/api/packages/public-repos/maven')
|
||||
@Grapes([
|
||||
@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)
|
||||
])
|
||||
import util.GlobalProperties
|
||||
import util.ScriptLog
|
||||
|
||||
println """
|
||||
#############################################################################
|
||||
SSH Profile Setup Script
|
||||
#############################################################################
|
||||
"""
|
||||
|
||||
|
||||
GlobalProperties.loadGlobalProperties()
|
||||
|
||||
ScriptLog.printf "Creating .ssh directory..."
|
||||
|
||||
def sshDir = new File("${System.getProperty("user.home")}/.ssh")
|
||||
sshDir.mkdirsUnsafe()
|
||||
|
||||
// Записываем приватный ключ
|
||||
|
||||
ScriptLog.printf "Writing ssh private key..."
|
||||
|
||||
def sshPrivateKey = sshDir.subFile("id_rsa")
|
||||
|
||||
//TODO: может быть тут посолить?
|
||||
def sshPrivateKeyBase64Content = System.getGlobalProperty("ci.deploy.ssh.profile.private-key-base64").removeAll(' ')
|
||||
def sshPrivateKeyDecodedContent = Base64.decoder.decode(sshPrivateKeyBase64Content)
|
||||
|
||||
sshPrivateKey.bytes = sshPrivateKeyDecodedContent
|
||||
|
||||
sh 'chmod 400 ~/.ssh/id_rsa'
|
||||
|
||||
// Записываем ssh config
|
||||
|
||||
ScriptLog.printf "Writing ssh config..."
|
||||
|
||||
def sshProfileName = System.getGlobalProperty("ci.deploy.ssh.profile")
|
||||
def sshHost = System.getGlobalProperty("ci.deploy.ssh.host")
|
||||
def sshPort = System.getGlobalProperty("ci.deploy.ssh.port")
|
||||
def sshUsername = System.getGlobalProperty("ci.deploy.ssh.username")
|
||||
|
||||
def sshConfigFile = sshDir.subFile("config")
|
||||
sshConfigFile.text = """
|
||||
Host ${sshProfileName}
|
||||
HostName ${sshHost}
|
||||
User ${sshUsername}
|
||||
Port ${sshPort}
|
||||
""".stripIndent(false)
|
||||
|
||||
|
||||
// Выполняем тестовое подключение к SSH, чтобы убедиться в корректности настройки.
|
||||
|
||||
ScriptLog.printf "Validating ssh configuration..."
|
||||
|
||||
sh "ssh ${sshProfileName} -o StrictHostKeyChecking=no -tt 'echo Testing connection!'"
|
||||
|
||||
ScriptLog.printf "SSH profile configured successfully!"
|
||||
|
||||
39
src/main/groovy/script/docker_build.groovy
Normal file
39
src/main/groovy/script/docker_build.groovy
Normal file
@@ -0,0 +1,39 @@
|
||||
package script
|
||||
|
||||
import util.Dockerfiles
|
||||
import util.DockerBuildCommandFactory
|
||||
import util.GlobalProperties
|
||||
import util.ScriptLog
|
||||
|
||||
/**
|
||||
* Скрипт для тестового прогона сборки докер. Просто проверить, что собирается.
|
||||
*/
|
||||
|
||||
@GrabResolver(name='gitea', root='https://git.tswf.io/api/packages/public-repos/maven')
|
||||
@Grapes([
|
||||
@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)
|
||||
])
|
||||
_
|
||||
|
||||
println """
|
||||
#############################################################################
|
||||
Build Docker Image Script
|
||||
#############################################################################
|
||||
"""
|
||||
|
||||
GlobalProperties.loadGlobalProperties()
|
||||
|
||||
// Проверяем, что установлен docker
|
||||
if (System.findExecutablesInPath(['docker']).isEmpty())
|
||||
throw new FileNotFoundException("Can't find installed docker-compose at that system!")
|
||||
|
||||
ScriptLog.printf "Building docker image without tags..."
|
||||
|
||||
for (def dockerfileName : Dockerfiles.getPresetDockerfiles()) {
|
||||
def dockerCommand = DockerBuildCommandFactory.getBuildCommand(dockerfileName)
|
||||
println "Using dockerfile '${dockerfileName}', full command: '$dockerCommand'"
|
||||
sh dockerCommand
|
||||
}
|
||||
|
||||
ScriptLog.printf "Docker image successfully built!"
|
||||
143
src/main/groovy/script/docker_gitea_release_publish.groovy
Normal file
143
src/main/groovy/script/docker_gitea_release_publish.groovy
Normal file
@@ -0,0 +1,143 @@
|
||||
package script
|
||||
|
||||
/**
|
||||
*
|
||||
* Скрипт для создания или обновления релиза в gitea для всех релизных тегов текущего коммита.
|
||||
* Ищет описание релиза в специальной папке, после чего добавляет его к релизу, если находит.
|
||||
*
|
||||
* Собирает докер образ, после чего из него копирует указанные как артефакты файлы и прикрепляет к релизу.
|
||||
*
|
||||
* Для работы требуется токен API Gitea и список нужных файлов, либо флаг того, что список может быть пустым.
|
||||
*/
|
||||
|
||||
|
||||
@GrabResolver(name='gitea', root='https://git.tswf.io/api/packages/public-repos/maven')
|
||||
@Grapes([
|
||||
@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)
|
||||
])
|
||||
import io.tswf.gitea.parser.repository.GiteaRepositoryMetadataParser
|
||||
import io.tswf.groovy.bettergroovy.scripting.v2.git.Git
|
||||
import io.tswf.groovy.bettergroovy.scripting.v2.gitea.Gitea
|
||||
import util.CIProperties
|
||||
import util.ReleaseArtifactNames
|
||||
import util.Dockerfiles
|
||||
import util.DockerBuildCommandFactory
|
||||
import util.GitTags
|
||||
import util.GlobalProperties
|
||||
import util.ReleaseArtifacts
|
||||
import util.ReleaseDescriptionReader
|
||||
import util.ScriptLog
|
||||
|
||||
println """
|
||||
#############################################################################
|
||||
Publish Release Artifacts Script
|
||||
#############################################################################
|
||||
"""
|
||||
|
||||
GlobalProperties.loadGlobalProperties()
|
||||
|
||||
def dockerFiles = Dockerfiles.getReleaseDockerfiles()
|
||||
|
||||
def isAnyArtifactRequiredForRelease = CIProperties.findProperty("gitea.release.artifacts.is-required")
|
||||
.map { it.toBoolean() }
|
||||
.orElse(true)
|
||||
|
||||
def releaseTags = GitTags.getReleaseTags()
|
||||
|
||||
if (releaseTags.isNullOrEmpty()) {
|
||||
ScriptLog.printf "There is no release tags to publish!"
|
||||
System.exit(1)
|
||||
}
|
||||
|
||||
def giteaHost = CIProperties.findProperty("gitea.origin") // В рамках обратной совместимости
|
||||
.orElseGet {
|
||||
CIProperties.getProperty("gitea.host") // Актуальное название
|
||||
}
|
||||
|
||||
def giteaToken = CIProperties.getProperty("gitea.token")
|
||||
|
||||
def giteaRepositoryParser = new GiteaRepositoryMetadataParser()
|
||||
def giteaRepositoryMetadata = giteaRepositoryParser.parseRepositoryMetadataFromOrigin(giteaHost)
|
||||
|
||||
def artifactLocalFiles = new ArrayList<File>()
|
||||
|
||||
for (def dockerfileName : dockerFiles) {
|
||||
ScriptLog.printf "Collecting artifacts from dockerfile named '${dockerfileName}'"
|
||||
|
||||
// Собираем докер образ и поднимаем контейнер, чтобы далее извлечь из него нужные артефакты.
|
||||
def temporaryDockerImage = Random.randomString(40)
|
||||
def temporaryDockerContainerName = "build_in_docker_${Random.randomString(25)}"
|
||||
|
||||
// Подчищаем за собою
|
||||
addShutdownHook {
|
||||
println "Removing temporary docker container '${temporaryDockerContainerName}' and image '${temporaryDockerImage}'"
|
||||
sh "docker rm -f ${temporaryDockerContainerName}"
|
||||
sh "docker image rm -f ${temporaryDockerImage}"
|
||||
}
|
||||
|
||||
def temporaryBuildDir = new File("./gitea-release-publication/tmp/build/${Random.randomString(10)}/")
|
||||
temporaryBuildDir.mkdirsUnsafe()
|
||||
temporaryBuildDir.deleteOnExit()
|
||||
|
||||
def dockerBuildCommand = DockerBuildCommandFactory.getBuildCommand(dockerfileName, "-t ${temporaryDockerImage}")
|
||||
sh dockerBuildCommand
|
||||
sh "docker create -it --name ${temporaryDockerContainerName} ${temporaryDockerImage} sh"
|
||||
|
||||
def artifactsToPublish = ReleaseArtifacts.getArtifacts(dockerfileName)
|
||||
|
||||
if (artifactsToPublish.isEmpty()) {
|
||||
ScriptLog.printf "There is no artifacts was configured for dockerfile named '${dockerfileName}'"
|
||||
} else {
|
||||
ScriptLog.printf "Copying artifacts for dockerfile named '${dockerfileName}': ${artifactsToPublish.join('\n')}"
|
||||
}
|
||||
|
||||
// Копируем все артефакты для публикации из временного докер контейнера
|
||||
artifactsToPublish.stream()
|
||||
.map { artifactToPublishAbsolutePath ->
|
||||
def artifactSimpleName = artifactToPublishAbsolutePath.split("/").last()
|
||||
|
||||
def artifactLocalFile = temporaryBuildDir.subFile(artifactSimpleName)
|
||||
sh "docker cp ${temporaryDockerContainerName}:${artifactToPublishAbsolutePath} ${artifactLocalFile.path}"
|
||||
|
||||
artifactLocalFile
|
||||
}
|
||||
|
||||
// Переименовываем файл, если это настроено
|
||||
.map { artifactFile ->
|
||||
ReleaseArtifactNames.getNewArtifactName(artifactFile, dockerfileName)
|
||||
.map { newName ->
|
||||
def newFile = artifactFile.parentFile.subFile(newName)
|
||||
ScriptLog.printf "Renaming artifact '${artifactFile.absoluteCanonicalFile.path}' to '${newName}' for dockerfile named '${dockerfileName}'"
|
||||
artifactFile.renameTo(newFile)
|
||||
|
||||
newFile
|
||||
}
|
||||
.orElse(artifactFile)
|
||||
}
|
||||
|
||||
.forEach {
|
||||
artifactLocalFiles << it
|
||||
}
|
||||
}
|
||||
|
||||
ScriptLog.printf "Total ${artifactLocalFiles.size()} artifacts collected: ${artifactLocalFiles}"
|
||||
|
||||
if (artifactLocalFiles.isEmpty() && isAnyArtifactRequiredForRelease) {
|
||||
ScriptLog.printf "Release artifacts required, but no one found!"
|
||||
System.exit(1)
|
||||
}
|
||||
|
||||
def giteaRepository = Gitea.getCIRepositoryUtilityV1(giteaToken, giteaRepositoryMetadata)
|
||||
|
||||
// Создаем релизы в гитее
|
||||
releaseTags.forEach { releaseTag ->
|
||||
def releaseName = GitTags.sanitizeTagFromPrefixes(releaseTag, GitTags.getReleasePrefixes())
|
||||
def releaseCommitSha = System.findGlobalProperty("ci.gitea.release.commit.short-sha")
|
||||
.orElse(Git.commitShortSha)
|
||||
|
||||
def releaseDescription = ReleaseDescriptionReader.readDescription(releaseName)
|
||||
.orElse("")
|
||||
|
||||
giteaRepository.createOrUpdateRelease(releaseTag, releaseCommitSha, releaseName, releaseDescription, artifactLocalFiles)
|
||||
}
|
||||
74
src/main/groovy/script/docker_push.groovy
Normal file
74
src/main/groovy/script/docker_push.groovy
Normal file
@@ -0,0 +1,74 @@
|
||||
package script
|
||||
|
||||
import util.DockerImageNames
|
||||
|
||||
/**
|
||||
* Скрипт для публикации докер образа при сборке.
|
||||
*/
|
||||
|
||||
@GrabResolver(name='gitea', root='https://git.tswf.io/api/packages/public-repos/maven')
|
||||
@Grapes([
|
||||
@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)
|
||||
])
|
||||
import util.DockerLogin
|
||||
import util.DockerBuildCommandFactory
|
||||
import util.Dockerfiles
|
||||
import util.GitTags
|
||||
import util.GlobalProperties
|
||||
import util.ScriptLog
|
||||
|
||||
println """
|
||||
#############################################################################
|
||||
Push Docker Images Script
|
||||
#############################################################################
|
||||
"""
|
||||
|
||||
GlobalProperties.loadGlobalProperties()
|
||||
|
||||
def gitReleaseTags = GitTags.getDeployTags()
|
||||
|
||||
if (gitReleaseTags.isNullOrEmpty()) {
|
||||
ScriptLog.printf "There is no release tags found!"
|
||||
System.exit(1)
|
||||
}
|
||||
else
|
||||
ScriptLog.printf "Found git release tags ${gitReleaseTags}"
|
||||
|
||||
ScriptLog.printf "Starting docker publishing..."
|
||||
|
||||
DockerLogin.perform()
|
||||
|
||||
def dockerfilesToPush = System.findGlobalPropertyList("ci.docker.image.push.files").orElse([])
|
||||
|
||||
gitReleaseTags.each { gitTag ->
|
||||
Dockerfiles.getPresetDockerfiles().each { dockerfileName ->
|
||||
if (!dockerfilesToPush.isEmpty() && dockerfileName !in dockerfilesToPush) {
|
||||
ScriptLog.printf "Skipping push of dockerfile '${dockerfileName}' because it is not in push list: ${dockerfilesToPush}"
|
||||
return
|
||||
}
|
||||
|
||||
ScriptLog.printf "Building docker tag: '${gitTag}' with dockerfile named '${dockerfileName}'"
|
||||
|
||||
def dockerImageBaseName = DockerImageNames.getImageName(dockerfileName)
|
||||
|
||||
// Для докер-тегов образов используем гит-теги с отброшенным префиксом
|
||||
def imageBaseTag = gitTag.replace(GitTags.getPrefix(), "")
|
||||
def imageTag = DockerImageNames.getImageTag(imageBaseTag, dockerfileName)
|
||||
|
||||
def imageFullName = "${dockerImageBaseName}:${imageTag}"
|
||||
|
||||
ScriptLog.printf "Full docker image name will be '${imageFullName}'"
|
||||
|
||||
def dockerBuildCommand = DockerBuildCommandFactory.getBuildCommand(dockerfileName, "-t ${imageFullName}")
|
||||
|
||||
ScriptLog.printf "Executing: '${dockerBuildCommand}'"
|
||||
|
||||
sh dockerBuildCommand
|
||||
sh "docker push '${imageFullName}'"
|
||||
|
||||
ScriptLog.printf "Docker tag ${imageTag} build and push completed with full image name: '${imageFullName}'"
|
||||
}
|
||||
}
|
||||
|
||||
ScriptLog.printf "Publishing done!"
|
||||
25
src/main/groovy/util/CIProperties.groovy
Normal file
25
src/main/groovy/util/CIProperties.groovy
Normal file
@@ -0,0 +1,25 @@
|
||||
package util
|
||||
|
||||
import groovy.transform.CompileStatic
|
||||
|
||||
@CompileStatic
|
||||
class CIProperties {
|
||||
|
||||
public static final String CI_PROPERTIES_PREFIX = "ci"
|
||||
|
||||
static String getProperty(String propertyName) {
|
||||
return findProperty(propertyName).orElseThrow { new IllegalStateException("Required CI property named '${propertyName}' was not found!") }
|
||||
}
|
||||
|
||||
static Optional<String> findProperty(String propertyName) {
|
||||
return System.findGlobalProperty("${CI_PROPERTIES_PREFIX}.${propertyName}")
|
||||
}
|
||||
|
||||
static List<String> getListProperty(String propertyName) {
|
||||
return findListProperty(propertyName).orElseThrow { new IllegalStateException("Required CI property named '${propertyName}' was not found!") }
|
||||
}
|
||||
|
||||
static Optional<List<String>> findListProperty(String propertyName) {
|
||||
return System.findGlobalPropertyList("${CI_PROPERTIES_PREFIX}.${propertyName}")
|
||||
}
|
||||
}
|
||||
26
src/main/groovy/util/DockerBuildCommandFactory.groovy
Normal file
26
src/main/groovy/util/DockerBuildCommandFactory.groovy
Normal file
@@ -0,0 +1,26 @@
|
||||
package util
|
||||
|
||||
import groovy.transform.CompileStatic
|
||||
|
||||
@CompileStatic
|
||||
class DockerBuildCommandFactory {
|
||||
|
||||
static String getBuildAdditionalArgs(String dockerfileName) {
|
||||
def propertyNames = [
|
||||
"docker.build.${dockerfileName.toLowerCase()}.additional-args",
|
||||
"docker.build.additional-args"
|
||||
]
|
||||
|
||||
return propertyNames.stream()
|
||||
.map { CIProperties.findProperty("ci.docker.build.additional-args").orNull() }
|
||||
.filter { it != null }
|
||||
.findFirst()
|
||||
.orElse("")
|
||||
}
|
||||
|
||||
static String getBuildCommand(String dockerfileName, String additionalArgs = "", String context = ".") {
|
||||
def dockerCommand = "docker build ${context} -f '${dockerfileName}' ${additionalArgs} ${getBuildAdditionalArgs(dockerfileName)}"
|
||||
return dockerCommand
|
||||
}
|
||||
|
||||
}
|
||||
29
src/main/groovy/util/DockerImageNames.groovy
Normal file
29
src/main/groovy/util/DockerImageNames.groovy
Normal file
@@ -0,0 +1,29 @@
|
||||
package util
|
||||
|
||||
import groovy.transform.CompileStatic
|
||||
|
||||
@CompileStatic
|
||||
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) {
|
||||
def propertyNames = [
|
||||
"docker.image.${dockerfileName.toLowerCase()}.name",
|
||||
"docker.image.base.name"
|
||||
]
|
||||
|
||||
return propertyNames.stream()
|
||||
.map { it.toString() }
|
||||
.map {
|
||||
CIProperties.findProperty(it).orNull()
|
||||
}
|
||||
.filter { it != null }
|
||||
.findFirst()
|
||||
.orElseThrow { new NoSuchElementException("There is no one property set: ${propertyNames}") }
|
||||
}
|
||||
|
||||
}
|
||||
29
src/main/groovy/util/DockerLogin.groovy
Normal file
29
src/main/groovy/util/DockerLogin.groovy
Normal file
@@ -0,0 +1,29 @@
|
||||
package util
|
||||
|
||||
import groovy.transform.CompileStatic
|
||||
import groovy.transform.Memoized
|
||||
|
||||
@CompileStatic
|
||||
class DockerLogin {
|
||||
|
||||
static void perform() {
|
||||
loginDockerInternal()
|
||||
}
|
||||
|
||||
@Memoized
|
||||
private static Object loginDockerInternal() {
|
||||
// Проверяем на всякий случай, что докер вообще установлен
|
||||
if (System.findExecutablesInPath(['docker']).isEmpty())
|
||||
throw new FileNotFoundException("Can't find installed docker-compose at that system!")
|
||||
|
||||
// Логинимся в Registry
|
||||
ScriptLog.printf "Performing login to registry..."
|
||||
def registryName = System.getGlobalProperty("ci.docker.registry")
|
||||
def registryUser = System.getGlobalProperty("ci.docker.registry.username")
|
||||
def registryPassword = System.getGlobalProperty("ci.docker.registry.password")
|
||||
|
||||
sh "docker login $registryName -u $registryUser -p $registryPassword"
|
||||
|
||||
ScriptLog.printf "Login into docker registry '${registryName}' successful!"
|
||||
}
|
||||
}
|
||||
17
src/main/groovy/util/DockerTags.groovy
Normal file
17
src/main/groovy/util/DockerTags.groovy
Normal file
@@ -0,0 +1,17 @@
|
||||
package util;
|
||||
|
||||
import groovy.transform.CompileStatic
|
||||
|
||||
@CompileStatic
|
||||
class DockerTags {
|
||||
|
||||
static String getDockerTagForDockerfile(String dockerfileName, String baseTag) {
|
||||
return "${baseTag}${getDockerTagPostfixForDockerfile(dockerfileName)}"
|
||||
}
|
||||
|
||||
static String getDockerTagPostfixForDockerfile(String dockerfileName) {
|
||||
return CIProperties.findProperty("docker.tag.${dockerfileName}.prefix")
|
||||
.orElse("")
|
||||
}
|
||||
|
||||
}
|
||||
38
src/main/groovy/util/Dockerfiles.groovy
Normal file
38
src/main/groovy/util/Dockerfiles.groovy
Normal file
@@ -0,0 +1,38 @@
|
||||
package util
|
||||
|
||||
import groovy.transform.CompileStatic
|
||||
|
||||
@CompileStatic
|
||||
class Dockerfiles {
|
||||
|
||||
static List<String> getPresetDockerfiles() {
|
||||
def preset = CIProperties.findProperty("docker.files.preset")
|
||||
.orElse("default")
|
||||
|
||||
def presetDockerfiles = CIProperties.findListProperty("docker.files.${preset}")
|
||||
.orElse([])
|
||||
|
||||
if (presetDockerfiles.isEmpty())
|
||||
return CIProperties.findListProperty("docker.files")
|
||||
.orElse(["Dockerfile"])
|
||||
else
|
||||
return presetDockerfiles
|
||||
}
|
||||
|
||||
static List<String> getReleaseDockerfiles() {
|
||||
return CIProperties.findListProperty("release.artifacts.dockerfiles")
|
||||
.orElseGet {
|
||||
CIProperties.findListProperty("gitea.release.artifacts.dockerfiles") // Обратная совместимость
|
||||
.orElseGet {
|
||||
getPresetDockerfiles()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static List<String> getDeployDockerfiles() {
|
||||
return CIProperties.findListProperty("deploy.dockerfile.names")
|
||||
.orElseGet {
|
||||
getPresetDockerfiles()
|
||||
}
|
||||
}
|
||||
}
|
||||
44
src/main/groovy/util/GitTags.groovy
Normal file
44
src/main/groovy/util/GitTags.groovy
Normal file
@@ -0,0 +1,44 @@
|
||||
package util
|
||||
|
||||
import groovy.transform.CompileStatic
|
||||
import io.tswf.groovy.bettergroovy.scripting.v2.git.Git
|
||||
|
||||
@CompileStatic
|
||||
class GitTags {
|
||||
public static final String DEFAULT_RELEASE_TAG_PREFIX = "release-"
|
||||
public static final String DEFAULT_DEPLOY_TAG_PREFIX = DEFAULT_RELEASE_TAG_PREFIX
|
||||
|
||||
static Set<String> getDeployPrefixes() {
|
||||
return CIProperties.findListProperty("git.tag.deploy.prefixes")
|
||||
.orElse([DEFAULT_DEPLOY_TAG_PREFIX])
|
||||
.toSet()
|
||||
}
|
||||
|
||||
static Set<String> getReleasePrefixes() {
|
||||
return CIProperties.findListProperty("git.tag.release.prefixes")
|
||||
.orElse([DEFAULT_RELEASE_TAG_PREFIX])
|
||||
.toSet()
|
||||
}
|
||||
|
||||
static List<String> getTagsWithPrefix(Collection<String> prefixes) {
|
||||
def matchingTags = Git.tags.stream()
|
||||
.filter {tag -> prefixes.any { tag.startsWith(it) }}
|
||||
.toList()
|
||||
|
||||
return matchingTags
|
||||
}
|
||||
|
||||
static List<String> getReleaseTags() {
|
||||
def releasePrefixes = getReleasePrefixes()
|
||||
return getTagsWithPrefix(releasePrefixes)
|
||||
}
|
||||
|
||||
static List<String> getDeployTags() {
|
||||
def deployPrefixes = getDeployPrefixes()
|
||||
return getTagsWithPrefix(deployPrefixes)
|
||||
}
|
||||
|
||||
static String sanitizeTagFromPrefixes(String tag, Collection<String> prefixes) {
|
||||
return tag.removeAll(prefixes)
|
||||
}
|
||||
}
|
||||
39
src/main/groovy/util/GlobalProperties.groovy
Normal file
39
src/main/groovy/util/GlobalProperties.groovy
Normal file
@@ -0,0 +1,39 @@
|
||||
package util
|
||||
|
||||
import groovy.transform.Memoized
|
||||
|
||||
class GlobalProperties {
|
||||
|
||||
@Memoized
|
||||
static loadGlobalProperties() {
|
||||
def propertiesFiles = System.findGlobalPropertyList("${CIProperties.CI_PROPERTIES_PREFIX}.properties.file.locations")
|
||||
.orElse([".ci/ci.properties"])
|
||||
.map { new File(it) }
|
||||
|
||||
if (propertiesFiles.isNotNullOrEmpty()) {
|
||||
propertiesFiles.forEach { propertiesFile ->
|
||||
if (propertiesFile.exists()) {
|
||||
loadPropertiesFile(propertiesFile)
|
||||
} else {
|
||||
ScriptLog.printf "Skipping non-existing properties file '${propertiesFile.absoluteCanonicalFile.path}'!"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void loadPropertiesFile(File propertiesFile) {
|
||||
if (propertiesFile == null) {
|
||||
return
|
||||
}
|
||||
|
||||
if (!propertiesFile.exists()) {
|
||||
throw new IOException("Could not find properties file '${propertiesFile}' (with absolute path: '${propertiesFile.absoluteCanonicalFile.path}')!")
|
||||
}
|
||||
|
||||
if (propertiesFile.isDirectory()) {
|
||||
throw new IOException("Directory found instead properties file '${propertiesFile}' (with absolute path: '${propertiesFile.absoluteCanonicalFile.path}')!")
|
||||
}
|
||||
|
||||
System.registerGlobalConfiguration(propertiesFile)
|
||||
}
|
||||
}
|
||||
67
src/main/groovy/util/ReleaseArtifactNames.groovy
Normal file
67
src/main/groovy/util/ReleaseArtifactNames.groovy
Normal file
@@ -0,0 +1,67 @@
|
||||
package util
|
||||
|
||||
import groovy.transform.CompileStatic
|
||||
|
||||
@CompileStatic
|
||||
class ReleaseArtifactNames {
|
||||
|
||||
static List<ArtifactNameResolver> artifactFileNameResolvers = new ArrayList<>()
|
||||
|
||||
static Optional<String> getNewArtifactName(File file, String dockerfileName) {
|
||||
def newArtifactName = artifactFileNameResolvers.stream()
|
||||
.map { it.resolveArtifactNewName(file, dockerfileName) }
|
||||
.filter { it != null }
|
||||
.firstOrNull()
|
||||
|
||||
return Optional.ofNullable(newArtifactName)
|
||||
}
|
||||
|
||||
static void registerArtifactResolver(ArtifactNameResolver resolver) {
|
||||
artifactFileNameResolvers << resolver
|
||||
}
|
||||
|
||||
static {
|
||||
registerArtifactResolver { file, dockerfile ->
|
||||
CIProperties.findProperty("release.artifact.${dockerfile}.${replacePathInvalidChars(file.absoluteCanonicalFile.path)}.name").orNull()
|
||||
}
|
||||
|
||||
registerArtifactResolver { file, dockerfile ->
|
||||
CIProperties.findProperty("release.artifact.${dockerfile}.${file.name}.name").orNull()
|
||||
}
|
||||
|
||||
registerArtifactResolver { file, dockerfile ->
|
||||
CIProperties.findProperty("release.artifact.${replacePathInvalidChars(file.absoluteCanonicalFile.path)}.name").orNull()
|
||||
}
|
||||
|
||||
registerArtifactResolver { file, dockerfile ->
|
||||
CIProperties.findProperty("release.artifact.${file.name}.name").orNull()
|
||||
}
|
||||
|
||||
// В рамках обратной совместимости
|
||||
registerArtifactResolver { file, dockerfile ->
|
||||
CIProperties.findProperty("gitea.release.artifact.${dockerfile}.${replacePathInvalidChars(file.absoluteCanonicalFile.path)}.name").orNull()
|
||||
}
|
||||
|
||||
registerArtifactResolver { file, dockerfile ->
|
||||
CIProperties.findProperty("gitea.release.artifact.${dockerfile}.${file.name}.name").orNull()
|
||||
}
|
||||
|
||||
registerArtifactResolver { file, dockerfile ->
|
||||
CIProperties.findProperty("gitea.release.artifact.${replacePathInvalidChars(file.absoluteCanonicalFile.path)}.name").orNull()
|
||||
}
|
||||
|
||||
registerArtifactResolver { file, dockerfile ->
|
||||
CIProperties.findProperty("gitea.release.artifact.${file.name}.name").orNull()
|
||||
}
|
||||
}
|
||||
|
||||
static String replacePathInvalidChars(String fileFullName) {
|
||||
return fileFullName
|
||||
.replace('\\', '_')
|
||||
.replace('/', '_')
|
||||
}
|
||||
|
||||
static interface ArtifactNameResolver {
|
||||
String resolveArtifactNewName(File artifactFile, String dockerfileName)
|
||||
}
|
||||
}
|
||||
23
src/main/groovy/util/ReleaseArtifacts.groovy
Normal file
23
src/main/groovy/util/ReleaseArtifacts.groovy
Normal file
@@ -0,0 +1,23 @@
|
||||
package util
|
||||
|
||||
import groovy.transform.CompileStatic
|
||||
|
||||
@CompileStatic
|
||||
class ReleaseArtifacts {
|
||||
|
||||
static List<String> getArtifacts(String dockerFile) {
|
||||
def propertyNames = [
|
||||
"gitea.release.${dockerFile}.artifacts", // В рамках обратной совместимости
|
||||
"gitea.release.artifacts", // В рамках обратной совместимости
|
||||
"release.artifacts.${dockerFile}.grab",
|
||||
"release.artifacts.grab",
|
||||
]
|
||||
|
||||
return propertyNames.stream()
|
||||
.map { it.toString() }
|
||||
.map { CIProperties.findListProperty(it).orNull() }
|
||||
.filter { it != null }
|
||||
.map { (List<String>) it }
|
||||
.findFirst().orElse([])
|
||||
}
|
||||
}
|
||||
27
src/main/groovy/util/ReleaseDescriptionReader.groovy
Normal file
27
src/main/groovy/util/ReleaseDescriptionReader.groovy
Normal file
@@ -0,0 +1,27 @@
|
||||
package util
|
||||
|
||||
import groovy.transform.CompileStatic
|
||||
|
||||
@CompileStatic
|
||||
class ReleaseDescriptionReader {
|
||||
|
||||
static Optional<String> readDescription(String releaseName) {
|
||||
def releaseDescriptionsDir = CIProperties.findProperty("release.descriptions.dir")
|
||||
.orElse(".releases/changelog/")
|
||||
.with {
|
||||
new File(it)
|
||||
}
|
||||
|
||||
def releaseDescriptionFile = releaseDescriptionsDir.subFile("${releaseName}-changelog.md")
|
||||
|
||||
if (releaseDescriptionFile.exists()) {
|
||||
def releaseAbsPath = releaseDescriptionFile.absoluteCanonicalFile.path
|
||||
ScriptLog.printf "Found description file for '$releaseName' release in '$releaseAbsPath'! It will be used for release creation."
|
||||
return Optional.of(releaseDescriptionFile.getText("UTF-8"))
|
||||
} else {
|
||||
ScriptLog.printf "Release description file not found in '${releaseDescriptionFile.absoluteCanonicalFile.path}', falling back to default value..."
|
||||
}
|
||||
|
||||
return Optional.empty()
|
||||
}
|
||||
}
|
||||
8
src/main/groovy/util/ScriptLog.groovy
Normal file
8
src/main/groovy/util/ScriptLog.groovy
Normal file
@@ -0,0 +1,8 @@
|
||||
package util
|
||||
|
||||
class ScriptLog {
|
||||
static void printf(Object text, Object... args) {
|
||||
def callerClassName = new Throwable().getStackTrace()[1].getClassName().split("\\.").last()
|
||||
println "[${callerClassName}]: ${String.format(String.valueOf(text), args)}"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user