-
Notifications
You must be signed in to change notification settings - Fork 42
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add telemetry for CI/CD platform to useragent (#665)
## Changes This PR adds CI/CD provider infromation to the HTTP user agent. This is useful to track how often and from which CI/CD platforms our tools are being run from. ## Tests Unit tests and manually For manual testing I ran a simple script in Go that list's all jobs in a workspace and inspected the user agent string. **Case** **github-actions:** command: ``` GITHUB_ACTIONS=true go run main.go ``` User Agent: ``` User-Agent: unknown/0.0.0 databricks-sdk-go/0.23.0 go/1.21.0 os/darwin sdk-feature/pagination auth/pat cicd/github ``` **Case** **google-cloud-build:** command: ``` PROJECT_ID=1 BUILD_ID=2 PROJECT_NUMBER=3 LOCATION=4 go run main.go ``` User agent: ``` User-Agent: unknown/0.0.0 databricks-sdk-go/0.23.0 go/1.21.0 os/darwin sdk-feature/pagination auth/pat cicd/google-cloud-build ```
- Loading branch information
1 parent
0a32a24
commit 2ec4258
Showing
3 changed files
with
214 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
package useragent | ||
|
||
import ( | ||
"os" | ||
"sync" | ||
) | ||
|
||
type envVar struct { | ||
// Name of the environment variable. | ||
name string | ||
|
||
// Expected value of the environment variable. If empty, only the presence | ||
// of the environment variable is checked. If non-empty, the value must | ||
// match exactly. | ||
expectedValue string | ||
} | ||
|
||
type cicdProvider struct { | ||
// The name of the CI/CD provider. This is the name included in the user | ||
// agent string. | ||
name string | ||
|
||
// The env vars that are expected to be set in the CI/CD provider's runner. | ||
envVars []envVar | ||
} | ||
|
||
// List of CI/CD providers and their env vars we can rely on to detect them. | ||
func listCiCdProviders() []cicdProvider { | ||
return []cicdProvider{ | ||
{"github", []envVar{{"GITHUB_ACTIONS", "true"}}}, | ||
{"gitlab", []envVar{{"GITLAB_CI", "true"}}}, | ||
{"jenkins", []envVar{{"JENKINS_URL", ""}}}, | ||
{"azure-devops", []envVar{{"TF_BUILD", "True"}}}, | ||
{"circle", []envVar{{"CIRCLECI", "true"}}}, | ||
{"travis", []envVar{{"TRAVIS", "true"}}}, | ||
{"bitbucket", []envVar{{"BITBUCKET_BUILD_NUMBER", ""}}}, | ||
{"google-cloud-build", []envVar{{"PROJECT_ID", ""}, {"BUILD_ID", ""}, {"PROJECT_NUMBER", ""}, {"LOCATION", ""}}}, | ||
{"aws-code-build", []envVar{{"CODEBUILD_BUILD_ARN", ""}}}, | ||
{"tf-cloud", []envVar{{"TFC_RUN_ID", ""}}}, | ||
} | ||
} | ||
|
||
// detect returns true if all env vars are set and have expected values. | ||
func (p cicdProvider) detect() bool { | ||
for _, envVar := range p.envVars { | ||
v, ok := os.LookupEnv(envVar.name) | ||
if !ok { | ||
return false | ||
} | ||
if envVar.expectedValue != "" && v != envVar.expectedValue { | ||
return false | ||
} | ||
} | ||
return true | ||
} | ||
|
||
// lookupCiCdProvider returns the name of the CI/CD provider if detected. Returns the | ||
// first one, if multiple are detected. | ||
func lookupCiCdProvider() string { | ||
for _, p := range listCiCdProviders() { | ||
if p.detect() { | ||
return p.name | ||
} | ||
} | ||
return "" | ||
} | ||
|
||
var provider string | ||
|
||
var providerOnce sync.Once | ||
|
||
func CiCdProvider() string { | ||
providerOnce.Do(func() { | ||
provider = lookupCiCdProvider() | ||
}) | ||
return provider | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
package useragent | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/databricks/databricks-sdk-go/internal/env" | ||
"github.com/stretchr/testify/assert" | ||
) | ||
|
||
func TestCiCdProviderDetect(t *testing.T) { | ||
cicdProvider := cicdProvider{ | ||
name: "foo", | ||
envVars: []envVar{ | ||
{"APPLE", "123"}, | ||
{"BANANA", "456"}, | ||
{"CHERRY", ""}, | ||
}, | ||
} | ||
|
||
// Set some of the env vars. | ||
t.Setenv("APPLE", "123") | ||
t.Setenv("CHERRY", "000") | ||
assert.False(t, cicdProvider.detect(), "should not detect when not all env vars are set") | ||
|
||
// Set the rest of the env vars. | ||
t.Setenv("BANANA", "456") | ||
assert.True(t, cicdProvider.detect(), "should detect when all env vars are set") | ||
} | ||
|
||
func TestCiCdProviderGithubActions(t *testing.T) { | ||
env.CleanupEnvironment(t) | ||
|
||
// No provider detected. | ||
assert.Equal(t, "", lookupCiCdProvider()) | ||
|
||
// Github Actions detected. | ||
t.Setenv("GITHUB_ACTIONS", "true") | ||
assert.Equal(t, "github", lookupCiCdProvider()) | ||
} | ||
|
||
func TestCiCdProviderGitlab(t *testing.T) { | ||
env.CleanupEnvironment(t) | ||
|
||
// Gitlab detected. | ||
t.Setenv("GITLAB_CI", "true") | ||
assert.Equal(t, "gitlab", lookupCiCdProvider()) | ||
} | ||
|
||
func TestCiCdProviderJenkins(t *testing.T) { | ||
env.CleanupEnvironment(t) | ||
|
||
// Jenkins detected. | ||
t.Setenv("JENKINS_URL", "https://jenkins.example.com") | ||
assert.Equal(t, "jenkins", lookupCiCdProvider()) | ||
} | ||
|
||
func TestCiCdProviderAzureDevops(t *testing.T) { | ||
env.CleanupEnvironment(t) | ||
|
||
// Azure Devops detected. | ||
t.Setenv("TF_BUILD", "True") | ||
assert.Equal(t, "azure-devops", lookupCiCdProvider()) | ||
} | ||
|
||
func TestCiCdProviderCircle(t *testing.T) { | ||
env.CleanupEnvironment(t) | ||
|
||
// Circle detected. | ||
t.Setenv("CIRCLECI", "true") | ||
assert.Equal(t, "circle", lookupCiCdProvider()) | ||
} | ||
|
||
func TestCiCdProviderTravis(t *testing.T) { | ||
env.CleanupEnvironment(t) | ||
|
||
// Travis detected. | ||
t.Setenv("TRAVIS", "true") | ||
assert.Equal(t, "travis", lookupCiCdProvider()) | ||
} | ||
|
||
func TestCiCdProviderBitbucket(t *testing.T) { | ||
env.CleanupEnvironment(t) | ||
|
||
// Bitbucket detected. | ||
t.Setenv("BITBUCKET_BUILD_NUMBER", "123") | ||
assert.Equal(t, "bitbucket", lookupCiCdProvider()) | ||
} | ||
|
||
func TestCiCdProviderGoogleCloudBuild(t *testing.T) { | ||
env.CleanupEnvironment(t) | ||
|
||
// Google Cloud Build detected. | ||
t.Setenv("PROJECT_ID", "foo") | ||
t.Setenv("BUILD_ID", "bar") | ||
t.Setenv("PROJECT_NUMBER", "baz") | ||
t.Setenv("LOCATION", "") | ||
assert.Equal(t, "google-cloud-build", lookupCiCdProvider()) | ||
} | ||
|
||
func TestCiCdProviderAwsCodeBuild(t *testing.T) { | ||
env.CleanupEnvironment(t) | ||
|
||
// AWS Code Build detected. | ||
t.Setenv("CODEBUILD_BUILD_ARN", "arn:aws:codebuild:us-east-1:123456789012:build/my-demo-project:b1e6deae-e4f2-4151-be79-3cc4e82a0bf0") | ||
assert.Equal(t, "aws-code-build", lookupCiCdProvider()) | ||
} | ||
|
||
func TestCiCdProviderTfCloud(t *testing.T) { | ||
env.CleanupEnvironment(t) | ||
|
||
// Terraform Cloud detected. | ||
t.Setenv("TFC_RUN_ID", "run-123") | ||
assert.Equal(t, "tf-cloud", lookupCiCdProvider()) | ||
} | ||
|
||
func TestCiCdProviderMultiple(t *testing.T) { | ||
env.CleanupEnvironment(t) | ||
|
||
// Multiple providers detected. The first one detected is set. | ||
t.Setenv("GITHUB_ACTIONS", "true") | ||
t.Setenv("GITLAB_CI", "true") | ||
assert.Equal(t, "github", lookupCiCdProvider()) | ||
} |