Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ability to test feature branches in TeamCity #15246

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .changelog/8388.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
```release-note:none
```
15 changes: 12 additions & 3 deletions .teamcity/components/build_config_package.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,14 @@
import jetbrains.buildServer.configs.kotlin.*
import jetbrains.buildServer.configs.kotlin.AbsoluteId

class packageDetails(name: String, displayName: String, environment: String) {
class packageDetails(name: String, displayName: String, environment: String, branchRef: String) {
val packageName = name
val displayName = displayName
val environment = environment
val branchRef = branchRef

// buildConfiguration returns a BuildType for a service package
// For BuildType docs, see https://teamcity.jetbrains.com/app/dsl-documentation/root/build-type/index.html
fun buildConfiguration(providerName : String, path : String, manualVcsRoot: AbsoluteId, nightlyTestsEnabled: Boolean, startHour: Int, parallelism: Int, daysOfWeek: String, daysOfMonth: String) : BuildType {
return BuildType {
// TC needs a consistent ID for dynamically generated packages
Expand Down Expand Up @@ -50,12 +53,18 @@ class packageDetails(name: String, displayName: String, environment: String) {
}

triggers {
RunNightly(nightlyTestsEnabled, startHour, daysOfWeek, daysOfMonth)
RunNightly(nightlyTestsEnabled, startHour, daysOfWeek, daysOfMonth, branchRef)
}
}
}

fun uniqueID(provider : String) : String {
return "%s_SERVICE_%s_%s".format(provider.replace("-", "").toUpperCase(), environment.toUpperCase(), packageName.toUpperCase())
// Replacing chars can be necessary, due to limitations on IDs
// "ID should start with a latin letter and contain only latin letters, digits and underscores (at most 225 characters)."
var pv = provider.replace("-", "").toUpperCase()
var env = environment.toUpperCase().replace("-", "").replace(".", "").toUpperCase()
var pkg = packageName.toUpperCase()

return "%s_SERVICE_%s_%s".format(pv, env, pkg)
}
}
3 changes: 3 additions & 0 deletions .teamcity/components/build_google.kt
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ class ClientConfiguration(var custId: String,
val identityUser : String ) {
}

// ParametrizedWithType.ConfigureGoogleSpecificTestParameters allows build configs to be created
// with the environment variables needed to configure the provider and/or configure test code.
// Extension of ParametrizedWithType. For docs, see https://teamcity.jetbrains.com/app/dsl-documentation/root/parametrized-with-type/index.html
fun ParametrizedWithType.ConfigureGoogleSpecificTestParameters(config: ClientConfiguration) {
hiddenPasswordVariable("env.GOOGLE_CUST_ID", config.custId, "The ID of the Google Identity Customer")
hiddenPasswordVariable("env.GOOGLE_ORG", config.org, "The Google Organization Id")
Expand Down
15 changes: 13 additions & 2 deletions .teamcity/components/generated/build_components.kt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,15 @@ import jetbrains.buildServer.configs.kotlin.triggers.schedule
//
// Until that changes, we'll continue to use `teamcity-go-test` to run
// each test individually

// NOTE: this file includes Extensions of Kotlin DSL classes
// See
// - BuildFeatures https://teamcity.jetbrains.com/app/dsl-documentation/root/build-features/index.html
// - BuildSteps https://teamcity.jetbrains.com/app/dsl-documentation/root/build-steps/index.html
// - ParametrizedWithType https://teamcity.jetbrains.com/app/dsl-documentation/root/parametrized-with-type/index.html
// - Triggers https://teamcity.jetbrains.com/app/dsl-documentation/root/triggers/index.html


const val useTeamCityGoTest = false

fun BuildFeatures.Golang() {
Expand Down Expand Up @@ -120,10 +129,12 @@ fun ParametrizedWithType.hiddenPasswordVariable(name: String, value: String, des
password(name, value, "", description, ParameterDisplay.HIDDEN)
}

fun Triggers.RunNightly(nightlyTestsEnabled: Boolean, startHour: Int, daysOfWeek: String, daysOfMonth: String) {
fun Triggers.RunNightly(nightlyTestsEnabled: Boolean, startHour: Int, daysOfWeek: String, daysOfMonth: String, branchRef: String) {
val filter = "+:" + branchRef // e.g. "+:refs/heads/main"

schedule{
enabled = nightlyTestsEnabled
branchFilter = "+:refs/heads/main"
branchFilter = filter

schedulingPolicy = cron {
hours = startHour.toString()
Expand Down
35 changes: 27 additions & 8 deletions .teamcity/components/generated/project.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,28 +6,33 @@ import jetbrains.buildServer.configs.kotlin.AbsoluteId

const val providerName = "google"

fun Google(environment: String, manualVcsRoot: AbsoluteId, configuration: ClientConfiguration) : Project {
// Google returns an instance of Project,
// which has multiple build configurations defined within it.
// See https://teamcity.jetbrains.com/app/dsl-documentation/root/project/index.html
fun Google(environment: String, manualVcsRoot: AbsoluteId, branchRef: String, configuration: ClientConfiguration) : Project {
return Project{

var buildConfigs = buildConfigurationsForPackages(packages, providerName, "google", environment, manualVcsRoot, configuration)
var buildConfigs = buildConfigurationsForPackages(packages, providerName, "google", environment, manualVcsRoot, branchRef, configuration)
buildConfigs.forEach { buildConfiguration ->
buildType(buildConfiguration)
}
}
}

fun buildConfigurationsForPackages(packages: Map<String, String>, providerName : String, path : String, environment: String, manualVcsRoot: AbsoluteId, config: ClientConfiguration): List<BuildType> {
fun buildConfigurationsForPackages(packages: Map<String, String>, providerName : String, path : String, environment: String, manualVcsRoot: AbsoluteId, branchRef: String, config: ClientConfiguration): List<BuildType> {
var list = ArrayList<BuildType>()

packages.forEach { (packageName, displayName) ->
if (packageName == "services") {
var serviceList = buildConfigurationsForPackages(services, providerName, path+"/"+packageName, environment, manualVcsRoot, config)
// `services` is a folder containing packages, not a package itself; call buildConfigurationsForPackages to iterate through directories found within `services`
var serviceList = buildConfigurationsForPackages(services, providerName, path+"/"+packageName, environment, manualVcsRoot, branchRef, config)
list.addAll(serviceList)
} else {
var defaultTestConfig = testConfiguration()
// other folders assumed to be packages
var testConfig = testConfiguration(environment)

var pkg = packageDetails(packageName, displayName, environment)
var buildConfig = pkg.buildConfiguration(providerName, path, manualVcsRoot, true, defaultTestConfig.startHour, defaultTestConfig.parallelism, defaultTestConfig.daysOfWeek, defaultTestConfig.daysOfMonth)
var pkg = packageDetails(packageName, displayName, environment, branchRef)
var buildConfig = pkg.buildConfiguration(providerName, path, manualVcsRoot, true, testConfig.startHour, testConfig.parallelism, testConfig.daysOfWeek, testConfig.daysOfMonth)

buildConfig.params.ConfigureGoogleSpecificTestParameters(config)

Expand All @@ -38,9 +43,23 @@ fun buildConfigurationsForPackages(packages: Map<String, String>, providerName :
return list
}

class testConfiguration(parallelism: Int = defaultParallelism, startHour: Int = defaultStartHour, daysOfWeek: String = defaultDaysOfWeek, daysOfMonth: String = defaultDaysOfMonth) {
class testConfiguration(environment: String, parallelism: Int = defaultParallelism, startHour: Int = defaultStartHour, daysOfWeek: String = defaultDaysOfWeek, daysOfMonth: String = defaultDaysOfMonth) {

// Default values are present if init doesn't change them
var parallelism = parallelism
var startHour = startHour
var daysOfWeek = daysOfWeek
var daysOfMonth = daysOfMonth

init {
// If the environment parameter is set to the value of MAJOR_RELEASE_TESTING,
// change the days of week to the day for v5.0.0 feature branch testing
if (environment == MAJOR_RELEASE_TESTING) {
this.parallelism = parallelism
this.startHour = startHour
this.daysOfWeek = "4" // Thursday for GA
this.daysOfMonth = daysOfMonth
}
}

}
20 changes: 20 additions & 0 deletions .teamcity/components/generated/settings.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// this file is auto-generated with mmv1, any changes made here will be overwritten

// specifies the default hour (UTC) at which tests should be triggered, if enabled
var defaultStartHour = 4

// specifies the default level of parallelism per-service-package
var defaultParallelism = 12

// specifies the default version of Terraform Core which should be used for testing
var defaultTerraformCoreVersion = "1.2.5"

// This represents a cron view of days of the week
const val defaultDaysOfWeek = "1-3,5-7" // All nights except Thursday for GA; feature branch testing happens on Thursdays

// Cron value for any day of month
const val defaultDaysOfMonth = "*"

// Values that `environment` parameter is checked against,
// when deciding to change how TeamCity objects are configured
const val MAJOR_RELEASE_TESTING = "major-release-5.0.0-testing"
16 changes: 0 additions & 16 deletions .teamcity/components/settings.kt

This file was deleted.

2 changes: 1 addition & 1 deletion .teamcity/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@
<format>kotlin</format>
<dstDir>target/generated-configs</dstDir>
<contextParameters>
<environment>public</environment>
<environment>default</environment>
</contextParameters>
</configuration>
</plugin>
Expand Down
15 changes: 13 additions & 2 deletions .teamcity/settings.kts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ import jetbrains.buildServer.configs.kotlin.*

version = "2023.05"

// The code below pulls context parameters from the TeamCity project.
// Context parameters aren't stored in VCS, and are managed manually.
// Due to this, the code needs to explicitly pull in values via the DSL and pass the values into other code.
// For DslContext docs, see https://teamcity.jetbrains.com/app/dsl-documentation/root/dsl-context/index.html

// Values of these parameters are used to set ENVs needed for acceptance tests within the build configurations.
var custId = DslContext.getParameter("custId", "")
var org = DslContext.getParameter("org", "")
var org2 = DslContext.getParameter("org2", "")
Expand All @@ -19,14 +25,19 @@ var region = DslContext.getParameter("region", "")
var serviceAccount = DslContext.getParameter("serviceAccount", "")
var zone = DslContext.getParameter("zone", "")
var credentials = DslContext.getParameter("credentials", "")
var environment = DslContext.getParameter("environment", "public")
var firestoreProject = DslContext.getParameter("firestoreProject", "")
var identityUser = DslContext.getParameter("identityUser", "")

// Get details of the VCS Root that's manually made when VCS is first
// connected to the Project in TeamCity
var manualVcsRoot = DslContext.settingsRootId

// Values of these parameters change configuration code behaviour.
var environment = DslContext.getParameter("environment", "default")
var branchRef = DslContext.getParameter("branch", "refs/heads/main")

var clientConfig = ClientConfiguration(custId, org, org2, billingAccount, billingAccount2, masterBillingAccount, credentials, project, orgDomain, projectNumber, region, serviceAccount, zone, firestoreProject, identityUser)

project(Google(environment, manualVcsRoot, clientConfig))
// This is the entry point of the code in .teamcity/
// See https://teamcity.jetbrains.com/app/dsl-documentation/root/project.html
project(Google(environment, manualVcsRoot, branchRef, clientConfig))
6 changes: 3 additions & 3 deletions .teamcity/tests/generated/configuration.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,15 @@ import useTeamCityGoTest
class ConfigurationTests {
@Test
fun buildShouldFailOnError() {
val project = Google("public", TestVcsRootId(), TestConfiguration())
val project = Google("public", TestVcsRootId(), "refs/heads/main", TestConfiguration())
project.buildTypes.forEach { bt ->
assertTrue("Build '${bt.id}' should fail on errors!", bt.failureConditions.errorMessage)
}
}

@Test
fun buildShouldHaveGoTestFeature() {
val project = Google("public", TestVcsRootId(), TestConfiguration())
val project = Google("public", TestVcsRootId(), "refs/heads/main",TestConfiguration())
project.buildTypes.forEach{ bt ->
var exists = false
bt.features.items.forEach { f ->
Expand All @@ -35,7 +35,7 @@ class ConfigurationTests {

@Test
fun buildShouldHaveTrigger() {
val project = Google("public", TestVcsRootId(), TestConfiguration())
val project = Google("public", TestVcsRootId(), "refs/heads/main", TestConfiguration())
var exists = false
project.buildTypes.forEach{ bt ->
bt.triggers.items.forEach { t ->
Expand Down
2 changes: 1 addition & 1 deletion .teamcity/tests/generated/vcs_roots.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import org.junit.Test
class VcsTests {
@Test
fun buildsHaveCleanCheckOut() {
val project = Google("public", TestVcsRootId(), TestConfiguration())
val project = Google("public", TestVcsRootId(), "refs/heads/main", TestConfiguration())
project.buildTypes.forEach { bt ->
assertTrue("Build '${bt.id}' doesn't use clean checkout", bt.vcs.cleanCheckout)
}
Expand Down