From 709fc65d10fc5a1f78067e4f647d6a645ef920e9 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Thu, 25 Apr 2024 15:03:43 +0100 Subject: [PATCH] Add a custom value for the detected CI provider Closes gh-77 --- .../gradle/BuildScanConventions.java | 70 ++++------------- .../gradle/ContinuousIntegration.java | 78 +++++++++++++++++++ .../gradle/BuildScanConventionsTests.java | 67 +++++++++++++++- 3 files changed, 156 insertions(+), 59 deletions(-) create mode 100644 src/main/java/io/spring/ge/conventions/gradle/ContinuousIntegration.java diff --git a/src/main/java/io/spring/ge/conventions/gradle/BuildScanConventions.java b/src/main/java/io/spring/ge/conventions/gradle/BuildScanConventions.java index 49f382e..fe6773d 100644 --- a/src/main/java/io/spring/ge/conventions/gradle/BuildScanConventions.java +++ b/src/main/java/io/spring/ge/conventions/gradle/BuildScanConventions.java @@ -35,10 +35,6 @@ */ class BuildScanConventions implements Action { - private static final String BAMBOO_RESULTS_ENV_VAR = "bamboo_resultsUrl"; - - private static final String CIRCLECI_BUILD_URL_ENV_VAR = "CIRCLE_BUILD_URL"; - private final DevelocityConfiguration develocity; private final ProcessRunner processRunner; @@ -64,10 +60,11 @@ public void execute(BuildScanConfiguration buildScan) { buildScan.obfuscation((obfuscation) -> obfuscation .ipAddresses((addresses) -> addresses.stream().map((address) -> "0.0.0.0").collect(Collectors.toList()))); configurePublishing(buildScan); - tagBuildScan(buildScan); + ContinuousIntegration ci = ContinuousIntegration.detect(this.env); + tagBuildScan(buildScan, ci); buildScan.background(this::addGitMetadata); - addCiMetadata(buildScan); - buildScan.getUploadInBackground().set(!isCi()); + addCiMetadata(buildScan, ci); + buildScan.getUploadInBackground().set(ci == null); buildScan.capture((settings) -> settings.getFileFingerprints().set(true)); } @@ -82,41 +79,14 @@ protected void configurePublishing(BuildScanConfiguration buildScan) { this.develocity.getServer().set("https://ge.spring.io"); } - private void tagBuildScan(BuildScanConfiguration buildScan) { - tagCiOrLocal(buildScan); + private void tagBuildScan(BuildScanConfiguration buildScan, ContinuousIntegration ci) { + tagCiOrLocal(buildScan, ci); tagJdk(buildScan); tagOperatingSystem(buildScan); } - private void tagCiOrLocal(BuildScanConfiguration buildScan) { - buildScan.tag(isCi() ? "CI" : "Local"); - } - - private boolean isCi() { - if (isBamboo() || isCircleCi() || isConcourse() || isJenkins() || isGitHubActions()) { - return true; - } - return false; - } - - private boolean isBamboo() { - return this.env.containsKey(BAMBOO_RESULTS_ENV_VAR); - } - - private boolean isCircleCi() { - return this.env.containsKey(CIRCLECI_BUILD_URL_ENV_VAR); - } - - private boolean isConcourse() { - return this.env.containsKey("CI"); - } - - private boolean isJenkins() { - return this.env.containsKey("JENKINS_URL"); - } - - private boolean isGitHubActions() { - return this.env.containsKey("GITHUB_ACTIONS"); + private void tagCiOrLocal(BuildScanConfiguration buildScan, ContinuousIntegration ci) { + buildScan.tag((ci != null) ? "CI" : "Local"); } private void tagJdk(BuildScanConfiguration buildScan) { @@ -151,25 +121,15 @@ private void addGitMetadata(BuildScanConfiguration buildScan) { }); } - private void addCiMetadata(BuildScanConfiguration buildScan) { - if (isBamboo()) { - buildScan.link("CI build", this.env.get(BAMBOO_RESULTS_ENV_VAR)); - } - else if (isJenkins()) { - String buildUrl = this.env.get("BUILD_URL"); - if (hasText(buildUrl)) { - buildScan.link("CI build", buildUrl); - } - } - else if (isCircleCi()) { - buildScan.link("CI build", this.env.get(CIRCLECI_BUILD_URL_ENV_VAR)); + private void addCiMetadata(BuildScanConfiguration buildScan, ContinuousIntegration ci) { + if (ci == null) { + return; } - else if (isGitHubActions()) { - String server = this.env.get("GITHUB_SERVER_URL"); - String repository = this.env.get("GITHUB_REPOSITORY"); - String runId = this.env.get("GITHUB_RUN_ID"); - buildScan.link("CI build", server + "/" + repository + "/actions/runs/" + runId); + String buildUrl = ci.buildUrlFrom(this.env); + if (hasText(buildUrl)) { + buildScan.link("CI build", buildUrl); } + buildScan.value("CI provider", ci.toString()); } private RunResult getBranch() { diff --git a/src/main/java/io/spring/ge/conventions/gradle/ContinuousIntegration.java b/src/main/java/io/spring/ge/conventions/gradle/ContinuousIntegration.java new file mode 100644 index 0000000..ba5aa12 --- /dev/null +++ b/src/main/java/io/spring/ge/conventions/gradle/ContinuousIntegration.java @@ -0,0 +1,78 @@ +/* + * Copyright 2024 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.spring.ge.conventions.gradle; + +import java.util.Map; +import java.util.function.Function; + +/** + * Known continuous integration environments. + * + * @author Andy Wilkinson + */ +enum ContinuousIntegration { + + BAMBOO("Bamboo", "bamboo_resultsUrl"), + + CIRCLE_CI("CircleCI", "CIRCLE_BUILD_URL"), + + CONCOURSE("Concourse", "CI", (env) -> null), + + JENKINS("Jenkins", "JENKINS_URL", (env) -> env.get("BUILD_URL")), + + GITHUB_ACTIONS("GitHub Actions", "GITHUB_ACTIONS", (env) -> { + String server = env.get("GITHUB_SERVER_URL"); + String repository = env.get("GITHUB_REPOSITORY"); + String runId = env.get("GITHUB_RUN_ID"); + return server + "/" + repository + "/actions/runs/" + runId; + }); + + private final String name; + + private final String environmentVariable; + + private final Function, String> buildUrl; + + ContinuousIntegration(String name, String environmentVariable) { + this(name, environmentVariable, (env) -> env.get(environmentVariable)); + } + + ContinuousIntegration(String name, String environmentVariable, Function, String> buildUrl) { + this.name = name; + this.environmentVariable = environmentVariable; + this.buildUrl = buildUrl; + } + + String buildUrlFrom(Map env) { + return this.buildUrl.apply(env); + } + + @Override + public String toString() { + return this.name; + } + + static ContinuousIntegration detect(Map env) { + for (ContinuousIntegration ci : values()) { + if (env.containsKey(ci.environmentVariable)) { + return ci; + } + } + return null; + } + +} diff --git a/src/test/java/io/spring/ge/conventions/gradle/BuildScanConventionsTests.java b/src/test/java/io/spring/ge/conventions/gradle/BuildScanConventionsTests.java index beff197..048a28d 100644 --- a/src/test/java/io/spring/ge/conventions/gradle/BuildScanConventionsTests.java +++ b/src/test/java/io/spring/ge/conventions/gradle/BuildScanConventionsTests.java @@ -91,6 +91,22 @@ void whenBambooResultEnvVarIsPresentThenBuildScanHasACiBuildLinkToIt() { assertThat(this.buildScan.links).containsEntry("CI build", "https://bamboo.example.com"); } + @Test + void whenBambooResultEnvVarIsPresentThenBuildScanHasBambooAsTheCiProviderValue() { + new BuildScanConventions(this.develocity, this.processRunner, + Collections.singletonMap("bamboo_resultsUrl", "https://bamboo.example.com")) + .execute(this.buildScan); + assertThat(this.buildScan.values).containsEntry("CI provider", "Bamboo"); + } + + @Test + void whenCircleBuildUrlEnvVarIsPresentThenBuildScanIsTaggedWithCiNotLocal() { + new BuildScanConventions(this.develocity, this.processRunner, + Collections.singletonMap("CIRCLE_BUILD_URL", "https://circleci.example.com/gh/org/project/123")) + .execute(this.buildScan); + assertThat(this.buildScan.tags).contains("CI").doesNotContain("Local"); + } + @Test void whenCircleBuildUrlEnvVarIsPresentThenBuildScanHasACiBuildLinkToIt() { new BuildScanConventions(this.develocity, this.processRunner, @@ -99,6 +115,22 @@ void whenCircleBuildUrlEnvVarIsPresentThenBuildScanHasACiBuildLinkToIt() { assertThat(this.buildScan.links).containsEntry("CI build", "https://circleci.example.com/gh/org/project/123"); } + @Test + void whenCircleBuildUrlEnvVarIsPresentThenBuildScanHasCircleCiAsTheCiProviderValue() { + new BuildScanConventions(this.develocity, this.processRunner, + Collections.singletonMap("CIRCLE_BUILD_URL", "https://circleci.example.com/gh/org/project/123")) + .execute(this.buildScan); + assertThat(this.buildScan.values).containsEntry("CI provider", "CircleCI"); + } + + @Test + void whenJenkinsUrlEnvVarIsPresentThenBuildScanIsTaggedWithCiNotLocal() { + new BuildScanConventions(this.develocity, this.processRunner, + Collections.singletonMap("JENKINS_URL", "https://jenkins.example.com")) + .execute(this.buildScan); + assertThat(this.buildScan.tags).contains("CI").doesNotContain("Local"); + } + @Test void whenJenkinsUrlAndBuildUrlEnvVarsArePresentThenBuildScanHasACiBuildLinkToBuildUrl() { Map env = new HashMap<>(); @@ -108,6 +140,14 @@ void whenJenkinsUrlAndBuildUrlEnvVarsArePresentThenBuildScanHasACiBuildLinkToBui assertThat(this.buildScan.links).containsEntry("CI build", "https://jenkins.example.com/builds/123"); } + @Test + void whenJenkinsUrlEnvVarIsPresentThenBuildScanHasJenkinsAsTheCiProviderValue() { + new BuildScanConventions(this.develocity, this.processRunner, + Collections.singletonMap("JENKINS_URL", "https://jenkins.example.com")) + .execute(this.buildScan); + assertThat(this.buildScan.values).containsEntry("CI provider", "Jenkins"); + } + @Test void whenCiEnvVarIsPresentThenBuildScanIsTaggedWithCiNotLocal() { new BuildScanConventions(this.develocity, this.processRunner, Collections.singletonMap("CI", null)) @@ -116,11 +156,10 @@ void whenCiEnvVarIsPresentThenBuildScanIsTaggedWithCiNotLocal() { } @Test - void whenJenkinsUrlEnvVarIsPresentThenBuildScanIsTaggedWithCiNotLocal() { - new BuildScanConventions(this.develocity, this.processRunner, - Collections.singletonMap("JENKINS_URL", "https://jenkins.example.com")) + void whenCiEnvVarIsPresentThenBuildScanHasConcourseAsTheCiProviderValue() { + new BuildScanConventions(this.develocity, this.processRunner, Collections.singletonMap("CI", null)) .execute(this.buildScan); - assertThat(this.buildScan.tags).contains("CI").doesNotContain("Local"); + assertThat(this.buildScan.values).containsEntry("CI provider", "Concourse"); } @Test @@ -143,12 +182,32 @@ void whenGitHubActionsEnvVarsArePresentThenBuildScanHasACiBuildLinkToIt() { "https://github.com/spring-projects/spring-boot/actions/runs/1234567890"); } + @Test + void whenGitHubActionsEnvVarIsPresentThenBuildScanHasGitHubActionsAsTheCiProviderValue() { + new BuildScanConventions(this.develocity, this.processRunner, + Collections.singletonMap("GITHUB_ACTIONS", "true")) + .execute(this.buildScan); + assertThat(this.buildScan.values).containsEntry("CI provider", "GitHub Actions"); + } + @Test void whenNoCiIndicatorsArePresentThenBuildScanIsTaggedWithLocalNotCi() { new BuildScanConventions(this.develocity, this.processRunner, Collections.emptyMap()).execute(this.buildScan); assertThat(this.buildScan.tags).contains("Local").doesNotContain("CI"); } + @Test + void whenNoCiIndicatorsArePresentThenBuildScanHasNoCiBuildLink() { + new BuildScanConventions(this.develocity, this.processRunner, Collections.emptyMap()).execute(this.buildScan); + assertThat(this.buildScan.links).doesNotContainKey("CI build"); + } + + @Test + void whenNoCiIndicatorsArePresentThenBuildScanHasNoCiProviderValue() { + new BuildScanConventions(this.develocity, this.processRunner, Collections.emptyMap()).execute(this.buildScan); + assertThat(this.buildScan.values).doesNotContainKey("CI provider"); + } + @Test void buildScanIsTaggedWithJdkVersion() { new BuildScanConventions(this.develocity, this.processRunner).execute(this.buildScan);