diff --git a/artifactory/commands/golang/go.go b/artifactory/commands/golang/go.go index b6950c50a..4847855a5 100644 --- a/artifactory/commands/golang/go.go +++ b/artifactory/commands/golang/go.go @@ -153,14 +153,12 @@ func (gc *GoCommand) run() (err error) { if err != nil { return } - repoUrl, err := goutils.GetArtifactoryRemoteRepoUrl(resolverDetails, gc.resolverParams.TargetRepo()) + // If noFallback=false, missing packages will be fetched directly from VCS + repoUrl, err := goutils.GetArtifactoryRemoteRepoUrl(resolverDetails, gc.resolverParams.TargetRepo(), goutils.GoProxyUrlParams{Direct: !gc.noFallback}) if err != nil { return } - // If noFallback=false, missing packages will be fetched directly from VCS - if !gc.noFallback { - repoUrl += "|direct" - } + err = biutils.RunGo(gc.goArg, repoUrl) if errorutils.CheckError(err) != nil { err = coreutils.ConvertExitCodeError(err) @@ -330,19 +328,21 @@ func buildPackageVersionRequest(name, branchName string) string { return path.Join(packageVersionRequest, "latest.info") } -func SetArtifactoryAsResolutionServer(serverDetails *config.ServerDetails, depsRepo string) (err error) { - err = setGoProxy(serverDetails, depsRepo) - if err != nil { +func SetArtifactoryAsResolutionServer(serverDetails *config.ServerDetails, depsRepo string, goProxyParams goutils.GoProxyUrlParams) (err error) { + if err = setGoProxy(serverDetails, depsRepo, goProxyParams); err != nil { err = fmt.Errorf("failed while setting Artifactory as a dependencies resolution registry: %s", err.Error()) } return } -func setGoProxy(server *config.ServerDetails, remoteGoRepo string) error { - repoUrl, err := goutils.GetArtifactoryRemoteRepoUrl(server, remoteGoRepo) +func setGoProxy(server *config.ServerDetails, remoteGoRepo string, goProxyParams goutils.GoProxyUrlParams) error { + repoUrl, err := goutils.GetArtifactoryRemoteRepoUrl(server, remoteGoRepo, goProxyParams) if err != nil { return err } - repoUrl += "|direct" return os.Setenv("GOPROXY", repoUrl) } + +func SetGoModCache(cacheFolder string) error { + return os.Setenv("GOMODCACHE", cacheFolder) +} diff --git a/artifactory/commands/golang/go_test.go b/artifactory/commands/golang/go_test.go index ed899aad3..6aaf77d26 100644 --- a/artifactory/commands/golang/go_test.go +++ b/artifactory/commands/golang/go_test.go @@ -3,6 +3,7 @@ package golang import ( "fmt" "github.com/jfrog/jfrog-cli-core/v2/utils/config" + "github.com/jfrog/jfrog-cli-core/v2/utils/coreutils" goutils "github.com/jfrog/jfrog-cli-core/v2/utils/golang" testsutils "github.com/jfrog/jfrog-client-go/utils/tests" "github.com/stretchr/testify/assert" @@ -60,9 +61,18 @@ func TestSetArtifactoryAsResolutionServer(t *testing.T) { cleanup := testsutils.SetEnvWithCallbackAndAssert(t, "GOPROXY", "") defer cleanup() - assert.NoError(t, SetArtifactoryAsResolutionServer(server, repo)) + assert.NoError(t, SetArtifactoryAsResolutionServer(server, repo, goutils.GoProxyUrlParams{Direct: true})) serverUrlWithoutHttp := strings.TrimPrefix(server.ArtifactoryUrl, "http://") expectedGoProxy := fmt.Sprintf("http://%s:%s@%sapi/go/%s|direct", server.User, server.Password, serverUrlWithoutHttp, repo) assert.Equal(t, expectedGoProxy, os.Getenv("GOPROXY")) + + // Verify that the EndpointPrefix value is correctly added to the GOPROXY. + // In this test case, the endpoint prefix is set to api/curation/audit/. + // This parameter allows downloading dependencies from a custom API instead of the default one. + assert.NoError(t, SetArtifactoryAsResolutionServer(server, repo, goutils.GoProxyUrlParams{Direct: true, EndpointPrefix: coreutils.CurationPassThroughApi})) + + serverUrlWithoutHttp = strings.TrimPrefix(server.ArtifactoryUrl, "http://") + expectedGoProxy = fmt.Sprintf("http://%s:%s@%sapi/curation/audit/api/go/%s|direct", server.User, server.Password, serverUrlWithoutHttp, repo) + assert.Equal(t, expectedGoProxy, os.Getenv("GOPROXY")) } diff --git a/go.mod b/go.mod index f5b9ece9b..a321d6552 100644 --- a/go.mod +++ b/go.mod @@ -98,6 +98,6 @@ require ( replace github.com/jfrog/jfrog-client-go => github.com/jfrog/jfrog-client-go v1.28.1-0.20240530101935-539b5837ce04 -// replace github.com/jfrog/build-info-go => github.com/jfrog/build-info-go dev +replace github.com/jfrog/build-info-go => github.com/asafambar/build-info-go v1.8.9-0.20240530151827-93c25df23371 // replace github.com/jfrog/gofrog => github.com/jfrog/gofrog v1.3.3-0.20231223133729-ef57bd08cedc diff --git a/go.sum b/go.sum index bb180023f..e468dfe92 100644 --- a/go.sum +++ b/go.sum @@ -18,6 +18,8 @@ github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer5 github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= +github.com/asafambar/build-info-go v1.8.9-0.20240530151827-93c25df23371 h1:U7bqNQ40AfeC55m9v9r8vy/Iza4xrUJiJ2DYcE031Oo= +github.com/asafambar/build-info-go v1.8.9-0.20240530151827-93c25df23371/go.mod h1:5TH6zBwOpQxGXgn+WX4OlH4q5P4k1zE1sFksU5VDhbE= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/bradleyjkemp/cupaloy/v2 v2.8.0 h1:any4BmKE+jGIaMpnU8YgH/I2LPiLBufr6oMMlVBbn9M= github.com/bradleyjkemp/cupaloy/v2 v2.8.0/go.mod h1:bm7JXdkRd4BHJk9HpwqAI8BoAY1lps46Enkdqw6aRX0= @@ -91,8 +93,6 @@ github.com/jedib0t/go-pretty/v6 v6.5.9 h1:ACteMBRrrmm1gMsXe9PSTOClQ63IXDUt03H5U+ github.com/jedib0t/go-pretty/v6 v6.5.9/go.mod h1:zbn98qrYlh95FIhwwsbIip0LYpwSG8SUOScs+v9/t0E= github.com/jfrog/archiver/v3 v3.6.0 h1:OVZ50vudkIQmKMgA8mmFF9S0gA47lcag22N13iV3F1w= github.com/jfrog/archiver/v3 v3.6.0/go.mod h1:fCAof46C3rAXgZurS8kNRNdSVMKBbZs+bNNhPYxLldI= -github.com/jfrog/build-info-go v1.9.27 h1:7RWJcajqtNNbGHuYkgOLUIG7mmRKF0yxC7mvYAbdVlU= -github.com/jfrog/build-info-go v1.9.27/go.mod h1:8T7/ajM9aGshvgpwCtXwIFpyF/R6CEn4W+/FLryNXWw= github.com/jfrog/gofrog v1.7.2 h1:VkAaA/9tmbw27IqgUOmaZWnO6ATUqL3vRzDnsROKATw= github.com/jfrog/gofrog v1.7.2/go.mod h1:WJFk88SR9Sr9mKl1bQBig7DmSdXiBGKV3WhL9O6jL9w= github.com/jfrog/jfrog-client-go v1.28.1-0.20240530101935-539b5837ce04 h1:ERLE/L7YPr6aCUTeAnE8SXU5VOZHd5/XK16rM1TEpts= diff --git a/utils/golang/utils.go b/utils/golang/utils.go index ab4176111..645b23bad 100644 --- a/utils/golang/utils.go +++ b/utils/golang/utils.go @@ -9,6 +9,8 @@ import ( "io" "net/url" "os/exec" + "path" + "strings" ) type GoCmdConfig struct { @@ -83,33 +85,41 @@ func GetModuleName(projectDir string) (string, error) { return path, nil } -func GetDependenciesList(projectDir string) (map[string]bool, error) { - deps, err := utils.GetDependenciesList(projectDir, log.Logger) - if err != nil { - return nil, errorutils.CheckError(err) - } - return deps, nil +type GoProxyUrlParams struct { + // Fallback to retrieve the modules directly from the source if + // the module failed to be retrieved from the proxy. + // add |direct to the end of the url. + // example: https://gocenter.io|direct + Direct bool + // The path from baseUrl to the standard Go repository path + // URL structure: //api/go/ + EndpointPrefix string } -func GetDependenciesGraph(projectDir string) (map[string][]string, error) { - deps, err := utils.GetDependenciesGraph(projectDir, log.Logger) - if err != nil { - return nil, errorutils.CheckError(err) +func (gdu *GoProxyUrlParams) BuildUrl(url *url.URL, repoName string) string { + url.Path = path.Join(url.Path, gdu.EndpointPrefix, "api/go/", repoName) + + return gdu.addDirect(url.String()) +} + +func (gdu *GoProxyUrlParams) addDirect(url string) string { + if gdu.Direct && !strings.HasSuffix(url, "|direct") { + return url + "|direct" } - return deps, nil + return url } -func GetArtifactoryRemoteRepoUrl(serverDetails *config.ServerDetails, repo string) (string, error) { +func GetArtifactoryRemoteRepoUrl(serverDetails *config.ServerDetails, repo string, goProxyParams GoProxyUrlParams) (string, error) { authServerDetails, err := serverDetails.CreateArtAuthConfig() if err != nil { return "", err } - return getArtifactoryApiUrl(repo, authServerDetails) + return getArtifactoryApiUrl(repo, authServerDetails, goProxyParams) } // Gets the URL of the specified repository Go API in Artifactory. // The URL contains credentials (username and access token or password). -func getArtifactoryApiUrl(repoName string, details auth.ServiceDetails) (string, error) { +func getArtifactoryApiUrl(repoName string, details auth.ServiceDetails, goProxyParams GoProxyUrlParams) (string, error) { rtUrl, err := url.Parse(details.GetUrl()) if err != nil { return "", errorutils.CheckError(err) @@ -129,6 +139,6 @@ func getArtifactoryApiUrl(repoName string, details auth.ServiceDetails) (string, if password != "" { rtUrl.User = url.UserPassword(username, password) } - rtUrl.Path += "api/go/" + repoName - return rtUrl.String(), nil + + return goProxyParams.BuildUrl(rtUrl, repoName), nil } diff --git a/utils/golang/utils_test.go b/utils/golang/utils_test.go index c2326b21b..e6ec32041 100644 --- a/utils/golang/utils_test.go +++ b/utils/golang/utils_test.go @@ -4,6 +4,8 @@ import ( "github.com/jfrog/jfrog-cli-core/v2/utils/config" "github.com/jfrog/jfrog-client-go/artifactory/auth" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "net/url" "testing" ) @@ -13,7 +15,7 @@ func TestGetArtifactoryRemoteRepoUrl(t *testing.T) { AccessToken: "eyJ0eXAiOiJKV1QifQ.eyJzdWIiOiJmYWtlXC91c2Vyc1wvdGVzdCJ9.MTIzNDU2Nzg5MA", } repoName := "test-repo" - repoUrl, err := GetArtifactoryRemoteRepoUrl(server, repoName) + repoUrl, err := GetArtifactoryRemoteRepoUrl(server, repoName, GoProxyUrlParams{}) assert.NoError(t, err) assert.Equal(t, "https://test:eyJ0eXAiOiJKV1QifQ.eyJzdWIiOiJmYWtlXC91c2Vyc1wvdGVzdCJ9.MTIzNDU2Nzg5MA@server.com/artifactory/api/go/test-repo", repoUrl) } @@ -25,15 +27,22 @@ func TestGetArtifactoryApiUrl(t *testing.T) { // Test username and password details.SetUser("frog") details.SetPassword("passfrog") - url, err := getArtifactoryApiUrl("test-repo", details) + url, err := getArtifactoryApiUrl("test-repo", details, GoProxyUrlParams{}) assert.NoError(t, err) assert.Equal(t, "https://frog:passfrog@test.com/artifactory/api/go/test-repo", url) + // Test username and password with EndpointPrefix and direct + details.SetUser("frog") + details.SetPassword("passfrog") + url, err = getArtifactoryApiUrl("test-repo", details, GoProxyUrlParams{EndpointPrefix: "test", Direct: true}) + assert.NoError(t, err) + assert.Equal(t, "https://frog:passfrog@test.com/artifactory/test/api/go/test-repo|direct", url) + // Test access token // Set fake access token with username "test" details.SetUser("") details.SetAccessToken("eyJ0eXAiOiJKV1QifQ.eyJzdWIiOiJmYWtlXC91c2Vyc1wvdGVzdCJ9.MTIzNDU2Nzg5MA") - url, err = getArtifactoryApiUrl("test-repo", details) + url, err = getArtifactoryApiUrl("test-repo", details, GoProxyUrlParams{}) assert.NoError(t, err) assert.Equal(t, "https://test:eyJ0eXAiOiJKV1QifQ.eyJzdWIiOiJmYWtlXC91c2Vyc1wvdGVzdCJ9.MTIzNDU2Nzg5MA@test.com/artifactory/api/go/test-repo", url) @@ -42,7 +51,46 @@ func TestGetArtifactoryApiUrl(t *testing.T) { // Expect username to be "frog" details.SetUser("frog") details.SetAccessToken("eyJ0eXAiOiJKV1QifQ.eyJzdWIiOiJmYWtlXC91c2Vyc1wvdGVzdCJ9.MTIzNDU2Nzg5MA") - url, err = getArtifactoryApiUrl("test-repo", details) + url, err = getArtifactoryApiUrl("test-repo", details, GoProxyUrlParams{}) assert.NoError(t, err) assert.Equal(t, "https://frog:eyJ0eXAiOiJKV1QifQ.eyJzdWIiOiJmYWtlXC91c2Vyc1wvdGVzdCJ9.MTIzNDU2Nzg5MA@test.com/artifactory/api/go/test-repo", url) } + +func TestGoProxyUrlParams_BuildUrl(t *testing.T) { + tests := []struct { + name string + RepoName string + Direct bool + EndpointPrefix string + ExpectedUrl string + }{ + { + name: "Url Without direct or Prefix", + RepoName: "go", + ExpectedUrl: "https://test/api/go/go", + }, + { + name: "Url With direct", + RepoName: "go", + Direct: true, + ExpectedUrl: "https://test/api/go/go|direct", + }, + { + name: "Url With Prefix", + RepoName: "go", + EndpointPrefix: "prefix", + ExpectedUrl: "https://test/prefix/api/go/go", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + remoteUrl, err := url.Parse("https://test") + require.NoError(t, err) + gdu := &GoProxyUrlParams{ + Direct: tt.Direct, + EndpointPrefix: tt.EndpointPrefix, + } + assert.Equalf(t, tt.ExpectedUrl, gdu.BuildUrl(remoteUrl, tt.RepoName), "BuildUrl(%v, %v)", remoteUrl, tt.RepoName) + }) + } +}