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  }