aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/cli/internal/ci
diff options
context:
space:
mode:
Diffstat (limited to 'cli/internal/ci')
-rw-r--r--cli/internal/ci/ci.go58
-rw-r--r--cli/internal/ci/ci_test.go105
-rw-r--r--cli/internal/ci/vendors.go253
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"}},
+ },
+}