pax_global_header00006660000000000000000000000064150417604250014516gustar00rootroot0000000000000052 comment=317625dcbda5dda4953371536559df855e91149c ciinfo-0.3.3/000077500000000000000000000000001504176042500127705ustar00rootroot00000000000000ciinfo-0.3.3/.github/000077500000000000000000000000001504176042500143305ustar00rootroot00000000000000ciinfo-0.3.3/.github/workflows/000077500000000000000000000000001504176042500163655ustar00rootroot00000000000000ciinfo-0.3.3/.github/workflows/tests.yml000066400000000000000000000015151504176042500202540ustar00rootroot00000000000000name: Pull Request on: pull_request: paths-ignore: - "*.md" branches: - main push: paths-ignore: - "*.md" branches: - main jobs: lint: name: Run linting runs-on: ubuntu-latest steps: - uses: actions/setup-go@v3 with: go-version: 1.20.x - uses: actions/checkout@v3 - name: Lint run: | make install-tools && make format && git diff --quiet - name: Stale constants run: make compile-constants && git diff --quiet test: name: Run tests runs-on: ubuntu-latest strategy: matrix: go: ["1.20.x"] steps: - uses: actions/checkout@v3 - name: Setup go uses: actions/setup-go@v3 with: go-version: ${{matrix.go}} - name: Run Tests run: make test ciinfo-0.3.3/.gitignore000066400000000000000000000004771504176042500147700ustar00rootroot00000000000000# Binaries for programs and plugins *.exe *.exe~ *.dll *.so *.dylib # Test binary, built with `go test -c` *.test # Output of the go coverage tool, specifically when used with LiteIDE *.out # Dependency directories (remove the comment below to include it) vendor/ **/.DS_Store .vscode compile-constants/vendors.go ciinfo-0.3.3/LICENSE000066400000000000000000000020641504176042500137770ustar00rootroot00000000000000MIT License Copyright (c) 2021 Georgios Kampitakis Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ciinfo-0.3.3/Makefile000066400000000000000000000023641504176042500144350ustar00rootroot00000000000000.PHONY: install-tools lint test test-verbose format help compile-constants .SILENT: install-tools lint test test-verbose format help compile-constants help: grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' install-tools: ## Install linting tools go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.49.0 go install mvdan.cc/gofumpt@v0.6.0 go install github.com/segmentio/golines@latest lint: ## Run golangci linter golangci-lint run -c ./golangci.yml ./... format: ## Format code gofumpt -l -w -extra . golines . -w test: ## Run tests go test -test.timeout 120s -count=1 . go test -test.timeout 120s -count=1 ./ciinfo/ test-verbose: ## Run tests with verbose output go test -test.timeout 120s -v -cover -count=1 . go test -test.timeout 120s -v -cover -count=1 ./ciinfo/ compile-constants: ## Generates 'constants.go' containing the list with constant values cp vendors.go compile-constants $(shell if [ "$$(uname)" = "Darwin" ]; then \ echo "sed -i '' 's/ciinfo/main/' compile-constants/vendors.go"; \ else \ echo "sed -i 's/ciinfo/main/' compile-constants/vendors.go"; \ fi) | sh go run ./compile-constants/main.go ./compile-constants/vendors.go ciinfo-0.3.3/README.md000066400000000000000000000212551504176042500142540ustar00rootroot00000000000000# CI Info ## Acknowledgement This repository is based on work done in [watson/ci-info](https://github.com/watson/ci-info) and the contributors. I will do my best to keep this library up to date and in sync with changes in [watson/ci-info](https://github.com/watson/ci-info). --- Get details about the current Continuous Integration environment. [![Tests](https://github.com/gkampitakis/ciinfo/actions/workflows/tests.yml/badge.svg)](https://github.com/gkampitakis/ciinfo/actions/workflows/tests.yml) ## Installation ```bash go get github.com/gkampitakis/ciinfo ``` ## Usage ```go import ( "fmt" "github.com/gkampitakis/ciinfo" ) if ciinfo.IsCI { fmt.Printf("The name of the CI server is: %s", ciinfo.Name) } else { fmt.Printf("This program is not running on a CI server") } ``` ## CLI Support `ciinfo` can also be used as a CLI. You can install it with ```sh go install github.com/gkampitakis/ciinfo/ciinfo@latest ``` Then `ciinfo` command will be successful ( code 0 ) if running on CI else error ( code -1 ). ```sh # will output isCI if running onCI ciinfo && echo 'isCI' ``` `ciinfo` also has ```shell Usage of ciinfo: -output string you can output info [json, pretty]. -pr check if shell is running on CI for a Pull Request. ``` ## Supported CI tools Officially supported CI servers: | Name | Constant | isPR | | ------------------------------------------------------------------------------- | --------------------------- | ---- | | [Agola CI](https://agola.io/) | `ci.AGOLA` | ✅ | | [AWS CodeBuild](https://aws.amazon.com/codebuild/) | `ciinfo.CODEBUILD` | ✅ | | [AppVeyor](http://www.appveyor.com) | `ciinfo.APPVEYOR` | ✅ | | [Azure Pipelines](https://azure.microsoft.com/en-us/services/devops/pipelines/) | `ciinfo.AZURE_PIPELINES` | ✅ | | [Appcircle](https://appcircle.io/) | `ciinfo.APPCIRCLE` | ✅ | | [Bamboo](https://www.atlassian.com/software/bamboo) by Atlassian | `ciinfo.BAMBOO` | 🚫 | | [Bitbucket Pipelines](https://bitbucket.org/product/features/pipelines) | `ciinfo.BITBUCKET` | ✅ | | [Bitrise](https://www.bitrise.io/) | `ciinfo.BITRISE` | ✅ | | [Buddy](https://buddy.works/) | `ciinfo.BUDDY` | ✅ | | [Buildkite](https://buildkite.com) | `ciinfo.BUILDKITE` | ✅ | | [CircleCI](http://circleci.com) | `ciinfo.CIRCLE` | ✅ | | [Cirrus CI](https://cirrus-ci.org) | `ciinfo.CIRRUS` | ✅ | | [Cloudflare Pages](https://pages.cloudflare.com/) | `ci.CLOUDFLARE_PAGES` | 🚫 | | [Cloudflare Workers](https://pages.cloudflare.com/) | `ci.CLOUDFLARE_WORKERS` | 🚫 | | [Codefresh](https://codefresh.io/) | `ciinfo.CODEFRESH` | ✅ | | [Codeship](https://codeship.com) | `ciinfo.CODESHIP` | 🚫 | | [Drone](https://drone.io) | `ciinfo.DRONE` | ✅ | | [dsari](https://github.com/rfinnie/dsari) | `ciinfo.DSARI` | 🚫 | | [Earthly CI](https://earthly.dev/) | `ci.EARTHLY` | 🚫 | | [Expo Application Services](https://expo.dev/eas) | `ciinfo.EAS` | 🚫 | | [Gerrit CI](https://www.gerritcodereview.com) | `ciinfo.GERRIT` | 🚫 | | [GitHub Actions](https://github.com/features/actions/) | `ciinfo.GITHUB_ACTIONS` | ✅ | | [GitLab CI](https://about.gitlab.com/gitlab-ci/) | `ciinfo.GITLAB` | ✅ | | [Gitea Actions](https://about.gitea.com/) | `ci.GITEA_ACTIONS` | 🚫 | | [GoCD](https://www.go.cd/) | `ciinfo.GOCD` | 🚫 | | [Google Cloud Build](https://cloud.google.com/build) | `ciinfo.GOOGLE_CLOUD_BUILD` | 🚫 | | [Harness CI](https://www.harness.io/products/continuous-integration) | `ciinfo.HARNESS` | 🚫 | | [Heroku](https://www.heroku.com) | `ciinfo.HEROKU` | 🚫 | | [Hudson](http://hudson-ci.org) | `ciinfo.HUDSON` | 🚫 | | [Jenkins CI](https://jenkins-ci.org) | `ciinfo.JENKINS` | ✅ | | [LayerCI](https://layerci.com/) | `ciinfo.LAYERCI` | ✅ | | [Magnum CI](https://magnum-ci.com) | `ciinfo.MAGNUM` | 🚫 | | [Netlify CI](https://www.netlify.com/) | `ciinfo.NETLIFY` | ✅ | | [Nevercode](http://nevercode.io/) | `ciinfo.NEVERCODE` | ✅ | | [Prow](https://docs.prow.k8s.io/) | `ci.PROW` | 🚫 | | [ReleaseHub](https://releasehub.com/) | `ciinfo.RELEASEHUB` | ✅ | | [Render](https://render.com/) | `ciinfo.RENDER` | ✅ | | [Sail CI](https://sail.ci/) | `ciinfo.SAIL` | ✅ | | [Screwdriver](https://screwdriver.cd/) | `ciinfo.SCREWDRIVER` | ✅ | | [Semaphore](https://semaphoreci.com) | `ciinfo.SEMAPHORE` | ✅ | | [Sourcehut](https://sourcehut.org/) | `ciinfo.SOURCEHUT` | 🚫 | | [Strider CD](https://strider-cd.github.io/) | `ciinfo.STRIDER` | 🚫 | | [TaskCluster](http://docs.taskcluster.net) | `ciinfo.TASKCLUSTER` | 🚫 | | [TeamCity](https://www.jetbrains.com/teamcity/) by JetBrains | `ciinfo.TEAMCITY` | 🚫 | | [Travis CI](http://travis-ci.org) | `ciinfo.TRAVIS` | ✅ | | [Vela](https://go-vela.github.io/docs/) | `ci.VELA` | ✅ | | [Vercel](https://vercel.com/) | `ciinfo.VERCEL` | ✅ | | [Visual Studio App Center](https://appcenter.ms/) | `ciinfo.APPCENTER` | 🚫 | | [Woodpecker](https://woodpecker-ci.org/) | `ciinfo.WOODPECKER` | ✅ | ## API ### `ciinfo.Name` Returns a string containing name of the CI server the code is running on. If CI server is not detected, it returns empty string `""`. Don't depend on the value of this string not to change for a specific vendor. If you find your self writing `ciinfo.Name === "Travis CI"`, you most likely want to use `ciinfo.IsVendor("TRAVIS")` instead. ### `ciinfo.IsCI` Returns a boolean. Will be `true` if the code is running on a CI server, otherwise `false`. Some CI servers not listed here might still trigger the `ciinfo.isCI` boolean to be set to `true` if they use certain vendor neutral environment variables. In those cases `ciinfo.Name` will be `""` and no vendor specific boolean will be set to `true`. ### `ciinfo.IsPR` Returns a boolean if PR detection is supported for the current CI server. Will be `true` if a PR is being tested, otherwise `false`. If PR detection is not supported for the current CI server, the value will be `false`. ### `ciinfo.` A vendor specific boolean constant is exposed for each support CI vendor. A constant will be `true` if the code is determined to run on the given CI server, otherwise `false`. Examples of vendor constants are `ciinfo.TRAVIS` or `ciinfo.APPVEYOR`. For a complete list, see the support table above. ciinfo-0.3.3/ciinfo.go000066400000000000000000000065111504176042500145710ustar00rootroot00000000000000package ciinfo import ( "os" "strings" ) var ( // Returns a boolean if PR detection is supported for the current CI server. // Will be `true` if a PR is being tested, otherwise `false`. If PR detection is // not supported for the current CI server, the value will be `null`. IsPr bool // Returns a boolean. Will be `true` if the code is running on a CI server, // otherwise `false`. // // Some CI servers not listed here might still trigger the `ci.isCI` // boolean to be set to `true` if they use certain vendor neutral environment // variables. In those cases `ci.name` will be `null` and no vendor specific // boolean will be set to `true`. IsCI bool // Returns a string containing name of the CI server the code is running on. If // CI server is not detected, it returns `null`. // // Don't depend on the value of this string not to change for a specific vendor. // If you find your self writing `ci.name === 'Travis CI'`, you most likely want // to use `ci.TRAVIS` instead. Name string // Returns a string containing the identifier of the CI server the code is running on. If // CI server is not detected, it returns `null`. ID string vendorsIsCI map[string]bool ) func init() { initialize() } func everyEnv(envs []env, check func(env) bool) bool { for _, e := range envs { if !check(e) { return false } } return true } func anyEnv(envs []env, check func(env) bool) bool { for _, env := range envs { if check(env) { return true } } return false } func initialize() { vendorsIsCI = make(map[string]bool) IsPr = false IsCI = false Name = "" for _, vendor := range vendors { checkEnvs := everyEnv if vendor.anyEnv { checkEnvs = anyEnv } isVendor := checkEnvs(vendor.env, verifyCI) vendorsIsCI[vendor.constant] = isVendor if !isVendor { continue } Name = vendor.name IsPr = anyPr(vendor.pr) ID = vendor.constant } IsCI = os.Getenv("CI") != "false" && (Name != "" || isCI()) } func isCI() bool { envKeys := []string{ "BUILD_ID", // Jenkins, Cloudbees "BUILD_NUMBER", // Jenkins, TeamCity "CI", // Travis CI, CircleCI, Cirrus CI, Gitlab CI, Appveyor, CodeShip, dsari, Cloudflare Pages/Workers "CI_APP_ID", // Appflow "CI_BUILD_ID", // Appflow "CI_BUILD_NUMBER", // Appflow "CI_NAME", // Codeship and others "CONTINUOUS_INTEGRATION", // Travis CI, Cirrus CI "RUN_ID", // TaskCluster, dsari } for _, key := range envKeys { if _, exists := os.LookupEnv(key); exists { return true } } return false } func IsVendor(vendor string) bool { return vendorsIsCI[vendor] } func verifyCI(e env) bool { value, exists := os.LookupEnv(e.key) if !exists { return false } if e.includes != "" && !strings.Contains(value, e.includes) { return false } if e.eq != "" && value != e.eq { return false } return true } func anyPr(pr []pr) bool { for _, p := range pr { if verifyPr(p) { return true } } return false } func verifyPr(p pr) bool { value, exists := os.LookupEnv(p.key) if !exists { return false } // if we have eq constraint then env value must be == with the eq if p.eq != "" { return value == p.eq } // if we have ne constraint then env value must be != with the ne if p.ne != "" { return value != p.ne } return true } ciinfo-0.3.3/ciinfo/000077500000000000000000000000001504176042500142375ustar00rootroot00000000000000ciinfo-0.3.3/ciinfo/main.go000066400000000000000000000021131504176042500155070ustar00rootroot00000000000000package main import ( "flag" "fmt" "io" "os" "github.com/gkampitakis/ciinfo" ) const ( YES = 0 NO = -1 ) func main() { isPr := flag.Bool("pr", false, "check if shell is running on CI for a Pull Request.") output := flag.String("output", "", "you can output info [json, pretty].") flag.Parse() run(os.Exit, os.Stdout, *isPr, *output) } func run(exit func(int), w io.Writer, isPR bool, output string) { result := NO if isPR { if ciinfo.IsPr { result = YES } exit(result) } if output == "json" { jsonPrint(w) return } if output == "pretty" { prettyPrint(w) return } if ciinfo.IsCI { result = YES } exit(result) } func jsonPrint(w io.Writer) { fmt.Fprintf(w, `{ "is_ci": %t, "ci_name": "%s", "pull_request": %t } `, ciinfo.IsCI, ciinfo.Name, ciinfo.IsPr) } func prettyPrint(w io.Writer) { if !ciinfo.IsCI { fmt.Fprintln(w, "Not running on CI.") return } if ciinfo.Name != "" { fmt.Fprintf(w, "CI Name: %s\n", ciinfo.Name) } else { fmt.Fprintln(w, "Running on CI.") } if ciinfo.IsPr { fmt.Fprintln(w, "Is Pull Request.") } } ciinfo-0.3.3/ciinfo/main_test.go000066400000000000000000000052111504176042500165500ustar00rootroot00000000000000package main import ( "io" "strings" "testing" "github.com/gkampitakis/ciinfo" ) func mockExit(t *testing.T, expectedCode int) func(int) { t.Helper() return func(i int) { if i != expectedCode { t.Errorf("expected code: %d but got: %d\n", expectedCode, i) } } } type mockWriter struct { mockWrite func([]byte) } func (m mockWriter) Write(p []byte) (int, error) { m.mockWrite(p) return 0, nil } func TestCMD(t *testing.T) { t.Run("should be successful in case of pr", func(t *testing.T) { ciinfo.IsPr = true run(mockExit(t, 0), io.Discard, true, "") }) t.Run("should exit with error in case not being a pr", func(t *testing.T) { ciinfo.IsPr = false ciinfo.IsCI = false run(mockExit(t, -1), io.Discard, true, "") }) t.Run("should print json output", func(t *testing.T) { ciinfo.IsPr = true ciinfo.Name = "mock-ci" ciinfo.IsCI = true m := mockWriter{ mockWrite: func(b []byte) { // clean up tabs,spaces and newlines chars := []string{" ", "\n", "\t"} v := string(b) for _, c := range chars { v = strings.ReplaceAll(v, c, "") } if v != `{"is_ci":true,"ci_name":"mock-ci","pull_request":true}` { t.Error("expected json format") } }, } run(nil, m, false, "json") }) t.Run("should print pretty output when not on ci", func(t *testing.T) { ciinfo.IsCI = false m := mockWriter{ mockWrite: func(b []byte) { if string(b) != "Not running on CI.\n" { t.Error("expected not running on CI.") } }, } run(nil, m, false, "pretty") }) t.Run("should print pretty output", func(t *testing.T) { ciinfo.IsPr = true ciinfo.Name = "mock-ci" ciinfo.IsCI = true msgs := []string{ "CI Name: mock-ci\n", "Is Pull Request.\n", } m := mockWriter{ mockWrite: func(b []byte) { msg := msgs[0] msgs = msgs[1:] if string(b) != msg { t.Errorf("unexpected message: %s", string(b)) } }, } run(nil, m, false, "pretty") }) t.Run("should print pretty output without name", func(t *testing.T) { ciinfo.IsPr = true ciinfo.Name = "" ciinfo.IsCI = true msgs := []string{ "Running on CI.\n", "Is Pull Request.\n", } m := mockWriter{ mockWrite: func(b []byte) { msg := msgs[0] msgs = msgs[1:] if string(b) != msg { t.Errorf("unexpected message: %s", string(b)) } }, } run(nil, m, false, "pretty") }) t.Run("should be successful code", func(t *testing.T) { ciinfo.IsPr = true ciinfo.Name = "mock-ci" ciinfo.IsCI = true run(mockExit(t, 0), io.Discard, false, "") }) t.Run("should be error code", func(t *testing.T) { ciinfo.IsCI = false run(mockExit(t, -1), io.Discard, false, "") }) } ciinfo-0.3.3/ciinfo_test.go000066400000000000000000000511441504176042500156320ustar00rootroot00000000000000package ciinfo import ( "os" "reflect" "testing" ) func assertEqual(t *testing.T, expected, actual interface{}, s ...string) { t.Helper() if !reflect.DeepEqual(expected, actual) { message := "" if len(s) > 0 { message = " - " + s[0] } t.Errorf("\n[expected]: %v\n[actual]: %v%s", expected, actual, message) } } func isActualPr() bool { value, exists := os.LookupEnv("GITHUB_EVENT_NAME") return exists && value == "pull_request" } func assertVendorConstants(t *testing.T, expected string) { t.Helper() for _, vendor := range vendors { boolean := vendor.constant == expected assertEqual(t, boolean, vendorsIsCI[vendor.constant], "ci."+vendor.constant) } } type ScenarioExpected struct { isPR bool name string constant string } type TestScenario struct { description string setup func(t *testing.T) expected ScenarioExpected } func TestCI(t *testing.T) { t.Run("Known CI", func(t *testing.T) { t.Setenv("GITHUB_ACTIONS", "true") initialize() assertEqual(t, 52, len(vendors), "We should have 51 vendors") assertEqual(t, true, IsCI) assertEqual(t, isActualPr(), IsPr) assertEqual(t, "GitHub Actions", Name) assertVendorConstants(t, "GITHUB_ACTIONS") }) t.Run("Not CI", func(t *testing.T) { t.Run("explicitly", func(t *testing.T) { os.Clearenv() // should ignore this and respect CI == false t.Setenv("BUILD_ID", "true") t.Setenv("CI", "false") initialize() assertEqual(t, false, IsCI) assertEqual(t, false, IsPr) assertEqual(t, "", Name) assertVendorConstants(t, "") }) t.Run("implicitly", func(t *testing.T) { os.Clearenv() initialize() assertEqual(t, false, IsCI) assertEqual(t, false, IsPr) assertEqual(t, "", Name) assertVendorConstants(t, "") }) }) t.Run("Anonymous CI", func(t *testing.T) { envKeys := []string{ "BUILD_ID", // Jenkins, Cloudbees "BUILD_NUMBER", // Jenkins, TeamCity "CI", // Travis CI, CircleCI, Cirrus CI, Gitlab CI, Appveyor, CodeShip, dsari, Cloudflare Pages "CI_APP_ID", // Appflow "CI_BUILD_ID", // Appflow "CI_BUILD_NUMBER", // Appflow "CI_NAME", // Codeship and others "CONTINUOUS_INTEGRATION", // Travis CI, Cirrus CI "RUN_ID", // TaskCluster, dsari } for _, key := range envKeys { t.Run(key, func(t *testing.T) { t.Setenv(key, "true") initialize() assertEqual(t, true, IsCI) assertEqual(t, false, IsPr) assertEqual(t, "", Name) assertVendorConstants(t, "") }) } }) for _, scenario := range []TestScenario{ { description: "AWS CodeBuild - PR", expected: ScenarioExpected{ isPR: true, name: "AWS CodeBuild", constant: "CODEBUILD", }, setup: func(t *testing.T) { t.Setenv("CODEBUILD_BUILD_ARN", "arn") t.Setenv("CODEBUILD_WEBHOOK_EVENT", "PULL_REQUEST_CREATED") }, }, { description: "AWS CodeBuild - PR", expected: ScenarioExpected{ isPR: true, name: "AWS CodeBuild", constant: "CODEBUILD", }, setup: func(t *testing.T) { t.Setenv("CODEBUILD_BUILD_ARN", "arn") t.Setenv("CODEBUILD_WEBHOOK_EVENT", "PULL_REQUEST_UPDATED") }, }, { description: "AWS CodeBuild - PR", expected: ScenarioExpected{ isPR: true, name: "AWS CodeBuild", constant: "CODEBUILD", }, setup: func(t *testing.T) { t.Setenv("CODEBUILD_BUILD_ARN", "arn") t.Setenv("CODEBUILD_WEBHOOK_EVENT", "PULL_REQUEST_REOPENED") }, }, { description: "AWS CodeBuild - Not PR", expected: ScenarioExpected{ isPR: false, name: "AWS CodeBuild", constant: "CODEBUILD", }, setup: func(t *testing.T) { t.Setenv("CODEBUILD_BUILD_ARN", "arn") t.Setenv("CODEBUILD_WEBHOOK_EVENT", "some-event") }, }, { description: "AppVeyor - PR", expected: ScenarioExpected{ isPR: true, name: "AppVeyor", constant: "APPVEYOR", }, setup: func(t *testing.T) { t.Setenv("APPVEYOR", "true") t.Setenv("APPVEYOR_PULL_REQUEST_NUMBER", "42") }, }, { description: "AppVeyor - Not PR", expected: ScenarioExpected{ isPR: false, name: "AppVeyor", constant: "APPVEYOR", }, setup: func(t *testing.T) { t.Setenv("APPVEYOR", "true") }, }, { description: "Azure Pipelines - PR", expected: ScenarioExpected{ isPR: true, name: "Azure Pipelines", constant: "AZURE_PIPELINES", }, setup: func(t *testing.T) { t.Setenv("TF_BUILD", "true") t.Setenv("BUILD_REASON", "PullRequest") }, }, { description: "Appcircle - PR", expected: ScenarioExpected{ isPR: true, name: "Appcircle", constant: "APPCIRCLE", }, setup: func(t *testing.T) { t.Setenv("AC_APPCIRCLE", "true") t.Setenv("AC_GIT_PR", "true") }, }, { description: "Appcircle - Not PR", expected: ScenarioExpected{ isPR: false, name: "Appcircle", constant: "APPCIRCLE", }, setup: func(t *testing.T) { t.Setenv("AC_APPCIRCLE", "true") t.Setenv("AC_GIT_PR", "false") }, }, { description: "Azure Pipelines - Not PR", expected: ScenarioExpected{ isPR: false, name: "Azure Pipelines", constant: "AZURE_PIPELINES", }, setup: func(t *testing.T) { t.Setenv("TF_BUILD", "true") }, }, { description: "Bitbucket Pipelines - PR", expected: ScenarioExpected{ isPR: true, name: "Bitbucket Pipelines", constant: "BITBUCKET", }, setup: func(t *testing.T) { t.Setenv("BITBUCKET_COMMIT", "true") t.Setenv("BITBUCKET_PR_ID", "42") }, }, { description: "Bitbucket Pipelines - Not PR", expected: ScenarioExpected{ isPR: false, name: "Bitbucket Pipelines", constant: "BITBUCKET", }, setup: func(t *testing.T) { t.Setenv("BITBUCKET_COMMIT", "true") }, }, { description: "Buildkite - PR", expected: ScenarioExpected{ isPR: true, name: "Buildkite", constant: "BUILDKITE", }, setup: func(t *testing.T) { t.Setenv("BUILDKITE", "true") t.Setenv("BUILDKITE_PULL_REQUEST", "42") }, }, { description: "Buildkite - Not PR", expected: ScenarioExpected{ isPR: false, name: "Buildkite", constant: "BUILDKITE", }, setup: func(t *testing.T) { t.Setenv("BUILDKITE", "true") t.Setenv("BUILDKITE_PULL_REQUEST", "false") }, }, { description: "CircleCI - PR", expected: ScenarioExpected{ isPR: true, name: "CircleCI", constant: "CIRCLE", }, setup: func(t *testing.T) { t.Setenv("CIRCLECI", "true") t.Setenv("CIRCLE_PULL_REQUEST", "42") }, }, { description: "CircleCI - Not PR", expected: ScenarioExpected{ isPR: false, name: "CircleCI", constant: "CIRCLE", }, setup: func(t *testing.T) { t.Setenv("CIRCLECI", "true") }, }, { description: "Cirrus CI - PR", expected: ScenarioExpected{ isPR: true, name: "Cirrus CI", constant: "CIRRUS", }, setup: func(t *testing.T) { t.Setenv("CIRRUS_CI", "true") t.Setenv("CIRRUS_PR", "42") }, }, { description: "Cirrus CI - Not PR", expected: ScenarioExpected{ isPR: false, name: "Cirrus CI", constant: "CIRRUS", }, setup: func(t *testing.T) { t.Setenv("CIRRUS_CI", "true") }, }, { description: "Cloudflare Pages - Not PR", expected: ScenarioExpected{ isPR: false, name: "Cloudflare Pages", constant: "CLOUDFLARE_PAGES", }, setup: func(t *testing.T) { t.Setenv("CF_PAGES", "1") }, }, { description: "Cloudflare Workers - Not PR", expected: ScenarioExpected{ isPR: false, name: "Cloudflare Workers", constant: "CLOUDFLARE_WORKERS", }, setup: func(t *testing.T) { t.Setenv("WORKERS_CI", "1") }, }, { description: "Codemagic - PR", expected: ScenarioExpected{ isPR: true, name: "Codemagic", constant: "CODEMAGIC", }, setup: func(t *testing.T) { t.Setenv("CM_BUILD_ID", "true") t.Setenv("CM_PULL_REQUEST", "42") }, }, { description: "Codemagic - Not PR", expected: ScenarioExpected{ isPR: false, name: "Codemagic", constant: "CODEMAGIC", }, setup: func(t *testing.T) { t.Setenv("CM_BUILD_ID", "true") }, }, { description: "Codefresh - PR", expected: ScenarioExpected{ isPR: true, name: "Codefresh", constant: "CODEFRESH", }, setup: func(t *testing.T) { t.Setenv("CF_BUILD_ID", "true") t.Setenv("CF_PULL_REQUEST_ID", "42") }, }, { description: "Codefresh - PR 2", expected: ScenarioExpected{ isPR: true, name: "Codefresh", constant: "CODEFRESH", }, setup: func(t *testing.T) { t.Setenv("CF_BUILD_ID", "true") t.Setenv("CF_PULL_REQUEST_NUMBER", "42") }, }, { description: "Codeship", expected: ScenarioExpected{ isPR: false, name: "Codeship", constant: "CODESHIP", }, setup: func(t *testing.T) { t.Setenv("CI_NAME", "codeship") }, }, { description: "Codefresh - Not PR", expected: ScenarioExpected{ isPR: false, name: "Codefresh", constant: "CODEFRESH", }, setup: func(t *testing.T) { t.Setenv("CF_BUILD_ID", "true") }, }, { description: "Drone", expected: ScenarioExpected{ isPR: false, name: "Drone", constant: "DRONE", }, setup: func(t *testing.T) { t.Setenv("DRONE", "true") }, }, { description: "Drone 2", expected: ScenarioExpected{ isPR: false, name: "Drone", constant: "DRONE", }, setup: func(t *testing.T) { t.Setenv("DRONE", "true") t.Setenv("DRONE_BUILD_EVENT", "test") }, }, { description: "Drone - Pr", expected: ScenarioExpected{ isPR: true, name: "Drone", constant: "DRONE", }, setup: func(t *testing.T) { t.Setenv("DRONE", "true") t.Setenv("DRONE_BUILD_EVENT", "pull_request") }, }, { description: "Jenkins - PR", expected: ScenarioExpected{ isPR: true, name: "Jenkins", constant: "JENKINS", }, setup: func(t *testing.T) { t.Setenv("JENKINS_URL", "true") t.Setenv("BUILD_ID", "true") t.Setenv("ghprbPullId", "true") }, }, { description: "Jenkins - PR 2", expected: ScenarioExpected{ isPR: true, name: "Jenkins", constant: "JENKINS", }, setup: func(t *testing.T) { t.Setenv("JENKINS_URL", "true") t.Setenv("BUILD_ID", "true") t.Setenv("CHANGE_ID", "true") }, }, { description: "Jenkins - Not PR", expected: ScenarioExpected{ isPR: false, name: "Jenkins", constant: "JENKINS", }, setup: func(t *testing.T) { t.Setenv("JENKINS_URL", "true") t.Setenv("BUILD_ID", "true") }, }, { description: "LayerCI - PR", expected: ScenarioExpected{ isPR: true, name: "LayerCI", constant: "LAYERCI", }, setup: func(t *testing.T) { t.Setenv("LAYERCI", "true") t.Setenv("LAYERCI_PULL_REQUEST", "LAYERCI_PULL_REQUEST") }, }, { description: "LayerCI - Not PR", expected: ScenarioExpected{ isPR: false, name: "LayerCI", constant: "LAYERCI", }, setup: func(t *testing.T) { t.Setenv("LAYERCI", "true") }, }, { description: "Render - PR", expected: ScenarioExpected{ isPR: true, name: "Render", constant: "RENDER", }, setup: func(t *testing.T) { t.Setenv("RENDER", "true") t.Setenv("IS_PULL_REQUEST", "true") }, }, { description: "Render - Not PR", expected: ScenarioExpected{ isPR: false, name: "Render", constant: "RENDER", }, setup: func(t *testing.T) { t.Setenv("RENDER", "true") t.Setenv("IS_PULL_REQUEST", "false") }, }, { description: "Semaphore - PR", expected: ScenarioExpected{ isPR: true, name: "Semaphore", constant: "SEMAPHORE", }, setup: func(t *testing.T) { t.Setenv("SEMAPHORE", "true") t.Setenv("PULL_REQUEST_NUMBER", "42") }, }, { description: "Semaphore - Not PR", expected: ScenarioExpected{ isPR: false, name: "Semaphore", constant: "SEMAPHORE", }, setup: func(t *testing.T) { t.Setenv("SEMAPHORE", "true") }, }, { description: "Sourcehut", expected: ScenarioExpected{ isPR: false, name: "Sourcehut", constant: "SOURCEHUT", }, setup: func(t *testing.T) { t.Setenv("CI_NAME", "sourcehut") }, }, { description: "Travis CI - PR", expected: ScenarioExpected{ isPR: true, name: "Travis CI", constant: "TRAVIS", }, setup: func(t *testing.T) { t.Setenv("TRAVIS", "true") t.Setenv("TRAVIS_PULL_REQUEST", "42") }, }, { description: "Travis CI - Not PR", expected: ScenarioExpected{ isPR: false, name: "Travis CI", constant: "TRAVIS", }, setup: func(t *testing.T) { t.Setenv("TRAVIS", "true") t.Setenv("TRAVIS_PULL_REQUEST", "false") }, }, { description: "Netlify CI - PR", expected: ScenarioExpected{ isPR: true, name: "Netlify CI", constant: "NETLIFY", }, setup: func(t *testing.T) { t.Setenv("NETLIFY", "true") t.Setenv("PULL_REQUEST", "true") }, }, { description: "Netlify CI - Not PR", expected: ScenarioExpected{ isPR: false, name: "Netlify CI", constant: "NETLIFY", }, setup: func(t *testing.T) { t.Setenv("NETLIFY", "true") t.Setenv("PULL_REQUEST", "false") }, }, { description: "ReleaseHub", expected: ScenarioExpected{ isPR: false, name: "ReleaseHub", constant: "RELEASEHUB", }, setup: func(t *testing.T) { t.Setenv("RELEASE_BUILD_ID", "") }, }, { description: "Vercel - NOW_BUILDER", expected: ScenarioExpected{ isPR: false, name: "Vercel", constant: "VERCEL", }, setup: func(t *testing.T) { t.Setenv("NOW_BUILDER", "1") }, }, { description: "Vercel - VERCEL", expected: ScenarioExpected{ isPR: false, name: "Vercel", constant: "VERCEL", }, setup: func(t *testing.T) { t.Setenv("VERCEL", "1") }, }, { description: "Vercel - PR", expected: ScenarioExpected{ isPR: true, name: "Vercel", constant: "VERCEL", }, setup: func(t *testing.T) { t.Setenv("VERCEL", "1") t.Setenv("VERCEL_GIT_PULL_REQUEST_ID", "23") }, }, { description: "Nevercode - PR", expected: ScenarioExpected{ isPR: true, name: "Nevercode", constant: "NEVERCODE", }, setup: func(t *testing.T) { t.Setenv("NEVERCODE", "true") t.Setenv("NEVERCODE_PULL_REQUEST", "true") }, }, { description: "Nevercode - Not PR", expected: ScenarioExpected{ isPR: false, name: "Nevercode", constant: "NEVERCODE", }, setup: func(t *testing.T) { t.Setenv("NEVERCODE", "true") t.Setenv("NEVERCODE_PULL_REQUEST", "false") }, }, { description: "Expo Application Services", expected: ScenarioExpected{ isPR: false, name: "Expo Application Services", constant: "EAS", }, setup: func(t *testing.T) { t.Setenv("EAS_BUILD", "1") }, }, { description: "GitHub Actions - PR", expected: ScenarioExpected{ isPR: true, name: "GitHub Actions", constant: "GITHUB_ACTIONS", }, setup: func(t *testing.T) { t.Setenv("GITHUB_ACTIONS", "true") t.Setenv("GITHUB_EVENT_NAME", "pull_request") }, }, { description: "GitHub Actions - Not PR", expected: ScenarioExpected{ isPR: false, name: "GitHub Actions", constant: "GITHUB_ACTIONS", }, setup: func(t *testing.T) { t.Setenv("GITHUB_ACTIONS", "true") t.Setenv("GITHUB_EVENT_NAME", "push") }, }, { description: "Screwdriver - PR", expected: ScenarioExpected{ isPR: true, name: "Screwdriver", constant: "SCREWDRIVER", }, setup: func(t *testing.T) { t.Setenv("SCREWDRIVER", "true") t.Setenv("SD_PULL_REQUEST", "1") }, }, { description: "Screwdriver - Not PR", expected: ScenarioExpected{ isPR: false, name: "Screwdriver", constant: "SCREWDRIVER", }, setup: func(t *testing.T) { t.Setenv("SCREWDRIVER", "true") t.Setenv("SD_PULL_REQUEST", "false") }, }, { description: "Visual Studio App Center", expected: ScenarioExpected{ isPR: false, name: "Visual Studio App Center", constant: "APPCENTER", }, setup: func(t *testing.T) { t.Setenv("APPCENTER_BUILD_ID", "1") }, }, { description: "Xcode Cloud - Not PR", expected: ScenarioExpected{ isPR: false, name: "Xcode Cloud", constant: "XCODE_CLOUD", }, setup: func(t *testing.T) { t.Setenv("CI_XCODE_PROJECT", "1") }, }, { description: "Xcode Cloud - PR", expected: ScenarioExpected{ isPR: true, name: "Xcode Cloud", constant: "XCODE_CLOUD", }, setup: func(t *testing.T) { t.Setenv("CI_XCODE_PROJECT", "1") t.Setenv("CI_PULL_REQUEST_NUMBER", "1") }, }, { description: "Xcode Server", expected: ScenarioExpected{ isPR: false, name: "Xcode Server", constant: "XCODE_SERVER", }, setup: func(t *testing.T) { t.Setenv("XCS", "1") }, }, { description: "Woodpecker - PR", expected: ScenarioExpected{ isPR: true, name: "Woodpecker", constant: "WOODPECKER", }, setup: func(t *testing.T) { t.Setenv("CI", "woodpecker") t.Setenv("CI_BUILD_EVENT", "pull_request") }, }, { description: "Woodpecker", expected: ScenarioExpected{ isPR: false, name: "Woodpecker", constant: "WOODPECKER", }, setup: func(t *testing.T) { t.Setenv("CI", "woodpecker") }, }, { description: "Heroku", expected: ScenarioExpected{ isPR: false, name: "Heroku", constant: "HEROKU", }, setup: func(t *testing.T) { t.Setenv("NODE", "/extra/content/app/.heroku/node/bin/node --extra --content") }, }, { description: "Gerrit", expected: ScenarioExpected{ isPR: false, name: "Gerrit", constant: "GERRIT", }, setup: func(t *testing.T) { t.Setenv("GERRIT_PROJECT", "1") }, }, { description: "Google Cloud Build", expected: ScenarioExpected{ isPR: false, name: "Google Cloud Build", constant: "GOOGLE_CLOUD_BUILD", }, setup: func(t *testing.T) { t.Setenv("BUILDER_OUTPUT", "1") }, }, { description: "Harness CI", expected: ScenarioExpected{ isPR: false, name: "Harness CI", constant: "HARNESS", }, setup: func(t *testing.T) { t.Setenv("HARNESS_BUILD_ID", "1") }, }, { description: "Gitea Actions", expected: ScenarioExpected{ isPR: false, name: "Gitea Actions", constant: "GITEA_ACTIONS", }, setup: func(t *testing.T) { t.Setenv("GITEA_ACTIONS", "") }, }, { description: "Agola CI", expected: ScenarioExpected{ isPR: false, name: "Agola CI", constant: "AGOLA", }, setup: func(t *testing.T) { t.Setenv("AGOLA_GIT_REF", "") }, }, { description: "Agola CI - PR", expected: ScenarioExpected{ isPR: true, name: "Agola CI", constant: "AGOLA", }, setup: func(t *testing.T) { t.Setenv("AGOLA_GIT_REF", "") t.Setenv("AGOLA_PULL_REQUEST_ID", "") }, }, { description: "Vela", expected: ScenarioExpected{ isPR: false, name: "Vela", constant: "VELA", }, setup: func(t *testing.T) { t.Setenv("VELA", "") }, }, { description: "Vela - PR", expected: ScenarioExpected{ isPR: true, name: "Vela", constant: "VELA", }, setup: func(t *testing.T) { t.Setenv("VELA", "") t.Setenv("VELA_PULL_REQUEST", "1") }, }, { description: "Prow", expected: ScenarioExpected{ isPR: false, name: "Prow", constant: "PROW", }, setup: func(t *testing.T) { t.Setenv("PROW_JOB_ID", "123") }, }, { description: "Earthly CI", expected: ScenarioExpected{ isPR: false, name: "Earthly", constant: "EARTHLY", }, setup: func(t *testing.T) { t.Setenv("EARTHLY_CI", "") }, }, } { t.Run(scenario.description, func(t *testing.T) { scenario.setup(t) initialize() assertEqual(t, true, IsCI) assertEqual(t, scenario.expected.isPR, IsPr) assertEqual(t, scenario.expected.name, Name) assertEqual(t, true, IsVendor(scenario.expected.constant)) assertEqual(t, scenario.expected.constant, ID) assertVendorConstants(t, scenario.expected.constant) }) } } ciinfo-0.3.3/compile-constants/000077500000000000000000000000001504176042500164325ustar00rootroot00000000000000ciinfo-0.3.3/compile-constants/main.go000066400000000000000000000011741504176042500177100ustar00rootroot00000000000000package main import ( "fmt" "os" ) const template = ` // CI is running on %s var %s = vendorsIsCI["%s"] ` func main() { f, err := os.Create("constants.go") if err != nil { fmt.Printf("error opening file %s\n", err) return } defer f.Close() if _, err := f.WriteString( "package ciinfo\n\n// This file is generated. Run `make compile-constants` to update.\n", ); err != nil { fmt.Printf("error writing on file %s\n", err) return } for _, v := range vendors { if _, err := fmt.Fprintf(f, template, v.name, v.constant, v.constant); err != nil { fmt.Printf("error writing on file %s\n", err) return } } } ciinfo-0.3.3/constants.go000066400000000000000000000073101504176042500153340ustar00rootroot00000000000000package ciinfo // This file is generated. Run `make compile-constants` to update. // CI is running on Agola CI var AGOLA = vendorsIsCI["AGOLA"] // CI is running on Appcircle var APPCIRCLE = vendorsIsCI["APPCIRCLE"] // CI is running on AppVeyor var APPVEYOR = vendorsIsCI["APPVEYOR"] // CI is running on AWS CodeBuild var CODEBUILD = vendorsIsCI["CODEBUILD"] // CI is running on Azure Pipelines var AZURE_PIPELINES = vendorsIsCI["AZURE_PIPELINES"] // CI is running on Bamboo var BAMBOO = vendorsIsCI["BAMBOO"] // CI is running on Bitbucket Pipelines var BITBUCKET = vendorsIsCI["BITBUCKET"] // CI is running on Bitrise var BITRISE = vendorsIsCI["BITRISE"] // CI is running on Buddy var BUDDY = vendorsIsCI["BUDDY"] // CI is running on Buildkite var BUILDKITE = vendorsIsCI["BUILDKITE"] // CI is running on CircleCI var CIRCLE = vendorsIsCI["CIRCLE"] // CI is running on Cloudflare Pages var CLOUDFLARE_PAGES = vendorsIsCI["CLOUDFLARE_PAGES"] // CI is running on Cloudflare Workers var CLOUDFLARE_WORKERS = vendorsIsCI["CLOUDFLARE_WORKERS"] // CI is running on Cirrus CI var CIRRUS = vendorsIsCI["CIRRUS"] // CI is running on Codefresh var CODEFRESH = vendorsIsCI["CODEFRESH"] // CI is running on Codemagic var CODEMAGIC = vendorsIsCI["CODEMAGIC"] // CI is running on Codeship var CODESHIP = vendorsIsCI["CODESHIP"] // CI is running on Drone var DRONE = vendorsIsCI["DRONE"] // CI is running on dsari var DSARI = vendorsIsCI["DSARI"] // CI is running on Earthly var EARTHLY = vendorsIsCI["EARTHLY"] // CI is running on Expo Application Services var EAS = vendorsIsCI["EAS"] // CI is running on Gerrit var GERRIT = vendorsIsCI["GERRIT"] // CI is running on Gitea Actions var GITEA_ACTIONS = vendorsIsCI["GITEA_ACTIONS"] // CI is running on GitHub Actions var GITHUB_ACTIONS = vendorsIsCI["GITHUB_ACTIONS"] // CI is running on GitLab CI var GITLAB = vendorsIsCI["GITLAB"] // CI is running on GoCD var GOCD = vendorsIsCI["GOCD"] // CI is running on Google Cloud Build var GOOGLE_CLOUD_BUILD = vendorsIsCI["GOOGLE_CLOUD_BUILD"] // CI is running on Harness CI var HARNESS = vendorsIsCI["HARNESS"] // CI is running on Hudson var HUDSON = vendorsIsCI["HUDSON"] // CI is running on Heroku var HEROKU = vendorsIsCI["HEROKU"] // CI is running on Jenkins var JENKINS = vendorsIsCI["JENKINS"] // CI is running on LayerCI var LAYERCI = vendorsIsCI["LAYERCI"] // CI is running on Magnum CI var MAGNUM = vendorsIsCI["MAGNUM"] // CI is running on Netlify CI var NETLIFY = vendorsIsCI["NETLIFY"] // CI is running on Prow var PROW = vendorsIsCI["PROW"] // CI is running on ReleaseHub var RELEASEHUB = vendorsIsCI["RELEASEHUB"] // CI is running on Nevercode var NEVERCODE = vendorsIsCI["NEVERCODE"] // CI is running on Render var RENDER = vendorsIsCI["RENDER"] // CI is running on Sail CI var SAIL = vendorsIsCI["SAIL"] // CI is running on Screwdriver var SCREWDRIVER = vendorsIsCI["SCREWDRIVER"] // CI is running on Semaphore var SEMAPHORE = vendorsIsCI["SEMAPHORE"] // CI is running on Sourcehut var SOURCEHUT = vendorsIsCI["SOURCEHUT"] // CI is running on Strider CD var STRIDER = vendorsIsCI["STRIDER"] // CI is running on TaskCluster var TASKCLUSTER = vendorsIsCI["TASKCLUSTER"] // CI is running on TeamCity var TEAMCITY = vendorsIsCI["TEAMCITY"] // CI is running on Travis CI var TRAVIS = vendorsIsCI["TRAVIS"] // CI is running on Vela var VELA = vendorsIsCI["VELA"] // CI is running on Vercel var VERCEL = vendorsIsCI["VERCEL"] // CI is running on Visual Studio App Center var APPCENTER = vendorsIsCI["APPCENTER"] // CI is running on Woodpecker var WOODPECKER = vendorsIsCI["WOODPECKER"] // CI is running on Xcode Cloud var XCODE_CLOUD = vendorsIsCI["XCODE_CLOUD"] // CI is running on Xcode Server var XCODE_SERVER = vendorsIsCI["XCODE_SERVER"] ciinfo-0.3.3/go.mod000066400000000000000000000000561504176042500140770ustar00rootroot00000000000000module github.com/gkampitakis/ciinfo go 1.20 ciinfo-0.3.3/go.sum000066400000000000000000000000001504176042500141110ustar00rootroot00000000000000ciinfo-0.3.3/golangci.yml000066400000000000000000000005731504176042500153030ustar00rootroot00000000000000linters: enable: - unconvert - stylecheck - lll - unparam - goimports - unparam - gofumpt - revive - unconvert - gocognit - errcheck linters-settings: gocognit: min-complexity: 15 lll: line-length: 130 gofumpt: extra-rules: true issues: exclude-files: - constants.go exclude-dirs: - compile-constants ciinfo-0.3.3/vendors.go000066400000000000000000000166161504176042500150110ustar00rootroot00000000000000package ciinfo type pr struct { key string ne string eq string } type env struct { key string eq string includes string } type vendor struct { name string constant string // if multiple keys on env only one is needed anyEnv bool env []env pr []pr } var vendors = []vendor{ { name: "Agola CI", constant: "AGOLA", env: []env{{key: "AGOLA_GIT_REF"}}, pr: []pr{{key: "AGOLA_PULL_REQUEST_ID"}}, }, { name: "Appcircle", constant: "APPCIRCLE", env: []env{{key: "AC_APPCIRCLE"}}, pr: []pr{{key: "AC_GIT_PR", ne: "false"}}, }, { name: "AppVeyor", constant: "APPVEYOR", env: []env{{key: "APPVEYOR"}}, pr: []pr{ { key: "APPVEYOR_PULL_REQUEST_NUMBER", }, }, }, { name: "AWS CodeBuild", constant: "CODEBUILD", env: []env{{key: "CODEBUILD_BUILD_ARN"}}, pr: []pr{ {key: "CODEBUILD_WEBHOOK_EVENT", eq: "PULL_REQUEST_CREATED"}, {key: "CODEBUILD_WEBHOOK_EVENT", eq: "PULL_REQUEST_UPDATED"}, {key: "CODEBUILD_WEBHOOK_EVENT", eq: "PULL_REQUEST_REOPENED"}, }, }, { name: "Azure Pipelines", constant: "AZURE_PIPELINES", env: []env{{key: "TF_BUILD"}}, pr: []pr{{key: "BUILD_REASON", eq: "PullRequest"}}, }, { name: "Bamboo", constant: "BAMBOO", env: []env{{key: "bamboo_planKey"}}, }, { name: "Bitbucket Pipelines", constant: "BITBUCKET", env: []env{{key: "BITBUCKET_COMMIT"}}, pr: []pr{ { key: "BITBUCKET_PR_ID", }, }, }, { name: "Bitrise", constant: "BITRISE", env: []env{{key: "BITRISE_IO"}}, pr: []pr{ { key: "BITRISE_PULL_REQUEST", }, }, }, { name: "Buddy", constant: "BUDDY", env: []env{{key: "BUDDY_WORKSPACE_ID"}}, pr: []pr{ { key: "BUDDY_EXECUTION_PULL_REQUEST_ID", }, }, }, { name: "Buildkite", constant: "BUILDKITE", env: []env{{key: "BUILDKITE"}}, pr: []pr{ { key: "BUILDKITE_PULL_REQUEST", ne: "false", }, }, }, { name: "CircleCI", constant: "CIRCLE", env: []env{{key: "CIRCLECI"}}, pr: []pr{ { key: "CIRCLE_PULL_REQUEST", }, }, }, { name: "Cloudflare Pages", constant: "CLOUDFLARE_PAGES", env: []env{{key: "CF_PAGES"}}, }, { name: "Cloudflare Workers", constant: "CLOUDFLARE_WORKERS", env: []env{{key: "WORKERS_CI"}}, }, { name: "Cirrus CI", constant: "CIRRUS", env: []env{{key: "CIRRUS_CI"}}, pr: []pr{{key: "CIRRUS_PR"}}, }, { name: "Codefresh", constant: "CODEFRESH", env: []env{{key: "CF_BUILD_ID"}}, pr: []pr{ {key: "CF_PULL_REQUEST_NUMBER"}, {key: "CF_PULL_REQUEST_ID"}, }, }, { name: "Codemagic", constant: "CODEMAGIC", env: []env{{key: "CM_BUILD_ID"}}, pr: []pr{{key: "CM_PULL_REQUEST"}}, }, { name: "Codeship", constant: "CODESHIP", env: []env{{key: "CI_NAME", eq: "codeship"}}, }, { name: "Drone", constant: "DRONE", env: []env{{key: "DRONE"}}, pr: []pr{ { key: "DRONE_BUILD_EVENT", eq: "pull_request", }, }, }, { name: "dsari", constant: "DSARI", env: []env{{key: "DSARI"}}, }, { name: "Earthly", constant: "EARTHLY", env: []env{{key: "EARTHLY_CI"}}, }, { name: "Expo Application Services", constant: "EAS", env: []env{{key: "EAS_BUILD"}}, }, { name: "Gerrit", constant: "GERRIT", env: []env{{key: "GERRIT_PROJECT"}}, }, { name: "Gitea Actions", constant: "GITEA_ACTIONS", env: []env{{key: "GITEA_ACTIONS"}}, }, { name: "GitHub Actions", constant: "GITHUB_ACTIONS", env: []env{{key: "GITHUB_ACTIONS"}}, pr: []pr{ { key: "GITHUB_EVENT_NAME", eq: "pull_request", }, }, }, { name: "GitLab CI", constant: "GITLAB", env: []env{{key: "GITLAB_CI"}}, pr: []pr{ { key: "CI_MERGE_REQUEST_ID", }, }, }, { name: "GoCD", constant: "GOCD", env: []env{{key: "GO_PIPELINE_LABEL"}}, }, { name: "Google Cloud Build", constant: "GOOGLE_CLOUD_BUILD", env: []env{{key: "BUILDER_OUTPUT"}}, }, { name: "Harness CI", constant: "HARNESS", env: []env{{key: "HARNESS_BUILD_ID"}}, }, { name: "Hudson", constant: "HUDSON", env: []env{{key: "HUDSON_URL"}}, }, { name: "Heroku", constant: "HEROKU", env: []env{{key: "NODE", includes: "/app/.heroku/node/bin/node"}}, }, { name: "Jenkins", constant: "JENKINS", env: []env{{key: "JENKINS_URL"}, {key: "BUILD_ID"}}, pr: []pr{ { key: "ghprbPullId", }, { key: "CHANGE_ID", }, }, }, { name: "LayerCI", constant: "LAYERCI", env: []env{{key: "LAYERCI"}}, pr: []pr{ { key: "LAYERCI_PULL_REQUEST", }, }, }, { name: "Magnum CI", constant: "MAGNUM", env: []env{{key: "MAGNUM"}}, }, { name: "Netlify CI", constant: "NETLIFY", env: []env{{key: "NETLIFY"}}, pr: []pr{ { key: "PULL_REQUEST", ne: "false", }, }, }, { name: "Prow", constant: "PROW", env: []env{{key: "PROW_JOB_ID"}}, }, { name: "ReleaseHub", constant: "RELEASEHUB", env: []env{{key: "RELEASE_BUILD_ID"}}, }, { name: "Nevercode", constant: "NEVERCODE", env: []env{{key: "NEVERCODE"}}, pr: []pr{ { key: "NEVERCODE_PULL_REQUEST", ne: "false", }, }, }, { name: "Render", constant: "RENDER", env: []env{{key: "RENDER"}}, pr: []pr{ { key: "IS_PULL_REQUEST", eq: "true", }, }, }, { name: "Sail CI", constant: "SAIL", env: []env{{key: "SAILCI"}}, pr: []pr{ { key: "SAIL_PULL_REQUEST_NUMBER", }, }, }, { name: "Screwdriver", constant: "SCREWDRIVER", env: []env{{key: "SCREWDRIVER"}}, pr: []pr{ { key: "SD_PULL_REQUEST", ne: "false", }, }, }, { name: "Semaphore", constant: "SEMAPHORE", env: []env{{key: "SEMAPHORE"}}, pr: []pr{ { key: "PULL_REQUEST_NUMBER", }, }, }, { name: "Sourcehut", constant: "SOURCEHUT", env: []env{{key: "CI_NAME", eq: "sourcehut"}}, }, { name: "Strider CD", constant: "STRIDER", env: []env{{key: "STRIDER"}}, }, { name: "TaskCluster", constant: "TASKCLUSTER", env: []env{{key: "TASK_ID"}, {key: "RUN_ID"}}, }, { name: "TeamCity", constant: "TEAMCITY", env: []env{{key: "TEAMCITY_VERSION"}}, }, { name: "Travis CI", constant: "TRAVIS", env: []env{{key: "TRAVIS"}}, pr: []pr{ { key: "TRAVIS_PULL_REQUEST", ne: "false", }, }, }, { name: "Vela", constant: "VELA", env: []env{{key: "VELA"}}, pr: []pr{{key: "VELA_PULL_REQUEST", eq: "1"}}, }, { name: "Vercel", constant: "VERCEL", anyEnv: true, env: []env{{key: "NOW_BUILDER"}, {key: "VERCEL"}}, pr: []pr{ {key: "VERCEL_GIT_PULL_REQUEST_ID"}, }, }, { name: "Visual Studio App Center", constant: "APPCENTER", env: []env{{key: "APPCENTER_BUILD_ID"}}, }, { name: "Woodpecker", constant: "WOODPECKER", env: []env{{key: "CI", eq: "woodpecker"}}, pr: []pr{{key: "CI_BUILD_EVENT", eq: "pull_request"}}, }, { name: "Xcode Cloud", constant: "XCODE_CLOUD", env: []env{{key: "CI_XCODE_PROJECT"}}, pr: []pr{{key: "CI_PULL_REQUEST_NUMBER"}}, }, { name: "Xcode Server", constant: "XCODE_SERVER", env: []env{{key: "XCS"}}, }, }