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