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  }