diff options
Diffstat (limited to 'cli/internal/ci')
| -rw-r--r-- | cli/internal/ci/ci.go | 58 | ||||
| -rw-r--r-- | cli/internal/ci/ci_test.go | 105 | ||||
| -rw-r--r-- | cli/internal/ci/vendors.go | 253 |
3 files changed, 416 insertions, 0 deletions
diff --git a/cli/internal/ci/ci.go b/cli/internal/ci/ci.go new file mode 100644 index 0000000..a22ad78 --- /dev/null +++ b/cli/internal/ci/ci.go @@ -0,0 +1,58 @@ +// Package ci is a simple utility to check if a program is being executed in common CI/CD/PaaS vendors. +// This is a partial port of https://github.com/watson/ci-info +package ci + +import "os" + +var isCI = os.Getenv("BUILD_ID") != "" || os.Getenv("BUILD_NUMBER") != "" || os.Getenv("CI") != "" || os.Getenv("CI_APP_ID") != "" || os.Getenv("CI_BUILD_ID") != "" || os.Getenv("CI_BUILD_NUMBER") != "" || os.Getenv("CI_NAME") != "" || os.Getenv("CONTINUOUS_INTEGRATION") != "" || os.Getenv("RUN_ID") != "" || os.Getenv("TEAMCITY_VERSION") != "" || false + +// IsCi returns true if the program is executing in a CI/CD environment +func IsCi() bool { + return isCI +} + +// Name returns the name of the CI vendor +func Name() string { + return Info().Name +} + +// Constant returns the name of the CI vendor as a constant +func Constant() string { + return Info().Constant +} + +// Info returns information about a CI vendor +func Info() Vendor { + // check both the env var key and value + for _, env := range Vendors { + if env.EvalEnv != nil { + for name, value := range env.EvalEnv { + if os.Getenv(name) == value { + return env + } + } + } else { + // check for any of the listed env var keys, with any value + if env.Env.Any != nil && len(env.Env.Any) > 0 { + for _, envVar := range env.Env.Any { + if os.Getenv(envVar) != "" { + return env + } + } + // check for all of the listed env var keys, with any value + } else if env.Env.All != nil && len(env.Env.All) > 0 { + all := true + for _, envVar := range env.Env.All { + if os.Getenv(envVar) == "" { + all = false + break + } + } + if all { + return env + } + } + } + } + return Vendor{} +} diff --git a/cli/internal/ci/ci_test.go b/cli/internal/ci/ci_test.go new file mode 100644 index 0000000..333ff61 --- /dev/null +++ b/cli/internal/ci/ci_test.go @@ -0,0 +1,105 @@ +package ci + +import ( + "os" + "reflect" + "strings" + "testing" +) + +func getVendor(name string) Vendor { + for _, v := range Vendors { + if v.Name == name { + return v + } + } + return Vendor{} +} + +func TestInfo(t *testing.T) { + tests := []struct { + name string + setEnv []string + want Vendor + }{ + { + name: "AppVeyor", + setEnv: []string{"APPVEYOR"}, + want: getVendor("AppVeyor"), + }, + { + name: "Vercel", + setEnv: []string{"VERCEL", "NOW_BUILDER"}, + want: getVendor("Vercel"), + }, + { + name: "Render", + setEnv: []string{"RENDER"}, + want: getVendor("Render"), + }, + { + name: "Netlify", + setEnv: []string{"NETLIFY"}, + want: getVendor("Netlify CI"), + }, + { + name: "Jenkins", + setEnv: []string{"BUILD_ID", "JENKINS_URL"}, + want: getVendor("Jenkins"), + }, + { + name: "Jenkins - failing", + setEnv: []string{"BUILD_ID"}, + want: getVendor(""), + }, + { + name: "GitHub Actions", + setEnv: []string{"GITHUB_ACTIONS"}, + want: getVendor("GitHub Actions"), + }, + { + name: "Codeship", + setEnv: []string{"CI_NAME=codeship"}, + want: getVendor("Codeship"), + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // unset existing envs + liveCi := "" + if Name() == "GitHub Actions" { + liveCi = os.Getenv("GITHUB_ACTIONS") + err := os.Unsetenv("GITHUB_ACTIONS") + if err != nil { + t.Errorf("Error un-setting GITHUB_ACTIONS env: %s", err) + } + } + // set envs + for _, env := range tt.setEnv { + envParts := strings.Split(env, "=") + val := "some value" + if len(envParts) > 1 { + val = envParts[1] + } + err := os.Setenv(envParts[0], val) + if err != nil { + t.Errorf("Error setting %s for %s test", envParts[0], tt.name) + } + defer os.Unsetenv(envParts[0]) //nolint errcheck + + } + // run test + if got := Info(); !reflect.DeepEqual(got, tt.want) { + t.Errorf("Info() = %v, want %v", got, tt.want) + } + + // reset env + if Name() == "GitHub Actions" { + err := os.Setenv("GITHUB_ACTIONS", liveCi) + if err != nil { + t.Errorf("Error re-setting GITHUB_ACTIONS env: %s", err) + } + } + }) + } +} diff --git a/cli/internal/ci/vendors.go b/cli/internal/ci/vendors.go new file mode 100644 index 0000000..13bce77 --- /dev/null +++ b/cli/internal/ci/vendors.go @@ -0,0 +1,253 @@ +package ci + +type vendorEnvs struct { + Any []string + All []string +} + +// Vendor describes a CI/CD vendor execution environment +type Vendor struct { + // Name is the name of the vendor + Name string + // Constant is the environment variable prefix used by the vendor + Constant string + // Env is one or many environment variables that can be used to quickly determine the vendor (using simple os.Getenv(env) check) + Env vendorEnvs + // EvalEnv is key/value map of environment variables that can be used to quickly determine the vendor + EvalEnv map[string]string +} + +// Vendors is a list of common CI/CD vendors (from https://github.com/watson/ci-info/blob/master/vendors.json) +var Vendors = []Vendor{ + { + Name: "Appcircle", + Constant: "APPCIRCLE", + Env: vendorEnvs{Any: []string{"AC_APPCIRCLE"}}, + }, + { + Name: "AppVeyor", + Constant: "APPVEYOR", + Env: vendorEnvs{Any: []string{"APPVEYOR"}}, + }, + { + Name: "AWS CodeBuild", + Constant: "CODEBUILD", + Env: vendorEnvs{Any: []string{"CODEBUILD_BUILD_ARN"}}, + }, + { + Name: "Azure Pipelines", + Constant: "AZURE_PIPELINES", + Env: vendorEnvs{Any: []string{"SYSTEM_TEAMFOUNDATIONCOLLECTIONURI"}}, + }, + { + Name: "Bamboo", + Constant: "BAMBOO", + Env: vendorEnvs{Any: []string{"bamboo_planKey"}}, + }, + { + Name: "Bitbucket Pipelines", + Constant: "BITBUCKET", + Env: vendorEnvs{Any: []string{"BITBUCKET_COMMIT"}}, + }, + { + Name: "Bitrise", + Constant: "BITRISE", + Env: vendorEnvs{Any: []string{"BITRISE_IO"}}, + }, + { + Name: "Buddy", + Constant: "BUDDY", + Env: vendorEnvs{Any: []string{"BUDDY_WORKSPACE_ID"}}, + }, + { + Name: "Buildkite", + Constant: "BUILDKITE", + Env: vendorEnvs{Any: []string{"BUILDKITE"}}, + }, + { + Name: "CircleCI", + Constant: "CIRCLE", + Env: vendorEnvs{Any: []string{"CIRCLECI"}}, + }, + { + Name: "Cirrus CI", + Constant: "CIRRUS", + Env: vendorEnvs{Any: []string{"CIRRUS_CI"}}, + }, + { + Name: "Codefresh", + Constant: "CODEFRESH", + Env: vendorEnvs{Any: []string{"CF_BUILD_ID"}}, + }, + { + Name: "Codemagic", + Constant: "CODEMAGIC", + Env: vendorEnvs{Any: []string{"CM_BUILD_ID"}}, + }, + { + Name: "Codeship", + Constant: "CODESHIP", + EvalEnv: map[string]string{ + "CI_NAME": "codeship", + }, + }, + { + Name: "Drone", + Constant: "DRONE", + Env: vendorEnvs{Any: []string{"DRONE"}}, + }, + { + Name: "dsari", + Constant: "DSARI", + Env: vendorEnvs{Any: []string{"DSARI"}}, + }, + { + Name: "Expo Application Services", + Constant: "EAS", + Env: vendorEnvs{Any: []string{"EAS_BUILD"}}, + }, + { + Name: "GitHub Actions", + Constant: "GITHUB_ACTIONS", + Env: vendorEnvs{Any: []string{"GITHUB_ACTIONS"}}, + }, + { + Name: "GitLab CI", + Constant: "GITLAB", + Env: vendorEnvs{Any: []string{"GITLAB_CI"}}, + }, + { + Name: "GoCD", + Constant: "GOCD", + Env: vendorEnvs{Any: []string{"GO_PIPELINE_LABEL"}}, + }, + { + Name: "Google Cloud Build", + Constant: "GOOGLE_CLOUD_BUILD", + Env: vendorEnvs{Any: []string{"BUILDER_OUTPUT"}}, + }, + { + Name: "LayerCI", + Constant: "LAYERCI", + Env: vendorEnvs{Any: []string{"LAYERCI"}}, + }, + { + Name: "Gerrit", + Constant: "GERRIT", + Env: vendorEnvs{Any: []string{"GERRIT_PROJECT"}}, + }, + { + Name: "Hudson", + Constant: "HUDSON", + Env: vendorEnvs{Any: []string{"HUDSON"}}, + }, + { + Name: "Jenkins", + Constant: "JENKINS", + Env: vendorEnvs{All: []string{"JENKINS_URL", "BUILD_ID"}}, + }, + { + Name: "Magnum CI", + Constant: "MAGNUM", + Env: vendorEnvs{Any: []string{"MAGNUM"}}, + }, + { + Name: "Netlify CI", + Constant: "NETLIFY", + Env: vendorEnvs{Any: []string{"NETLIFY"}}, + }, + { + Name: "Nevercode", + Constant: "NEVERCODE", + Env: vendorEnvs{Any: []string{"NEVERCODE"}}, + }, + { + Name: "ReleaseHub", + Constant: "RELEASEHUB", + Env: vendorEnvs{Any: []string{"RELEASE_BUILD_ID"}}, + }, + { + Name: "Render", + Constant: "RENDER", + Env: vendorEnvs{Any: []string{"RENDER"}}, + }, + { + Name: "Sail CI", + Constant: "SAIL", + Env: vendorEnvs{Any: []string{"SAILCI"}}, + }, + { + Name: "Screwdriver", + Constant: "SCREWDRIVER", + Env: vendorEnvs{Any: []string{"SCREWDRIVER"}}, + }, + { + Name: "Semaphore", + Constant: "SEMAPHORE", + Env: vendorEnvs{Any: []string{"SEMAPHORE"}}, + }, + { + Name: "Shippable", + Constant: "SHIPPABLE", + Env: vendorEnvs{Any: []string{"SHIPPABLE"}}, + }, + { + Name: "Solano CI", + Constant: "SOLANO", + Env: vendorEnvs{Any: []string{"TDDIUM"}}, + }, + { + Name: "Sourcehut", + Constant: "SOURCEHUT", + EvalEnv: map[string]string{ + "CI_NAME": "sourcehut", + }, + }, + { + Name: "Strider CD", + Constant: "STRIDER", + Env: vendorEnvs{Any: []string{"STRIDER"}}, + }, + { + Name: "TaskCluster", + Constant: "TASKCLUSTER", + Env: vendorEnvs{All: []string{"TASK_ID", "RUN_ID"}}, + }, + { + Name: "TeamCity", + Constant: "TEAMCITY", + Env: vendorEnvs{Any: []string{"TEAMCITY_VERSION"}}, + }, + { + Name: "Travis CI", + Constant: "TRAVIS", + Env: vendorEnvs{Any: []string{"TRAVIS"}}, + }, + { + Name: "Vercel", + Constant: "VERCEL", + Env: vendorEnvs{Any: []string{"NOW_BUILDER", "VERCEL"}}, + }, + { + Name: "Visual Studio App Center", + Constant: "APPCENTER", + Env: vendorEnvs{Any: []string{"APPCENTER"}}, + }, + { + Name: "Woodpecker", + Constant: "WOODPECKER", + EvalEnv: map[string]string{ + "CI": "woodpecker", + }, + }, + { + Name: "Xcode Cloud", + Constant: "XCODE_CLOUD", + Env: vendorEnvs{Any: []string{"CI_XCODE_PROJECT"}}, + }, + { + Name: "Xcode Server", + Constant: "XCODE_SERVER", + Env: vendorEnvs{Any: []string{"XCS"}}, + }, +} |
