From 1f9b0cec9c328ed45b53ce6404b4d4bbb1033a6e Mon Sep 17 00:00:00 2001 From: Modular Magician Date: Fri, 21 Jul 2023 11:50:59 +0000 Subject: [PATCH] Add ability to test feature branches in TeamCity (#8388) * Add comments, link to TeamCity Kotlin DSL docs * Change default value of environment from "public" to "default" This is because Google provider has no public TeamCity resources * Enable different default cron values for `ga`/`beta` downstreams * Enable non-default cron values to be used, based on value of `environment` parameter in TeamCity * Add ability to make VCS root and nightly trigger use non-main branch * Update tests to reflect how entrypoint func requires `branch` parameter now * Update `MAJOR_RELEASE_TESTING` value, and update code to handle unsupported chars * Update `uniqueID` method to uppercase the environment value Signed-off-by: Modular Magician --- .changelog/8388.txt | 2 ++ .teamcity/components/build_config_package.kt | 15 ++++++-- .teamcity/components/build_google.kt | 3 ++ .../components/generated/build_components.kt | 15 ++++++-- .teamcity/components/generated/project.kt | 35 ++++++++++++++----- .teamcity/components/generated/settings.kt | 20 +++++++++++ .teamcity/components/settings.kt | 16 --------- .teamcity/pom.xml | 2 +- .teamcity/settings.kts | 15 ++++++-- .teamcity/tests/generated/configuration.kt | 6 ++-- .teamcity/tests/generated/vcs_roots.kt | 2 +- 11 files changed, 95 insertions(+), 36 deletions(-) create mode 100644 .changelog/8388.txt create mode 100644 .teamcity/components/generated/settings.kt delete mode 100644 .teamcity/components/settings.kt diff --git a/.changelog/8388.txt b/.changelog/8388.txt new file mode 100644 index 00000000000..e4e22fc1e47 --- /dev/null +++ b/.changelog/8388.txt @@ -0,0 +1,2 @@ +```release-note:none +``` diff --git a/.teamcity/components/build_config_package.kt b/.teamcity/components/build_config_package.kt index e5e0993449e..d6136cb22dc 100644 --- a/.teamcity/components/build_config_package.kt +++ b/.teamcity/components/build_config_package.kt @@ -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 @@ -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) } } diff --git a/.teamcity/components/build_google.kt b/.teamcity/components/build_google.kt index 4baaddfc522..d0209eb36cd 100644 --- a/.teamcity/components/build_google.kt +++ b/.teamcity/components/build_google.kt @@ -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") diff --git a/.teamcity/components/generated/build_components.kt b/.teamcity/components/generated/build_components.kt index af12d7cd171..e10f39ca226 100644 --- a/.teamcity/components/generated/build_components.kt +++ b/.teamcity/components/generated/build_components.kt @@ -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() { @@ -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() diff --git a/.teamcity/components/generated/project.kt b/.teamcity/components/generated/project.kt index 3742f4b1fdd..3d177b2e4eb 100644 --- a/.teamcity/components/generated/project.kt +++ b/.teamcity/components/generated/project.kt @@ -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, providerName : String, path : String, environment: String, manualVcsRoot: AbsoluteId, config: ClientConfiguration): List { +fun buildConfigurationsForPackages(packages: Map, providerName : String, path : String, environment: String, manualVcsRoot: AbsoluteId, branchRef: String, config: ClientConfiguration): List { var list = ArrayList() 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) @@ -38,9 +43,23 @@ fun buildConfigurationsForPackages(packages: Map, 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 + } + } + } diff --git a/.teamcity/components/generated/settings.kt b/.teamcity/components/generated/settings.kt new file mode 100644 index 00000000000..2e165c68e53 --- /dev/null +++ b/.teamcity/components/generated/settings.kt @@ -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" diff --git a/.teamcity/components/settings.kt b/.teamcity/components/settings.kt deleted file mode 100644 index 44f98ded7b4..00000000000 --- a/.teamcity/components/settings.kt +++ /dev/null @@ -1,16 +0,0 @@ -// this file is copied from 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, Monday - Friday. -const val defaultDaysOfWeek = "*" - -// Cron value for any day of month -const val defaultDaysOfMonth = "*" diff --git a/.teamcity/pom.xml b/.teamcity/pom.xml index 841f0c188a9..20e58b60413 100644 --- a/.teamcity/pom.xml +++ b/.teamcity/pom.xml @@ -95,7 +95,7 @@ kotlin target/generated-configs - public + default diff --git a/.teamcity/settings.kts b/.teamcity/settings.kts index 746a124cad9..03425801063 100644 --- a/.teamcity/settings.kts +++ b/.teamcity/settings.kts @@ -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", "") @@ -19,7 +25,6 @@ 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", "") @@ -27,6 +32,12 @@ var identityUser = DslContext.getParameter("identityUser", "") // 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)) diff --git a/.teamcity/tests/generated/configuration.kt b/.teamcity/tests/generated/configuration.kt index f382ffddf66..2fdffdd577c 100644 --- a/.teamcity/tests/generated/configuration.kt +++ b/.teamcity/tests/generated/configuration.kt @@ -10,7 +10,7 @@ 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) } @@ -18,7 +18,7 @@ class ConfigurationTests { @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 -> @@ -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 -> diff --git a/.teamcity/tests/generated/vcs_roots.kt b/.teamcity/tests/generated/vcs_roots.kt index e2f5fb1c422..fbe87e76e4b 100644 --- a/.teamcity/tests/generated/vcs_roots.kt +++ b/.teamcity/tests/generated/vcs_roots.kt @@ -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) }