first commit

This commit is contained in:
Sergey Paramoshkin 2025-12-07 11:10:46 +03:00
commit 2fc95f8174
8 changed files with 607 additions and 0 deletions

32
.gitignore vendored Normal file
View File

@ -0,0 +1,32 @@
# Maven
target/
pom.xml.tag
pom.xml.releaseBackup
pom.xml.versionsBackup
pom.xml.next
release.properties
dependency-reduced-pom.xml
buildNumber.properties
.mvn/timing.properties
.mvn/wrapper/maven-wrapper.jar
# Gradle
.gradle/
build/
!gradle/wrapper/gradle-wrapper.jar
!**/src/main/**/build/
!**/src/test/**/build/
# IntelliJ IDEA
.idea/
*.iws
*.iml
*.ipr
out/
# VS Code
.vscode/
# OS
.DS_Store
Thumbs.db

71
.teamcity/examples/ExamplePhpProject.kt vendored Normal file
View File

@ -0,0 +1,71 @@
package examples
import templates.PhpDockerBuildTemplate
/**
* Example: How to use PhpDockerBuildTemplate in your project
*
* In your project's .teamcity/settings.kts:
*
* ```kotlin
* import jetbrains.buildServer.configs.kotlin.*
* import templates.PhpDockerBuildTemplate
*
* version = "2024.07"
*
* project {
* buildType(Build)
* }
*
* object Build : PhpDockerBuildTemplate({
* backendDir = "backend"
* enableCs = true
* enableAnalyse = true
* composerAuth = """
* {
* "http-basic": {
* "nexus.dot-dot.ru": {
* "username": "%env.NEXUS_USER%",
* "password": "%env.NEXUS_PASSWORD%"
* }
* }
* }
* """.trimIndent()
* })
* ```
*/
object ExamplePhpBuild : PhpDockerBuildTemplate({
// Registry settings
registry = "registry.dot-dot.ru"
dockerRegistryId = "PROJECT_EXT_2"
// PHP settings
backendDir = "backend"
enableCs = true
enableAnalyse = true
// Composer auth for private packages
composerAuth = """
{
"http-basic": {
"nexus.dot-dot.ru": {
"username": "ddadmin",
"password": "secret"
}
}
}
""".trimIndent()
// Docker settings
dockerfile = "Dockerfile"
tag = "%teamcity.build.branch%"
nexusHost = "nexus.dot-dot.ru"
nexusIp = "192.168.100.110"
// Gitea settings
giteaUrl = "https://gitea.dot-dot.ru/api/v1"
giteaTokenId = "credentialsJSON:gitea-token"
// Triggers
enableVcsTrigger = true
})

View File

@ -0,0 +1,19 @@
package examples
import templates.DockerBuildTemplate
/**
* Example: Simple Docker build without PHP-specific features
*
* Use this for non-PHP projects that just need Docker build + push
*/
object ExampleSimpleBuild : DockerBuildTemplate({
registry = "registry.dot-dot.ru"
dockerfile = "Dockerfile"
tag = "%teamcity.build.branch%"
buildArgs = "--pull"
extraHosts = listOf("nexus.dot-dot.ru:192.168.100.110")
dockerRegistryId = "PROJECT_EXT_2"
giteaUrl = "https://gitea.dot-dot.ru/api/v1"
enableVcsTrigger = true
})

88
.teamcity/pom.xml vendored Normal file
View File

@ -0,0 +1,88 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>ru.dot-dot.ci</groupId>
<artifactId>ci-templates</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<kotlin.version>1.9.10</kotlin.version>
<teamcity.dsl.version>2024.07</teamcity.dsl.version>
</properties>
<repositories>
<repository>
<id>jetbrains-all</id>
<url>https://download.jetbrains.com/teamcity-repository</url>
</repository>
<repository>
<id>mavenCentral</id>
<url>https://repo1.maven.org/maven2/</url>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>jetbrains-all</id>
<url>https://download.jetbrains.com/teamcity-repository</url>
</pluginRepository>
</pluginRepositories>
<dependencies>
<dependency>
<groupId>org.jetbrains.teamcity</groupId>
<artifactId>configs-dsl-kotlin-latest</artifactId>
<version>${teamcity.dsl.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.jetbrains.teamcity</groupId>
<artifactId>configs-dsl-kotlin-plugins-latest</artifactId>
<version>1.0-SNAPSHOT</version>
<type>pom</type>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib-jdk8</artifactId>
<version>${kotlin.version}</version>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-script-runtime</artifactId>
<version>${kotlin.version}</version>
</dependency>
</dependencies>
<build>
<sourceDirectory>.</sourceDirectory>
<plugins>
<plugin>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-plugin</artifactId>
<version>${kotlin.version}</version>
<executions>
<execution>
<id>compile</id>
<phase>compile</phase>
<goals>
<goal>compile</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.jetbrains.teamcity</groupId>
<artifactId>teamcity-configs-maven-plugin</artifactId>
<version>${teamcity.dsl.version}</version>
<configuration>
<format>kotlin</format>
<dstDir>target/generated-configs</dstDir>
</configuration>
</plugin>
</plugins>
</build>
</project>

7
.teamcity/settings.kts vendored Normal file
View File

@ -0,0 +1,7 @@
import jetbrains.buildServer.configs.kotlin.*
version = "2024.07"
project {
description = "CI Templates - reusable TeamCity DSL configurations"
}

View File

@ -0,0 +1,134 @@
package templates
import jetbrains.buildServer.configs.kotlin.*
import jetbrains.buildServer.configs.kotlin.buildFeatures.commitStatusPublisher
import jetbrains.buildServer.configs.kotlin.buildFeatures.dockerRegistryConnections
import jetbrains.buildServer.configs.kotlin.buildSteps.DockerCommandStep
import jetbrains.buildServer.configs.kotlin.buildSteps.dockerCommand
import jetbrains.buildServer.configs.kotlin.triggers.vcs
/**
* Configuration for Docker build template
*/
open class DockerBuildConfig {
/** Docker registry URL (without protocol) */
var registry: String = "registry.dot-dot.ru"
/** Path to Dockerfile relative to repo root */
var dockerfile: String = "Dockerfile"
/** Image tag expression, supports TeamCity parameters */
var tag: String = "%teamcity.build.branch%"
/** Additional docker build arguments */
var buildArgs: String = "--pull"
/** Docker registry connection ID in TeamCity */
var dockerRegistryId: String = "PROJECT_EXT_2"
/** Extra hosts for docker build (host:ip format) */
var extraHosts: List<String> = listOf()
/** Enable VCS trigger */
var enableVcsTrigger: Boolean = true
/** Branch filter for VCS trigger (e.g., "+:*" for all branches, "+:refs/tags/*" for tags) */
var branchFilter: String = ""
/** Enable trigger on tags */
var triggerOnTags: Boolean = false
/** Gitea API URL for commit status */
var giteaUrl: String = "https://gitea.dot-dot.ru/api/v1"
/** Gitea token credential ID */
var giteaTokenId: String = "credentialsJSON:gitea-token"
}
/**
* Template for building and pushing Docker images
*
* Usage:
* ```
* object Build : DockerBuildTemplate({
* registry = "registry.dot-dot.ru"
* dockerfile = "Dockerfile"
* extraHosts = listOf("nexus.dot-dot.ru:192.168.100.110")
* })
* ```
*/
open class DockerBuildTemplate(
configure: DockerBuildConfig.() -> Unit = {}
) : BuildType() {
protected val config = DockerBuildConfig().apply(configure)
init {
name = "build"
allowExternalStatus = true
steps {
dockerCommand {
name = "build image"
id = "build_image"
commandType = build {
source = file {
path = config.dockerfile
}
platform = DockerCommandStep.ImagePlatform.Linux
namesAndTags = "${config.registry}/%env.TEAMCITY_PROJECT_NAME%:${config.tag}"
commandArgs = buildDockerArgs()
}
}
dockerCommand {
name = "push to registry"
id = "push_to_registry"
commandType = push {
namesAndTags = "${config.registry}/%env.TEAMCITY_PROJECT_NAME%:${config.tag}"
}
}
}
if (config.enableVcsTrigger) {
triggers {
vcs {
// Build branch filter
val filters = mutableListOf<String>()
if (config.branchFilter.isNotEmpty()) {
filters.add(config.branchFilter)
}
if (config.triggerOnTags) {
filters.add("+:refs/tags/*")
}
if (filters.isNotEmpty()) {
branchFilter = filters.joinToString("\n")
}
}
}
}
features {
dockerRegistryConnections {
loginToRegistry = on {
dockerRegistryId = config.dockerRegistryId
}
}
commitStatusPublisher {
publisher = github {
githubUrl = config.giteaUrl
authType = personalToken {
token = config.giteaTokenId
}
}
}
}
}
private fun buildDockerArgs(): String {
val args = mutableListOf(config.buildArgs)
config.extraHosts.forEach { host ->
args.add("--add-host=$host")
}
return args.joinToString(" ")
}
}

View File

@ -0,0 +1,169 @@
package templates
import jetbrains.buildServer.configs.kotlin.*
import jetbrains.buildServer.configs.kotlin.buildFeatures.commitStatusPublisher
import jetbrains.buildServer.configs.kotlin.buildFeatures.dockerRegistryConnections
import jetbrains.buildServer.configs.kotlin.buildSteps.DockerCommandStep
import jetbrains.buildServer.configs.kotlin.buildSteps.dockerCommand
import jetbrains.buildServer.configs.kotlin.buildSteps.script
import jetbrains.buildServer.configs.kotlin.triggers.vcs
/**
* Configuration for PHP Docker build template
*/
open class PhpDockerBuildConfig : DockerBuildConfig() {
/** Path to backend directory */
var backendDir: String = "backend"
/** CI image for running linters/analysis */
var ciImage: String? = null
/** Enable composer code style check */
var enableCs: Boolean = false
/** Enable composer static analysis */
var enableAnalyse: Boolean = false
/** Composer auth.json content (for private packages) */
var composerAuth: String? = null
/** Nexus host for private packages */
var nexusHost: String = "nexus.dot-dot.ru"
/** Nexus IP for /etc/hosts */
var nexusIp: String = "192.168.100.110"
}
/**
* Template for PHP projects with Docker build
*
* Usage:
* ```
* object Build : PhpDockerBuildTemplate({
* backendDir = "backend"
* enableCs = true
* enableAnalyse = true
* composerAuth = """{"http-basic": {"nexus.dot-dot.ru": {"username": "user", "password": "pass"}}}"""
* extraHosts = listOf("nexus.dot-dot.ru:192.168.100.110")
* })
* ```
*/
open class PhpDockerBuildTemplate(
configure: PhpDockerBuildConfig.() -> Unit = {}
) : BuildType() {
protected val phpConfig = PhpDockerBuildConfig().apply(configure)
init {
name = "build"
allowExternalStatus = true
steps {
// Create auth.json if configured
phpConfig.composerAuth?.let { auth ->
script {
name = "create auth.json"
id = "create_auth_json"
scriptContent = """
echo '$auth' > ${phpConfig.backendDir}/auth.json
""".trimIndent()
}
}
// CI image for linting
val ciImg = phpConfig.ciImage ?: "${phpConfig.registry}/%env.TEAMCITY_PROJECT_NAME%-ci:latest"
val dockerRunArgs = buildDockerRunArgs()
// Code style check
if (phpConfig.enableCs) {
dockerCommand {
name = "composer cs"
id = "composer_cs"
commandType = other {
subCommand = "run"
commandArgs = """
$dockerRunArgs
-v %system.teamcity.build.workingDir%/${phpConfig.backendDir}:/application
-w /application $ciImg /usr/local/bin/composer cs
""".trimIndent()
}
}
}
// Static analysis
if (phpConfig.enableAnalyse) {
dockerCommand {
name = "composer analyse"
id = "composer_analyse"
commandType = other {
subCommand = "run"
commandArgs = """
$dockerRunArgs
-v %system.teamcity.build.workingDir%/${phpConfig.backendDir}:/application
-w /application $ciImg /usr/local/bin/composer analyse-ci
""".trimIndent()
}
}
}
// Build image
dockerCommand {
name = "build image"
id = "build_image"
commandType = build {
source = file {
path = phpConfig.dockerfile
}
platform = DockerCommandStep.ImagePlatform.Linux
namesAndTags = "${phpConfig.registry}/%env.TEAMCITY_PROJECT_NAME%:${phpConfig.tag}"
commandArgs = buildDockerBuildArgs()
}
}
// Push to registry
dockerCommand {
name = "push to registry"
id = "push_to_registry"
commandType = push {
namesAndTags = "${phpConfig.registry}/%env.TEAMCITY_PROJECT_NAME%:${phpConfig.tag}"
}
}
}
if (phpConfig.enableVcsTrigger) {
triggers {
vcs {}
}
}
features {
dockerRegistryConnections {
loginToRegistry = on {
dockerRegistryId = phpConfig.dockerRegistryId
}
}
commitStatusPublisher {
publisher = github {
githubUrl = phpConfig.giteaUrl
authType = personalToken {
token = phpConfig.giteaTokenId
}
}
}
}
}
private fun buildDockerRunArgs(): String {
val args = mutableListOf("--add-host=${phpConfig.nexusHost}:${phpConfig.nexusIp}", "-i", "--rm")
return args.joinToString(" ")
}
private fun buildDockerBuildArgs(): String {
val args = mutableListOf(phpConfig.buildArgs)
args.add("--add-host=${phpConfig.nexusHost}:${phpConfig.nexusIp}")
phpConfig.extraHosts.forEach { host ->
args.add("--add-host=$host")
}
return args.joinToString(" ")
}
}

87
README.md Normal file
View File

@ -0,0 +1,87 @@
# CI Templates
Переиспользуемые TeamCity DSL шаблоны для проектов dot-dot.ru.
## Подключение к проекту
### 1. Добавить зависимость в pom.xml вашего проекта
```xml
<dependency>
<groupId>ru.dot-dot.ci</groupId>
<artifactId>ci-templates</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
```
### 2. Настроить Versioned Settings в TeamCity
В настройках проекта TeamCity → Versioned Settings:
- Synchronization enabled: **Yes**
- Project settings VCS root: выбрать VCS root вашего проекта
- Settings format: **Kotlin**
- Allow editing project settings via UI: по желанию
Добавить дополнительный VCS root для ci-templates:
- VCS URL: `git@gitea.dot-dot.ru:dot-dot/ci-templates.git`
- Checkout rules: `+:.teamcity => .teamcity/ci-templates`
## Доступные шаблоны
### DockerBuildTemplate
Базовый шаблон для сборки и публикации Docker образов.
```kotlin
import templates.DockerBuildTemplate
object Build : DockerBuildTemplate({
registry = "registry.dot-dot.ru"
dockerfile = "Dockerfile"
tag = "%teamcity.build.branch%"
extraHosts = listOf("nexus.dot-dot.ru:192.168.100.110")
})
```
### PhpDockerBuildTemplate
Шаблон для PHP проектов с поддержкой Composer, линтеров и анализаторов.
```kotlin
import templates.PhpDockerBuildTemplate
object Build : PhpDockerBuildTemplate({
backendDir = "backend"
enableCs = true
enableAnalyse = true
composerAuth = """{"http-basic": {"nexus.dot-dot.ru": {"username": "%env.NEXUS_USER%", "password": "%env.NEXUS_PASSWORD%"}}}"""
})
```
## Параметры
### DockerBuildConfig
| Параметр | По умолчанию | Описание |
|----------|--------------|----------|
| registry | registry.dot-dot.ru | Docker registry URL |
| dockerfile | Dockerfile | Путь к Dockerfile |
| tag | %teamcity.build.branch% | Тег образа |
| buildArgs | --pull | Аргументы docker build |
| dockerRegistryId | PROJECT_EXT_2 | ID подключения к registry в TeamCity |
| extraHosts | [] | Дополнительные хосты (host:ip) |
| enableVcsTrigger | true | Включить VCS триггер |
| giteaUrl | https://gitea.dot-dot.ru/api/v1 | URL Gitea API |
| giteaTokenId | credentialsJSON:gitea-token | ID токена Gitea |
### PhpDockerBuildConfig (наследует DockerBuildConfig)
| Параметр | По умолчанию | Описание |
|----------|--------------|----------|
| backendDir | backend | Директория с PHP кодом |
| ciImage | null | CI образ для линтеров (по умолчанию {project}-ci:latest) |
| enableCs | false | Включить проверку code style |
| enableAnalyse | false | Включить статический анализ |
| composerAuth | null | Содержимое auth.json |
| nexusHost | nexus.dot-dot.ru | Хост Nexus |
| nexusIp | 192.168.100.110 | IP Nexus для /etc/hosts |