Skip to content

Commit

Permalink
fix(golangBuild): BOM creation failed with private Go modules (#4460)
Browse files Browse the repository at this point in the history
* quickly try to only specify base private repo URLs with git config

* fix the test

* refactoring of private modules

* test

* fix test

* fix url

* typo

* Adding gitConfiguration

* typo

* unit test

* unit test

---------

Co-authored-by: I557621 <[email protected]>
Co-authored-by: aibaend1 <[email protected]>
Co-authored-by: asadu <[email protected]>
  • Loading branch information
4 people authored Aug 14, 2023
1 parent 9189ab3 commit d01c161
Show file tree
Hide file tree
Showing 2 changed files with 75 additions and 133 deletions.
66 changes: 18 additions & 48 deletions cmd/golangBuild.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import (
"os"
"path"
"path/filepath"
"regexp"
"strings"

"github.com/SAP/jenkins-library/pkg/buildsettings"
Expand All @@ -24,7 +23,6 @@ import (
"github.com/SAP/jenkins-library/pkg/versioning"

"golang.org/x/mod/modfile"
"golang.org/x/mod/module"
)

const (
Expand Down Expand Up @@ -341,26 +339,11 @@ func prepareGolangEnvironment(config *golangBuildOptions, goModFile *modfile.Fil
// pass private repos to go process
os.Setenv("GOPRIVATE", config.PrivateModules)

repoURLs, err := lookupGolangPrivateModulesRepositories(goModFile, config.PrivateModules, utils)

err = gitConfigurationForPrivateModules(config.PrivateModules, config.PrivateModulesGitToken, utils)
if err != nil {
return err
}

// configure credentials git shall use for pulling repos
for _, repoURL := range repoURLs {
if match, _ := regexp.MatchString("(?i)^https?://", repoURL); !match {
continue
}

authenticatedRepoURL := strings.Replace(repoURL, "://", fmt.Sprintf("://%s@", config.PrivateModulesGitToken), 1)

err = utils.RunExecutable("git", "config", "--global", fmt.Sprintf("url.%s.insteadOf", authenticatedRepoURL), repoURL)
if err != nil {
return err
}
}

return nil
}

Expand Down Expand Up @@ -539,36 +522,6 @@ func runGolangBuildPerArchitecture(config *golangBuildOptions, goModFile *modfil
return binaryNames, nil
}

// lookupPrivateModulesRepositories returns a slice of all modules that match the given glob pattern
func lookupGolangPrivateModulesRepositories(goModFile *modfile.File, globPattern string, utils golangBuildUtils) ([]string, error) {
if globPattern == "" {
return []string{}, nil
}

if goModFile == nil {
return nil, fmt.Errorf("couldn't find go.mod file")
} else if goModFile.Require == nil {
return []string{}, nil // no modules referenced, nothing to do
}

privateModules := []string{}

for _, goModule := range goModFile.Require {
if !module.MatchPrefixPatterns(globPattern, goModule.Mod.Path) {
continue
}

repo, err := utils.GetRepositoryURL(goModule.Mod.Path)

if err != nil {
return nil, err
}

privateModules = append(privateModules, repo)
}
return privateModules, nil
}

func runBOMCreation(utils golangBuildUtils, outputFilename string) error {

if err := utils.RunExecutable("cyclonedx-gomod", "mod", "-licenses", fmt.Sprintf("-verbose=%t", GeneralConfig.Verbose), "-test", "-output", outputFilename, "-output-version", "1.4"); err != nil {
Expand Down Expand Up @@ -631,3 +584,20 @@ func isMainPackage(utils golangBuildUtils, pkg string) (bool, error) {

return true, nil
}

func gitConfigurationForPrivateModules(privateMod string, token string, utils golangBuildUtils) error {
privateMod = strings.ReplaceAll(privateMod, "/*", "")
privateMod = strings.ReplaceAll(privateMod, "*.", "")
modules := strings.Split(privateMod, ",")
for _, v := range modules {
authenticatedRepoURL := fmt.Sprintf("https://%s@%s", token, v)
repoBaseURL := fmt.Sprintf("https://%s", v)
err := utils.RunExecutable("git", "config", "--global", fmt.Sprintf("url.%s.insteadOf", authenticatedRepoURL), repoBaseURL)
if err != nil {
return err
}

}

return nil
}
142 changes: 57 additions & 85 deletions cmd/golangBuild_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -911,8 +911,7 @@ go 1.17`
expect: expectations{
envVars: []string{"GOPRIVATE=*.example.com"},
commandsExecuted: [][]string{
{"git", "config", "--global", "url.https://[email protected]/private/repo.git.insteadOf", "https://private1.example.com/private/repo.git"},
{"git", "config", "--global", "url.https://[email protected]/another/repo.git.insteadOf", "https://private2.example.com/another/repo.git"},
{"git", "config", "--global", "url.https://[email protected]", "https://example.com"},
},
},
},
Expand Down Expand Up @@ -943,89 +942,6 @@ go 1.17`
}
}

func TestLookupGolangPrivateModulesRepositories(t *testing.T) {
t.Parallel()

modTestFile := `
module private.example.com/m
require (
example.com/public/module v1.0.0
private1.example.com/private/repo v0.1.0
private2.example.com/another/repo v0.2.0
)
go 1.17`

type expectations struct {
repos []string
errorMessage string
}
tests := []struct {
name string
modFileContent string
globPattern string
expect expectations
}{
{
name: "Does nothing if glob pattern is empty",
modFileContent: modTestFile,
expect: expectations{
repos: []string{},
},
},
{
name: "Does nothing if there is no go.mod file",
globPattern: "private.example.com",
modFileContent: "",
expect: expectations{
repos: []string{},
},
},
{
name: "Detects all private repos using a glob pattern",
modFileContent: modTestFile,
globPattern: "*.example.com",
expect: expectations{
repos: []string{"https://private1.example.com/private/repo.git", "https://private2.example.com/another/repo.git"},
},
},
{
name: "Detects all private repos",
modFileContent: modTestFile,
globPattern: "private1.example.com,private2.example.com",
expect: expectations{
repos: []string{"https://private1.example.com/private/repo.git", "https://private2.example.com/another/repo.git"},
},
},
{
name: "Detects a dedicated repo",
modFileContent: modTestFile,
globPattern: "private2.example.com",
expect: expectations{
repos: []string{"https://private2.example.com/another/repo.git"},
},
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
utils := newGolangBuildTestsUtils()

goModFile, _ := modfile.Parse("", []byte(tt.modFileContent), nil)

repos, err := lookupGolangPrivateModulesRepositories(goModFile, tt.globPattern, utils)

if tt.expect.errorMessage == "" {
assert.NoError(t, err)
assert.Equal(t, tt.expect.repos, repos)
} else {
assert.EqualError(t, err, tt.expect.errorMessage)
}
})
}
}

func TestRunGolangciLint(t *testing.T) {
t.Parallel()

Expand Down Expand Up @@ -1153,3 +1069,59 @@ func TestRetrieveGolangciLint(t *testing.T) {
})
}
}

func Test_gitConfigurationForPrivateModules(t *testing.T) {
type args struct {
privateMod string
token string
}
type expectations struct {
commandsExecuted [][]string
}
tests := []struct {
name string
args args

expected expectations
}{
{
name: "with one private module",
args: args{
privateMod: "example.com/*",
token: "mytoken",
},
expected: expectations{
commandsExecuted: [][]string{
{"git", "config", "--global", "url.https://[email protected]", "https://example.com"},
},
},
},
{
name: "with multiple private modules",
args: args{
privateMod: "example.com/*,test.com/*",
token: "mytoken",
},
expected: expectations{
commandsExecuted: [][]string{
{"git", "config", "--global", "url.https://[email protected]", "https://example.com"},
{"git", "config", "--global", "url.https://[email protected]", "https://test.com"},
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
utils := newGolangBuildTestsUtils()

err := gitConfigurationForPrivateModules(tt.args.privateMod, tt.args.token, utils)

if assert.NoError(t, err) {
for i, expectedCommand := range tt.expected.commandsExecuted {
assert.Equal(t, expectedCommand[0], utils.Calls[i].Exec)
assert.Equal(t, expectedCommand[1:], utils.Calls[i].Params)
}
}
})
}
}

0 comments on commit d01c161

Please sign in to comment.