internal/platform/sarifVersioning.go (119 lines of code) (raw):

/* * Copyright 2021-2024 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package platform import ( "errors" "net/url" "os" "path/filepath" "strings" cienvironment "github.com/cucumber/ci-environment/go" log "github.com/sirupsen/logrus" "github.com/JetBrains/qodana-cli/internal/platform/utils" "github.com/JetBrains/qodana-cli/internal/sarif" ) // it is needed for third party linters // GetVersionDetails returns the version control details for the current repository. func GetVersionDetails(pwd string) (sarif.VersionControlDetails, error) { ret := sarif.VersionControlDetails{} if os.Getenv("QODANA_REMOTE_URL") != "" { ret.RepositoryUri = os.Getenv("QODANA_REMOTE_URL") // TODO : reuse consts } else { uri, err := getRepositoryUri(pwd) if err != nil { return ret, err } ret.RepositoryUri = uri } ret.Branch = os.Getenv("QODANA_BRANCH") if ret.Branch == "" { branch, err := getBranchName(pwd) if err != nil { return ret, err } ret.Branch = branch } // Sometimes in CI the HEAD is detached even on push-based runs. // As a last resort, try to pick up the branch name from pre-defined environment variables. if ret.Branch == "" { ci := cienvironment.DetectCIEnvironment() if ci != nil && ci.Git != nil { ret.Branch = ci.Git.Branch } } if os.Getenv("QODANA_REVISION") != "" { ret.RevisionId = os.Getenv("QODANA_REVISION") } else { rev, err := getRevisionId(pwd) if err != nil { return ret, err } ret.RevisionId = rev } ret.Properties = &sarif.PropertyBag{} ret.Properties.AdditionalProperties = map[string]interface{}{ "repoUrl": ret.RepositoryUri, "vcsType": "Git", "lastAuthorName": getLastAuthorName(pwd), "lastAuthorEmail": getAuthorEmail(pwd), } return ret, nil } func getRepositoryUri(pwd string) (string, error) { uri, stderr, ret, err := utils.RunCmdRedirectOutput(pwd, "git", "ls-remote", "--get-url") if err != nil { return "", err } if ret != 0 { // Returned when a remote is not configured or multiple remotes exist, none of which is the default log.Warn("Failed to retrieve remote URI: ", stderr) uriStruct := url.URL{ Scheme: "file", Host: "", Path: filepath.ToSlash(pwd), } return uriStruct.String(), nil } trimUrl := strings.TrimSpace(uri) if !strings.Contains(trimUrl, "://") { trimUrl = "ssh://" + trimUrl } return trimUrl, nil } func getRevisionId(pwd string) (string, error) { rev, _, ret, err := utils.RunCmdRedirectOutput(pwd, "git", "rev-parse", "HEAD") if err != nil { return "", err } if ret != 0 { return "", errors.New("git rev-parse HEAD failed") } return strings.TrimSpace(rev), nil } func getBranchName(pwd string) (string, error) { // note: git branch --show-current not used because the flag is too recent at the time of writing branch, _, ret, err := utils.RunCmdRedirectOutput(pwd, "git", "rev-parse", "--abbrev-ref", "HEAD") if err != nil { return "", err } if ret == 128 { // this approach covers some corner cases, notably when no commits exist branch, _, ret, err = utils.RunCmdRedirectOutput(pwd, "git", "symbolic-ref", "--short", "HEAD") if err != nil { return "", err } } if ret != 0 { return "", errors.New("git rev-parse --abbrev-ref HEAD failed") } branch = strings.TrimSpace(branch) if branch == "HEAD" { // HEAD is a reserved name in git, so HEAD can only mean that HEAD is detached. return "", nil } return branch, nil } func getLastAuthorName(pwd string) string { name, _, ret, err := utils.RunCmdRedirectOutput(pwd, "git", "log", "-1", "--pretty=format:%an") if err != nil || ret != 0 { return "" } return strings.TrimSpace(name) } func getAuthorEmail(pwd string) string { email, _, ret, err := utils.RunCmdRedirectOutput(pwd, "git", "log", "-1", "--pretty=format:%ae") if err != nil || ret != 0 { return "" } return strings.TrimSpace(email) }