Skip to content

Commit d74f9a2

Browse files
committed
update
1 parent 8e44e6b commit d74f9a2

File tree

2 files changed

+98
-65
lines changed

2 files changed

+98
-65
lines changed

databricks-sdk-java/src/main/java/com/databricks/sdk/core/UserAgent.java

Lines changed: 80 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,15 @@
11
package com.databricks.sdk.core;
22

3+
import com.databricks.sdk.core.utils.Environment;
4+
import java.io.File;
35
import java.util.ArrayList;
46
import java.util.Arrays;
57
import java.util.Collections;
6-
import java.util.HashMap;
78
import java.util.List;
8-
import java.util.Map;
99
import java.util.regex.Pattern;
1010
import java.util.stream.Collectors;
11-
import org.slf4j.Logger;
12-
import org.slf4j.LoggerFactory;
1311

1412
public class UserAgent {
15-
private static final Logger log = LoggerFactory.getLogger(UserAgent.class);
1613
private static String product = "unknown";
1714
private static String productVersion = "0.0.0";
1815

@@ -128,9 +125,9 @@ public static String asString() {
128125
segments.add(String.format("databricks-sdk-java/%s", version));
129126
segments.add(String.format("jvm/%s", jvmVersion()));
130127
segments.add(String.format("os/%s", osName()));
131-
String ciProvider = cicdProvider();
132-
if (!ciProvider.isEmpty()) {
133-
segments.add(String.format("ci/%s", ciProvider));
128+
String cicdProvider = cicdProvider();
129+
if (!cicdProvider.isEmpty()) {
130+
segments.add(String.format("cicd/%s", cicdProvider));
134131
}
135132
// Concurrent iteration over ArrayList must be guarded with synchronized.
136133
synchronized (otherInfo) {
@@ -142,34 +139,35 @@ public static String asString() {
142139
return segments.stream().collect(Collectors.joining(" "));
143140
}
144141

145-
// Map of CI/CD providers that are used to detect them.
146-
private static final Map<String, List<EnvVar>> PROVIDERS = new HashMap<>();
147-
148-
static {
149-
PROVIDERS.put("github", Collections.singletonList(new EnvVar("GITHUB_ACTIONS", "true")));
150-
PROVIDERS.put("gitlab", Collections.singletonList(new EnvVar("GITLAB_CI", "true")));
151-
PROVIDERS.put("jenkins", Collections.singletonList(new EnvVar("JENKINS_URL", "")));
152-
PROVIDERS.put("azure-devops", Collections.singletonList(new EnvVar("TF_BUILD", "True")));
153-
PROVIDERS.put("circle", Collections.singletonList(new EnvVar("CIRCLECI", "true")));
154-
PROVIDERS.put("travis", Collections.singletonList(new EnvVar("TRAVIS", "true")));
155-
PROVIDERS.put("bitbucket", Collections.singletonList(new EnvVar("BITBUCKET_BUILD_NUMBER", "")));
156-
PROVIDERS.put(
157-
"google-cloud-build",
158-
Arrays.asList(
159-
new EnvVar("PROJECT_ID", ""),
160-
new EnvVar("BUILD_ID", ""),
161-
new EnvVar("PROJECT_NUMBER", ""),
162-
new EnvVar("LOCATION", "")));
163-
PROVIDERS.put(
164-
"aws-code-build", Collections.singletonList(new EnvVar("CODEBUILD_BUILD_ARN", "")));
165-
PROVIDERS.put("tf-cloud", Collections.singletonList(new EnvVar("TFC_RUN_ID", "")));
166-
}
167-
168-
// This is a static private variable to store the CI/CD provider.
169-
// This is thread-safe because static initializers are executed
170-
// in a thread-safe manner by the Java ClassLoader.
171-
private static final String cicdProvider = lookupCiCdProvider();
142+
// List of CI/CD providers and their environment variables for detection
143+
private static List<CicdProvider> listCiCdProviders() {
144+
return Arrays.asList(
145+
new CicdProvider("github", Collections.singletonList(new EnvVar("GITHUB_ACTIONS", "true"))),
146+
new CicdProvider("gitlab", Collections.singletonList(new EnvVar("GITLAB_CI", "true"))),
147+
new CicdProvider("jenkins", Collections.singletonList(new EnvVar("JENKINS_URL", ""))),
148+
new CicdProvider("azure-devops", Collections.singletonList(new EnvVar("TF_BUILD", "True"))),
149+
new CicdProvider("circle", Collections.singletonList(new EnvVar("CIRCLECI", "true"))),
150+
new CicdProvider("travis", Collections.singletonList(new EnvVar("TRAVIS", "true"))),
151+
new CicdProvider("bitbucket", Collections.singletonList(new EnvVar("BITBUCKET_BUILD_NUMBER", ""))),
152+
new CicdProvider("google-cloud-build", Arrays.asList(
153+
new EnvVar("PROJECT_ID", ""),
154+
new EnvVar("BUILD_ID", ""),
155+
new EnvVar("PROJECT_NUMBER", ""),
156+
new EnvVar("LOCATION", "")
157+
)),
158+
new CicdProvider("aws-code-build", Collections.singletonList(new EnvVar("CODEBUILD_BUILD_ARN", ""))),
159+
new CicdProvider("tf-cloud", Collections.singletonList(new EnvVar("TFC_RUN_ID", "")))
160+
);
161+
}
162+
163+
// Volatile fields to ensure thread-safe lazy initialization
164+
// The 'volatile' keyword ensures that changes to these variables
165+
// are immediately visible to all threads. It prevents instruction
166+
// reordering by the compiler.
167+
protected static volatile String cicdProvider = null;
168+
protected static volatile Environment env = null;
172169

170+
// Represents an environment variable with its name and expected value
173171
private static class EnvVar {
174172
private final String name;
175173
private final String expectedValue;
@@ -178,23 +176,62 @@ public EnvVar(String name, String expectedValue) {
178176
this.name = name;
179177
this.expectedValue = expectedValue;
180178
}
179+
}
180+
181+
// Represents a CI/CD provider with its name and associated environment variables
182+
private static class CicdProvider {
183+
private final String name;
184+
private final List<EnvVar> envVars;
181185

182-
public boolean detect() {
183-
String value = System.getenv(name);
184-
return value != null && (expectedValue.isEmpty() || value.equals(expectedValue));
186+
public CicdProvider(String name, List<EnvVar> envVars) {
187+
this.name = name;
188+
this.envVars = envVars;
189+
}
190+
191+
public boolean detect(Environment env) {
192+
for (EnvVar envVar : envVars) {
193+
String value = env.get(envVar.name);
194+
if (value == null) {
195+
return false;
196+
}
197+
if (!envVar.expectedValue.isEmpty() && !value.equals(envVar.expectedValue)) {
198+
return false;
199+
}
200+
}
201+
return true;
185202
}
186203
}
187204

188-
private static String lookupCiCdProvider() {
189-
for (Map.Entry<String, List<EnvVar>> entry : PROVIDERS.entrySet()) {
190-
if (entry.getValue().stream().allMatch(EnvVar::detect)) {
191-
return entry.getKey();
205+
// Looks up the active CI/CD provider based on environment variables
206+
private static String lookupCiCdProvider(Environment env) {
207+
for (CicdProvider provider : listCiCdProviders()) {
208+
if (provider.detect(env)) {
209+
return provider.name;
192210
}
193211
}
194212
return "";
195213
}
196214

197-
public static String cicdProvider() {
215+
// Thread-safe lazy initialization of CI/CD provider detection
216+
private static String cicdProvider() {
217+
// First check (not synchronized) to avoid unnecessary synchronization
218+
if (cicdProvider == null) {
219+
// Synchronize only if cicdProvider is null
220+
synchronized (UserAgent.class) {
221+
// Second check (synchronized) to ensure only one thread initializes
222+
// This is necessary because multiple threads might have passed the first check
223+
if (cicdProvider == null) {
224+
cicdProvider = lookupCiCdProvider(env());
225+
}
226+
}
227+
}
198228
return cicdProvider;
199229
}
230+
231+
protected static Environment env() {
232+
if (env == null) {
233+
env = new Environment(System.getenv(), System.getenv("PATH").split(File.pathSeparator), System.getProperty("os.name"));
234+
}
235+
return env;
236+
}
200237
}

databricks-sdk-java/src/test/java/com/databricks/sdk/core/UserAgentTest.java

Lines changed: 18 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package com.databricks.sdk.core;
22

3-
import java.util.Properties;
3+
import java.util.ArrayList;
4+
import java.util.HashMap;
5+
import com.databricks.sdk.core.utils.Environment;
46
import org.junit.jupiter.api.*;
57
import org.slf4j.Logger;
68
import org.slf4j.LoggerFactory;
@@ -61,35 +63,29 @@ public void testUserAgentWithSemverValue() {
6163
Assertions.assertTrue(userAgent.contains("key1/1.0.0-dev+metadata"));
6264
}
6365

64-
private Properties originalProperties;
65-
66-
@BeforeEach
67-
public void saveProperties() {
68-
// Save original system properties
69-
originalProperties = (Properties) System.getProperties().clone();
70-
}
71-
72-
@AfterEach
73-
public void restoreProperties() {
74-
// Restore original system properties
75-
System.setProperties(originalProperties);
76-
}
77-
7866
@Test
7967
public void testUserAgentCicdNoProvider() {
80-
Assertions.assertEquals("", UserAgent.cicdProvider());
68+
UserAgent.env = new Environment(new HashMap<>(), new ArrayList<>(), System.getProperty("os.name"));
69+
Assertions.assertFalse(UserAgent.asString().contains("cicd"));
70+
UserAgent.env = null;
8171
}
8272

8373
@Test
8474
public void testUserAgentCicdOneProvider() {
85-
System.setProperty("GITHUB_ACTIONS", "true");
86-
Assertions.assertEquals("github", UserAgent.cicdProvider());
75+
UserAgent.env = new Environment(new HashMap<String, String>() {{
76+
put("GITHUB_ACTIONS", "true");
77+
}}, new ArrayList<>(), System.getProperty("os.name"));
78+
Assertions.assertTrue(UserAgent.asString().contains("cicd/github"));
79+
UserAgent.env = null;
8780
}
8881

8982
@Test
90-
public void testUserAgentCicdMultipleProviders() {
91-
System.setProperty("GITHUB_ACTIONS", "true");
92-
System.setProperty("GITLAB_CI", "true");
93-
Assertions.assertEquals("github", UserAgent.cicdProvider());
83+
public void testUserAgentCicdTwoProvider() {
84+
UserAgent.env = new Environment(new HashMap<String, String>() {{
85+
put("GITLAB_CI", "true");
86+
put("JENKINS_URL", "");
87+
}}, new ArrayList<>(), System.getProperty("os.name"));
88+
Assertions.assertTrue(UserAgent.asString().contains("cicd/gitlab"));
89+
UserAgent.env = null;
9490
}
9591
}

0 commit comments

Comments
 (0)