github.com/jfrog/jfrog-cli-core/v2@v2.52.0/utils/dependencies/utils.go (about)

     1  package dependencies
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"net/http"
     7  	"os"
     8  	"path"
     9  
    10  	biutils "github.com/jfrog/build-info-go/utils"
    11  	"github.com/jfrog/jfrog-cli-core/v2/utils/config"
    12  	"github.com/jfrog/jfrog-cli-core/v2/utils/coreutils"
    13  	"github.com/jfrog/jfrog-client-go/http/httpclient"
    14  	"github.com/jfrog/jfrog-client-go/http/jfroghttpclient"
    15  	"github.com/jfrog/jfrog-client-go/utils/errorutils"
    16  	"github.com/jfrog/jfrog-client-go/utils/io/fileutils"
    17  	"github.com/jfrog/jfrog-client-go/utils/io/httputils"
    18  	"github.com/jfrog/jfrog-client-go/utils/log"
    19  )
    20  
    21  const (
    22  	ChecksumFileName = "checksum.sha2"
    23  )
    24  
    25  // Download the relevant build-info-extractor jar.
    26  // By default, the jar is downloaded directly from jfrog releases.
    27  //
    28  // targetPath: The local download path (without the file name).
    29  // downloadPath: Artifactory download path.
    30  func DownloadExtractor(targetPath, downloadPath string) error {
    31  	artDetails, remotePath, err := GetExtractorsRemoteDetails(downloadPath)
    32  	if err != nil {
    33  		return err
    34  	}
    35  
    36  	return DownloadDependency(artDetails, remotePath, targetPath, false)
    37  }
    38  
    39  func CreateChecksumFile(targetPath, checksum string) (err error) {
    40  	out, err := os.Create(targetPath)
    41  	defer func() {
    42  		err = errors.Join(err, errorutils.CheckError(out.Close()))
    43  	}()
    44  	if errorutils.CheckError(err) != nil {
    45  		return err
    46  	}
    47  	if _, err = out.Write([]byte(checksum)); err != nil {
    48  		return errorutils.CheckError(err)
    49  	}
    50  	return
    51  }
    52  
    53  // GetExtractorsRemoteDetails retrieves the server details necessary to download the build-info extractors from a remote repository.
    54  // downloadPath - specifies the path in the remote repository from which the extractors will be downloaded.
    55  func GetExtractorsRemoteDetails(downloadPath string) (server *config.ServerDetails, remoteRepo string, err error) {
    56  	// Download from the remote repository that proxies https://releases.jfrog.io
    57  	server, remoteRepo, err = getExtractorsRemoteDetailsFromEnv(downloadPath)
    58  	if remoteRepo == "" && err == nil {
    59  		// Fallback to the deprecated JFROG_CLI_EXTRACTORS_REMOTE environment variable
    60  		server, remoteRepo, err = getExtractorsRemoteDetailsFromLegacyEnv(downloadPath)
    61  	}
    62  	if remoteRepo != "" || err != nil {
    63  		return
    64  	}
    65  	// Download directly from https://releases.jfrog.io
    66  	log.Info("The build-info-extractor jar is not cached locally. Downloading it now...\n" +
    67  		"You can set the repository from which this jar is downloaded.\n" +
    68  		"Read more about it at " + coreutils.JFrogHelpUrl + "jfrog-cli/downloading-the-maven-and-gradle-extractor-jars")
    69  	log.Debug("'" + coreutils.ReleasesRemoteEnv + "' environment variable is not configured. Downloading directly from releases.jfrog.io.")
    70  	// If not configured to download through a remote repository in Artifactory, download from releases.jfrog.io.
    71  	return &config.ServerDetails{ArtifactoryUrl: coreutils.JfrogReleasesUrl}, path.Join("oss-release-local", downloadPath), nil
    72  }
    73  
    74  func getExtractorsRemoteDetailsFromEnv(downloadPath string) (server *config.ServerDetails, remoteRepo string, err error) {
    75  	server, remoteRepo, err = GetRemoteDetails(coreutils.ReleasesRemoteEnv)
    76  	if remoteRepo != "" && err == nil {
    77  		remoteRepo = getFullExtractorsPathInArtifactory(remoteRepo, coreutils.ReleasesRemoteEnv, downloadPath)
    78  	}
    79  	return
    80  }
    81  
    82  func getExtractorsRemoteDetailsFromLegacyEnv(downloadPath string) (server *config.ServerDetails, remoteRepo string, err error) {
    83  	server, remoteRepo, err = GetRemoteDetails(coreutils.DeprecatedExtractorsRemoteEnv)
    84  	if remoteRepo != "" && err == nil {
    85  		log.Warn(fmt.Sprintf("You are using the deprecated %q environment variable. Use %q instead.\nRead more about it at %sjfrog-cli/downloading-the-maven-and-gradle-extractor-jars",
    86  			coreutils.DeprecatedExtractorsRemoteEnv, coreutils.ReleasesRemoteEnv, coreutils.JFrogHelpUrl))
    87  		remoteRepo = getFullExtractorsPathInArtifactory(remoteRepo, coreutils.DeprecatedExtractorsRemoteEnv, downloadPath)
    88  	}
    89  	return
    90  }
    91  
    92  // GetRemoteDetails function retrieves the server details and downloads path for the build-info extractor file.
    93  // serverAndRepo - the server id and the remote repository that proxies releases.jfrog.io, in form of '<ServerID>/<RemoteRepo>'.
    94  // downloadPath - specifies the path in the remote repository from which the extractors will be downloaded.
    95  // remoteEnv - the relevant environment variable that was used: releasesRemoteEnv/ExtractorsRemoteEnv.
    96  // The function returns the server that matches the given server ID, the complete path of the build-info extractor concatenated with the specified remote repository, and an error if occurred.
    97  func GetRemoteDetails(remoteEnv string) (server *config.ServerDetails, repoName string, err error) {
    98  	serverID, repoName, err := coreutils.GetServerIdAndRepo(remoteEnv)
    99  	if err != nil {
   100  		return
   101  	}
   102  	if serverID == "" && repoName == "" {
   103  		// Remote details weren't configured. Assuming that https://releases.jfrog.io should be used.
   104  		return
   105  	}
   106  	server, err = config.GetSpecificConfig(serverID, false, true)
   107  	return
   108  }
   109  
   110  func getFullExtractorsPathInArtifactory(repoName, remoteEnv, downloadPath string) string {
   111  	if remoteEnv == coreutils.ReleasesRemoteEnv {
   112  		return path.Join(repoName, "artifactory", "oss-release-local", downloadPath)
   113  	}
   114  	return path.Join(repoName, downloadPath)
   115  }
   116  
   117  // Downloads the requested resource.
   118  //
   119  // artDetails: The artifactory server details to download the resource from.
   120  // downloadPath: Artifactory download path.
   121  // targetPath: The local download path (without the file name).
   122  func DownloadDependency(artDetails *config.ServerDetails, downloadPath, targetPath string, shouldExplode bool) (err error) {
   123  	downloadUrl := artDetails.ArtifactoryUrl + downloadPath
   124  	log.Info("Downloading JFrog's Dependency from", downloadUrl)
   125  	filename, localDir := fileutils.GetFileAndDirFromPath(targetPath)
   126  	tempDirPath, err := fileutils.CreateTempDir()
   127  	if err != nil {
   128  		return err
   129  	}
   130  	defer func() {
   131  		err = errors.Join(err, fileutils.RemoveTempDir(tempDirPath))
   132  	}()
   133  
   134  	// Get the expected check-sum before downloading
   135  	client, httpClientDetails, err := CreateHttpClient(artDetails)
   136  	if err != nil {
   137  		return err
   138  	}
   139  	expectedSha1 := ""
   140  	remoteFileDetails, _, err := client.GetRemoteFileDetails(downloadUrl, &httpClientDetails)
   141  	if err == nil {
   142  		expectedSha1 = remoteFileDetails.Checksum.Sha1
   143  	} else {
   144  		log.Warn(fmt.Sprintf("Failed to get remote file details.\n Got: %s", err))
   145  	}
   146  	// Download the file
   147  	downloadFileDetails := &httpclient.DownloadFileDetails{
   148  		FileName:      filename,
   149  		DownloadPath:  downloadUrl,
   150  		LocalPath:     tempDirPath,
   151  		LocalFileName: filename,
   152  		ExpectedSha1:  expectedSha1,
   153  	}
   154  	client, httpClientDetails, err = CreateHttpClient(artDetails)
   155  	if err != nil {
   156  		return err
   157  	}
   158  	resp, err := client.DownloadFile(downloadFileDetails, "", &httpClientDetails, shouldExplode, false)
   159  	if err != nil {
   160  		err = errorutils.CheckErrorf("received error while attempting to download '%s': %s"+downloadUrl, err.Error())
   161  	}
   162  	if err = errorutils.CheckResponseStatus(resp, http.StatusOK); err != nil {
   163  		return err
   164  	}
   165  	err = coreutils.SetPermissionsRecursively(tempDirPath, 0755)
   166  	if err != nil {
   167  		return err
   168  	}
   169  	return biutils.CopyDir(tempDirPath, localDir, true, nil)
   170  }
   171  
   172  func CreateHttpClient(artDetails *config.ServerDetails) (rtHttpClient *jfroghttpclient.JfrogHttpClient, httpClientDetails httputils.HttpClientDetails, err error) {
   173  	auth, err := artDetails.CreateArtAuthConfig()
   174  	if err != nil {
   175  		return
   176  	}
   177  	certsPath, err := coreutils.GetJfrogCertsDir()
   178  	if err != nil {
   179  		return
   180  	}
   181  
   182  	httpClientDetails = auth.CreateHttpClientDetails()
   183  	rtHttpClient, err = jfroghttpclient.JfrogClientBuilder().
   184  		SetCertificatesPath(certsPath).
   185  		SetInsecureTls(artDetails.InsecureTls).
   186  		SetClientCertPath(auth.GetClientCertPath()).
   187  		SetClientCertKeyPath(auth.GetClientCertKeyPath()).
   188  		AppendPreRequestInterceptor(auth.RunPreRequestFunctions).
   189  		Build()
   190  	return
   191  }