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