github.com/Mistwind/reviewdog@v0.0.0-20230317041057-48e69b6d9e86/cienv/cienv.go (about) 1 // Package cienv provides utility for environment variable in CI services. 2 package cienv 3 4 import ( 5 "errors" 6 "os" 7 "regexp" 8 "strconv" 9 "strings" 10 ) 11 12 // BuildInfo represents build information about GitHub or GitLab project. 13 type BuildInfo struct { 14 Owner string 15 Repo string 16 SHA string 17 18 // Optional. only for gitlab-push-commit 19 BeforeSHA string 20 21 // Optional. 22 PullRequest int // MergeRequest for GitLab. 23 24 // Optional. 25 Branch string 26 27 // Gerrit related params 28 GerritChangeID string 29 GerritRevisionID string 30 } 31 32 // GetBuildInfo returns BuildInfo from environment variables. 33 // 34 // Supported CI services' documents: 35 // - Travis CI: https://docs.travis-ci.com/user/environment-variables/ 36 // - Circle CI: https://circleci.com/docs/environment-variables/ 37 // - Drone.io: http://docs.drone.io/environment-reference/ 38 // - GitLab CI: https://docs.gitlab.com/ee/ci/variables/#predefined-variables-environment-variables 39 // - GitLab CI doesn't export ID of Merge Request. https://gitlab.com/gitlab-org/gitlab-ce/issues/15280 40 func GetBuildInfo() (prInfo *BuildInfo, isPR bool, err error) { 41 if IsInGitHubAction() { 42 return getBuildInfoFromGitHubAction() 43 } 44 owner, repo := getOwnerAndRepoFromSlug([]string{ 45 "TRAVIS_REPO_SLUG", 46 "DRONE_REPO", // drone<=0.4 47 "BITBUCKET_REPO_FULL_NAME", 48 }) 49 if owner == "" { 50 owner = getOneEnvValue([]string{ 51 "CI_REPO_OWNER", // common 52 "CIRCLE_PROJECT_USERNAME", 53 "DRONE_REPO_OWNER", 54 "CI_PROJECT_NAMESPACE", // GitLab CI 55 }) 56 } 57 if owner == "" { 58 return nil, false, errors.New("cannot get repo owner from environment variable. Set CI_REPO_OWNER?") 59 } 60 61 if repo == "" { 62 repo = getOneEnvValue([]string{ 63 "CI_REPO_NAME", // common 64 "CIRCLE_PROJECT_REPONAME", 65 "DRONE_REPO_NAME", 66 "CI_PROJECT_NAME", // GitLab CI 67 }) 68 } 69 70 if repo == "" { 71 return nil, false, errors.New("cannot get repo name from environment variable. Set CI_REPO_NAME?") 72 } 73 74 sha := getOneEnvValue([]string{ 75 "CI_COMMIT", // common 76 "TRAVIS_PULL_REQUEST_SHA", 77 "TRAVIS_COMMIT", 78 "CIRCLE_SHA1", 79 "DRONE_COMMIT", 80 "CI_COMMIT_SHA", // GitLab CI 81 "BITBUCKET_COMMIT", 82 }) 83 if sha == "" { 84 return nil, false, errors.New("cannot get commit SHA from environment variable. Set CI_COMMIT?") 85 } 86 87 beforeSHA := getOneEnvValue([]string{ 88 "CI_COMMIT_BEFORE_SHA", 89 }) 90 91 branch := getOneEnvValue([]string{ 92 "CI_BRANCH", // common 93 "TRAVIS_PULL_REQUEST_BRANCH", 94 "CIRCLE_BRANCH", 95 "DRONE_COMMIT_BRANCH", 96 // present only if PR pipeline 97 "BITBUCKET_PR_DESTINATION_BRANCH", 98 "BITBUCKET_BRANCH", 99 }) 100 101 pr := getPullRequestNum() 102 103 return &BuildInfo{ 104 Owner: owner, 105 Repo: repo, 106 PullRequest: pr, 107 SHA: sha, 108 BeforeSHA: beforeSHA, 109 Branch: branch, 110 }, pr != 0, nil 111 } 112 113 // GetGerritBuildInfo returns Gerrit specific build info 114 func GetGerritBuildInfo() (*BuildInfo, error) { 115 changeID := os.Getenv("GERRIT_CHANGE_ID") 116 if changeID == "" { 117 return nil, errors.New("cannot get change id from environment variable. Set GERRIT_CHANGE_ID ?") 118 } 119 120 revisionID := os.Getenv("GERRIT_REVISION_ID") 121 if revisionID == "" { 122 return nil, errors.New("cannot get revision id from environment variable. Set GERRIT_REVISION_ID ?") 123 } 124 125 branch := os.Getenv("GERRIT_BRANCH") 126 if branch == "" { 127 return nil, errors.New("cannot get branch from environment variable. Set GERRIT_BRANCH ?") 128 } 129 130 return &BuildInfo{ 131 GerritChangeID: changeID, 132 GerritRevisionID: revisionID, 133 Branch: branch, 134 }, nil 135 } 136 137 func getPullRequestNum() int { 138 envs := []string{ 139 // Common. 140 "CI_PULL_REQUEST", 141 // Travis CI. 142 "TRAVIS_PULL_REQUEST", 143 // Circle CI. 144 "CIRCLE_PULL_REQUEST", // CircleCI 2.0 145 "CIRCLE_PR_NUMBER", // For Pull Request by a fork repository 146 // drone.io. 147 "DRONE_PULL_REQUEST", 148 // GitLab CI MergeTrains 149 "CI_MERGE_REQUEST_IID", 150 "BITBUCKET_PR_ID", 151 } 152 // regexp.MustCompile() in func intentionally because this func is called 153 // once for one run. 154 re := regexp.MustCompile(`[1-9]\d*$`) 155 for _, env := range envs { 156 prm := re.FindString(os.Getenv(env)) 157 pr, _ := strconv.Atoi(prm) 158 if pr != 0 { 159 return pr 160 } 161 } 162 return 0 163 } 164 165 func getOneEnvValue(envs []string) string { 166 for _, env := range envs { 167 if v := os.Getenv(env); v != "" { 168 return v 169 } 170 } 171 return "" 172 } 173 174 func getOwnerAndRepoFromSlug(slugEnvs []string) (string, string) { 175 repoSlug := getOneEnvValue(slugEnvs) 176 ownerAndRepo := strings.SplitN(repoSlug, "/", 2) 177 if len(ownerAndRepo) < 2 { 178 return "", "" 179 } 180 return ownerAndRepo[0], ownerAndRepo[1] 181 }