From 87a908fe34a35a04ee12b6e43fee3613e56e60a4 Mon Sep 17 00:00:00 2001 From: The Magician Date: Thu, 24 Aug 2023 13:41:03 -0400 Subject: [PATCH] TeamCity: Usability improvements : tag builds to distinguish nightly builds vs ad hoc builds, add project descriptions (#8685) (#15613) * Add ability to tag TeamCity builds based on whether they're automated or ad-hoc. Nightly builds tagged with the date. * Add ability to set project descriptions using a context parameter * Refactor how date is formatted, to avoid problem where TeamCity interprets `%Y-%` as interpolating a `Y-` parameter * Remove use of `TRIGGERED_BY`; value in build didn't match UI and isn't useful * Update tag for nightly test builds to be static/consistent Signed-off-by: Modular Magician --- .changelog/8685.txt | 3 +++ .teamcity/components/build_config_package.kt | 1 + .../components/generated/build_components.kt | 23 +++++++++++++++++++ .teamcity/components/generated/project.kt | 5 ++-- .teamcity/components/sweepers.kt | 1 + .teamcity/settings.kts | 3 ++- .teamcity/tests/generated/configuration.kt | 6 ++--- .teamcity/tests/generated/vcs_roots.kt | 2 +- 8 files changed, 37 insertions(+), 7 deletions(-) create mode 100644 .changelog/8685.txt diff --git a/.changelog/8685.txt b/.changelog/8685.txt new file mode 100644 index 00000000000..8ec013c0699 --- /dev/null +++ b/.changelog/8685.txt @@ -0,0 +1,3 @@ +```release-note:none + +``` diff --git a/.teamcity/components/build_config_package.kt b/.teamcity/components/build_config_package.kt index c443f1373ff..d46cf0c6e52 100644 --- a/.teamcity/components/build_config_package.kt +++ b/.teamcity/components/build_config_package.kt @@ -35,6 +35,7 @@ class packageDetails(packageName: String, displayName: String, providerName: Str steps { SetGitCommitBuildId() + TagBuildToIndicatePurpose() ConfigureGoEnv() DownloadTerraformBinary() RunAcceptanceTests() diff --git a/.teamcity/components/generated/build_components.kt b/.teamcity/components/generated/build_components.kt index 7478d5c6e75..5924646f9f2 100644 --- a/.teamcity/components/generated/build_components.kt +++ b/.teamcity/components/generated/build_components.kt @@ -58,6 +58,29 @@ fun BuildSteps.SetGitCommitBuildId() { }) } +fun BuildSteps.TagBuildToIndicatePurpose() { + step(ScriptBuildStep { + name = "Set build tag to indicate if build is run automatically or manually triggered" + scriptContent = """ + #!/bin/bash + TRIGGERED_BY_USERNAME=%teamcity.build.triggeredBy.username% + + if [[ "${'$'}TRIGGERED_BY_USERNAME" = "n/a" ]] ; then + echo "Build was triggered as part of automated testing. We know this because the `triggeredBy.username` value was `n/a`, value: ${'$'}{TRIGGERED_BY_USERNAME}" + TAG="nightly-test" + echo "##teamcity[addBuildTag '${'$'}{TAG}']" + else + echo "Build wasn't triggered as part of automated testing. We know this because the `triggeredBy.username` value was not `n/a`, value: ${'$'}{TRIGGERED_BY_USERNAME}" + TAG="one-off-build" + echo "##teamcity[addBuildTag '${'$'}{TAG}']" + fi + """.trimIndent() + // ${'$'} is required to allow creating a script in TeamCity that contains + // parts like ${GIT_HASH_SHORT} without having Kotlin syntax issues. For more info see: + // https://youtrack.jetbrains.com/issue/KT-2425/Provide-a-way-for-escaping-the-dollar-sign-symbol-in-multiline-strings-and-string-templates + }) +} + fun BuildSteps.DownloadTerraformBinary() { // https://releases.hashicorp.com/terraform/0.12.28/terraform_0.12.28_linux_amd64.zip var terraformUrl = "https://releases.hashicorp.com/terraform/%env.TERRAFORM_CORE_VERSION%/terraform_%env.TERRAFORM_CORE_VERSION%_linux_amd64.zip" diff --git a/.teamcity/components/generated/project.kt b/.teamcity/components/generated/project.kt index ffc0a5c5d65..2d6a46d3b7b 100644 --- a/.teamcity/components/generated/project.kt +++ b/.teamcity/components/generated/project.kt @@ -12,7 +12,7 @@ const val providerName = "google" // 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 { +fun Google(environment: String, projDescription: String, manualVcsRoot: AbsoluteId, branchRef: String, configuration: ClientConfiguration) : Project { // Create build configs for each package defined in packages.kt and services.kt files val allPackages = packages + services @@ -28,9 +28,10 @@ fun Google(environment: String, manualVcsRoot: AbsoluteId, branchRef: String, co postSweeperConfig.addTrigger(triggerConfig) } - return Project{ + description = projDescription + // Register build configs in the project buildType(preSweeperConfig) packageConfigs.forEach { buildConfiguration -> diff --git a/.teamcity/components/sweepers.kt b/.teamcity/components/sweepers.kt index 6f3076b5e38..9614f12b17e 100644 --- a/.teamcity/components/sweepers.kt +++ b/.teamcity/components/sweepers.kt @@ -37,6 +37,7 @@ class sweeperDetails() { steps { SetGitCommitBuildId() + TagBuildToIndicatePurpose() ConfigureGoEnv() DownloadTerraformBinary() RunSweepers(sweeperName) diff --git a/.teamcity/settings.kts b/.teamcity/settings.kts index 1fbfb387316..8402cfc7524 100644 --- a/.teamcity/settings.kts +++ b/.teamcity/settings.kts @@ -40,9 +40,10 @@ var manualVcsRoot = DslContext.settingsRootId // Values of these context parameters change configuration code behaviour. var environment = DslContext.getParameter("environment", "default") var branchRef = DslContext.getParameter("branch", "refs/heads/main") +var projDescription = DslContext.getParameter("description", "") var clientConfig = ClientConfiguration(custId, org, org2, billingAccount, billingAccount2, masterBillingAccount, credentials, project, orgDomain, projectNumber, region, serviceAccount, zone, firestoreProject, identityUser) // 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)) +project(Google(environment, projDescription, manualVcsRoot, branchRef, clientConfig)) diff --git a/.teamcity/tests/generated/configuration.kt b/.teamcity/tests/generated/configuration.kt index 0b107543ceb..eb48350b3a5 100644 --- a/.teamcity/tests/generated/configuration.kt +++ b/.teamcity/tests/generated/configuration.kt @@ -15,7 +15,7 @@ import useTeamCityGoTest class ConfigurationTests { @Test fun buildShouldFailOnError() { - val project = Google("default", testVcsRootId(), "refs/heads/main", testConfiguration()) + val project = Google("default", "description", testVcsRootId(), "refs/heads/main", testConfiguration()) project.buildTypes.forEach { bt -> assertTrue("Build '${bt.id}' should fail on errors!", bt.failureConditions.errorMessage) } @@ -23,7 +23,7 @@ class ConfigurationTests { @Test fun buildShouldHaveGoTestFeature() { - val project = Google("default", testVcsRootId(), "refs/heads/main",testConfiguration()) + val project = Google("default", "description", testVcsRootId(), "refs/heads/main", testConfiguration()) project.buildTypes.forEach{ bt -> var exists = false bt.features.items.forEach { f -> @@ -42,7 +42,7 @@ class ConfigurationTests { // Once I have the ability to run tests I'll address this - writing new tests for the new config // @Test // fun buildShouldHaveTrigger() { - // val project = Google("default", testVcsRootId(), "refs/heads/main", testConfiguration()) + // val project = Google("default", "description", 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 3bcfd323196..299f00847a4 100644 --- a/.teamcity/tests/generated/vcs_roots.kt +++ b/.teamcity/tests/generated/vcs_roots.kt @@ -14,7 +14,7 @@ import org.junit.Test class VcsTests { @Test fun buildsHaveCleanCheckOut() { - val project = Google("default", testVcsRootId(), "refs/heads/main", testConfiguration()) + val project = Google("default", "description", testVcsRootId(), "refs/heads/main", testConfiguration()) project.buildTypes.forEach { bt -> assertTrue("Build '${bt.id}' doesn't use clean checkout", bt.vcs.cleanCheckout) }